itube-specs 0.0.911 → 0.0.912

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.
@@ -300,18 +300,18 @@ $like-bar-bg: $color-zinc-700;
300
300
  $like-bar-likes: $color-zinc-500;
301
301
 
302
302
  // ─── Playlist input ───
303
- $playlist-input-label: $color-white;
304
- $playlist-input-toggle-icon: $color-zinc-500;
305
- $playlist-input-toggle-icon--active: $color-zinc-400;
306
- $playlist-input-hint-text: $color-zinc-900;
307
- $playlist-input-hint-bg: $color-zinc-600;
308
- $playlist-input-hint-bg--hover: $color-zinc-500;
303
+ $playlist-input-bg: $color-zinc-900;
309
304
  $playlist-input-back: $color-zinc-500;
310
305
  $playlist-input-back--hover: $color-white;
311
- $playlist-input-tooltip-highlight: $color-white;
312
- $playlist-input-delete-text: $color-red-400;
313
- $playlist-input-delete-bg: $color-red-500-10;
314
- $playlist-input-delete-bg--hover: $color-red-500-20;
306
+ $playlist-input-back-bg: $color-zinc-800;
307
+ $playlist-input-back-bg--hover: $color-zinc-700;
308
+
309
+ // ─── Playlist visibility ───
310
+ $playlist-visibility-icon: $color-zinc-400;
311
+ $playlist-visibility-hint-text: $color-zinc-900;
312
+ $playlist-visibility-hint-bg: $color-zinc-600;
313
+ $playlist-visibility-hint-bg--hover: $color-zinc-500;
314
+ $playlist-visibility-tooltip-highlight: $color-white;
315
315
 
316
316
  // ─── Playlist add ───
317
317
  $playlist-add-item-bg--hover: $color-zinc-800-50;
@@ -324,8 +324,10 @@ $playlist-add-item-edit: $color-zinc-500;
324
324
  $playlist-add-item-edit--hover: $color-white;
325
325
 
326
326
  // ─── Playlist edit ───
327
- $playlist-edit-border: $color-zinc-800;
328
- $playlist-edit-icon: $color-indigo-600;
327
+ $playlist-edit-delete-text: $color-red-400;
328
+ $playlist-edit-delete-text--hover: $color-red-300;
329
+ $playlist-edit-confirm-highlight: $color-white;
330
+ $playlist-edit-confirm-note: $color-zinc-500;
329
331
 
330
332
  // ─── Playlist delete video ───
331
333
  $playlist-delete-video-icon: $color-indigo-600;
@@ -554,7 +556,7 @@ $select-border--active: $color-indigo-500;
554
556
 
555
557
  // ─── Toggle ───
556
558
  $toggle-bg: $color-zinc-700;
557
- $toggle-bg--active: $color-emerald-500;
559
+ $toggle-bg--active: $color-zinc-500;
558
560
  $toggle-handle: $mono-white-50;
559
561
  $toggle-handle--active: $mono-white-100;
560
562
  $toggle-text--disabled: $mono-white-25;
@@ -980,7 +982,6 @@ $contacts-link: $color-indigo-400;
980
982
  $contacts-link--hover: $color-indigo-300;
981
983
 
982
984
  // ─── Page: playlists ───
983
- $page-playlists-border: $color-zinc-800;
984
985
 
985
986
  // ─── Page: channels ───
986
987
  $page-channels-border: $color-zinc-800;
@@ -357,18 +357,18 @@ $like-bar-bg: $color-slate-700;
357
357
  $like-bar-likes: $color-gray-500;
358
358
 
359
359
  // ─── Playlist input ───
360
- $playlist-input-label: $color-white;
361
- $playlist-input-toggle-icon: $color-gray-500;
362
- $playlist-input-toggle-icon--active: $color-gray-400;
363
- $playlist-input-hint-text: $color-gray-900;
364
- $playlist-input-hint-bg: $color-gray-600;
365
- $playlist-input-hint-bg--hover: $color-gray-500;
360
+ $playlist-input-bg: $color-slate-900;
366
361
  $playlist-input-back: $color-gray-400;
