itube-specs 0.0.1060 → 0.0.1061

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 (44) hide show
  1. package/SITE_UPDATES.md +11 -0
  2. package/assets/icons/common/arrow-up.svg +1 -0
  3. package/assets/icons/common/link.svg +1 -0
  4. package/assets/icons/common/loader.svg +1 -0
  5. package/assets/icons/common/upload-arrow.svg +1 -0
  6. package/assets/scss/themes/dark/_theme.scss +58 -2
  7. package/assets/scss/themes/light/_theme.scss +56 -0
  8. package/assets/scss/vars/_palette.scss +1 -0
  9. package/components/layout/burger.vue +16 -8
  10. package/components/layout/footer-language.vue +4 -1
  11. package/components/layout/header.vue +129 -112
  12. package/components/ui/confirm.vue +2 -1
  13. package/components/ui/link.vue +1 -1
  14. package/components/upload/combobox.vue +380 -0
  15. package/components/upload/details.vue +239 -0
  16. package/components/upload/main.vue +195 -0
  17. package/components/upload/models.vue +162 -0
  18. package/components/upload/picker.vue +319 -0
  19. package/components/upload/progress.vue +477 -0
  20. package/components/upload/publish.vue +153 -0
  21. package/components/upload/requirements.vue +179 -0
  22. package/components/upload/rules.vue +144 -0
  23. package/components/upload/section.vue +55 -0
  24. package/components/upload/sent.vue +299 -0
  25. package/components/upload/spinner.vue +35 -0
  26. package/components/upload/tags.vue +81 -0
  27. package/composables/use-upload-suggest.ts +40 -0
  28. package/composables/use-upload.ts +165 -0
  29. package/i18n/locales/en-US.json +207 -0
  30. package/package.json +1 -1
  31. package/pages/upload.vue +19 -0
  32. package/runtime/constants/upload.ts +61 -0
  33. package/runtime/index.ts +1 -0
  34. package/types/index.d.ts +1 -0
  35. package/types/upload.d.ts +40 -0
  36. package/utils/upload/__tests__/check-upload-file.test.ts +34 -0
  37. package/utils/upload/__tests__/format-file-size.test.ts +16 -0
  38. package/utils/upload/__tests__/format-video-quality.test.ts +19 -0
  39. package/utils/upload/__tests__/strip-html-tags.test.ts +12 -0
  40. package/utils/upload/check-upload-file.ts +123 -0
  41. package/utils/upload/create-idle-upload-checks.ts +11 -0
  42. package/utils/upload/format-file-size.ts +8 -0
  43. package/utils/upload/format-video-quality.ts +13 -0
  44. package/utils/upload/strip-html-tags.ts +3 -0
package/SITE_UPDATES.md CHANGED
@@ -11,6 +11,17 @@
11
11
  коммиты до бампа); `npm run patch` сам проставит ей номер версии. Формат пункта: что сделать и где,
12
12
  кому именно (всем / сайтам с N), что будет, если не сделать.
13
13
 
14
+ ## 0.0.1061 — 2026-11-02
15
+
16
+ - **Всем — решить флаг:** новая страница загрузки видео `/upload` (выбор файла с проверкой в браузере → детали
17
+ во время загрузки → «отправлено» со статусом обработки) и кнопка Upload в шапке (десктоп) — за флагом
18
+ `FeatureUploadEnabled`. API загрузки ещё нет: прогресс и обработка имитируются таймерами. Хотите страницу —
19
+ добавьте `FeatureUploadEnabled: true` в `config/feature-flags.config.ts` (gay-cool-new включил). Без флага
20
+ `/upload` отдаёт 404, кнопки нет, а мёртвая кнопка Upload из бургера (была без действия) пропадает — делать
21
+ ничего не надо.
22
+ - **Всем — к сведению:** новая группа общих ключей `upload_page` (пока только en-US, остальные локали — через CSV
23
+ слоя). У `UiConfirm` необязательный проп `cancelText` (по умолчанию «Cancel», как было).
24
+
14
25
  ## 0.0.1060 — 2026-11-02
