itube-specs 0.0.899 → 0.0.904

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 (51) hide show
  1. package/assets/icons/common/clock-filled.svg +1 -0
  2. package/assets/icons/common/flag.svg +1 -1
  3. package/assets/icons/common/heart-stroke.svg +1 -1
  4. package/assets/icons/common/heart.svg +1 -1
  5. package/assets/icons/common/playlist.svg +1 -1
  6. package/assets/icons/common/share.svg +1 -1
  7. package/assets/icons/common/thumbs-down-filled.svg +1 -0
  8. package/assets/icons/common/thumbs-down.svg +1 -1
  9. package/assets/icons/common/thumbs-up-filled.svg +1 -0
  10. package/assets/icons/common/thumbs-up.svg +1 -1
  11. package/assets/scss/mixins/_breakpoints.scss +3 -2
  12. package/assets/scss/themes/dark/_theme.scss +29 -27
  13. package/assets/scss/themes/light/_theme.scss +29 -27
  14. package/assets/scss/vars/_palette.scss +1 -0
  15. package/components/card/category.vue +4 -8
  16. package/components/card/dropdown/index.vue +47 -9
  17. package/components/card/dropdown/trigger.vue +1 -0
  18. package/components/card/model.vue +1 -1
  19. package/components/card/playlist.vue +15 -6
  20. package/components/card/round.vue +3 -3
  21. package/components/card/video/actions.vue +123 -0
  22. package/components/card/video/index.vue +149 -30
  23. package/components/filter/selects.vue +1 -1
  24. package/components/info/grid.vue +1 -1
  25. package/components/layout/header-user.vue +1 -1
  26. package/components/library/feed.vue +65 -24
  27. package/components/library/follows.vue +8 -44
  28. package/components/library/item.vue +4 -4
  29. package/components/player/interface.vue +132 -58
  30. package/components/player/like.vue +81 -59
  31. package/components/player/playlist.vue +2 -2
  32. package/components/report/main.vue +20 -5
  33. package/components/ui/checkbox.vue +1 -1
  34. package/components/ui/dropdown.vue +1 -1
  35. package/components/ui/input.vue +2 -2
  36. package/components/ui/radio.vue +1 -1
  37. package/components/ui/toggle.vue +1 -1
  38. package/composables/use-report-popup.ts +26 -3
  39. package/composables/use-video-collections.ts +30 -0
  40. package/lib/scheme/profile-navigation-items.ts +1 -1
  41. package/package.json +1 -1
  42. package/pages/history.vue +12 -36
  43. package/pages/library/categories.vue +2 -1
  44. package/pages/library/channels.vue +1 -1
  45. package/pages/library/index.vue +3 -3
  46. package/pages/library/models.vue +4 -4
  47. package/stubs/library-follows.ts +19 -16
  48. package/types/library-feed.d.ts +6 -0
  49. package/types/video-card.d.ts +4 -0
  50. package/assets/icons/common/crest-in-circle.svg +0 -1
  51. package/components/card/history.vue +0 -359
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><mask id="clock-filled-hands"><rect width="24" height="24" fill="#fff"/><path d="M12 6v6l4 2" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></mask><circle cx="12" cy="12" r="10" fill="currentColor" mask="url(#clock-filled-hands)"/></svg>
@@ -1 +1 @@
1
- <svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1zm0 7v-7"/></svg>
1
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 22V4a1 1 0 0 1 .4-.8A6 6 0 0 1 8 2c3 0 5 2 7.333 2q2 0 3.067-.8A1 1 0 0 1 20 4v10a1 1 0 0 1-.4.8A6 6 0 0 1 16 16c-3 0-5-2-8-2a6 6 0 0 0-4 1.528"></path></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-heart w-5 h-5" aria-hidden="true"><path d="M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5"></path></svg>
1
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5"></path></svg>
@@ -1 +1 @@
1
- <svg fill="currentColor" viewBox="0 0 24 24"><path d="m12 21.35-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54z"/></svg>
1
+ <svg viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5"></path></svg>
@@ -1 +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
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 5H3"></path><path d="M10 12H3"></path><path d="M10 19H3"></path><path d="M15 12.003a1 1 0 0 1 1.517-.859l4.997 2.997a1 1 0 0 1 0 1.718l-4.997 2.997a1 1 0 0 1-1.517-.86z"></path></svg>
@@ -1 +1 @@
1
- <svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="m8.59 13.51 6.83 3.98m-.01-10.98-6.82 3.98"/></svg>
1
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" x2="15.42" y1="13.51" y2="17.49"></line><line x1="15.41" x2="8.59" y1="6.51" y2="10.49"></line></svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z"></path><path d="M17 14V2"></path></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true" viewBox="0 0 24 24"><path d="M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88M17 14V2"/></svg>
1
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z"></path><path d="M17 14V2"></path></svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z"></path><path d="M7 10v12"></path></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true" viewBox="0 0 24 24"><path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88M7 10v12"/></svg>
1
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z"></path><path d="M7 10v12"></path></svg>
@@ -77,12 +77,13 @@ $brkpnts: (
77
77
  }
