itube-specs 0.0.904 → 0.0.910

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.
@@ -1 +1 @@
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
+ <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>
@@ -43,7 +43,7 @@ $nav-border--hover: transparent;
43
43
  $nav-border: $color-zinc-700-50;
44
44
  $nav-icon: $color-zinc-400;
45
45
  $nav-icon-accent: $color-red-400;
46
- $nav-icon--active: $nav-text--active;
46
+ $nav-icon--active: $color-zinc-400;
47
47
  $nav-container-bg: $color-black;
48
48
  $nav-down-icon: $color-zinc-500;
49
49
  $nav-menu-bg: $color-zinc-900;
@@ -105,7 +105,7 @@ $count-bg: $color-zinc-800;
105
105
  $count-text: $color-zinc-400;
106
106
 
107
107
  // ─── Search ───
108
- $search-bg: $color-zinc-800;
108
+ $search-bg: $color-zinc-800-50;
109
109
  $search-text: $color-white;
110
110
  $search-bg--mobile: $color-zinc-800;
111
111
  $search-border--active--mobile: $color-indigo-500;
@@ -119,7 +119,7 @@ $search-button-bg: $color-indigo-600;
119
119
  $search-button-bg--hover: $color-indigo-500;
120
120
  $search-max-width: 500px;
121
121
  $search-max-width-md: 672px;
122
- $search-mobile-bg: $mono-black-95;
122
+ $search-mobile-bg: $color-neutral-940-95;
123
123
  $search-mobile-border: $color-zinc-800;
124
124
  $search-mobile-decorate: $mono-black-50;
125
125
  $search-mobile-button: $color-zinc-200;
@@ -468,18 +468,29 @@ $dropdown-crest: $color-zinc-500;
468
468
  $dropdown-separator: $color-zinc-800-60;
469
469
 
470
470
  // ─── Snackbar ───
471
+ $snackbar-bg: $color-zinc-800;
472
+ $snackbar-border: $color-zinc-700;
471
473
  $snackbar-text: $color-zinc-200;
472
- $snackbar-close: $color-zinc-500;
473
- $snackbar-close--hover: $color-zinc-300;
474
- $snackbar-close-bg--hover: $color-zinc-700;
475
- $snackbar-success: $color-green-200;
476
- $snackbar-success-bg: $color-green-925;
477
- $snackbar-success-icon: $color-green-300;
478
- $snackbar-success-icon-bg: $color-green-800;
479
- $snackbar-error: $color-red-200;
480
- $snackbar-error-bg: $color-red-925;
481
- $snackbar-error-icon: $color-red-300;
482
- $snackbar-error-icon-bg: $color-red-800;
474
+ $snackbar-highlight: $color-white;
475
+ $snackbar-icon: $color-zinc-300;
476
+ $snackbar-icon-bg: $color-zinc-700-60;
477
+ $snackbar-shadow: 0 10px 15px -3px #{$mono-black-25}, 0 4px 6px -4px #{$mono-black-25};
478
+ $snackbar-action-bg: $color-indigo-600;
479
+ $snackbar-action-bg--hover: $color-indigo-500;
480
+ $snackbar-action-text: $color-white;
481
+
482
+ // ─── Follow button ───
483
+ $follow-btn-bg: $color-indigo-600;
484
+ $follow-btn-bg--hover: $color-indigo-500;
485
+ $follow-btn-border: $color-indigo-500;
486
+ $follow-btn-border--hover: $color-indigo-500;
487
+ $follow-btn-text: $color-white;
488
+ $follow-btn-following-bg: $color-zinc-800;
489
+ $follow-btn-following-bg--hover: $color-red-600;
490
+ $follow-btn-following-border: $color-zinc-800;
491
+ $follow-btn-following-border--hover: $color-red-500;
492
+ $follow-btn-following-text: $color-white;
493
+ $follow-btn-following-text--hover: $color-white;
483
494
 
484
495
  // ─── Button ───
485
496
  $button-primary-bg: $color-indigo-600;
@@ -854,6 +865,18 @@ $playlist-card-placeholder-bg: $color-zinc-700;
854
865
  $playlist-card-dropdown-icon: $color-zinc-500;