15
26
 
16
27
  - **Сайтам со своими `assets/scss/vars` (hdsex-remaster и др.), срочно:** с 0.0.1059 (лоадер, `0d412182`) без новых
@@ -0,0 +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="1.75" viewBox="0 0 24 24"><path d="m5 12 7-7 7 7M12 19V5"/></svg>
@@ -0,0 +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" viewBox="0 0 24 24"><path d="M9 17H7A5 5 0 0 1 7 7h2M15 7h2a5 5 0 1 1 0 10h-2M8 12h8"/></svg>
@@ -0,0 +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" viewBox="0 0 24 24"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="none" viewBox="0 0 18 18"><path fill="currentColor" d="M9 2.25 4.5 6.75h2.75V12h3.5V6.75h2.75z"/><path stroke="currentColor" stroke-linecap="round" stroke-width="1.5" d="M2.5 14v1.25c0 .97.78 1.75 1.75 1.75h9.5c.97 0 1.75-.78 1.75-1.75V14"/></svg>
@@ -424,6 +424,8 @@ $niche-switch-text--checked: $color-white;
424
424
  $footer-language-bg: $color-zinc-800-50;
425
425
  $footer-language-bg--hover: $color-zinc-700;
426
426
  $footer-language-text: $color-zinc-400;
427
+ $footer-language-text--hover: $color-white;
428
+ $footer-language-badge: $color-zinc-200;
427
429
  $footer-language-menu-bg: $color-night;
428
430
  $footer-language-menu-border: $color-zinc-800;
429
431
  $footer-language-menu-shadow: 0 20px 25px -5px #{$mono-black-25}, 0 8px 10px -6px #{$mono-black-25};
@@ -456,8 +458,9 @@ $header-burger-icon: $color-zinc-200;
456
458
  $header-user-icon: $color-zinc-500;
457
459
  $header-language-trigger: $color-zinc-400;
458
460
  $header-language-trigger--hover: $color-white;
459
- $header-language-trigger-bg: transparent;
460
- $header-language-trigger-bg--hover: transparent;
461
+ $header-language-trigger-bg: $color-zinc-800-50;
462
+ $header-language-trigger-bg--hover: $color-zinc-700;
463
+ $header-language-badge: $color-zinc-200;
461
464
  $header-language-menu-bg: $color-night;
462
465
  $header-language-menu-border: $color-zinc-800;
463
466
  $header-language-menu-shadow: 0 20px 25px -5px #{$mono-black-25}, 0 8px 10px -6px #{$mono-black-25};
@@ -1214,3 +1217,56 @@ $player-tags-chip-bg: $color-zinc-800;
1214
1217
  $player-tags-tag-bg: $color-zinc-800-50;
1215
1218
  $player-tags-more-text: $color-zinc-400;
1216
1219
  $player-tags-more-text--hover: $color-white;