367
362
  $playlist-input-back--hover: $color-white;
368
- $playlist-input-tooltip-highlight: $color-white;
369
- $playlist-input-delete-text: $color-red-400;
370
- $playlist-input-delete-bg: $color-red-500-10;
371
- $playlist-input-delete-bg--hover: $color-red-500-20;
363
+ $playlist-input-back-bg: $color-slate-750;
364
+ $playlist-input-back-bg--hover: $color-slate-700;
365
+
366
+ // ─── Playlist visibility ───
367
+ $playlist-visibility-icon: $color-gray-400;
368
+ $playlist-visibility-hint-text: $color-gray-900;
369
+ $playlist-visibility-hint-bg: $color-gray-600;
370
+ $playlist-visibility-hint-bg--hover: $color-gray-500;
371
+ $playlist-visibility-tooltip-highlight: $color-white;
372
372
 
373
373
  // ─── Playlist add ───
374
374
  $playlist-add-item-bg--hover: $color-slate-750;
@@ -381,8 +381,10 @@ $playlist-add-item-edit: $color-gray-400;
381
381
  $playlist-add-item-edit--hover: $color-white;
382
382
 
383
383
  // ─── Playlist edit ───
384
- $playlist-edit-border: transparent;
385
- $playlist-edit-icon: $color-indigo-600;
384
+ $playlist-edit-delete-text: $color-red-400;
385
+ $playlist-edit-delete-text--hover: $color-red-300;
386
+ $playlist-edit-confirm-highlight: $color-white;
387
+ $playlist-edit-confirm-note: $color-gray-500;
386
388
 
387
389
  // ─── Playlist delete video ───
388
390
  $playlist-delete-video-icon: $color-indigo-600;
@@ -1008,7 +1010,6 @@ $contacts-link: $color-indigo-400;
1008
1010
  $contacts-link--hover: $color-indigo-300;
1009
1011
 
1010
1012
  // ─── Page: playlists ───
1011
- $page-playlists-border: transparent;
1012
1013
 
1013
1014
  // ─── Page: channels ───
1014
1015
  $page-channels-border: transparent;
