Skip to content

Commit 87bd76a

Browse files
committed
feat(panel): refactor panels, adds person details and adds links
1 parent 1b4bb1a commit 87bd76a

23 files changed

+838
-252
lines changed
+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<script lang="ts" setup>
2+
import { NTag } from 'naive-ui';
3+
import { type PropType, toRefs } from 'vue';
4+
5+
import type { TagLink } from '~/models/tag.model';
6+
7+
const props = defineProps({
8+
tag: {
9+
type: Object as PropType<TagLink>,
10+
required: true,
11+
},
12+
});
13+
14+
const { tag } = toRefs(props);
15+
16+
const emit = defineEmits<{
17+
(e: 'onClick', href?: string): void;
18+
}>();
19+
20+
const onClick = (e: MouseEvent) => {
21+
if (!tag?.value?.url) return;
22+
e.preventDefault();
23+
e.stopPropagation();
24+
emit('onClick', tag?.value?.url);
25+
};
26+
</script>
27+
28+
<template>
29+
<a :href="tag?.url" @click="onClick">
30+
<NTag
31+
class="tag"
32+
:class="{ meta: tag?.meta, link: !!tag?.url }"
33+
size="small"
34+
v-bind="tag"
35+
>
36+
<span class="label">{{ tag?.label }}</span>
37+
</NTag>
38+
</a>
39+
</template>
40+
41+
<style lang="scss" scoped>
42+
.tag {
43+
width: fit-content;
44+
}
45+
46+
.link {
47+
cursor: pointer;
48+
49+
.label {
50+
transition: filter 0.3s var(--n-bezier);
51+
}
52+
53+
&:hover {
54+
background-color: color-mix(
55+
in srgb,
56+
var(--n-close-icon-color-hover),
57+
transparent 90%
58+
);
59+
60+
.label {
61+
filter: saturate(1.5);
62+
}
63+
}
64+
}
65+
</style>