855
866
  $playlist-card-dropdown-icon--hover: $color-white;
856
867
 
868
+ // ─── Collections section ───
869
+ $collections-badge-text: $color-indigo-300;
870
+ $collections-badge-bg: $color-indigo-500-20;
871
+ $collections-badge-border: $color-indigo-500-40;
872
+ $collections-more-bg: $color-zinc-800;
873
+ $collections-more-border: $color-zinc-800-50;
874
+ $collections-more-border--hover: $color-zinc-700;
875
+ $collections-more-scrim: $color-black-50;
876
+ $collections-more-scrim--hover: $color-black-42;
877
+ $collections-more-count: $color-white;
878
+ $collections-more-text: $color-zinc-200;
879
+
857
880
  // ─── Channel card ───
858
881
  $channel-card-border: $color-zinc-800-50;
859
882
  $channel-card-bg: $color-zinc-800-50;
@@ -986,7 +1009,6 @@ $library-item-badge-bordered-text: $color-zinc-400;
986
1009
  // ─── Library feed (блок подписки: модель / канал / категория) ───
987
1010
  $library-feed-bg: $color-zinc-900-40;
988
1011
  $library-feed-border: $color-zinc-800;
989
- $library-feed-fade: $color-zinc-950;
990
1012
  $library-feed-avatar-border: $color-zinc-700;
991
1013
  $library-feed-avatar-border--hover: $color-zinc-500;
992
1014
  $library-feed-initials: $color-zinc-400;
@@ -523,18 +523,29 @@ $dropdown-crest: $color-gray-400;
523
523
  $dropdown-separator: $color-slate-750;
524
524
 
525
525
  // ─── Snackbar ───
526
+ $snackbar-bg: $color-slate-800;
527
+ $snackbar-border: $color-slate-750;
526
528
  $snackbar-text: $color-gray-200;
527
- $snackbar-close: $color-gray-500;
528
- $snackbar-close--hover: $color-gray-300;
529
- $snackbar-close-bg--hover: $color-slate-700;
530
- $snackbar-success: $color-green-200;
531
- $snackbar-success-bg: $color-green-925;
532
- $snackbar-success-icon: $color-green-300;
533
- $snackbar-success-icon-bg: $color-green-800;
534
- $snackbar-error: $color-red-200;
535
- $snackbar-error-bg: $color-red-925;
536
- $snackbar-error-icon: $color-red-300;
537
- $snackbar-error-icon-bg: $color-red-800;
529
+ $snackbar-highlight: $color-white;
530
+ $snackbar-icon: $color-gray-300;
531
+ $snackbar-icon-bg: $color-slate-700;
532
+ $snackbar-shadow: 0 10px 15px -3px #{$mono-black-25}, 0 4px 6px -4px #{$mono-black-25};
533
+ $snackbar-action-bg: $color-indigo-600;
534
+ $snackbar-action-bg--hover: $color-indigo-500;
535
+ $snackbar-action-text: $color-white;
536
+
537
+ // ─── Follow button ───
538
+ $follow-btn-bg: $color-indigo-600;
539
+ $follow-btn-bg--hover: $color-indigo-500;
540
+ $follow-btn-border: $color-indigo-500;
541
+ $follow-btn-border--hover: $color-indigo-500;
542
+ $follow-btn-text: $color-white;
543
+ $follow-btn-following-bg: $color-gray-300;
544
+ $follow-btn-following-bg--hover: $color-red-500;
545
+ $follow-btn-following-border: $color-gray-300;
546
+ $follow-btn-following-border--hover: $color-red-500;
547
+ $follow-btn-following-text: $color-gray-700;
548
+ $follow-btn-following-text--hover: $color-white;
538
549
 
539
550
  // ─── Button ───
540
551
  $button-primary-bg: $color-indigo-600;
@@ -882,6 +893,18 @@ $playlist-card-placeholder-bg: $color-gray-300;
882
893
  $playlist-card-dropdown-icon: $color-gray-600;
883
894
  $playlist-card-dropdown-icon--hover: $color-gray-900;
884
895
 