@@ -171,10 +171,6 @@ async function submit() {
171
171
 
172
172
  await useUser(AuthorizationApiService).register(form.value);
173
173
 
174
- // Создаем сразу Favorites плейлист, возможно костыль убрать когда будет нормальная логика с беком
175
- // const { FAVORITES_KEY } = useFavorites();
176
- // await PlaylistsApiService.postPlaylist(FAVORITES_KEY, PlaylistType.Private);
177
-
178
174
  useAuthPopup().closeAuthPopup();
179
175
  show('Registration success');
180
176
  } catch (err) {
@@ -141,19 +141,6 @@
141
141
  compact
142
142
  />
143
143
  </div>
144
-
145
- <!-- <UiLink
146
- :to="item.to"
147
- theme="secondary"
148
- class="library-feed__all _to-sm"
149
- wide
150
- >
151
- {{ videosText }}
152
- <UiIcon
153
- name="arrow-right"
154
- size="16"
155
- />
156
- </UiLink> -->
157
144
  </article>
158
145
  </template>
159
146
 
@@ -7,6 +7,7 @@
7
7
  :back="!!onBack"
8
8
  class="playlist-add"
9
9
  sheet
10
+ transparent-backdrop
10
11
  @back="handleBack"
11
12
  @close="closePopup"
12
13
  >
@@ -23,37 +24,12 @@
23
24
  :class="{'_loading': loadingUserPlaylists || loadingPostVideo}"
24
25
  class="playlist-add__wrapper"
25
26
  >
26
- <UiCheckbox
27
- right
28
- :model-value="selectValue.includes('favorites')"
29
- class="playlist-add__item"
30
- @update:model-value="togglePlaylist('favorites')"
31
- >
32
- <div class="playlist-add__item-wrapper --favorites">
33
- <UiIcon
34
- name="heart"
35
- size="20"
36
- />
37
- {{ $t('favorites')}}
38
- </div>
39
- </UiCheckbox>
40
27
  <template v-if="playlistsItems && playlistsItems.length">
41
28
  <template
42
29
  v-for="(item, index) in playlistsItems"
43
30
  :key="`playlist-${index}`"
44
31
  >
45
- <PlaylistInput
46
- v-if="editingId === item.value"
47
- v-model="editName"
48
- v-model:type="editType"
49
- :placeholder="$t('playlist_name')"
50
- edit-playlist
51
- @close="editingId = null"
52
- @submit="saveEdit(item.value)"
53
- @delete="deleteEdit(item.value)"
54
- />
55
32
  <UiCheckbox
56
- v-else
57
33
  :model-value="selectValue.includes(item.value)"
58
34
  class="playlist-add__item"
59
35
  right
@@ -62,27 +38,27 @@
62
38
  <div class="playlist-add__item-wrapper">
63
39
  <UiIcon
64
40
  class="playlist-add__item-icon"
65
- name="list"
41
+ name="playlist"
66
42
  size="20"
67
43
  />
68
44
  <span class="playlist-add__item-text">{{ item.title }}</span>
69
45
  <UiIcon
70
46
  :name="item.typeIcon"
71
47
  class="playlist-add__item-icon"
72
- size="16"
48
+ size="14"
73
49
  />
74
- <div class="playlist-add__item-icon">
50
+ <span class="playlist-add__item-icon">
75
51
  <UiIcon
76
52
  name="video-camera"
77
- size="16"
53
+ size="14"
78
54
  />
79
55
  {{ item.videosCount }}
80
- </div>
56
+ </span>
81
57
  </div>
82
58
  <button
83
59
  class="playlist-add__item-edit"
84
60
  type="button"
85
- @click.prevent="startEdit(item)"
61
+ @click.prevent="openEdit(item.card)"
86
62
  >
87
63
  <UiIcon
88
64
  name="pen"
@@ -92,31 +68,29 @@
92
68
  </UiCheckbox>
93
69
  </template>
94
70
  </template>
95
- <template v-if="!editingId">
96
- <button
97
- v-if="step !== 'new'"
98
- type="button"
99
- class="playlist-add__item --new"
100
- @click="step = 'new'"
101
- >
102
- <UiIcon
103
- class="playlist-add__item-icon --new"
104
- name="plus"
105
- size="20"
106
- />
107
- {{ $t('playlist.new_playlist') }}
108
- </button>
109
- <PlaylistInput
110
- v-else
111
- v-model="newPlaylistName"
112
- v-model:type="playlistType"
113
- :error="nameError"
114
- :placeholder="$t('playlist_name')"
115
- @update:error="nameError = $event"
116
- @close="clearNewPlaylist"
117
- @submit="onSaveClick"
71
+ <button
72
+ v-if="step !== 'new'"
73
+ type="button"
74
+ class="playlist-add__item --new"
75
+ @click="step = 'new'"
76
+ >
77
+ <UiIcon
78
+ class="playlist-add__item-icon --new"
79
+ name="plus"
80
+ size="20"
118
81
  />
119
- </template>
82
+ {{ $t('playlist.new_playlist') }}
83
+ </button>
84
+ <PlaylistInput
85
+ v-else
86
+ v-model="newPlaylistName"
87
+ v-model:type="playlistType"
88
+ :error="nameError"
89
+ :placeholder="$t('playlist_name')"
90
+ @update:error="nameError = $event"
91
+ @close="clearNewPlaylist"
92
+ @submit="onSaveClick"
93
+ />
120
94
  </div>
121
95
 
122
96
  <template #footer>
@@ -131,11 +105,11 @@
131
105
  </UiBtn>
132
106
  <UiBtn
133
107
  wide
134
- :disabled="loadingUserPlaylists || loadingPostVideo"
108
+ :disabled="submitDisabled"
135
109
  size="l"
136
110
  theme="primary"
137
- @click="editingId ? saveEdit(editingId) : onSaveClick()"
138
- >{{ editingId ? $t('update') : $t('add') }}
111
+ @click="onSaveClick"
112
+ >{{ $t('add') }}
139
113
  </UiBtn>
140
114
  </div>
141
115
  </template>
@@ -147,7 +121,7 @@
147
121
 
148
122
  <script setup lang="ts">
149
123
  import type { IPlaylistVideoFormType, IPlaylistShort, IPlaylistCard, PaginatedResponse } from '../../types';
150
- import { PlaylistType } from '../../runtime';
124
+ import { PLAYLIST_TYPE_ICON, PlaylistStep, PlaylistType } from '../../runtime';
151
125
 
152
126
  const props = defineProps<{
153
127
  loadingPostPlaylist: boolean
@@ -163,11 +137,22 @@ const emit = defineEmits<{
163
137
  id: string
164
138
  newPlaylist?: IPlaylistShort
165
139
  }): void
166
- (eventName: 'edit-playlist', eventValue: { id: string, playlistName: string, playlistType: PlaylistType }): void
167
- (eventName: 'delete-playlist', eventValue: string): void
168
140
  }>();