1220
+
1221
+ // ─── Header upload button ───
1222
+ $header-upload-bg: $color-indigo-600-20;
1223
+ $header-upload-border: $color-indigo-500-50;
1224
+ $header-upload-border--hover: $color-indigo-500;
1225
+ $header-upload-text: $color-white;
1226
+
1227
+ // ─── Upload page ───
1228
+ $upload-title: $color-white;
1229
+ $upload-text: $color-zinc-450;
1230
+ $upload-card-bg: $color-zinc-925;
1231
+ $upload-accent: $color-indigo-600;
1232
+ $upload-accent-text: $color-indigo-500;
1233
+ $upload-success: $color-emerald-500;
1234
+ $upload-error: $color-red-500;
1235
+ $upload-icon-bg: $color-zinc-850;
1236
+ $upload-dot: $color-zinc-550;
1237
+ $upload-check-icon: $color-zinc-550;
1238
+ $upload-dropzone-border: $color-zinc-800;
1239
+ $upload-dropzone-border--error: $color-red-500-50;
1240
+ $upload-dropzone-bg--active: $color-zinc-925;
1241
+ $upload-field-bg: $color-zinc-850;
1242
+ $upload-field-text: $color-white;
1243
+ $upload-field-placeholder: $color-zinc-450;
1244
+ $upload-field-border: transparent;
1245
+ $upload-field-border--hover: $color-zinc-800;
1246
+ $upload-field-bg--hover: $color-zinc-800;
1247
+ $upload-chip-bg: $color-zinc-850;
1248
+ $upload-chip-text: $color-white;
1249
+ $upload-chip-icon: $color-zinc-450;
1250
+ $upload-chip-icon--hover: $color-white;
1251
+ $upload-avatar-bg: $color-zinc-925;
1252
+ $upload-avatar-text: $color-zinc-450;
1253
+ $upload-listbox-bg: $color-zinc-850;
1254
+ $upload-listbox-border: $color-zinc-800;
1255
+ $upload-listbox-shadow: 0 12px 32px #{$mono-black-50}, 0 2px 8px #{$mono-black-42};
1256
+ $upload-option-bg--active: $color-zinc-800;
1257
+ $upload-poster-bg: $color-zinc-850;
1258
+ $upload-overlay-bg: $color-black-50;
1259
+ $upload-overlay-text: $color-white;
1260
+ $upload-overlay-subtext: $color-white-75;
1261
+ $upload-badge-bg: $color-black-75;
1262
+ $upload-badge-text: $color-white;
1263
+ $upload-track-bg: $color-black-50;
1264
+ $upload-stripes-from: $color-indigo-600;
1265
+ $upload-stripes-to: $color-indigo-800;
1266
+ $upload-step-line: $color-zinc-700;
1267
+ $upload-step-time: $color-zinc-500;
1268
+ $upload-step-icon: $color-white;
1269
+ $upload-radio-ring: $color-zinc-700;
1270
+ $upload-radio-dot: $color-white;
1271
+ $upload-rules-text: $color-zinc-400;
1272
+ $upload-rules-dot: $color-zinc-600;
@@ -482,6 +482,8 @@ $niche-switch-text--checked: $color-white;
482
482
  $footer-language-bg: $color-slate-750;
483
483
  $footer-language-bg--hover: $color-slate-700;
484
484
  $footer-language-text: $color-white;
485
+ $footer-language-text--hover: $color-white;
486
+ $footer-language-badge: $color-gray-200;
485
487
  $footer-language-menu-bg: $color-slate-850;
486
488
  $footer-language-menu-border: $color-white-10;
487
489
  $footer-language-menu-shadow: 0 20px 25px -5px #{$mono-black-25}, 0 8px 10px -6px #{$mono-black-25};
@@ -516,6 +518,7 @@ $header-language-trigger: $color-white;
516
518
  $header-language-trigger--hover: $color-white;
517
519
  $header-language-trigger-bg: $color-slate-750;
518
520
  $header-language-trigger-bg--hover: $color-slate-700;
521
+ $header-language-badge: $color-gray-200;
519
522
  $header-language-menu-bg: $color-slate-850;
520
523
  $header-language-menu-border: $color-white-10;
521
524
  $header-language-menu-shadow: 0 20px 25px -5px #{$mono-black-25}, 0 8px 10px -6px #{$mono-black-25};
@@ -1243,3 +1246,56 @@ $player-tags-chip-bg: $color-gray-200;
1243
1246
  $player-tags-tag-bg: $color-gray-100;
1244
1247
  $player-tags-more-text: $color-gray-700;
1245
1248
  $player-tags-more-text--hover: $color-gray-900;