src/components/common/buttons/TitleLink.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const onTitleClick = (e: MouseEvent) => {
2626

2727
<template>
2828
<a class="anchor-link" @click="onTitleClick">
29-
<component :is="component" class="hover-link">
29+
<component :is="component" :class="{ 'hover-link': !!$attrs.href }">
3030
<slot />
3131
</component>
3232
</a>

src/components/common/list/ListItemPanel.vue

+3-38
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
import { computed, defineProps, type PropType, ref, toRefs } from 'vue';
1313
1414
import PosterPlaceholder from '~/assets/images/poster-placholder.webp';
15+
import TagLink from '~/components/common/buttons/TagLink.vue';
1516
import { type ListScrollItem } from '~/models/list-scroll.model';
1617
1718
import { useShowStore } from '~/stores/data/show.store';
@@ -92,9 +93,7 @@ const tooltipOptions = computed<PopoverProps>(() => ({
9293
}));
9394
9495
const { openTab } = useExtensionSettingsStore();
95-
const onTagClick = (e: MouseEvent, url?: string) => {
96-
e.preventDefault();
97-
e.stopPropagation();
96+
const onTagClick = (url?: string) => {
9897
if (!url) return;
9998
openTab(url);
10099
};
@@ -128,17 +127,7 @@ const onTagClick = (e: MouseEvent, url?: string) => {
128127
<NFlex v-if="date || tags?.length" size="medium" class="tags">
129128
<template v-for="(tag, i) of tags" :key="`${i}-${tag.label}`">
130129
<NSkeleton v-if="loading" text style="width: 6%" />
131-
<a v-else :href="tag.url" @click="e => onTagClick(e, tag.url)">
132-
<NTag
133-
class="tag"
134-
:class="{ meta: tag.meta, link: !!tag.url }"
135-
size="small"
136-
:bordered="tag.bordered ?? false"
137-
v-bind="tag"
138-
>
139-
<span class="label">{{ tag.label }}</span>
140-
</NTag>
141-
</a>
130+
<TagLink :tag="tag" @on-click="onTagClick" />
142131
</template>
143132
<template v-if="date">
144133
<NSkeleton v-if="loading" text style="width: 6%" />
@@ -223,30 +212,6 @@ const onTagClick = (e: MouseEvent, url?: string) => {
223212
.tags {
224213
gap: 0.5rem !important;
225214
margin-top: 0.3rem;
226-
227-
.tag {
228-
width: fit-content;
229-
}
230-
231-
.link {
232-
cursor: pointer;
233-
234-
.label {
235-
transition: filter 0.3s var(--n-bezier);
236-
}
237-
238-
&:hover {
239-
background-color: color-mix(
240-
in srgb,
241-
var(--n-close-icon-color-hover),
242-
transparent 90%
243-
);
244-
245-
.label {
246-
filter: saturate(1.5);
247-
}
248-
}
249-
}
250215
}
251216
252217
.panel-progress {

src/components/common/list/use-list-scroll.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export const getTags = (item: Pick<ListScrollSourceItem, 'episode' | 'season'>,
106106
if (premiere) {
107107
tags.push({
108108
label: premiere,
109-
i18n: ['common', 'tag', 'label'],
109+
i18n: ['common', 'tag'],
110110
type: premiere === 'season' ? 'info' : 'primary',
111111
bordered: true,
112112
});

src/components/views/panel/MoviePanel.vue

+7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import { computed, onMounted, onUnmounted, ref, toRefs, watch } from 'vue';
55
import type { TraktMovieExtended } from '~/models/trakt/trakt-movie.model';
66
77
import TitleLink from '~/components/common/buttons/TitleLink.vue';
8+
import MoviePanelDetails from '~/components/views/panel/MoviePanelDetails.vue';
9+
import MoviePanelOverview from '~/components/views/panel/MoviePanelOverview.vue';
810
import PanelPoster from '~/components/views/panel/PanelPoster.vue';
11+
912
import { ResolveExternalLinks } from '~/settings/external.links';
1013
import { useMovieStore } from '~/stores/data/movie.store';
1114
import { useExtensionSettingsStore } from '~/stores/settings/extension.store';
@@ -68,6 +71,10 @@ const { openTab } = useExtensionSettingsStore();
6871
<NSkeleton v-else class="show-title-skeleton" style="width: 50dvh" round />
6972

7073
<PanelPoster :tmdb="movie?.ids.tmdb" mode="movie" />
74+
75+
<MoviePanelDetails :movie="movie" />
76+
77+
<MoviePanelOverview :movie="movie" />
7178
</NFlex>
7279
</template>
7380

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<script lang="ts" setup>
2+
import { NFlex } from 'naive-ui';
3+
import { computed, type PropType, toRefs } from 'vue';
4+
5+
import type { TraktMovieExtended } from '~/models/trakt/trakt-movie.model';
6+
7+
import PanelDetail from '~/components/views/panel/PanelDetail.vue';
8+
9+
import PanelLinks from '~/components/views/panel/PanelLinks.vue';
10+
import { useI18n } from '~/utils';
11+
import { capitalizeEachWord } from '~/utils/string.utils';
12+
13+
const props = defineProps({
14+
movie: {
15+
type: Object as PropType<TraktMovieExtended>,
16+
required: false,
17+
},
18+
});
19+
20+
const { movie } = toRefs(props);
21+
22+
const i18n = useI18n('panel', 'detail');
23+
24+
const released = computed(() => {
25+
if (!movie?.value) return;
26+
if (!movie.value?.released) return '-';
27+
return new Date(movie.value?.released).toLocaleDateString();
28+
});
29+
30+
const runtime = computed(() => {
31+
if (!movie?.value) return;
32+
if (!movie.value?.runtime) return '-';
33+
return `${movie.value.runtime} min`;
34+
});
35+
36+
const genres = computed(() => {
37+
if (!movie?.value) return;
38+
return movie.value?.genres?.map(g => ({ label: capitalizeEachWord(g) })) ?? [];
39+
});
40+
41+
const year = computed(() => {
42+
if (!movie?.value) return;
43+
return movie.value?.year ?? '-';
44+
});
45+
46+
const status = computed(() => {
47+
if (!movie?.value) return;
48+
return capitalizeEachWord(movie.value?.status) ?? '-';
49+
});
50+
51+
const country = computed(() => {
52+
if (!movie?.value) return;
53+
return movie.value?.country ?? '-';
54+
});
55+
</script>
56+
57+
<template>
58+
<NFlex size="large" class="container" vertical>
59+
<NFlex class="row" size="large">
60+
<!-- Year -->
61+
<PanelDetail :label="i18n('year')" :value="year" :skeleton="{ width: '2.25rem' }" />
62+
63+
<!-- Country -->
64+
<PanelDetail
65+
:label="i18n('country')"
66+
:value="country"
67+
:skeleton="{ width: '2ch' }"
68+
/>
69+
70+
<!-- Status -->
71+
<PanelDetail
72+
:label="i18n('status')"
73+
:value="status"
74+
:skeleton="{ width: '7.5rem' }"
75+
/>
76+
</NFlex>
77+
78+
<NFlex class="row" size="large">
79+
<!-- Release date -->
80+
<PanelDetail
81+
:label="i18n('released')"
82+
:value="released"
83+
:skeleton="{ width: '5.125rem' }"
84+
/>
85+
86+
<!-- Runtime -->
87+
<PanelDetail
88+
:label="i18n('runtime')"
89+
:value="runtime"
90+
:skeleton="{ width: '3.75rem' }"
91+
/>
92+
</NFlex>
93+
94+
<!-- Genres -->
95+
<PanelDetail
96+
:label="i18n('genres')"
97+
:values="genres"
98+
:skeleton="{ width: '3rem' }"
99+
array
100+
/>
101+
102+
<!-- links -->
103+
<PanelLinks :ids="movie?.ids" mode="movie" />
104+
</NFlex>
105+
</template>
106+
107+
<style lang="scss" scoped>
108+
.container,
109+
.row {
110+
flex: 1 1 auto;
111+
width: 100%;
112+
}
113+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<script setup lang="ts">
2+
import { computed, type PropType, toRefs } from 'vue';
3+
4+
import type { TraktMovieExtended } from '~/models/trakt/trakt-movie.model';
5+
6+
import PanelOverview from '~/components/views/panel/PanelOverview.vue';
7+
import { deCapitalise } from '~/utils/string.utils';
8+
9+
const props = defineProps({
10+
movie: {
11+
type: Object as PropType<TraktMovieExtended>,
12+
required: false,
13+
},
14+
});
15+
16+
const { movie } = toRefs(props);
17+
18+
const title = computed(() => {
19+
if (!movie?.value) return;
20+
if (!movie.value?.tagline) return '-';
21+
return deCapitalise(movie.value?.tagline);
22+
});
23+
24+
const overview = computed(() => {
25+
if (!movie?.value) return;
26+
return movie?.value?.overview ?? '-';
27+
});
28+
</script>
29+
30+
<template>
31+
<PanelOverview :title="title" :overview="overview" />
32+
</template>

0 commit comments

Comments
 (0)