Skip to content

Commit 4853eb0

Browse files
committed
feat(list): adds hover button support
1 parent 26b2f6f commit 4853eb0

File tree

2 files changed

+57
-6
lines changed

2 files changed

+57
-6
lines changed

src/components/common/list/ListItem.vue

+54-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import { NEmpty, NFlex, NSkeleton, NTime, NTimelineItem } from 'naive-ui';
2+
import { NButtonGroup, NEmpty, NFlex, NSkeleton, NTime, NTimelineItem } from 'naive-ui';
33
44
import {
55
computed,
@@ -115,8 +115,13 @@ const i18n = useI18n('list', 'item');
115115
const { item, noHeader, nextHasHeader, poster, hideDate, scrollIntoView, height, color } =
116116
toRefs(props);
117117
118-
const onHover = (_hover: boolean) => {
118+
const itemRef = ref<HTMLElement & InstanceType<typeof NTimelineItem>>();
119+
120+
const open = ref(false);
121+
const onHover = (_event: MouseEvent, _hover: boolean) => {
119122
emit('onHover', { item: item?.value, hover: _hover });
123+
itemRef?.value?.$el?.focus();
124+
open.value = _hover && (_event.altKey || _event.ctrlKey);
120125
};
121126
122127
const noHead = computed(
@@ -135,8 +140,6 @@ const year = new Date().getFullYear();
135140
const sameYear = computed(() => date.value?.getFullYear() === year);
136141
const loading = computed(() => item?.value?.loading);
137142
138-
const itemRef = ref<HTMLElement & InstanceType<typeof NTimelineItem>>();
139-
140143
onMounted(() => {
141144
if (!scrollIntoView.value) return;
142145
emit('onScrollIntoView', {
@@ -165,6 +168,7 @@ const onClick = (e: MouseEvent | KeyboardEvent) =>
165168
<NTimelineItem
166169
ref="itemRef"
167170
:key="item.key"
171+
tabindex="0"
168172
class="timeline-item"
169173
:class="{
170174
'no-header': noHead,
@@ -182,8 +186,11 @@ const onClick = (e: MouseEvent | KeyboardEvent) =>
182186
:line-type="loading ? 'dashed' : lineType"
183187
:color="loading ? 'grey' : _color"
184188
@click="onClick"
185-
@mouseenter="onHover(true)"
186-
@mouseleave="onHover(false)"
189+
@keydown.enter="onClick"
190+
@keydown.alt="open = !open"
191+
@keydown.ctrl="open = !open"
192+
@mouseenter="e => onHover(e, true)"
193+
@mouseleave="e => onHover(e, false)"
187194
>
188195
<template #icon>
189196
<slot name="tag" />
@@ -198,6 +205,7 @@ const onClick = (e: MouseEvent | KeyboardEvent) =>
198205
size="small"
199206
:theme-overrides="{ gapSmall: '0' }"
200207
:wrap="false"
208+
align="center"
201209
>
202210
<NFlex
203211
v-if="!hideDate"
@@ -250,6 +258,14 @@ const onClick = (e: MouseEvent | KeyboardEvent) =>
250258
>
251259
<slot :item="item" :loading="loading" />
252260
</ListItemPanel>
261+
<NButtonGroup
262+
v-if="$slots.buttons"
263+
class="tile-buttons"
264+
:class="{ open }"
265+
vertical
266+
>
267+
<slot name="buttons" :open="open" :item="item" />
268+
</NButtonGroup>
253269
</NFlex>
254270
</NFlex>
255271
</template>
@@ -268,6 +284,7 @@ const onClick = (e: MouseEvent | KeyboardEvent) =>
268284
.timeline-item {
269285
height: var(--list-item-height, 145px);
270286
margin: 0 1rem;
287+
outline: none;
271288
272289
&.show-date {
273290
margin-left: 4rem;
@@ -286,19 +303,49 @@ const onClick = (e: MouseEvent | KeyboardEvent) =>
286303
flex: 1 1 auto;
287304
margin: 0.25rem 0;
288305
padding: 0.3rem 0.25rem 0.25rem;
306+
overflow: hidden;
289307
border-radius: 0.75rem;
290308
291309
&:active {
292310
background-color: var(--bg-color-30);
293311
box-shadow: var(--inset-box-shadow);
294312
}
313+
314+
&-buttons {
315+
position: absolute;
316+
right: 0;
317+
z-index: layers.$in-front;
318+
transition: translate 0.25s var(--n-bezier);
319+
translate: 150%;
320+
321+
&:focus-within,
322+
&.open {
323+
translate: 0;
324+
display: flex;
325+
}
326+
327+
:first-child {
328+
border-top-right-radius: 0.75rem;
329+
}
330+
331+
:last-child {
332+
border-bottom-right-radius: 0.75rem;
333+
}
334+
}
295335
}
296336
297337
.placeholder {
298338
flex: 1 1 auto;
299339
}
300340
}
301341
342+
&:focus-visible .content .tile {
343+
background-color: var(--bg-color-20);
344+
// stylelint-disable-next-line property-no-vendor-prefix -- necessary for safari
345+
-webkit-backdrop-filter: var(--bg-blur-hover);
346+
backdrop-filter: var(--bg-blur-hover);
347+
}
348+
302349
.header {
303350
position: absolute;
304351
top: 0;
@@ -311,6 +358,7 @@ const onClick = (e: MouseEvent | KeyboardEvent) =>
311358
transition:
312359
color 0.2s var(--n-bezier),
313360
border 0.2s var(--n-bezier);
361+
pointer-events: none;
314362
315363
&.today {
316364
color: var(--color-warning);

src/components/common/list/ListScroll.vue

+3
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,9 @@ const listPaddingBottom = computed(() => {
323323
@on-scroll-out-of-view="(...args) => $emit('onScrollOutOfView', ...args)"
324324
>
325325
<slot :item="item" :loading="item.loading" />
326+
<template v-if="$slots.buttons" #buttons="{ open }">
327+
<slot name="buttons" :item="item" :loading="item.loading" :open="open" />
328+
</template>
326329
</ListItem>
327330
</template>
328331
</NVirtualList>

0 commit comments

Comments
 (0)