1249
+
1250
+ // ─── Header upload button ───
1251
+ $header-upload-bg: $color-indigo-600-20;
1252
+ $header-upload-border: $color-indigo-500-50;
1253
+ $header-upload-border--hover: $color-indigo-500;
1254
+ $header-upload-text: $color-white;
1255
+
1256
+ // ─── Upload page ───
1257
+ $upload-title: $color-gray-900;
1258
+ $upload-text: $color-gray-500;
1259
+ $upload-card-bg: $color-gray-100;
1260
+ $upload-accent: $color-indigo-600;
1261
+ $upload-accent-text: $color-indigo-600;
1262
+ $upload-success: $color-emerald-600;
1263
+ $upload-error: $color-red-500;
1264
+ $upload-icon-bg: $color-gray-200;
1265
+ $upload-dot: $color-gray-400;
1266
+ $upload-check-icon: $color-gray-400;
1267
+ $upload-dropzone-border: $color-gray-300;
1268
+ $upload-dropzone-border--error: $color-red-300;
1269
+ $upload-dropzone-bg--active: $color-gray-100;
1270
+ $upload-field-bg: $color-white;
1271
+ $upload-field-text: $color-gray-900;
1272
+ $upload-field-placeholder: $color-gray-400;
1273
+ $upload-field-border: $color-gray-200;
1274
+ $upload-field-border--hover: $color-gray-300;
1275
+ $upload-field-bg--hover: $color-gray-200;
1276
+ $upload-chip-bg: $color-gray-200;
1277
+ $upload-chip-text: $color-gray-900;
1278
+ $upload-chip-icon: $color-gray-500;
1279
+ $upload-chip-icon--hover: $color-gray-900;
1280
+ $upload-avatar-bg: $color-gray-100;
1281
+ $upload-avatar-text: $color-gray-500;
1282
+ $upload-listbox-bg: $color-white;
1283
+ $upload-listbox-border: $color-gray-200;
1284
+ $upload-listbox-shadow: 0 12px 32px #{$mono-black-15}, 0 2px 8px #{$mono-black-10};
1285
+ $upload-option-bg--active: $color-gray-100;
1286
+ $upload-poster-bg: $color-gray-200;
1287
+ $upload-overlay-bg: $color-black-50;
1288
+ $upload-overlay-text: $color-white;
1289
+ $upload-overlay-subtext: $color-white-75;
1290
+ $upload-badge-bg: $color-black-75;
1291
+ $upload-badge-text: $color-white;
1292
+ $upload-track-bg: $color-black-50;
1293
+ $upload-stripes-from: $color-indigo-600;
1294
+ $upload-stripes-to: $color-indigo-800;
1295
+ $upload-step-line: $color-gray-200;
1296
+ $upload-step-time: $color-gray-400;
1297
+ $upload-step-icon: $color-white;
1298
+ $upload-radio-ring: $color-gray-300;
1299
+ $upload-radio-dot: $color-white;
1300
+ $upload-rules-text: $color-gray-300;
1301
+ $upload-rules-dot: $color-gray-500;
@@ -133,6 +133,7 @@ $color-red-400: lab(63.7053% 60.745 31.3109);
133
133
  $color-red-500: lab(55.4814% 75.0732 48.8528);
