itube-specs 0.0.877 → 0.0.881

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/assets/icons/common/history.svg +1 -0
  2. package/assets/icons/common/library.svg +1 -0
  3. package/assets/icons/common/playlist.svg +1 -0
  4. package/assets/icons/common/trash-bin.svg +1 -1
  5. package/assets/scss/base/_hacks.scss +0 -2
  6. package/assets/scss/mixins/_surface.scss +2 -2
  7. package/assets/scss/themes/dark/_theme.scss +27 -1
  8. package/assets/scss/themes/light/_theme.scss +27 -1
  9. package/assets/scss/vars/_palette.scss +2 -0
  10. package/components/card/history.vue +357 -0
  11. package/components/card/playlist.vue +5 -12
  12. package/components/card/{video/preview.vue → preview.vue} +9 -9
  13. package/components/card/video/index.vue +1 -1
  14. package/components/grid/playlists.vue +28 -1
  15. package/components/profile/delete.vue +9 -41
  16. package/components/section/nav-links.vue +10 -6
  17. package/components/section/toolbar.vue +2 -2
  18. package/components/ui/confirm.vue +61 -0
  19. package/composables/__tests__/use-format-time-ago.test.ts +19 -0
  20. package/composables/use-format-time-ago.ts +15 -3
  21. package/composables/use-meta.ts +2 -2
  22. package/layouts/default.vue +1 -1
  23. package/lib/index.ts +1 -0
  24. package/lib/scheme/categories-navigation.ts +2 -2
  25. package/lib/scheme/history-periods.ts +31 -0
  26. package/lib/scheme/navigation-items.ts +6 -32
  27. package/lib/scheme/profile-navigation-items.ts +3 -3
  28. package/lib/scheme/sort-channels.ts +2 -2
  29. package/lib/scheme/sort-items-default.ts +2 -2
  30. package/lib/scheme/sort-models.ts +2 -2
  31. package/lib/scheme/sort-playlists.ts +2 -2
  32. package/package.json +2 -1
  33. package/pages/history.vue +314 -0
  34. package/pages/library.vue +6 -0
  35. package/runtime/utils/get-multiple-query.ts +2 -2
  36. package/runtime/utils/get-selected-query.ts +2 -2
  37. package/stubs/history.ts +65 -0
  38. package/types/index.d.ts +1 -0
  39. package/types/nav-link-item.d.ts +18 -0
  40. package/utils/history/__tests__/group-by-watched-day.test.ts +66 -0
  41. package/utils/history/group-by-watched-day.ts +53 -0
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"></path><path d="M3 3v5h5"></path><path d="M12 7v5l4 2"></path></svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 4m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v14a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path><path d="M9 4m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v14a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path><path d="M5 8h4"></path><path d="M9 16h4"></path><path d="M13.803 4.56l2.184 -.53c.562 -.135 1.133 .19 1.282 .732l3.695 13.418a1.02 1.02 0 0 1 -.634 1.219l-.133 .041l-2.184 .53c-.562 .135 -1.133 -.19 -1.282 -.732l-3.695 -13.418a1.02 1.02 0 0 1 .634 -1.219l.133 -.041z"></path><path d="M14 9l4 -1"></path><path d="M16 16l3.923 -.98"></path></svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 12H3"></path><path d="M16 6H3"></path><path d="M12 18H3"></path><path d="m16 12 5 3-5 3v-6Z"></path></svg>
@@ -1 +1 @@
1
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
1
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 11v6"/><path d="M14 11v6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
@@ -15,9 +15,7 @@ select:-webkit-autofill:focus {
15
15
  box-shadow: 0 0 0 1000px transparent inset !important;
16
16
  -webkit-text-fill-color: var(--input-autofill-text, #{$input-autofill-text}) !important;
17
17
  color: var(--input-autofill-text, #{$input-autofill-text}) !important;
18
- border: none !important;
19
18
  transition: background-color 5000s ease-in-out 0s !important;
20
- font-size: inherit !important;
21
19
  }
22
20
 
23
21
  /**
@@ -32,8 +32,8 @@
32
32
  // А вот опечатка в @include surface(...) иначе прошла бы молча, отсюда список.
33
33
  $surface-names: (overlay,);
34
34
  $surface-roles: (
35
- text: (--video-mini-card-title, --radio-text, --checkbox-text),
36
- text-muted: (--video-mini-card-channel, --video-mini-card-duration),
35
+ text: (--video-mini-card-title, --radio-text, --checkbox-text, --page-history-confirm-text),
36
+ text-muted: (--video-mini-card-channel, --video-mini-card-duration, --page-history-confirm-muted),
37
37
  control-bg: (--input-bg, --select-bg),
38
38
  control-compact-bg: (--input-s-bg,),
39
39
  control-bg--disabled: (--input-bg--disabled,),
@@ -204,7 +204,23 @@ $video-card-mini-title: $color-zinc-300;
204
204
  $video-card-mini-weight: 400;
205
205
  $video-card-user-text: $color-zinc-400;
206
206
  $video-card-user-bg: $color-zinc-700;
207
- $video-card-preview-loader-bg: linear-gradient(to right, $color-indigo-muted, $color-indigo-deep);
207
+ $card-preview-loader-bg: linear-gradient(to right, $color-indigo-muted, $color-indigo-deep);
208
+
209
+ // ─── History card ───
210
+ $history-card-bg: $color-zinc-900-40;
211
+ $history-card-wrapper-bg: $color-zinc-900-60;
212
+ $history-card-border: $color-zinc-800;
213
+ $history-card-border--hover: $color-zinc-700;
214
+ $history-card-poster-border: $color-zinc-700-50;
215
+ $history-card-title: $color-white;
216
+ $history-card-title--hover: $color-indigo-400;
217
+ $history-card-secondary: $color-zinc-500;
218
+ $history-card-user-text: $color-zinc-400;
219
+ $history-card-user-bg: $color-zinc-700;
220
+ $history-card-dropdown-icon: $color-zinc-500;
221
+ $history-card-dropdown-icon--hover: $color-white;
222
+ $history-card-progress-bg: $mono-white-50;
223
+ $history-card-progress-fill: $color-indigo-500;
208
224
 
209
225
  // ─── Video card dropdown ───
210
226
  $video-card-dropdown-border: $color-zinc-800;
@@ -932,6 +948,16 @@ $page-channels-border: $color-zinc-800;
932
948
  // ─── Page: channel ───
933
949
  $page-channel-border: $color-zinc-800;
934
950
 
951
+ // ─── Page: history ───
952
+ $page-history-group-title: $color-zinc-300;
953
+ $page-history-group-icon: $color-zinc-500;
954
+ $page-history-clear-icon: $color-zinc-400;
955
+ $page-history-group-count: $color-zinc-500;
956
+ $page-history-confirm-text: $color-zinc-300;
957
+ $page-history-confirm-muted: $color-zinc-500;
958
+ $page-history-confirm-link: $color-indigo-400;
959
+ $page-history-confirm-link--hover: $color-indigo-300;
960
+
935
961
  // ─── Document ───
936
962
  $document-header-border: $color-zinc-700;
937
963
  $document-section-bg: $color-night;
@@ -261,9 +261,25 @@ $video-card-mini-title: $color-gray-900;
261
261
  $video-card-mini-weight: 500;
262
262
  $video-card-user-text: $color-gray-700;
263
263
  $video-card-user-bg: $color-gray-300;
264
- $video-card-preview-loader-bg: linear-gradient(to right, $color-indigo-muted, $color-indigo-deep);
264
+ $card-preview-loader-bg: linear-gradient(to right, $color-indigo-muted, $color-indigo-deep);
265
265
  $video-card-dropdown-size--hover: 1.25;
266
266
 
267
+ // ─── History card ───
268
+ $history-card-bg: $color-gray-100;
269
+ $history-card-wrapper-bg: $color-gray-100;
270
+ $history-card-border: transparent;
271
+ $history-card-border--hover: transparent;
272
+ $history-card-poster-border: transparent;
273
+ $history-card-title: $color-gray-900;
274
+ $history-card-title--hover: $color-gray-900;
275
+ $history-card-secondary: $color-gray-600;
276
+ $history-card-user-text: $color-gray-700;
277
+ $history-card-user-bg: $color-gray-300;
278
+ $history-card-dropdown-icon: $color-gray-600;
279
+ $history-card-dropdown-icon--hover: $color-gray-900;
280
+ $history-card-progress-bg: $color-gray-300;
281
+ $history-card-progress-fill: $color-indigo-500;
282
+
267
283
  // ─── Video card dropdown ───
268
284
  $video-card-dropdown-border: transparent;
269
285
 
@@ -962,6 +978,16 @@ $page-channels-border: transparent;
962
978
  // ─── Page: channel ───
963
979
  $page-channel-border: transparent;
964
980
 
981
+ // ─── Page: history ───
982
+ $page-history-group-title: $color-gray-900;
983
+ $page-history-group-icon: $color-gray-500;
984
+ $page-history-clear-icon: $color-gray-400;
985
+ $page-history-group-count: $color-gray-500;
986
+ $page-history-confirm-text: $color-gray-900;
987
+ $page-history-confirm-muted: $color-gray-500;
988
+ $page-history-confirm-link: $color-indigo-400;
989
+ $page-history-confirm-link--hover: $color-indigo-300;
990
+
965
991
  // ─── Document ───
966
992
  $document-header-border: $color-gray-300;
967
993
  $document-section-bg: $color-gray-200;
@@ -5,7 +5,9 @@ $color-zinc-950-0: lab(2.51107% .242703 -.886115 / 0%); // zinc-950 / 0%
5
5
  $color-zinc-950-75: lab(2.51107% .242703 -.886115 / 75%); // zinc-950 / 75%
6
6
  $color-zinc-950-90: lab(2.51107% .242703 -.886115 / 90%); // zinc-950 / 90%
7
7
  $color-zinc-900: lab(8.30603% .618205 -2.16572); // #18181b
8
+ $color-zinc-900-60: lab(8.30603% .618205 -2.16572 / 60%); // zinc-900 / 60%
8
9
  $color-zinc-900-50: lab(8.30603% .618205 -2.16572 / 50%); // #18181b
10
+ $color-zinc-900-40: lab(8.30603% .618205 -2.16572 / 40%); // zinc-900 / 40%
9
11
  $color-zinc-900-95: lab(8.30603% .618205 -2.16572 / 95%); // #18181b
10
12
  $color-zinc-900-98: lab(8.30603% .618205 -2.16572 / 98%); // #18181b
11
13
  $color-zinc-850: lab(11.9084% 1.043 -2.7081); // #1f1f23
@@ -0,0 +1,357 @@
1
+ <!-- Карточка истории просмотров: постер с полосой прогресса, канал и время последнего просмотра -->
2
+ <template>
3
+ <div class="card-history">
4
+ <NuxtLink
5
+ :to="link"
6
+ class="card-history__poster-wrapper"
7
+ @mouseenter.passive="startPreviewByHover(previewId)"
8
+ @mouseleave.passive="stopPreviewByHover(previewId)"
9
+ @touchstart.passive="startPreviewByTouch(previewId)"
10
+ >
11
+ <UiImg
12
+ :src="currentPoster"
13
+ :urls="card.thumbUrls"
14
+ :loading="loading"
15
+ :priority="priority"
16
+ :sizes="`(max-width: ${CSS_BREAKPOINTS.lg}px) 180px, 480px`"
17
+ :alt="card.title"
18
+ class="card-history__poster"
19
+ width="480"
20
+ height="270"
21
+ @error="posterError = true"
22
+ />
23
+ <CardPreview
24
+ v-if="isPreviewShow && card.previewUrl"
25
+ :url="card.previewUrl"
26
+ />
27
+ <UiLabel
28
+ v-if="card.quality"
29
+ :text="`${card.quality}p`"
30
+ class="card-history__resolution"
31
+ theme="yellow"
32
+ />
33
+ <UiLabel
34
+ v-if="card.duration"
35
+ :text="duration"
36
+ class="card-history__duration"
37
+ theme="black"
38
+ />
39
+ <span
40
+ :style="{ '--card-history-progress': `${progress}%` }"
41
+ :aria-label="t('watched_percent', { percent: progress })"
42
+ :aria-valuenow="progress"
43
+ class="card-history__progress"
44
+ role="progressbar"
45
+ aria-valuemin="0"
46
+ aria-valuemax="100"
47
+ />
48
+ </NuxtLink>
49
+
50
+ <div class="card-history__wrapper">
51
+ <component
52
+ :is="channelComponent"
53
+ :to="channelLink"
54
+ class="card-history__logo-wrapper"
55
+ >
56
+ <UiImg
57
+ v-if="card.channelAvatar"
58
+ :src="card.channelAvatar"
59
+ :alt="card.channelName"
60
+ :loading="loading"
61
+ class="card-history__channel-logo"
62
+ width="32"
63
+ height="32"
64
+ sizes="32px"
65
+ />
66
+ <span
67
+ v-else
68
+ class="card-history__user"
69
+ >U</span>
70
+ </component>
71
+
72
+ <NuxtLink
73
+ :to="link"
74
+ :title="card.title"
75
+ class="card-history__title _truncate"
76
+ >{{ card.title }}
77
+ </NuxtLink>
78
+
79
+ <p class="card-history__meta">
80
+ <NuxtLink
81
+ v-if="card.channelName"
82
+ :to="channelLink"
83
+ class="card-history__channel _truncate"
84
+ >{{ card.channelName }}</NuxtLink>
85
+ <span class="card-history__watched">
86
+ <UiIcon
87
+ name="clock"
88
+ size="12"
89
+ />
90
+ {{ watchedAgo }}
91
+ </span>
92
+ </p>
93
+
94
+ <LazyCardVideoDropdown
95
+ v-if="dropdownMounted"
96
+ :card="videoCard"
97
+ class="card-history__dropdown"
98
+ show-remove
99
+ show-share
100
+ auto-open
101
+ @share="emit('share', videoCard)"
102
+ @remove="emit('remove', card)"
103
+ />
104
+ <button
105
+ v-else
106
+ :aria-label="t('open_menu')"
107
+ class="card-history__dropdown card-video-dropdown__trigger"
108
+ type="button"
109
+ @click="dropdownMounted = true"
110
+ >
111
+ <UiIcon
112
+ name="dots-vertical"
113
+ size="18"
114
+ />
115
+ </button>
116
+ </div>
117
+ </div>
118
+ </template>
119
+
120
+ <script setup lang="ts">
121
+ import type { HistoryStubCard } from '../../stubs/history';
122
+ import type { IVideoCard } from '../../types';
123
+ import { convertString, getDuration } from '../../runtime';
124
+ import { CSS_BREAKPOINTS } from '../../config/css-breakpoints';
125
+
126
+ const props = defineProps<{
127
+ card: HistoryStubCard
128
+ loading?: 'eager' | 'lazy'
129
+ priority?: boolean
130
+ }>();
131
+
132
+ const emit = defineEmits<{
133
+ (e: 'remove', card: HistoryStubCard): void
134
+ (e: 'share', card: IVideoCard): void
135
+ }>();
136
+
137
+ const { t, locale } = useI18n();
138
+
139
+ const { generateLink } = useGenerateLink();
140
+
141
+ const NuxtLinkComponent = resolveComponent('NuxtLink');
142
+
143
+ const channelComponent = computed(() => props.card.channelName ? NuxtLinkComponent : 'span');
144
+
145
+ const posterError = ref(false);
146
+
147
+ const currentPoster = computed(() => {
148
+ if (posterError.value || !props.card.thumbUrls) return '/img/placeholder.webp';
149
+ return undefined;
150
+ });
151
+
152
+ const previewId = useId();
153
+ const { activePreviewId, startPreviewByHover, stopPreviewByHover, startPreviewByTouch } = useVideoPreview();
154
+
155
+ const isPreviewShow = computed(() => activePreviewId.value === previewId);
156
+
157
+ const duration = computed(() => getDuration(props.card.duration));
158
+
159
+ const progress = computed(() => Math.min(100, Math.max(0, Math.round(props.card.progress))));
160
+
161
+ const watchedAgo = computed(() => useFormatTimeAgo(props.card.watchedAt, locale.value, true));
162
+
163
+ const link = computed(() => generateLink(`/videos/${props.card.id}`));
164
+
165
+ const channelLink = computed(() => props.card.channelName
166
+ ? generateLink(`/channels/${convertString().toSlug(props.card.channelName)}`)
167
+ : undefined);
168
+
169
+ /** Дропдаун и репорт работают с обычной карточкой видео — переиспользуем её контракт. */
170
+ const videoCard = computed<IVideoCard>(() => ({
171
+ guid: props.card.guid,
172
+ id: props.card.id,
173
+ title: props.card.title,
174
+ duration: props.card.duration,
175
+ quality: props.card.quality,
176
+ channelName: props.card.channelName,
177
+ channelAvatar: props.card.channelAvatar,
178
+ thumbUrls: props.card.thumbUrls,
179
+ }));
180
+
181
+ const dropdownMounted = ref(false);
182
+ </script>
183
+
184
+ <style lang="scss">
185
+ $inset-label: 8px;
186
+ $progress-height: 4px;
187
+
188
+ .card-history {
189
+ @include video-card;
190
+
191
+ row-gap: 0;
192
+ background-color: $history-card-bg;
193
+ border: 1px solid $history-card-border;
194
+ border-radius: $radius-xl;
195
+
196
+ @include to-br(xs) {
197
+ touch-action: manipulation;
198
+ }
199
+
200
+ @include hover(border-color, box-shadow) {
201
+ border-color: $history-card-border--hover;
202
+ box-shadow: $card-ring-shadow--hover;
203
+ }
204
+ }
205
+
206
+ .card-history__poster-wrapper {
207
+ @include video-card-poster;
208
+
209
+ border-radius: calc($radius-xl - 1px) calc($radius-xl - 1px) 0 0;
210
+ border-bottom: 1px solid $history-card-poster-border;
211
+ }
212
+
213
+ .card-history__poster {
214
+ width: 100%;
215
+ height: auto;
216
+ object-fit: cover;
217
+ aspect-ratio: 16/9;
218
+ }
219
+
220
+ .card-history__duration,
221
+ .card-history__resolution {
222
+ position: absolute;
223
+ }
224
+
225
+ .card-history__resolution {
226
+ top: $inset-label;
227
+ right: $inset-label;
228
+ }
229
+
230
+ .card-history__duration {
231
+ bottom: $inset-label + $progress-height;
232
+ left: $inset-label;
233
+ }
234
+
235
+ .card-history__progress {
236
+ position: absolute;
237
+ right: 0;
238
+ bottom: 0;
239
+ left: 0;
240
+ height: $progress-height;
241
+ background-color: $history-card-progress-bg;
242
+
243
+ &::after {
244
+ content: '';
245
+ display: block;
246
+ width: var(--card-history-progress, 0%);
247
+ height: 100%;
248
+ background-color: $history-card-progress-fill;
249
+ }
250
+ }
251
+
252
+ .card-history__wrapper {
253
+ display: grid;
254
+ column-gap: 8px;
255
+ grid-template-columns: 32px 1fr auto;
256
+ align-items: start;
257
+ padding: 12px;
258
+ color: $history-card-secondary;
259
+ background-color: $history-card-wrapper-bg;
260
+ border-radius: 0 0 calc($radius-xl - 1px) calc($radius-xl - 1px);
261
+ }
262
+
263
+ .card-history__logo-wrapper {
264
+ grid-row: 1/3;
265
+ grid-column: 1;
266
+ }
267
+
268
+ .card-history__channel-logo {
269
+ border-radius: $radius-full;
270
+ }
271
+
272
+ .card-history__user {
273
+ @include font-xs;
274
+
275
+ width: 32px;
276
+ height: 32px;
277
+ display: grid;
278
+ place-content: center;
279
+ font-weight: 700;
280
+ text-transform: uppercase;
281
+ color: $history-card-user-text;
282
+ background-color: $history-card-user-bg;
283
+ border-radius: $radius-full;
284
+ }
285
+
286
+ .card-history__title {
287
+ @include font-sm;
288
+
289
+ --line-clamp: 2;
290
+
291
+ grid-row: 1;
292
+ grid-column: 2;
293
+ color: $history-card-title;
294
+
295
+ @include hover(color) {
296
+ color: $history-card-title--hover;
297
+ }
298
+ }
299
+
300
+ .card-history__meta {
301
+ @include font-xs;
302
+
303
+ display: flex;
304
+ align-items: center;
305
+ grid-row: 2;
306
+ grid-column: 2;
307
+ margin-top: 6px;
308
+ font-weight: 400;
309
+ color: $history-card-secondary;
310
+ }
311
+
312
+ .card-history__channel {
313
+ color: $history-card-secondary;
314
+ }
315
+
316
+ .card-history__channel + .card-history__watched::before {
317
+ content: '·';
318
+ margin-inline: 6px;
319
+ }
320
+
321
+ .card-history__watched {
322
+ display: flex;
323
+ align-items: center;
324
+ column-gap: 4px;
325
+ flex-shrink: 0;
326
+ }
327
+
328
+ .card-history__dropdown {
329
+ grid-row: 1/3;
330
+ grid-column: 3;
331
+ align-self: start;
332
+ color: $history-card-dropdown-icon;
333
+
334
+ @include hover(color) {
335
+ color: $history-card-dropdown-icon--hover;
336
+ }
337
+ }
338
+
339
+ @include to-br(xs) {
340
+ .card-history__logo-wrapper {
341
+ display: none;
342
+ }
343
+
344
+ .card-history__wrapper {
345
+ grid-template-columns: 1fr auto;
346
+ }
347
+
348
+ .card-history__title,
349
+ .card-history__meta {
350
+ grid-column: 1;
351
+ }
352
+
353
+ .card-history__dropdown {
354
+ grid-column: 2;
355
+ }
356
+ }
357
+ </style>
@@ -141,7 +141,7 @@
141
141
  <button
142
142
  :class="className"
143
143
  type="button"
144
- @click="isShareOpen = true"
144
+ @click="emit('share', card)"
145
145
  >
146
146
  <UiIcon
147
147
  name="share"
@@ -160,15 +160,6 @@
160
160
  {{ card.searchTags.join(' · ')}}
161
161
  </p>
162
162
  </div>
163
-
164
- <ShareMain
165
- v-model="isShareOpen"
166
- :url="link"
167
- >
168
- <template #card>
169
- <CardPlaylistMini :card="card" />
170
- </template>
171
- </ShareMain>
172
163
  </div>
173
164
  </template>
174
165
 
@@ -184,12 +175,14 @@ const props = defineProps<{
184
175
  priority?: boolean
185
176
  }>();
186
177
 
178
+ const emit = defineEmits<{
179
+ (e: 'share', card: IPlaylistCard): void
180
+ }>();
181
+
187
182
  const { t } = useI18n();
188
183
 
189
184
  const { generateLink } = useGenerateLink();
190
185
 
191
- const isShareOpen = ref(false);
192
-
193
186
  const userProfileData = computed(() => useState<IProfileData>('profileData').value);
194
187
 
195
188
  const { isAuthorized } = useUser(AuthorizationApiService);
@@ -1,11 +1,11 @@
1
1
  <!-- Видео-предпросмотр (автоплей) поверх постера карточки видео + индикатор загрузки -->
2
2
  <template>
3
- <span class="card-video-preview">
3
+ <span class="card-preview">
4
4
  <video
5
5
  ref="videoRef"
6
6
  :src="url"
7
7
  :class="{'--playing': isPlaying}"
8
- class="card-video-preview__video"
8
+ class="card-preview__video"
9
9
  loop
10
10
  muted
11
11
  autoplay
@@ -19,7 +19,7 @@
19
19
  <span
20
20
  :class="{'--done': isLoaded}"
21
21
  :style="{'--preview-progress': isLoaded ? 1 : progress}"
22
- class="card-video-preview__loader"
22
+ class="card-preview__loader"
23
23
  />
24
24
  </span>
25
25
  </template>
@@ -104,13 +104,13 @@ onBeforeUnmount(stopCreep);
104
104
  <style lang="scss">
105
105
  $loader-height: 4px;
106
106
 
107
- .card-video-preview {
107
+ .card-preview {
108
108
  position: absolute;
109
109
  inset: 0;
110
110
  z-index: $z-index-plus;
111
111
  }
112
112
 
113
- .card-video-preview__video {
113
+ .card-preview__video {
114
114
  width: 100%;
115
115
  height: 100%;
116
116
  object-fit: cover;
@@ -118,22 +118,22 @@ $loader-height: 4px;
118
118
  }
119
119
 
120
120
  /* Пока автоплей не стартовал, браузер рисует поверх видео нативную кнопку плей — держим элемент невидимым */
121
- .card-video-preview__video.--playing {
121
+ .card-preview__video.--playing {
122
122
  opacity: 1;
123
123
  }
124
124
 
125
- .card-video-preview__loader {
125
+ .card-preview__loader {
126
126
  position: absolute;
127
127
  inset: 0 0 auto;
128
128
  height: $loader-height;
129
- background: $video-card-preview-loader-bg;
129
+ background: $card-preview-loader-bg;
130
130
  transform: scaleX(var(--preview-progress, 0));
131
131
  transform-origin: left center;
132
132
  transition: var(--transition) transform, var(--transition) var(--default-transition-duration) opacity;
133
133
  }
134
134
 
135
135
  /* Догрузилось или пошло воспроизведение — полоса доезжает до конца и только потом гаснет */
136
- .card-video-preview__loader.--done {
136
+ .card-preview__loader.--done {
137
137
  opacity: 0;
138
138
  }
139
139
  </style>
@@ -27,7 +27,7 @@
27
27
  height="270"
28
28
  @error="onPosterError"
29
29
  />
30
- <CardVideoPreview
30
+ <CardPreview
31
31
  v-if="isPreviewShow && card.previewUrl"
32
32
  :url="card.previewUrl"
33
33
  />
@@ -1,4 +1,4 @@
1
- <!-- Сетка карточек плейлистов, 2-4 колонки -->
1
+ <!-- Сетка карточек плейлистов, 2-4 колонки; попап шаринга — один на список -->
2
2
  <template>
3
3
  <div class="grid-playlists">
4
4
  <CardPlaylist
@@ -7,7 +7,20 @@
7
7
  :card="item"
8
8
  :top-playlists="topPlaylists"
9
9
  :priority="priority && index === 0"
10
+ @share="onShare"
10
11
  />
12
+
13
+ <ShareMain
14
+ v-model="isShareOpen"
15
+ :url="shareUrl"
16
+ >
17
+ <template #card>
18
+ <CardPlaylistMini
19
+ v-if="shareCard"
20
+ :card="shareCard"
21
+ />
22
+ </template>
23
+ </ShareMain>
11
24
  </div>
12
25
  </template>
13
26
 
@@ -19,6 +32,20 @@ defineProps<{
19
32
  topPlaylists?: boolean
20
33
  priority?: boolean
21
34
  }>();
35
+
36
+ const { generateLink } = useGenerateLink();
37
+
38
+ const isShareOpen = ref(false);
39
+ const shareCard = ref<IPlaylistCard | null>(null);
40
+
41
+ const shareUrl = computed(() => shareCard.value
42
+ ? generateLink(`/playlists/${shareCard.value.id}/${shareCard.value.firstVideoId}`)
43
+ : '');
44
+
45
+ function onShare(card: IPlaylistCard) {
46
+ shareCard.value = card;
47
+ isShareOpen.value = true;
48
+ }
22
49
  </script>
23
50
 
24
51
  <style lang="scss">