169
141
 
170
142
  const { videoCard, closePlaylistAdd, isPlaylistAdd, selectValue, step, onBack } = usePlaylistAdd();
143
+ const { openPlaylistEdit, onBack: onEditBack } = usePlaylistEdit();
144
+
145
+ /**
146
+ * Редактирование открывается вместо добавления, не поверх. Карточку видео не сбрасываем —
147
+ * из редактирования по «назад» возвращаемся сюда же.
148
+ */
149
+ function openEdit(card: IPlaylistCard) {
150
+ onEditBack.value = () => {
151
+ isPlaylistAdd.value = true;
152
+ };
153
+ isPlaylistAdd.value = false;
154
+ openPlaylistEdit(PlaylistStep.Edit, false, card);
155
+ }
171
156
 
172
157
  function handleBack() {
173
158
  const callback = onBack.value;
@@ -175,17 +160,14 @@ function handleBack() {
175
160
  callback?.();
176
161
  }
177
162
 
178
- const { FAVORITES_KEY } = useFavorites();
179
-
180
163
  const playlistsItems = computed(() => {
181
164
  return props.userPlaylists?.items
182
- ?.filter((item: IPlaylistCard) => item.name !== FAVORITES_KEY)
183
- .map((item: IPlaylistCard) => ({
165
+ ?.map((item: IPlaylistCard) => ({
184
166
  title: item.name,
185
167
  value: item.id,
186
168
  videosCount: item.videosCount,
187
- typeIcon: item.playlistType === PlaylistType.Private ? 'lock' : 'user-group',
188
- playlistType: item.playlistType === PlaylistType.Private ? 'private' as const : 'public' as const,
169
+ typeIcon: PLAYLIST_TYPE_ICON[item.playlistType] ?? PLAYLIST_TYPE_ICON[PlaylistType.Public],
170
+ card: item,
189
171
  }));
190
172
  });
191
173
 
@@ -201,7 +183,7 @@ function togglePlaylist(value: string) {
201
183
 
202
184
  watch(isPlaylistAdd, (val) => {
203
185
  if (val) {
204
- selectValue.value = ['favorites'];
186
+ selectValue.value = [];
205
187
  emit('execute-user-playlists');
206
188
  }
207
189
  }, { immediate: true });
@@ -210,46 +192,26 @@ async function closePopup() {
210
192
  step.value = 'add';
211
193
  selectValue.value = [];
212
194
  newPlaylistName.value = '';
213
- playlistType.value = 'public';
214
- editingId.value = null;
195
+ playlistType.value = PlaylistType.Public;
215
196
  isPlaylistAdd.value = false;
216
197
  closePlaylistAdd();
217
198
  }
218
199
 
219
200
  const newPlaylistName = ref('');
220
- const playlistType = ref<'public' | 'private'>('public');
201
+ const playlistType = ref<PlaylistType>(PlaylistType.Public);
221
202
  const nameError = ref(false);
222
203
 
223
204
  function clearNewPlaylist() {
224
205
  newPlaylistName.value = '';
225
- playlistType.value = 'public';
206
+ playlistType.value = PlaylistType.Public;
226
207
  step.value = 'add';
227
208
  }
228
209
 
229
- const editingId = ref<string | null>(null);
230
- const editName = ref('');
231
- const editType = ref<'public' | 'private'>('public');
232
-
233
- function startEdit(item: { value: string, title: string, playlistType: 'public' | 'private' }) {
234
- clearNewPlaylist();
235
- editingId.value = item.value;
236
- editName.value = item.title;
237
- editType.value = item.playlistType;
238
- }
239
-
240
- function saveEdit(id: string) {
241
- emit('edit-playlist', {
242
- id,
243
- playlistName: editName.value,
244
- playlistType: editType.value === 'private' ? PlaylistType.Private : PlaylistType.Public,
245
- });
246
- editingId.value = null;
247
- }
248
-
249
- function deleteEdit(id: string) {
250
- editingId.value = null;
251
- emit('delete-playlist', id);
252
- }
210
+ const submitDisabled = computed(() => {
211
+ if (props.loadingUserPlaylists || props.loadingPostVideo) return true;
212
+ if (step.value === 'new') return !newPlaylistName.value.trim();
213
+ return !selectValue.value.length;
214
+ });
253
215
 
254
216
  const postVideoForm = computed(() => ({
255
217
  playlistId: selectValue.value.length ? selectValue.value : undefined,
@@ -262,6 +224,35 @@ const postVideoForm = computed(() => ({
262
224
  }));
263
225
 
264
226
  const { setErrorState, show, showError } = useSnackbar();
227
+ const { generateLink } = useGenerateLink();
228
+ const { t } = useI18n();
229
+
230
+ /**
231
+ * Куда добавили: при одном плейлисте — его имя и ссылка на него же, при нескольких —
232
+ * количество и ссылка на список. У нового плейлиста id появляется только после ответа
233
+ * бэка, поэтому для него тоже ссылка на список.
234
+ */
235
+ function getTarget(newPlaylistName?: string) {
236
+ const selected = props.userPlaylists?.items
237
+ ?.filter((item: IPlaylistCard) => selectValue.value.includes(item.id)) ?? [];
238
+ const total = selected.length + (newPlaylistName ? 1 : 0);
239
+ const single = total === 1 ? selected[0] : undefined;
240
+
241
+ if (single) {
242
+ // Пустому плейлисту первым видео становится только что добавленное.
243
+ const firstVideoId = single.firstVideoId ?? videoCard.value?.id;
244
+
245
+ return {
246
+ label: single.name,
247
+ link: generateLink(`/playlists/${single.id}/${firstVideoId}`),
248
+ };
249
+ }
250
+
251
+ return {
252
+ label: total === 1 ? newPlaylistName! : t('plural.playlists', total),
253
+ link: generateLink('/playlists'),
254
+ };
255
+ }
265
256
 
266
257
  async function onSaveClick() {
267
258
  let newPlaylist: IPlaylistShort | undefined;
@@ -282,10 +273,12 @@ async function onSaveClick() {
282
273
 
283
274
  newPlaylist = {
284
275
  name,
285
- type: playlistType.value === 'private' ? PlaylistType.Private : PlaylistType.Public,
276
+ type: playlistType.value,
286
277
  };
287
278
  }
288
279
 
280
+ const target = getTarget(newPlaylist?.name);
281
+
289
282
  try {
290
283
  emit('save-video-to-playlist', {
291
284
  form: postVideoForm.value as IPlaylistVideoFormType,
@@ -293,23 +286,17 @@ async function onSaveClick() {
293
286
  newPlaylist,
294
287
  });
295
288
  closePopup();
296
- show('video_added');
289
+ show('added_to', {
290
+ icon: 'playlist',
291
+ highlight: target.label,
292
+ actionText: 'see_list',
293
+ onAction: () => navigateTo(target.link),
294
+ });
297
295
  } catch (error) {
298
296
  setErrorState(error);
299
297
  console.log(error, 'error add video to playlist');
300
298
  }
301
299
  }
302
-
303
- watch(
304
- playlistsItems,
305
- (items) => {
306
- if (!items?.length) return;
307
- if (selectValue.value.length) return;
308
-
309
- selectValue.value = ['favorites'];
310
- },
311
- { immediate: true }
312
- );
313
300
  </script>
314
301
 
315
302
  <style lang="scss">
@@ -319,6 +306,13 @@ watch(
319
306
  }
320
307
  }
321
308
 
309
+ /* Ширина фиксирована: длинное название плейлиста обрезается, а не растягивает модалку. */
310
+ .playlist-add .ui-popup__wrapper {
311
+ @include from-br(sm) {
312
+ width: var(--popup-min-width);
313
+ }
314
+ }
315
+
322
316
  .playlist-add .ui-popup__back {
323
317
  @include from-br(sm) {
324
318
  display: none;
@@ -329,8 +323,15 @@ watch(
329
323
  padding: 0;
330
324
  }
331
325
 
326
+ .playlist-add .ui-popup__footer {
327
+ padding-inline: 24px;
328
+ }
329
+
330
+ /* minmax(0, 1fr), а не дефолтный auto-трек: иначе трек растёт до max-content и длинное
331
+ название плейлиста делает строку шире модалки вместо обрезки. */
332
332
  .playlist-add__wrapper {
333
333
  display: grid;
334
+ grid-template-columns: minmax(0, 1fr);
334
335
  }
335
336
 
336
337
  .playlist-add__item {
@@ -345,6 +346,15 @@ watch(
345
346
  }
346
347
  }
347
348
 
349
+ /* Два класса не случайно: `.ui-checkbox` едет отдельным CSS-чанком и с одним классом
350
+ перебивает display по порядку загрузки. */
351
+ .playlist-add .playlist-add__item {
352
+ display: flex;
353
+ align-items: center;
354
+ justify-content: space-between;
355
+ column-gap: 12px;
356
+ }
357
+
348
358
  .playlist-add__item:not(:last-child) {
349
359
  border-bottom: 1px solid $playlist-add-item-border;
350
360
  }
@@ -357,10 +367,6 @@ watch(
357
367
  min-width: 0;
358
368
  }
359
369
 
360
- .playlist-add__item-wrapper.--favorites {
361
- text-transform: capitalize;
362
- }
363
-
364
370
  .ui-checkbox__label .playlist-add__item-wrapper {
365
371
  @include font-base;
366
372
 
@@ -372,6 +378,7 @@ watch(
372
378
  justify-content: space-between;
373
379
  align-items: center;
374
380
  width: 100%;
381
+ min-width: 0;
375
382
  }
376
383
 
377
384
  .playlist-add__item:last-child {
@@ -410,7 +417,10 @@ watch(
410
417
  }
411
418
 
412
419
  .playlist-add__item.--new {
420
+ @include font-base;
421
+
413
422
  color: $playlist-add-item-new;
423
+ font-weight: 500;
414
424
  }
415
425
 
416
426
  .playlist-add__item-edit {