134
134
  $color-red-500-10: color-mix(in oklab, #{$color-red-500} 10%, transparent);
135
135
  $color-red-500-20: color-mix(in oklab, #{$color-red-500} 20%, transparent);
136
+ $color-red-500-50: color-mix(in oklab, #{$color-red-500} 50%, transparent);
136
137
  $color-red-600: lab(48.4493% 77.4328 61.5452);
137
138
  $color-red-600-20: color-mix(in oklab, #{$color-red-600} 20%, transparent);
138
139
  $color-red-600-30: color-mix(in oklab, #{$color-red-600} 30%, transparent);
@@ -33,11 +33,13 @@
33
33
  <UiAvatar :size="40"/>
34
34
  <span class="layout-burger__button-text">{{ data.username }}</span>
35
35
  </NuxtLink>
36
- <button
36
+ <component
37
37
  v-for="(btn, index) in burgerButtons"
38
+ :is="btn.link ? NuxtLink : 'button'"
38
39
  :key="`burger-btn-${index}`"
40
+ :to="btn.link"
41
+ :type="btn.link ? undefined : 'button'"
39
42
  class="layout-burger__button"
40
- type="button"
41
43
  @click="btn.action?.(); close()"
42
44
  >
43
45
  <span class="layout-burger__button-icon">
@@ -47,7 +49,7 @@
47
49
  />
48
50
  </span>
49
51
  {{ t(btn.label) }}
50
- </button>
52
+ </component>
51
53
  </div>
52
54
 
53
55
  <nav class="layout-burger__nav">
@@ -84,6 +86,7 @@
84
86
  </template>
85
87
 
86
88
  <script setup lang="ts">
89
+ import { NuxtLink } from '#components';
87
90
  import { AuthSteps, onBackdropClick, checkDeviceWidth } from '../../runtime';
88
91
  import type { IProfileData } from '../../types';
89
92
  import { CSS_BREAKPOINTS } from '../../config/css-breakpoints';
@@ -158,7 +161,9 @@ function logOut() {
158
161
  useUser(AuthorizationApiService).signOut();
159
162
  }
160
163
 
161
- const burgerButtons = computed(() => [
164
+ const featureFlags = useRuntimeConfig().public.featureFlags;
165
+
166
+ const burgerButtons = computed<{ icon: string, label: string, action?: () => void, link?: string }[]>(() => [
162
167
  ...isAuthorized.value ? [
163
168
  {
164
169
  icon: 'logout',
@@ -178,10 +183,13 @@ const burgerButtons = computed(() => [
178
183
  action: () => openAuthPopup(AuthSteps.Login),
179
184
  },
180
185
  ],
181
- {
182
- icon: 'upload',
183
- label: 'upload'
184
- },
186
+ ...featureFlags.FeatureUploadEnabled ? [
187
+ {
188
+ icon: 'upload',
189
+ label: 'upload',
190
+ link: generateLink('/upload'),
191
+ }
192
+ ] : [],
185
193
  ]);
186
194
 
187
195
  const router = useRouter();
@@ -82,13 +82,16 @@ function handler() {
82
82
  background-color: $footer-language-bg;
83
83
  color: $footer-language-text;
84
84
 
85
- @include hover(background-color) {
85
+ @include hover(background-color, color) {
86
+ color: $footer-language-text--hover;
86
87
  background-color: $footer-language-bg--hover;
87
88
  }
88
89
  }
89
90
 
90
91
  .layout-footer-language__badge {
91
92
  @include locale-badge;
93
+
94
+ color: $footer-language-badge;
92
95
  }
93
96
 
94
97
  .layout-footer-language__angle {
@@ -1,73 +1,95 @@
1
1
  <template>
2
2
  <header
3
- :class="{'--burger-open': burgerShow, '--no-notifications': !featureFlags.FeatureFollowEnabled}"
3
+ :class="{
4
+ '--burger-open': burgerShow,
5
+ '--no-notifications': !featureFlags.FeatureFollowEnabled,
6
+ }"
4
7
  class="layout-header"
5
8
  >
6
9
 
7
10
  <SearchMain class="layout-header__search"/>
8
11
 
9
- <LayoutLogo
10
- glow
11
- class="layout-header__logo"
12
- />
12
+ <div class="layout-header__left">
13
+ <LayoutLogo
14
+ glow
15
+ class="layout-header__logo"
16
+ />
13
17
 
14
- <LayoutNiche
15
- class="layout-header__niche _from-sd"
16
- icon-size="20"
17
- />
18
- <LayoutNotifications
19
- v-if="featureFlags.FeatureFollowEnabled"
20
- class="layout-header__notifications"
21
- />
18
+ <LayoutNiche
19
+ class="layout-header__niche _from-sd"
20
+ icon-size="20"
21
+ />
22
+ </div>
22
23
 
23
- <div
24
- ref="languageRef"
25
- class="layout-header__language _from-sd"
26
- >
27
- <button
28
- :class="{'--open': showLanguage}"
29
- :aria-label="$t('language')"
30
- class="layout-header__language-trigger"
31
- type="button"
32
- @click="showLanguageMenu"
24
+ <div class="layout-header__right">
25
+ <LayoutNotifications
26
+ v-if="featureFlags.FeatureFollowEnabled"
27
+ class="layout-header__notifications"
28
+ />
29
+ <NuxtLink
30
+ v-if="featureFlags.FeatureUploadEnabled"
31
+ :to="uploadLink"
32
+ :aria-current="isUploadPage ? 'page' : undefined"
33
+ :tabindex="isUploadPage ? -1 : undefined"
34
+ :class="{'--current': isUploadPage}"
35
+ class="layout-header__upload _from-sd"
33
36
  >
34
37
  <UiIcon
35
- class="layout-header__language-icon"
36
- name="earth"
37
- size="16"
38
+ name="upload-arrow"
39
+ size="18"
40
+ aria-hidden="true"
38
41
  />
39
- <span class="layout-header__language-badge">{{ locale }}</span>
40
- <UiIcon
41
- class="layout-header__language-angle"
42
- name="angle-down"
43
- size="12"
44
- />
45
- </button>
46
- <transition mode="out-in">
47
- <KeepAlive>
48
- <LazyLayoutLanguage
49
- v-if="showLanguage"
50
- class="layout-header__language-menu"
51
- icon-size="20"
52
- @change="closeLanguage"
42
+ {{ $t('upload') }}
43
+ </NuxtLink>
44
+ <div
45
+ ref="languageRef"
46
+ class="layout-header__language _from-sd"
47
+ >
48
+ <button
49
+ :class="{'--open': showLanguage}"
50
+ :aria-label="$t('language')"
51
+ class="layout-header__language-trigger"
52
+ type="button"
53
+ @click="showLanguageMenu"
54
+ >
55
+ <UiIcon
56
+ class="layout-header__language-icon"
57
+ name="earth"
58
+ size="16"
53
59
  />
54
- </KeepAlive>
55
- </transition>
56
- </div>
57
-
58
- <LazyLayoutHeaderUser
59
- v-if="isAuthorized"
60
- class="layout-header__user _from-sd"
61
- />
60
+ <span class="layout-header__language-badge">{{ locale }}</span>
61
+ <UiIcon
62
+ class="layout-header__language-angle"
63
+ name="angle-down"
64
+ size="12"
65
+ />
66
+ </button>
67
+ <transition mode="out-in">
68
+ <KeepAlive>
69
+ <LazyLayoutLanguage
70
+ v-if="showLanguage"
71
+ class="layout-header__language-menu"
72
+ icon-size="20"
73
+ @change="closeLanguage"
74
+ />
75
+ </KeepAlive>
76
+ </transition>
77
+ </div>
78
+
79
+ <LazyLayoutHeaderUser
80
+ v-if="isAuthorized"
81
+ class="layout-header__user _from-sd"
82
+ />
62
83
 
63
- <template v-else>
64
- <UiBtn
65
- class="layout-header__sign-up _from-sd"
66
- theme="primary"
67
- @click="openAuthPopup(AuthSteps.Registration)"
68
- >{{ $t('sign_up_button') }}
69
- </UiBtn>
70
- </template>
84
+ <template v-else>
85
+ <UiBtn
86
+ class="layout-header__sign-up _from-sd"
87
+ theme="primary"
88
+ @click="openAuthPopup(AuthSteps.Registration)"
89
+ >{{ $t('sign_up_button') }}
90
+ </UiBtn>
91
+ </template>
92
+ </div>
71
93
 
72
94
  <button
73
95
  :aria-label="$t('show_menu')"
@@ -95,6 +117,11 @@ import { AuthorizationApiService } from '../../services/api/authorization.servic
95
117
 
96
118
  const { locale } = useI18n();
97
119
  const featureFlags = useRuntimeConfig().public.featureFlags;
120
+ const route = useRoute();
121
+ const { generateLink } = useGenerateLink();
122
+
123
+ const uploadLink = computed(() => generateLink('/upload'));
124
+ const isUploadPage = computed(() => route.path === uploadLink.value);
98
125
 
99
126
  const burgerShow = ref(false);
100
127
  const showLanguage = ref(false);
@@ -127,7 +154,12 @@ function closeLanguage() {
127
154
  width: 100%;
128
155
 
129
156
  @include from-br($brkpnt-burger) {
130
- grid-template-columns: auto auto minmax(0, 1fr) auto auto auto;
157
+ grid-template-columns: minmax(max-content, 1fr) minmax(0, $search-max-width) minmax(max-content, 1fr);
158
+ justify-content: stretch;
159
+ }
160
+
161
+ @include from-br(md) {
162
+ grid-template-columns: minmax(max-content, 1fr) minmax(0, $search-max-width-md) minmax(max-content, 1fr);
131
163
  }
132
164
 
133
165
  /* поиск | лого | колокольчик | бургер — 1fr держит лого по центру */
@@ -138,34 +170,38 @@ function closeLanguage() {
138
170
  }
139
171
 
140
172
  .layout-header.--no-notifications {
141
- @include from-br($brkpnt-burger) {
142
- grid-template-columns: auto auto minmax(0, 1fr) auto auto;
143
- }
144
-
145
173
  @include to-br($brkpnt-burger) {
146
174
  grid-template-columns: auto 1fr auto;
147
175
  }
148
176
  }
149
177
 
150
- .layout-header.--no-notifications .layout-header__language {
178
+ .layout-header__left,
179
+ .layout-header__right {
180
+ display: contents;
181
+
151
182
  @include from-br($brkpnt-burger) {
152
- grid-column: 4;
183
+ display: flex;
184
+ align-items: center;
185
+ gap: 12px;
186
+ grid-row: 1;
153
187
  }
154
188
  }
155
189
 
156
- .layout-header.--no-notifications .layout-header__user,
157
- .layout-header.--no-notifications .layout-header__sign-up {
190
+ .layout-header__left {
158
191
  @include from-br($brkpnt-burger) {
159
- grid-column: 5;
192
+ grid-column: 1;
193
+ justify-self: start;
160
194
  }
161
195
  }
162
196
 
163
- .layout-header__logo {
197
+ .layout-header__right {
164
198
  @include from-br($brkpnt-burger) {
165
- grid-column: 1;
166
- grid-row: 1;
199
+ grid-column: 3;
200
+ justify-self: end;
167
201
  }
202
+ }
168
203
 
204
+ .layout-header__logo {
169
205
  @include to-br($brkpnt-burger) {
170
206
  justify-self: center;
171
207
  }
@@ -189,58 +225,46 @@ function closeLanguage() {
189
225
  }
190
226
  }
191
227
 
192
- /* Поиск живёт в своей колонке (свободный 1fr между лого и правым блоком), а не оверлеем
193
- поверх всей строки: так он сжимается вместе с ней и на него ничто не наезжает */
194
-
195
- /**
196
- * Поиск стоит строго по центру ШАПКИ, а не по центру свободного места: он растянут
197
- * на всю строку сетки и центрирован `justify-self`. Иначе центр уезжал бы к более
198
- * узкой группе: левая (лого + ниша) и правая (колокольчик, язык, вход) разной ширины.
199
- *
200
- * Наезжать при этом не на что: по обе стороны резервируется `--header-side-space`
201
- * — ширина более широкой группы (сейчас левая: лого + ниша ~340, правая ~290).
202
- * Считается БЕЗ бокового паддинга шапки, потому что `100%` у поиска — это уже
203
- * контентная ширина. Не хватило места — поиск сужается, а боковые группы остаются
204
- * на своих местах. Значение вынесено в переменную: у сайта без переключателя ниш
205
- * левая группа уже, и он может его уменьшить.
206
- */
207
228
  .layout-header__search {
208
- --header-side-space: 358px;
209
-
210
229
  @include from-br($brkpnt-burger) {
211
- grid-column: 1 / -1;
230
+ grid-column: 2;
212
231
  grid-row: 1;
213
- justify-self: center;
214
232
  width: 100%;
215
233
  min-width: 0;
216
-
217
- // 24px — два column-gap шапки по бокам от поиска
218
- max-width: min($search-max-width, calc(100% - 2 * var(--header-side-space) - 24px));
219
234
  }
235
+ }
220
236
 
221
- @include from-br(md) {
222
- max-width: min($search-max-width-md, calc(100% - 2 * var(--header-side-space) - 24px));
237
+ .layout-header__upload {
238
+ @include font-sm;
239
+
240
+ display: flex;
241
+ align-items: center;
242
+ gap: 8px;
243
+ height: 36px;
244
+ padding-inline: 12px;
245
+ color: $header-upload-text;
246
+ background-color: $header-upload-bg;
247
+ border: 1px solid $header-upload-border;
248
+ border-radius: $radius-md;
249
+
250
+ @include hover(border-color) {
251
+ border-color: $header-upload-border--hover;
223
252
  }
224
253
  }
225
254
 
255
+ .layout-header__upload.--current {
256
+ cursor: default;
257
+ pointer-events: none;
258
+ }
259
+
226
260
  .layout-header__user {
227
261
  display: grid;
228
262
  place-content: center;
229
263
  color: $header-user-icon;
230
264
  }
231
265
 
232
- .layout-header__user,
233
- .layout-header__sign-up {
234
- @include from-br($brkpnt-burger) {
235
- grid-column: 6;
236
- grid-row: 1;
237
- }
238
- }
239
-
240
266
  .layout-header__niche {
241
267
  @include from-br($brkpnt-burger) {
242
- grid-column: 2;
243
- grid-row: 1;
244
268
  margin-left: 4px;
245
269
  }
246
270
  }
@@ -251,18 +275,9 @@ function closeLanguage() {
251
275
  margin: 0 16px;
252
276
  }
253
277
 
254
- .layout-header__notifications {
255
- @include from-br($brkpnt-burger) {
256
- grid-column: 4;
257
- grid-row: 1;
258
- }
259
- }
260
-
261
278
  .layout-header__language {
262
279
  @include from-br($brkpnt-burger) {
263
280
  position: relative;
264
- grid-column: 5;
265
- grid-row: 1;
266
281
  }
267
282
  }
268
283
 
@@ -287,6 +302,8 @@ function closeLanguage() {
287
302
 
288
303
  .layout-header__language-badge {
289
304
  @include locale-badge;
305
+
306
+ color: $header-language-badge;
290
307
  }
291
308
 
292
309
  .layout-header__language-angle {
@@ -23,7 +23,7 @@
23
23
  size="l"
24
24
  theme="secondary"
25
25
  @click="emit('update:modelValue', false)"
26
- >{{ $t('cancel') }}
26
+ >{{ cancelText || $t('cancel') }}
27
27
  </UiBtn>
28
28
  <UiBtn
29
29
  :disabled="loading"
@@ -44,6 +44,7 @@ defineProps<{
44
44
  modelValue: boolean
45
45
  title: string
46
46
  confirmText: string
47
+ cancelText?: string
47
48
  loading?: boolean
48
49
  transparentBackdrop?: boolean
49
50
  }>();
@@ -34,7 +34,7 @@ import type { ButtonThemes } from '../../types';
34
34
  withDefaults(
35
35
  defineProps<{
36
36
  theme?: ButtonThemes,
37
- size?: 'm' | 's',
37
+ size?: 'm' | 's' | 'l',
38
38
  wide?: boolean,
39
39
  active?: boolean,
40
40
  disabled?: boolean,