Skip to content

Commit b8b00e4

Browse files
committed
feat(progress): adds hover tooltip
1 parent 1b93f2b commit b8b00e4

File tree

5 files changed

+147
-59
lines changed

5 files changed

+147
-59
lines changed

src/components/common/list/ListItemPanel.vue

+124-53
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
<script setup lang="ts">
2-
import { NEllipsis, NFlex, NProgress, NSkeleton, NTag } from 'naive-ui';
3-
4-
import { computed, defineProps, type PropType, toRefs } from 'vue';
2+
import {
3+
NEllipsis,
4+
NFlex,
5+
NProgress,
6+
NSkeleton,
7+
NTag,
8+
NTooltip,
9+
type PopoverProps,
10+
} from 'naive-ui';
11+
12+
import { computed, defineProps, type PropType, ref, toRefs } from 'vue';
513
614
import PosterPlaceholder from '~/assets/images/poster-placholder.webp';
715
import { type ListScrollItem } from '~/models/list-scroll.model';
@@ -10,7 +18,7 @@ import { useShowStore } from '~/stores/data/show.store';
1018
import { useI18n } from '~/utils';
1119
import { deCapitalise } from '~/utils/string.utils';
1220
13-
const i18n = useI18n('list-item-panel');
21+
const i18n = useI18n('list', 'item', 'panel');
1422
1523
const props = defineProps({
1624
item: {
@@ -74,6 +82,13 @@ const progress = computed(() => {
7482
if (!id) return;
7583
return getShowProgress(id).value;
7684
});
85+
86+
const innerContainer = ref();
87+
const tooltipOptions = computed<PopoverProps>(() => ({
88+
to: innerContainer.value,
89+
showArrow: false,
90+
delay: 500,
91+
}));
7792
</script>
7893

7994
<template>
@@ -84,59 +99,95 @@ const progress = computed(() => {
8499
size="small"
85100
:theme-overrides="{ gapSmall: '0' }"
86101
>
87-
<div class="meta type">
88-
<NSkeleton v-if="loading" text style="width: 10%" round />
89-
<NEllipsis v-else :line-clamp="1">{{ type }}</NEllipsis>
90-
</div>
91-
<div class="title">
92-
<NSkeleton v-if="loading" text style="width: 70%" round />
93-
<NEllipsis v-else :line-clamp="2">{{ title }}</NEllipsis>
94-
</div>
95-
<div class="content">
96-
<NSkeleton v-if="loading" text style="width: 60%" round />
97-
<NEllipsis v-else :line-clamp="2">{{ content }}</NEllipsis>
98-
</div>
99-
<NFlex v-if="date || tags?.length" size="medium" class="tags">
100-
<template v-for="tag of tags" :key="tag.label">
101-
<NSkeleton v-if="loading" text style="width: 6%" />
102-
<NTag
103-
v-else
104-
class="tag"
105-
:class="{ meta: tag.meta }"
106-
size="small"
107-
:type="tag.type"
108-
:bordered="tag.bordered ?? false"
102+
<div ref="innerContainer">
103+
<div class="meta type">
104+
<NSkeleton v-if="loading" text style="width: 10%" round />
105+
<NEllipsis v-else :line-clamp="1" :tooltip="tooltipOptions">{{ type }}</NEllipsis>
106+
</div>
107+
<div class="title">
108+
<NSkeleton v-if="loading" text style="width: 70%" round />
109+
<NEllipsis v-else :line-clamp="2" :tooltip="tooltipOptions">{{
110+
title
111+
}}</NEllipsis>
112+
</div>
113+
<div class="content">
114+
<NSkeleton v-if="loading" text style="width: 60%" round />
115+
<NEllipsis v-else :line-clamp="1" :tooltip="tooltipOptions">{{
116+
content
117+
}}</NEllipsis>
118+
</div>
119+
<NFlex v-if="date || tags?.length" size="medium" class="tags">
120+
<template v-for="tag of tags" :key="tag.label">
121+
<NSkeleton v-if="loading" text style="width: 6%" />
122+
<NTag
123+
v-else
124+
class="tag"
125+
:class="{ meta: tag.meta }"
126+
size="small"
127+
:type="tag.type"
128+
:bordered="tag.bordered ?? false"
129+
>
130+
{{ tag.label }}
131+
</NTag>
132+
</template>
133+
<template v-if="date">
134+
<NSkeleton v-if="loading" text style="width: 6%" />
135+
<NTag v-else class="tag" size="small" type="default" :bordered="false">
136+
{{ date }}
137+
</NTag>
138+
</template>
139+
</NFlex>
140+
<div v-if="showProgress" class="panel-progress">
141+
<NTooltip
142+
class="panel-progress-tooltip"
143+
:disabled="!progress"
144+
placement="top-end"
145+
:delay="100"
146+
:to="innerContainer"
109147
>
110-
{{ tag.label }}
111-
</NTag>
112-
</template>
113-
<template v-if="date">
114-
<NSkeleton v-if="loading" text style="width: 6%" />
115-
<NTag v-else class="tag" size="small" type="default" :bordered="false">
116-
{{ date }}
117-
</NTag>
118-
</template>
119-
</NFlex>
120-
<NProgress
121-
v-if="showProgress"
122-
:data-percentage="progress?.percentage"
123-
:data-last="progress?.last_episode"
124-
:data-next="progress?.next_episode"
125-
class="progress"
126-
:theme-overrides="{
127-
railHeight: 'var(--rail-height)',
128-
}"
129-
:percentage="progress?.percentage ?? 0"
130-
:show-indicator="false"
131-
color="var(--trakt-red-dark)"
132-
/>
148+
<NFlex v-if="progress" vertical align="flex-end">
149+
<div>
150+
<span class="metric">{{ progress?.completed }}</span>
151+
<span> / </span>
152+
<span class="metric">{{ progress?.total }}</span>
153+
<span>&nbsp;</span>
154+
<span>{{ i18n('tooltip_episodes') }}</span>
155+
</div>
156+
<div>
157+
<span class="metric">{{ Math.round(progress?.percentage) }}</span>
158+
<span>%</span>
159+
<span>&nbsp;</span>
160+
<span>{{ i18n('tooltip_watched') }}</span>
161+
</div>
162+
<div>
163+
<span class="metric">{{ progress?.total - progress?.completed }}</span>
164+
<span>&nbsp;</span>
165+
<span>{{ i18n('tooltip_remaining') }}</span>
166+
</div>
167+
</NFlex>
168+
<template #trigger>
169+
<NProgress
170+
class="line"
171+
:data-show-id="progress?.id"
172+
:data-percentage="progress?.percentage"
173+
:theme-overrides="{
174+
railHeight: 'var(--rail-height)',
175+
}"
176+
:percentage="progress?.percentage ?? 0"
177+
:show-indicator="false"
178+
color="var(--trakt-red-dark)"
179+
/>
180+
</template>
181+
</NTooltip>
182+
</div>
183+
</div>
133184
</NFlex>
134185
</template>
135186

136187
<style lang="scss" scoped>
137188
.panel {
138189
flex: 1 1 auto;
139-
margin: 0.25rem 0;
190+
margin: 0 0 0.25rem;
140191
141192
.title {
142193
margin-top: 0.1rem;
@@ -168,10 +219,30 @@ const progress = computed(() => {
168219
}
169220
}
170221
171-
.progress {
172-
margin-top: 0.75rem;
173-
222+
.panel-progress {
174223
--rail-height: 4px;
224+
225+
padding-top: 0.75rem;
226+
227+
&:hover {
228+
--trakt-red-dark: var(--trakt-red);
229+
}
230+
231+
&-tooltip {
232+
font-size: 0.8rem;
233+
234+
.metric {
235+
color: var(--vt-c-white);
236+
font-weight: bolder;
237+
font-variant-numeric: tabular-nums;
238+
}
239+
}
175240
}
176241
}
177242
</style>
243+
244+
<style lang="scss">
245+
.panel-progress-tooltip.n-tooltip.n-tooltip {
246+
background: var(--bg-color-80);
247+
}
248+
</style>

src/components/container/ContainerComponent.ce.vue

+2-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ const root = ref<HTMLElement>();
114114
}
115115
}
116116
117-
.n-tooltip.n-tooltip {
117+
.n-tooltip.n-tooltip,
118+
.n-popover-arrow.n-popover-arrow.n-popover-arrow {
118119
background: var(--bg-color-60);
119120
backdrop-filter: blur(var(--bg-blur));
120121

src/i18n/en/list/list.json

+12
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,17 @@
2222
"list__item__placeholder_empty": {
2323
"message": "Nothing on this day.",
2424
"description": "Empty placeholder for list item."
25+
},
26+
"list__item__panel__tooltip_episodes": {
27+
"message": "episodes",
28+
"description": "Tooltip for episodes watched in list item panel."
29+
},
30+
"list__item__panel__tooltip_watched": {
31+
"message": "watched",
32+
"description": "Tooltip for watched percentages in list item panel."
33+
},
34+
"list__item__panel__tooltip_remaining": {
35+
"message": "remaining",
36+
"description": "Tooltip for remaining episodes in list item panel."
2537
}
2638
}

src/models/list-scroll.model.ts

+3
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,13 @@ export const ListScrollItemProgressType = {
5757
} as const;
5858

5959
export type ListScrollItemProgress = BaseTraktProgress & {
60+
id: string | number;
6061
type: (typeof ListScrollItemProgressType)[keyof typeof ListScrollItemProgressType];
6162
date: Date;
6263
seasons: ListScrollItemProgressSeason[];
6364
percentage: number;
65+
completed: number;
66+
total: number;
6467
};
6568

6669
export const ListScrollItemType = {

src/stores/data/show.store.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,27 @@ type ShowDictionary = Record<string, TraktShowExtended>;
1414
type ShowProgressDictionary = Record<string, TraktWatchedProgress>;
1515
type LoadingDictionary = Record<string, boolean>;
1616

17-
const watchProgressToListProgress = (progress: TraktWatchedProgress): ListScrollItemProgress => {
17+
const watchProgressToListProgress = (progress: TraktWatchedProgress, id: string | number): ListScrollItemProgress => {
1818
let total = 0;
19-
let watched = 0;
19+
let completed = 0;
2020
const item = {
21+
id,
2122
...progress,
2223
type: ListScrollItemProgressType.watched,
2324
date: new Date(progress.last_watched_at),
2425
seasons: progress.seasons.map(season => ({
2526
...season,
2627
episodes: season.episodes.map(episode => {
2728
total += 1;
28-
if (episode.completed) watched += 1;
29+
if (episode.completed) completed += 1;
2930
return {
3031
...episode,
3132
date: new Date(episode.last_watched_at),
3233
};
3334
}),
3435
})),
3536
};
36-
return { ...item, percentage: (watched / total) * 100 };
37+
return { ...item, percentage: (completed / total) * 100, total, completed };
3738
};
3839

3940
export const useShowStore = defineStore('data.show', () => {
@@ -87,7 +88,7 @@ export const useShowStore = defineStore('data.show', () => {
8788
return computed(() => {
8889
const progress = showsProgress[id.toString()];
8990
if (!progress) return undefined;
90-
return watchProgressToListProgress(progress);
91+
return watchProgressToListProgress(progress, id);
9192
});
9293
};
9394

0 commit comments

Comments
 (0)