78
78
 
79
79
  /**
80
- Hover с transition
80
+ Hover с transition. Условие — наличие наведения у устройства, а не ширина окна: на тач-экране
81
+ hover «залипает» после тапа и его правда не нужно, а вот узкому окну с мышью он нужен.
81
82
  @include hover(background-color) { background-color: red; }
82
83
  @include hover(background-color, color) { background-color: red; color: white; }
83
84
  */
84
85
  @mixin hover($properties...) {
85
- @include from-br(sm) {
86
+ @media (hover: hover) {
86
87
  $transitions: ();
87
88
 
88
89
  @each $prop in $properties {
@@ -197,30 +197,26 @@ $video-card-text: $color-white;
197
197
  $video-card-text--hover: $color-indigo-400;
198
198
  $video-card-dropdown-icon: $color-zinc-500;
199
199
  $video-card-dropdown-icon--hover: $color-white;
200
- $video-card-resolution-bg: $color-green-500;
201
- $video-card-resolution-text: $mono-black-100;
202
200
  $video-card-secondary: $color-zinc-500;
203
201
  $video-card-mini-title: $color-zinc-300;
204
202
  $video-card-mini-weight: 400;
205
203
  $video-card-user-text: $color-zinc-400;
206
204
  $video-card-user-bg: $color-zinc-700;
207
-
208
- // ─── History card ───
209
- $history-card-bg: $color-zinc-900-40;
210
- $history-card-wrapper-bg: $color-zinc-900-60;
211
- $history-card-border: $color-zinc-800;
212
- $history-card-border--hover: $color-zinc-700;
213
- $history-card-poster-border: $color-zinc-700-50;
214
- $history-card-title: $color-white;
215
- $history-card-title--hover: $color-indigo-400;
216
- $history-card-secondary: $color-zinc-500;
217
- $history-card-user-text: $color-zinc-400;
218
- $history-card-user-bg: $color-zinc-700;
219
- $history-card-dropdown-icon: $color-zinc-500;
220
- $history-card-dropdown-icon--hover: $color-white;
221
- $history-card-progress-bg: $mono-white-50;
222
- $history-card-progress-fill: $color-indigo-500;
223
- $history-card-remaining: $color-indigo-400;
205
+ $video-card-progress-bg: $mono-white-50;
206
+ $video-card-progress-fill: $color-indigo-500;
207
+ $video-card-remaining: $color-indigo-400;
208
+
209
+ // ─── Video card actions (избранное / смотреть позже на постере) ───
210
+ $video-card-action-bg: $color-zinc-900-70;
211
+ $video-card-action-bg--hover: $color-zinc-800;
212
+ $video-card-action-border: $color-zinc-800-60;
213
+ $video-card-action-icon: $color-zinc-300;
214
+ $video-card-action-active-bg: $color-zinc-800;
215
+ $video-card-action-active-bg--hover: $color-red-600;
216
+ $video-card-action-active-border: $color-zinc-800;
217
+ $video-card-action-active-border--hover: $color-red-600;
218
+ $video-card-action-active-icon: $color-white;
219
+ $video-card-action-active-icon--hover: $color-white;
224
220
 
225
221
  // ─── Video card dropdown ───
226
222
  $video-card-dropdown-border: $color-zinc-800;
@@ -234,6 +230,13 @@ $video-info-description-text: $color-zinc-400;
234
230
  // ─── Video interface ───
235
231
  $video-interface-button-bg: $color-zinc-800;
236
232
  $video-interface-button-text: $color-zinc-400;
233
+ $video-interface-button-bg--hover: $color-zinc-700;
234
+ $video-interface-button-text--hover: $color-white;
235
+ $video-interface-button-bg--active: $color-zinc-700;
236
+ $video-interface-button-text--active: $color-white;
237
+ $video-interface-mobile-icon: $color-white;
238
+ $video-interface-mobile-label: $color-zinc-300;
239
+ $video-interface-mobile-border: $color-zinc-800;
237
240
 
238
241
  // ─── Video channel ───
239
242
  $video-channel-bg: $color-zinc-800-60;
@@ -287,11 +290,12 @@ $video-playlist-header-button-text--active: $color-indigo-400;
287
290
  $video-playlist-header-button-bg--active: $color-indigo-500-10;
288
291
 
289
292
  // ─── Like ───
290
- $like-text: $color-zinc-400;
291
293
  $like-icon: $color-zinc-400;
292
- $like-text--hover: $color-white;
293
- $like-icon--active: $color-green-500;
294
- $like-icon-dislike--active: $color-red-500;
294
+ $like-icon--hover: $color-white;
295
+ $like-icon--active: $color-white;
296
+ $like-count: $color-zinc-500;
297
+ $like-mobile-icon: $color-white;
298
+ $like-mobile-count: $color-zinc-300;
295
299
  $like-bar-bg: $color-zinc-700;
296
300
  $like-bar-likes: $color-zinc-500;
297
301
 
@@ -992,18 +996,16 @@ $library-feed-name: $color-white;
992
996
  $library-feed-name--hover: $color-indigo-400;
993
997
  $library-feed-badge-bg: $color-indigo-600;
994
998
  $library-feed-badge-text: $color-white;
999
+ $library-feed-params: $color-zinc-400;
1000
+ $library-feed-age: $color-zinc-500;
995
1001
  $library-feed-meta: $color-zinc-500;
996
1002
  $library-feed-meta-dot: $color-zinc-600;
997
1003
  $library-feed-title: $color-zinc-500;
998
1004
 
999
1005
  // ─── Page: library ───
1000
1006
  $page-library-toggle-text: $color-zinc-300;
1001
- $page-library-confirm-text: $color-zinc-300;
1002
1007
 
1003
1008
  // ─── Page: history ───
1004
- $page-history-group-title: $color-zinc-300;
1005
- $page-history-group-icon: $color-zinc-500;
1006
- $page-history-group-count: $color-zinc-500;
1007
1009
  $page-history-confirm-text: $color-zinc-300;
1008
1010
  $page-history-confirm-muted: $color-zinc-500;
1009
1011
  $page-history-confirm-link: $color-indigo-400;
@@ -254,31 +254,27 @@ $video-card-text: $color-gray-900;
254
254
  $video-card-text--hover: $color-gray-900;
255
255
  $video-card-dropdown-icon: $color-gray-600;
256
256
  $video-card-dropdown-icon--hover: $color-gray-900;
257
- $video-card-resolution-bg: $color-green-500;
258
- $video-card-resolution-text: $mono-black-100;
259
257
  $video-card-secondary: $color-gray-600;
260
258
  $video-card-mini-title: $color-gray-900;
261
259
  $video-card-mini-weight: 500;
262
260
  $video-card-user-text: $color-gray-700;
263
261
  $video-card-user-bg: $color-gray-300;
264
262
  $video-card-dropdown-size--hover: 1.25;
265
-
266
- // ─── History card ───
267
- $history-card-bg: $color-gray-100;
268
- $history-card-wrapper-bg: $color-gray-100;
269
- $history-card-border: transparent;
270
- $history-card-border--hover: transparent;
271
- $history-card-poster-border: transparent;
272
- $history-card-title: $color-gray-900;
273
- $history-card-title--hover: $color-gray-900;
274
- $history-card-secondary: $color-gray-600;
275
- $history-card-user-text: $color-gray-700;
276
- $history-card-user-bg: $color-gray-300;
277
- $history-card-dropdown-icon: $color-gray-600;
278
- $history-card-dropdown-icon--hover: $color-gray-900;
279
- $history-card-progress-bg: $color-gray-300;
280
- $history-card-progress-fill: $color-indigo-500;
281
- $history-card-remaining: $color-indigo-600;
263
+ $video-card-progress-bg: $color-gray-300;
264
+ $video-card-progress-fill: $color-indigo-500;
265
+ $video-card-remaining: $color-indigo-600;
266
+
267
+ // ─── Video card actions (избранное / смотреть позже на постере) ───
268
+ $video-card-action-bg: $color-white-75;
269
+ $video-card-action-bg--hover: $color-white;
270
+ $video-card-action-border: $color-gray-300-50;
271
+ $video-card-action-icon: $color-gray-700;
272
+ $video-card-action-active-bg: $color-white;
273
+ $video-card-action-active-bg--hover: $color-red-600;
274
+ $video-card-action-active-border: $color-gray-300;
275
+ $video-card-action-active-border--hover: $color-red-600;
276
+ $video-card-action-active-icon: $color-gray-900;
277
+ $video-card-action-active-icon--hover: $color-white;
282
278
 
283
279
  // ─── Video card dropdown ───
284
280
  $video-card-dropdown-border: transparent;
@@ -291,6 +287,13 @@ $video-info-description-text: $color-gray-700;
291
287
  // ─── Video interface ───
292
288
  $video-interface-button-bg: $color-slate-750;
293
289
  $video-interface-button-text: $color-gray-400;
290
+ $video-interface-button-bg--hover: $color-slate-700;
291
+ $video-interface-button-text--hover: $color-white;
292
+ $video-interface-button-bg--active: $color-slate-700;
293
+ $video-interface-button-text--active: $color-white;
294
+ $video-interface-mobile-icon: $color-gray-900;
295
+ $video-interface-mobile-label: $color-gray-500;
296
+ $video-interface-mobile-border: $color-gray-200;
294
297
 
295
298
  // ─── Video channel ───
296
299
  $video-channel-bg: $color-gray-200;
@@ -344,11 +347,12 @@ $video-playlist-header-button-text--active: $color-indigo-soft;
344
347
  $video-playlist-header-button-bg--active: $color-indigo-500-10;
345
348
 
346
349
  // ─── Like ───
347
- $like-text: $color-gray-400;
348
350
  $like-icon: $color-gray-500;
349
- $like-text--hover: $color-gray-900;
350
- $like-icon--active: $color-green-500;
351
- $like-icon-dislike--active: $color-red-500;
351
+ $like-icon--hover: $color-gray-900;
352
+ $like-icon--active: $color-gray-900;
353
+ $like-count: $color-gray-400;
354
+ $like-mobile-icon: $color-gray-900;
355
+ $like-mobile-count: $color-gray-500;
352
356
  $like-bar-bg: $color-slate-700;
353
357
  $like-bar-likes: $color-gray-500;
354
358
 
@@ -1020,18 +1024,16 @@ $library-feed-name: $color-gray-900;
1020
1024
  $library-feed-name--hover: $color-indigo-600;
1021
1025
  $library-feed-badge-bg: $color-indigo-600;
1022
1026
  $library-feed-badge-text: $color-white;
1027
+ $library-feed-params: $color-gray-600;
1028
+ $library-feed-age: $color-gray-500;
1023
1029
  $library-feed-meta: $color-gray-500;
1024
1030
  $library-feed-meta-dot: $color-gray-400;
1025
1031
  $library-feed-title: $color-gray-500;
1026
1032
 
1027
1033
  // ─── Page: library ───
1028
1034
  $page-library-toggle-text: $color-gray-700;
1029
- $page-library-confirm-text: $color-gray-700;
1030
1035
 
1031
1036
  // ─── Page: history ───
1032
- $page-history-group-title: $color-gray-900;
1033
- $page-history-group-icon: $color-gray-500;
1034
- $page-history-group-count: $color-gray-500;
1035
1037
  $page-history-confirm-text: $color-gray-900;
1036
1038
  $page-history-confirm-muted: $color-gray-500;
1037
1039
  $page-history-confirm-link: $color-indigo-400;
@@ -5,6 +5,7 @@ $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-70: lab(8.30603% .618205 -2.16572 / 70%); // zinc-900 / 70%
8
9
  $color-zinc-900-60: lab(8.30603% .618205 -2.16572 / 60%); // zinc-900 / 60%
9
10
  $color-zinc-900-50: lab(8.30603% .618205 -2.16572 / 50%); // #18181b
10
11
  $color-zinc-900-40: lab(8.30603% .618205 -2.16572 / 40%); // zinc-900 / 40%
@@ -85,12 +85,8 @@ const slug = computed(() => {
85
85
  padding: 2px;
86
86
  border-radius: $radius-xl;
87
87
 
88
- @include from-br(sm) {
89
- transition: box-shadow var(--transition);
90
-
91
- &:is(:hover, :focus-visible) {
92
- box-shadow: $card-ring-shadow--hover;
93
- }
88
+ @include hover(box-shadow) {
89
+ box-shadow: $card-ring-shadow--hover;
94
90
  }
95
91
  }
96
92
 
@@ -117,7 +113,7 @@ const slug = computed(() => {
117
113
  text-overflow: ellipsis;
118
114
  color: $category-card-title;
119
115
 
120
- @include from-br(sm) {
116
+ @media (hover: hover) {
121
117
  transition: var(--transition) color;
122
118
  }
123
119
  }
@@ -132,7 +128,7 @@ const slug = computed(() => {
132
128
  }
133
129
 
134
130
  .card-category:is(:hover, :focus-visible) .card-category__title {
135
- @include from-br(sm) {
131
+ @media (hover: hover) {
136
132
  color: $category-card-title--hover;
137
133
  }
138
134
  }
@@ -1,4 +1,4 @@
1
- <!-- Дропдаун-меню видео: добавить в плейлист, смотреть позже, поделиться, пожаловаться -->
1
+ <!-- Дропдаун-меню видео: смотреть позже, избранное, добавить в плейлист, поделиться, пожаловаться -->
2
2
  <template>
3
3
  <UiDropdown
4
4
  ref="dropdownRef"
@@ -28,27 +28,39 @@
28
28
  </template>
29
29
  <template #items="className">
30
30
  <button
31
+ v-if="showWatchLater"
31
32
  :class="className"
32
33
  type="button"
33
- @click="onAddPlaylist"
34
+ @click="toggleWatchLater(card.guid)"
34
35
  >
35
36
  <UiIcon
36
- name="star"
37
+ name="clock"
37
38
  size="20"
38
39
  />
39
- {{ $t('video_submenu.add_to') }}
40
+ {{ watchLaterLabel }}
40
41
  </button>
41
42
  <button
42
- v-if="showWatchLater"
43
+ v-if="showFavorites"
43
44
  :class="className"
44
45
  type="button"
45
- @click="emit('watchLater')"
46
+ @click="toggleFavorite(card.guid)"
46
47
  >
47
48
  <UiIcon
48
- name="time"
49
+ name="heart-stroke"
49
50
  size="20"
50
51
  />
51
- {{ $t('video_submenu.watch_later') }}
52
+ {{ favoritesLabel }}
53
+ </button>
54
+ <button
55
+ :class="className"
56
+ type="button"
57
+ @click="onAddPlaylist"
58
+ >
59
+ <UiIcon
60
+ name="playlist"
61
+ size="20"
62
+ />
63
+ {{ $t('add_to_playlist') }}
52
64
  </button>
53
65
  <button
54
66
  v-if="showShare"
@@ -97,6 +109,7 @@ import { AuthorizationApiService } from '../../../services/api/authorization.ser
97
109
  const props = defineProps<{
98
110
  card: IVideoCard
99
111
  showWatchLater?: boolean
112
+ showFavorites?: boolean
100
113
  showShare?: boolean
101
114
  showReport?: boolean
102
115
  showRemove?: boolean
@@ -104,16 +117,41 @@ const props = defineProps<{
104
117
  }>();
105
118
 
106
119
  const emit = defineEmits<{
107
- (e: 'watchLater'): void
108
120
  (e: 'share'): void
109
121
  (e: 'remove'): void
110
122
  }>();
111
123
 
124
+ const { t } = useI18n();
125
+
112
126
  const { isAuthorized } = useUser(AuthorizationApiService);
113
127
  const { openReportPopup } = useReportPopup();
128
+ const { isFavorite, isWatchLater, toggleFavorite, toggleWatchLater } = useVideoCollections();
129
+
130
+ const favorite = computed(() => isFavorite(props.card.guid));
131
+ const later = computed(() => isWatchLater(props.card.guid));
132
+
133
+ // Тем же кликом, что переключает коллекцию, меню закрывается. Если менять надпись сразу, она
134
+ // дёргается прямо во время анимации закрытия, поэтому в меню живёт снапшот состояния — он
135
+ // догоняет реальное только к следующему открытию, когда пункт всё равно перерисовывается с нуля.
136
+ const shownFavorite = ref(favorite.value);
137
+ const shownLater = ref(later.value);
138
+
139
+ const favoritesLabel = computed(() => shownFavorite.value
140
+ ? t('video_actions.remove_favorites')
141
+ : t('video_actions.add_favorites'));
142
+
143
+ const watchLaterLabel = computed(() => shownLater.value
144
+ ? t('video_actions.remove_watch_later')
145
+ : t('video_actions.add_watch_later'));
114
146
 
115
147
  const dropdownRef = ref();
116
148
 
149
+ watch(() => dropdownRef.value?.open, (isOpen) => {
150
+ if (!isOpen) return;
151
+ shownFavorite.value = favorite.value;
152
+ shownLater.value = later.value;
153
+ });
154
+
117
155
  function openDropdown() {
118
156
  dropdownRef.value?.openDropdown();
119
157
  }
@@ -30,6 +30,7 @@ defineOptions({ inheritAttrs: false });
30
30
  const props = defineProps<{
31
31
  card: IVideoCard
32
32
  showWatchLater?: boolean
33
+ showFavorites?: boolean
33
34
  showShare?: boolean
34
35
  showReport?: boolean
35
36
  showRemove?: boolean
@@ -180,7 +180,7 @@ function getValue(item: { title?: string; value?: string }) {
180
180
  }
181
181
  }
182
182
 
183
- @include from-br(sm) {
183
+ @media (hover: hover) {
184
184
  .card-model:is(:hover, :focus-visible) .card-model__info-name {
185
185
  color: $model-card-name--hover;
186
186
  }
@@ -109,6 +109,7 @@
109
109
  </p>
110
110
  <div class="card-playlist__content-wrapper">
111
111
  <UiDropdown
112
+ ref="dropdownRef"
112
113
  right
113
114
  use-v-if
114
115
  class="card-playlist__dropdown"
@@ -132,26 +133,26 @@
132
133
  </template>
133
134
  <template #items="className">
134
135
  <button
135
- v-if="card.firstVideoId"
136
136
  :class="className"
137
137
  type="button"
138
+ @click="emit('share', card)"
138
139
  >
139
140
  <UiIcon
140
- name="star"
141
+ name="share"
141
142
  size="16"
142
143
  />
143
- {{ t('add_to') }}
144
+ {{ t('share') }}
144
145
  </button>
145
146
  <button
146
147
  :class="className"
147
148
  type="button"
148
- @click="emit('share', card)"
149
+ @click="onReport"
149
150
  >
150
151
  <UiIcon
151
- name="share"
152
+ name="flag"
152
153
  size="16"
153
154
  />
154
- {{ t('share') }}
155
+ {{ t('report') }}
155
156
  </button>
156
157
  </template>
157
158
  </UiDropdown>
@@ -187,6 +188,14 @@ const { t } = useI18n();
187
188
 
188
189
  const { generateLink } = useGenerateLink();
189
190
 
191
+ const { openPlaylistReportPopup } = useReportPopup();
192
+
193
+ const dropdownRef = ref();
194
+
195
+ function onReport() {
196
+ openPlaylistReportPopup(props.card, () => dropdownRef.value?.openDropdown());
197
+ }
198
+
190
199
  const userProfileData = computed(() => useState<IProfileData>('profileData').value);
191
200
 
192
201
  const { isAuthorized } = useUser(AuthorizationApiService);
@@ -126,7 +126,7 @@ $badge-inset--mobile: 4px;
126
126
  }
127
127
 
128
128
  .card-round:is(:hover, :focus-visible) .card-round__img {
129
- @include from-br(sm) {
129
+ @media (hover: hover) {
130
130
  border-color: $card-round-border--hover;
131
131
  box-shadow: $card-ring-shadow--hover;
132
132
  }
@@ -158,13 +158,13 @@ $badge-inset--mobile: 4px;
158
158
  white-space: nowrap;
159
159
  text-overflow: ellipsis;
160
160
 
161
- @include from-br(sm) {
161
+ @media (hover: hover) {
162
162
  transition: var(--transition) color;
163
163
  }
164
164
  }
165
165
 
166
166
  .card-round:is(:hover, :focus-visible) .card-round__title {
167
- @include from-br(sm) {
167
+ @media (hover: hover) {
168
168
  color: $card-round-text--hover;
169
169
  }
170
170
  }
@@ -0,0 +1,123 @@
1
+ <!-- Кнопки коллекций поверх постера: избранное (только когда ролик уже в нём) и «смотреть позже» -->
2
+ <template>
3
+ <div class="card-video-actions">
4
+ <button
5
+ v-if="showFavorites && favorite"
6
+ :aria-label="t('video_actions.remove_aria', { title: card.title, name: t('favorites') })"
7
+ class="card-video-actions__btn --active"
8
+ type="button"
9
+ aria-pressed="true"
10
+ @click="toggleFavorite(card.guid)"
11
+ >
12
+ <UiIcon
13
+ name="heart"
14
+ size="18"
15
+ />
16
+ </button>
17
+ <button
18
+ v-if="showWatchLater"
19
+ :aria-label="later
20
+ ? t('video_actions.remove_aria', { title: card.title, name: t('video_submenu.watch_later') })
21
+ : t('video_actions.add_aria', { title: card.title, name: t('video_submenu.watch_later') })"
22
+ :aria-pressed="later"
23
+ :class="['card-video-actions__btn', '--watch-later', { '--active': later }]"
24
+ type="button"
25
+ @click="toggleWatchLater(card.guid)"
26
+ >
27
+ <UiIcon
28
+ :name="later ? 'clock-filled' : 'clock'"
29
+ size="18"
30
+ />
31
+ </button>
32
+ </div>
33
+ </template>
34
+
35
+ <script setup lang="ts">
36
+ import type { IVideoCard } from '../../../types';
37
+
38
+ const props = defineProps<{
39
+ card: IVideoCard
40
+ showFavorites?: boolean
41
+ showWatchLater?: boolean
42
+ }>();
43
+
44
+ const { t } = useI18n();
45
+
46
+ const { isFavorite, isWatchLater, toggleFavorite, toggleWatchLater } = useVideoCollections();
47
+
48
+ const favorite = computed(() => isFavorite(props.card.guid));
49
+ const later = computed(() => isWatchLater(props.card.guid));
50
+ </script>
51
+
52
+ <style lang="scss">
53
+ $action-inset: 8px;
54
+ $action-size: 36px;
55
+
56
+ .card-video-actions {
57
+ position: absolute;
58
+ top: $action-inset;
59
+ right: $action-inset;
60
+ left: $action-inset;
61
+ z-index: $z-index-sub-loader;
62
+ display: flex;
63
+ column-gap: $action-inset;
64
+ pointer-events: none;
65
+ }
66
+
67
+ .card-video-actions__btn {
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ width: $action-size;
72
+ height: $action-size;
73
+ border: 1px solid $video-card-action-border;
74
+ border-radius: $radius-md;
75
+ background-color: $video-card-action-bg;
76
+ color: $video-card-action-icon;
77
+ pointer-events: auto;
78
+ }
79
+
80
+ .card-video-actions__btn.--watch-later {
81
+ margin-left: auto;
82
+ }
83
+
84
+ .card-video-actions__btn.--active {
85
+ background-color: $video-card-action-active-bg;
86
+ border-color: $video-card-action-active-border;
87
+ color: $video-card-action-active-icon;
88
+ opacity: 1;
89
+ }
90
+
91
+ /* С мышью неактивные кнопки проявляются по наведению на карточку. На тач-устройствах наведения нет,
92
+ поэтому там неактивные кнопки не показываем вовсе — только те, что уже в коллекции. */
93
+ @media (hover: hover) {
94
+ .card-video-actions__btn {
95
+ opacity: 0;
96
+ transition:
97
+ var(--transition) opacity,
98
+ var(--transition) background-color,
99
+ var(--transition) border-color,
100
+ var(--transition) color;
101
+ }
102
+
103
+ .card-video:is(:hover, :focus-within) .card-video-actions__btn {
104
+ opacity: 1;
105
+ }
106
+
107
+ .card-video-actions__btn:is(:hover, :focus-visible) {
108
+ background-color: $video-card-action-bg--hover;
109
+ }
110
+
111
+ .card-video-actions__btn.--active:is(:hover, :focus-visible) {
112
+ background-color: $video-card-action-active-bg--hover;
113
+ border-color: $video-card-action-active-border--hover;
114
+ color: $video-card-action-active-icon--hover;
115
+ }
116
+ }
117
+
118
+ @media (hover: none) {
119
+ .card-video-actions__btn:not(.--active) {
120
+ display: none;
121
+ }
122
+ }
123
+ </style>