896
+ // ─── Collections section ───
897
+ $collections-badge-text: $color-indigo-600;
898
+ $collections-badge-bg: $color-indigo-500-10;
899
+ $collections-badge-border: $color-indigo-500-20;
900
+ $collections-more-bg: $color-gray-300;
901
+ $collections-more-border: $color-gray-300;
902
+ $collections-more-border--hover: $color-gray-400;
903
+ $collections-more-scrim: $color-black-45;
904
+ $collections-more-scrim--hover: $color-black-32;
905
+ $collections-more-count: $color-white;
906
+ $collections-more-text: $color-white;
907
+
885
908
  // ─── Channel card ───
886
909
  $channel-card-border: transparent;
887
910
  $channel-card-bg: $color-gray-100;
@@ -1014,7 +1037,6 @@ $library-item-badge-bordered-text: $color-gray-600;
1014
1037
  // ─── Library feed (блок подписки: модель / канал / категория) ───
1015
1038
  $library-feed-bg: $color-gray-100;
1016
1039
  $library-feed-border: transparent;
1017
- $library-feed-fade: $color-gray-100;
1018
1040
  $library-feed-avatar-border: $color-gray-300;
1019
1041
  $library-feed-avatar-border--hover: $color-indigo-500-50;
1020
1042
  $library-feed-initials: $color-gray-600;
@@ -22,6 +22,7 @@ $color-zinc-750: lab(21.3843% .9572 -2.5207); // #333337
22
22
  $color-zinc-700: lab(26.8019% 1.35387 -4.68303); // #3f3f46
23
23
  $color-zinc-700-80: lab(26.8019% 1.35387 -4.68303 / 80%); // zinc-700 / 70%
24
24
  $color-zinc-700-70: lab(26.8019% 1.35387 -4.68303 / 70%); // zinc-700 / 70%
25
+ $color-zinc-700-60: lab(26.8019% 1.35387 -4.68303 / 60%); // zinc-700 / 60%
25
26
  $color-zinc-700-50: lab(26.8019% 1.35387 -4.68303 / 50%); // zinc-700 / 50%
26
27
  $color-zinc-700-30: lab(26.8019% 1.35387 -4.68303 / 30%); // zinc-700 / 50%
27
28
  $color-zinc-650: lab(31.3256% 2.0703 -5.3354); // #494952
@@ -65,6 +66,10 @@ $color-indigo-800: lab(26.6645% 37.9804 -68.6402); // #372aac
65
66
  $color-indigo-900: lab(23.3911% 24.6978 -50.4718); // #312c85
66
67
  $color-indigo-950: lab(12.9255% 18.7583 -31.0075); // #1e1a4d
67
68
 
69
+ // Neutral gray (untinted — no zinc blue tint)
70
+ $color-neutral-940: lab(3.6355% 0 0); // #0d0d0d
71
+ $color-neutral-940-95: lab(3.6355% 0 0 / 95%); // #0d0d0d / 95%
72
+
68
73
  // Monochrome black (opacity variants)
69
74
  $mono-black-100: lab(0% 0 0);
70
75
  $mono-black-95: lab(0% 0 0 / 95%);
@@ -5,6 +5,7 @@ $radius: 10px;
5
5
  $radius-small: 4px;
6
6
  $radius-md: 8px;
7
7
  $radius-lg: 10px;
8
+ $radius-12: 12px;
8
9
  $radius-xl: 14px;
9
10
  $radius-2xl: 16px;
10
11
  $radius-full: 9999px;
@@ -94,7 +94,7 @@ function onCreateClick() {
94
94
 
95
95
  const loading = ref(false);
96
96
 
97
- const { setErrorState, showError, showSuccess, resetSnackbar } = useSnackbar();
97
+ const { setErrorState, showError, show, resetSnackbar } = useSnackbar();
98
98
 
99
99
  async function login() {
100
100
  resetSnackbar();
@@ -115,7 +115,7 @@ async function login() {
115
115
  loading.value = true;
116
116
  await useUser(AuthorizationApiService).login(form.value);
117
117
  useAuthPopup().closeAuthPopup();
118
- showSuccess('Welcome back!');
118
+ show('Welcome back!');
119
119
  } catch (error) {
120
120
  setErrorState(error);
121
121
  } finally {
@@ -70,7 +70,7 @@ function onLoginClick() {
70
70
 
71
71
  const loading = ref(false);
72
72
 
73
- const { setErrorState, showError, showSuccess, resetSnackbar } = useSnackbar();
73
+ const { setErrorState, showError, show, resetSnackbar } = useSnackbar();
74
74
 
75
75
  // Загружаем reCAPTCHA при открытии формы (onMounted) — даёт Google время собрать данные о поведении
76
76
  onMounted(async () => {
@@ -97,7 +97,7 @@ async function submit() {
97
97
  form.value.token = await getRecaptchaToken('password_reset');
98
98
  await useUser(AuthorizationApiService).password(form.value);
99
99
  useAuthPopup().closeAuthPopup();
100
- showSuccess('Check your email');
100
+ show('Check your email');
101
101
  } catch (error) {
102
102
  setErrorState(error);
103
103
  } finally {
@@ -129,7 +129,7 @@ function onLoginClick() {
129
129
 
130
130
  const loading = ref(false);
131
131
 
132
- const { setErrorState, showError, showSuccess, resetSnackbar } = useSnackbar();
132
+ const { setErrorState, showError, show, resetSnackbar } = useSnackbar();
133
133
  // Загружаем reCAPTCHA при открытии формы (onMounted) — даёт Google время собрать данные о поведении
134
134
  onMounted(async () => {
135
135
  try {
@@ -176,7 +176,7 @@ async function submit() {
176
176
  // await PlaylistsApiService.postPlaylist(FAVORITES_KEY, PlaylistType.Private);
177
177
 
178
178
  useAuthPopup().closeAuthPopup();
179
- showSuccess('Registration success');
179
+ show('Registration success');
180
180
  } catch (err) {
181
181
  setErrorState(err);
182
182
  console.error('Ошибка reCAPTCHA или регистрации:', err);
@@ -0,0 +1,133 @@
1
+ <!-- Плитка «показать ещё» в сетке коллекций: 16 размытых обложек сеткой 4×4, поверх счётчик и подпись -->
2
+ <template>
3
+ <NuxtLink
4
+ :to="generateLink(to)"
5
+ class="card-collections-more"
6
+ >
7
+ <span
8
+ v-if="images.length > 0"
9
+ class="card-collections-more__thumbs"
10
+ aria-hidden="true"
11
+ >
12
+ <UiImg
13
+ v-for="(src, index) in images"
14
+ :key="`collections-more-thumb-${index}`"
15
+ :src="src"
16
+ :width="ThumbSize.Small.split('x')[0]"
17
+ :height="ThumbSize.Small.split('x')[1]"
18
+ alt=""
19
+ class="card-collections-more__thumb"
20
+ />
21
+ </span>
22
+
23
+ <span class="card-collections-more__content">
24
+ <span
25
+ v-if="count"
26
+ class="card-collections-more__count"
27
+ >+{{ count }}</span>
28
+ <span class="card-collections-more__text">{{ text }}</span>
29
+ </span>
30
+ </NuxtLink>
31
+ </template>
32
+
33
+ <script setup lang="ts">
34
+ import { ThumbSize } from '../../runtime';
35
+ import type { IThumbUrls } from '../../types';
36
+
37
+ const props = defineProps<{
38
+ to: string
39
+ text: string
40
+ /** Обложки для мозаики: лишние отбрасываем, если меньше 16 — повторяем по кругу */
41
+ thumbs?: IThumbUrls[]
42
+ /** Сколько коллекций осталось за кадром — «+18» */
43
+ count?: number
44
+ }>();
45
+
46
+ const { generateLink } = useGenerateLink();
47
+
48
+ /** Ячеек в мозаике — сетка 4×4 */
49
+ const CELLS = 16;
50
+
51
+ const images = computed(() => {
52
+ const sources = (props.thumbs || [])
53
+ .map(thumb => thumb?.webp?.[ThumbSize.Small])
54
+ .filter((src): src is string => Boolean(src));
55
+
56
+ if (sources.length === 0) return [];
57
+
58
+ /* добиваем по кругу, чтобы мозаика всегда была полной, а не с дырами */
59
+ return Array.from({ length: CELLS }, (_, index) => sources[index % sources.length]);
60
+ });
61
+ </script>
62
+
63
+ <style lang="scss">
64
+ .card-collections-more {
65
+ position: relative;
66
+ display: grid;
67
+ place-content: center;
68
+ min-height: 240px;
69
+ padding: 12px;
70
+ overflow: hidden;
71
+ text-align: center;
72
+ border-radius: $radius-xl;
73
+ border: 1px solid $collections-more-border;
74
+ background-color: $collections-more-bg;
75
+
76
+ @include hover(border-color) {
77
+ border-color: $collections-more-border--hover;
78
+
79
+ &::after {
80
+ background-color: $collections-more-scrim--hover;
81
+ }
82
+ }
83
+
84
+ /* затемнение поверх обложек — псевдоэлементом, без отдельной ноды-оверлея */
85
+ &::after {
86
+ content: '';
87
+ position: absolute;
88
+ inset: 0;
89
+ background-color: $collections-more-scrim;
90
+ transition: var(--transition) background-color;
91
+ }
92
+ }
93
+
94
+ .card-collections-more__thumbs {
95
+ position: absolute;
96
+ inset: 0;
97
+ display: grid;
98
+
99
+ /* minmax(0, 1fr), иначе auto-минимум трека = min-content картинки и сетка распирает блок */
100
+ grid-template-columns: repeat(4, minmax(0, 1fr));
101
+ grid-template-rows: repeat(4, minmax(0, 1fr));
102
+ gap: 2px;
103
+ filter: blur(12px);
104
+ transform: scale(1.1);
105
+ }
106
+
107
+ .card-collections-more__thumb {
108
+ width: 100%;
109
+ height: 100%;
110
+ object-fit: cover;
111
+ }
112
+
113
+ .card-collections-more__content {
114
+ position: relative;
115
+ z-index: $z-index-plus;
116
+ display: grid;
117
+ row-gap: 2px;
118
+ }
119
+
120
+ .card-collections-more__count {
121
+ @include font-2xl;
122
+
123
+ font-weight: 700;
124
+ color: $collections-more-count;
125
+ }
126
+
127
+ .card-collections-more__text {
128
+ @include font-sm;
129
+
130
+ font-weight: 500;
131
+ color: $collections-more-text;
132
+ }
133
+ </style>
@@ -107,7 +107,10 @@
107
107
  <p class="card-playlist__user">
108
108
  {{ card.username }}
109
109
  </p>
110
- <div class="card-playlist__content-wrapper">
110
+ <div
111
+ v-if="!noActions"
112
+ class="card-playlist__content-wrapper"
113
+ >
111
114
  <UiDropdown
112
115
  ref="dropdownRef"
113
116
  right
@@ -177,6 +180,8 @@ import { CSS_BREAKPOINTS } from '../../config/css-breakpoints';
177
180
  const props = defineProps<{
178
181
  card: IPlaylistCard
179
182
  topPlaylists?: boolean
183
+ /** Без меню «три точки» — вместе с ним уходят share и report */
184
+ noActions?: boolean
180
185
  priority?: boolean
181
186
  }>();
182
187
 
@@ -6,6 +6,7 @@
6
6
  :key="`user-playlist-${index}`"
7
7
  :card="item"
8
8
  :top-playlists="topPlaylists"
9
+ :no-actions="noActions"
9
10
  :priority="priority && index === 0"
10
11
  @share="onShare"
11
12
  />
@@ -32,6 +33,8 @@ import type { IPlaylistCard } from '../../types';
32
33
  defineProps<{
33
34
  items: IPlaylistCard[]
34
35
  topPlaylists?: boolean
36
+ /** Скрыть меню карточки (три точки: share/report) — например у редакционных подборок */
37
+ noActions?: boolean
35
38
  priority?: boolean
36
39
  }>();
37
40
 
@@ -30,19 +30,6 @@
30
30
  class="info-main__count"
31
31
  icon="video-camera"
32
32
  >{{ videosCount || card.videosCount }}</UiCount>
33
- <UiBtn
34
- v-if="featureFlags.FeatureFollowEnabled"
35
- theme="muted"
36
- class="info-main__header-button"
37
- size="s"
38
- @click="onFollowClick"
39
- >
40
- <UiIcon
41
- name="plus"
42
- size="16"
43
- />
44
- {{ t('follow') }}
45
- </UiBtn>
46
33
  <UiBtn
47
34
  theme="muted"
48
35
  size="s"
@@ -55,6 +42,14 @@
55
42
  />
56
43
  {{ t('share') }}
57
44
  </UiBtn>
45
+ <UiFollowBtn
46
+ v-if="featureFlags.FeatureFollowEnabled"
47
+ :following="isFollowing"
48
+ :name="title"
49
+ class="info-main__header-follow"
50
+ size="s"
51
+ @toggle="onFollowClick"
52
+ />
58
53
  </div>
59
54
  </div>
60
55
 
@@ -190,9 +185,12 @@ function showParameters() {
190
185
 
191
186
  const authPopupText = 'register_first';
192
187
 
188
+ /** Заглушка вместо ручки подписки: состояние живёт только в компоненте, на бэк ничего не уходит */
189
+ const isFollowing = ref(false);
190
+
193
191
  function onFollowClick() {
194
192
  if (isAuthorized.value) {
195
- console.log('чото делать');
193
+ isFollowing.value = !isFollowing.value;
196
194
  } else {
197
195
  openAuthPopup(AuthSteps.Login, authPopupText);
198
196
  }
@@ -464,4 +462,10 @@ const socialItems = computed<ILinkItem[]>(() => {
464
462
  .info-main__header-button {
465
463
  @include button-variant(info-button);
466
464
  }
465
+
466
+ .info-main__header-follow {
467
+ @include to-br(sm) {
468
+ margin-inline-start: auto;
469
+ }
470
+ }
467
471
  </style>
@@ -59,6 +59,7 @@
59
59
  class="library-feed__tags"
60
60
  hydrate-on-visible
61
61
  micro
62
+ hide-more
62
63
  />
63
64
 
64
65
  <p
@@ -86,22 +87,17 @@
86
87
  </p>
87
88
  </div>
88
89
 
89
- <div class="library-feed__actions _from-sm">
90
- <UiBtn
91
- :theme="following ? 'secondary' : 'primary'"
92
- class="library-feed__follow"
93
- @click="emit('toggle-follow', item)"
94
- >
95
- <UiIcon
96
- :name="following ? 'check' : 'plus'"
97
- size="16"
98
- />
99
- {{ following ? t('library.following_state') : t('follow') }}
100
- </UiBtn>
90
+ <div class="library-feed__actions">
91
+ <UiFollowBtn
92
+ :following="following"
93
+ :name="item.title"
94
+ @toggle="emit('toggle-follow', item)"
95
+ />
101
96
 
102
97
  <UiLink
103
98
  :to="item.to"
104
99
  theme="secondary"
100
+ class="_from-sm"
105
101
  >
106
102
  {{ videosText }}
107
103
  <UiIcon
@@ -112,10 +108,10 @@
112
108
  </div>
113
109
  </header>
114
110
 
115
- <h3
111
+ <!-- <h3
116
112
  v-if="item.feedTitle"
117
113
  class="library-feed__feed-title"
118
- >{{ item.feedTitle }}</h3>
114
+ >{{ item.feedTitle }}</h3> -->
119
115
 
120
116
  <div class="library-feed__grid">
121
117
  <CardVideo
@@ -128,7 +124,7 @@
128
124
  />
129
125
  </div>
130
126
 
131
- <UiLink
127
+ <!-- <UiLink
132
128
  :to="item.to"
133
129
  theme="secondary"
134
130
  class="library-feed__all _to-sm"
@@ -139,7 +135,7 @@
139
135
  name="arrow-right"
140
136
  size="16"
141
137
  />
142
- </UiLink>
138
+ </UiLink> -->
143
139
  </article>
144
140
  </template>
145
141
 
@@ -185,9 +181,6 @@ const ageText = computed(() => {
185
181
 
186
182
  <style lang="scss">
187
183
  .library-feed {
188
- /* Растушёвка «ещё» у тегов уходит в фон карточки, а не страницы */
189
- --expand-row-fade-end: #{$library-feed-fade};
190
-
191
184
  @include from-br(sm) {
192
185
  padding: 20px;
193
186
  border: 1px solid $library-feed-border;
@@ -198,23 +191,15 @@ const ageText = computed(() => {
198
191
 
199
192
  .library-feed__header {
200
193
  display: grid;
201
- grid-template-columns: auto minmax(0, 1fr);
194
+ grid-template-columns: auto minmax(0, 1fr) auto;
202
195
  column-gap: 16px;
203
196
  row-gap: 12px;
204
197
  align-items: center;
205
198
  margin-bottom: 20px;
206
-
207
- @include from-br(sm) {
208
- grid-template-columns: auto minmax(0, 1fr) auto;
209
- }
210
199
  }
211
200
 
212
201
  .library-feed__header.--without-avatar {
213
- grid-template-columns: minmax(0, 1fr);
214
-
215
- @include from-br(sm) {
216
- grid-template-columns: minmax(0, 1fr) auto;
217
- }
202
+ grid-template-columns: minmax(0, 1fr) auto;
218
203
  }
219
204
 
220
205
  .library-feed__avatar {
@@ -344,17 +329,13 @@ const ageText = computed(() => {
344
329
  column-gap: 8px;
345
330
  }
346
331
 
347
- .library-feed__follow {
348
- min-width: 124px;
349
- }
350
-
351
- .library-feed__feed-title {
352
- @include font-sm;
332
+ // .library-feed__feed-title {
333
+ // @include font-sm;
353
334
 
354
- margin: 0 0 12px;
355
- font-weight: 400;
356
- color: $library-feed-title;
357
- }
335
+ // margin: 0 0 12px;
336
+ // font-weight: 400;
337
+ // color: $library-feed-title;
338
+ // }
358
339
 
359
340
  .library-feed__grid {
360
341
  display: grid;
@@ -84,10 +84,21 @@ function isFollowing(item: ILibraryFeedItem) {
84
84
  return !unfollowed.value.includes(item.guid);
85
85
  }
86
86
 
87
+ function follow(guid: string) {
88
+ unfollowed.value = unfollowed.value.filter(item => item !== guid);
89
+ }
90
+
91
+ function unfollow(guid: string) {
92
+ unfollowed.value = [...unfollowed.value, guid];
93
+ }
94
+
95
+ /** Заглушка: подписка живёт в локальном стейте, API ещё нет. Снекбар с undo рисует сама кнопка. */
87
96
  function onToggleFollow(item: ILibraryFeedItem) {
88
- unfollowed.value = isFollowing(item)
89
- ? [...unfollowed.value, item.guid]
90
- : unfollowed.value.filter(guid => guid !== item.guid);
97
+ if (isFollowing(item)) {
98
+ unfollow(item.guid);
99
+ } else {
100
+ follow(item.guid);
101
+ }
91
102
  }
92
103
  </script>
93
104
 
@@ -106,6 +117,10 @@ function onToggleFollow(item: ILibraryFeedItem) {
106
117
 
107
118
  .library-follows__list {
108
119
  display: grid;
109
- row-gap: 24px;
120
+ row-gap: 40px;
121
+
122
+ @include from-br(sm) {
123
+ row-gap: 24px;
124
+ }
110
125
  }
111
126
  </style>
@@ -261,7 +261,7 @@ const postVideoForm = computed(() => ({
261
261
  duration: videoCard.value?.duration ?? 0,
262
262
  }));
263
263
 
264
- const { setErrorState, showSuccess, showError } = useSnackbar();
264
+ const { setErrorState, show, showError } = useSnackbar();
265
265
 
266
266
  async function onSaveClick() {
267
267
  let newPlaylist: IPlaylistShort | undefined;
@@ -293,7 +293,7 @@ async function onSaveClick() {
293
293
  newPlaylist,
294
294
  });
295
295
  closePopup();
296
- showSuccess('video_added');
296
+ show('video_added');
297
297
  } catch (error) {
298
298
  setErrorState(error);
299
299
  console.log(error, 'error add video to playlist');