itube-specs 0.0.897 → 0.0.899

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.
@@ -2,14 +2,16 @@
2
2
  <NuxtLink
3
3
  :class="{'layout-logo--glow': glow}"
4
4
  :to="generateLink('/')"
5
+ active-class=""
6
+ exact-active-class=""
5
7
  class="layout-logo"
6
8
  @click="onLogoClick"
7
9
  >
8
10
  <img
9
11
  class="layout-logo__image"
10
12
  src="/img/logo.svg"
11
- width="144"
12
- height="24"
13
+ width="120"
14
+ height="20"
13
15
  alt="gay.cool"
14
16
  >
15
17
  </NuxtLink>
@@ -38,4 +40,11 @@ function onLogoClick() {
38
40
  opacity: 0.9;
39
41
  }
40
42
  }
43
+
44
+ .layout-logo__image {
45
+ @include from-br(sm) {
46
+ width: 144px;
47
+ height: 24px;
48
+ }
49
+ }
41
50
  </style>
@@ -0,0 +1,79 @@
1
+ <!-- Колокольчик уведомлений в шапке: ссылка на подписки + счётчик обновлений. Данные пока заглушка -->
2
+ <template>
3
+ <NuxtLink
4
+ :to="generateLink('/following')"
5
+ :aria-label="t('notifications')"
6
+ :title="t('notifications')"
7
+ class="layout-notifications"
8
+ >
9
+ <UiIcon
10
+ class="layout-notifications__icon"
11
+ name="bell"
12
+ size="24"
13
+ />
14
+ <span
15
+ v-if="count > 0"
16
+ class="layout-notifications__badge"
17
+ >{{ badge }}</span>
18
+ </NuxtLink>
19
+ </template>
20
+
21
+ <script setup lang="ts">
22
+ import { getFollowingUpdatesTotal } from '../../stubs/library-follows';
23
+
24
+ const MAX_COUNT = 99;
25
+
26
+ const { t } = useI18n();
27
+ const { generateLink } = useGenerateLink();
28
+
29
+ const count = computed(() => getFollowingUpdatesTotal());
30
+
31
+ const badge = computed(() => count.value > MAX_COUNT ? `${MAX_COUNT}+` : `${count.value}`);
32
+ </script>
33
+
34
+ <style lang="scss">
35
+ /* Бейдж лежит в потоке и наезжает на иконку отрицательным отступом: разряды уходят
36
+ вправо, растягивая контрол. Место под «99+» не резервируется, а зазор от края
37
+ бейджа до соседа в шапке одинаков при любом числе — его держит $badge-inset. */
38
+ $padding: 8px;
39
+ $badge-inset: 2px;
40
+ $badge-overlap: 10px;
41
+ $badge-raise: 6px;
42
+
43
+ .layout-notifications {
44
+ display: inline-flex;
45
+ align-items: center;
46
+ padding: $padding $badge-inset $padding $padding;
47
+ color: $notifications-icon;
48
+
49
+ /* На мобиле это такой же тап-контрол, как бургер рядом */
50
+ @include to-br(sm) {
51
+ color: $notifications-icon--mobile;
52
+ }
53
+
54
+ @include hover(color) {
55
+ color: $notifications-icon--hover;
56
+ }
57
+ }
58
+
59
+ .layout-notifications__badge {
60
+ @include font-4xs;
61
+
62
+ /* Левым краем на верхний правый угол иконки, дальше растёт вправо */
63
+ position: relative;
64
+ z-index: $z-index-plus;
65
+ display: grid;
66
+ place-content: center;
67
+ align-self: flex-start;
68
+ min-width: 16px;
69
+ height: 16px;
70
+ margin-top: -$badge-raise;
71
+ margin-left: -$badge-overlap;
72
+ padding-inline: 4px;
73
+ font-weight: 700;
74
+ color: $notifications-badge-text;
75
+ background-color: $notifications-badge-bg;
76
+ border-radius: $radius-full;
77
+ box-shadow: 0 0 0 2px $notifications-badge-ring;
78
+ }
79
+ </style>
@@ -1,13 +1,30 @@
1
1
  <!-- Горизонтальный свайпер аватаров моделей с заголовком и ссылкой «все» -->
2
2
  <template>
3
3
  <section class="section-swiper-cards">
4
- <UiSectionTitle
5
- :title="title || t('top_models')"
6
- :count="items.length"
7
- class="section-swiper-cards__title"
8
- icon="user"
9
- title-tag="h2"
10
- />
4
+ <header class="section-swiper-cards__header">
5
+ <UiSectionTitle
6
+ :title="title || t('top_models')"
7
+ :count="count ?? items.length"
8
+ :icon="icon"
9
+ :size="size"
10
+ class="section-swiper-cards__title"
11
+ title-tag="h2"
12
+ />
13
+
14
+ <!-- На мобиле плитки «все» в ленте нет, поэтому ссылка вынесена в шапку -->
15
+ <UiLink
16
+ v-if="allLink"
17
+ :to="generateLink(allLink)"
18
+ class="section-swiper-cards__link _to-sm"
19
+ theme="simple"
20
+ >
21
+ {{ headerLinkText ?? allLinkText }}
22
+ <UiIcon
23
+ name="arrow-right"
24
+ size="16"
25
+ />
26
+ </UiLink>
27
+ </header>
11
28
  <div class="section-swiper-cards__grid">
12
29
  <UiSwiper
13
30
  :items="items"
@@ -17,6 +34,7 @@
17
34
  <CardRound
18
35
  :card="item"
19
36
  :placeholder-avatar="placeholderAvatar"
37
+ :without-count="withoutCount"
20
38
  class="section-swiper-cards__card"
21
39
  />
22
40
  </template>
@@ -24,11 +42,12 @@
24
42
  v-if="allLink"
25
43
  #append
26
44
  >
27
- <NuxtLink
28
- :to="generateLink(allLink ?? '/')"
29
- class="section-swiper-cards__all"
30
- >{{ allLinkText }}
31
- </NuxtLink>
45
+ <UiViewAll
46
+ :to="allLink ?? '/'"
47
+ :text="allLinkText ?? ''"
48
+ :note="allLinkNote"
49
+ mini
50
+ />
32
51
  </template>
33
52
  </UiSwiper>
34
53
  </div>
@@ -38,14 +57,27 @@
38
57
  <script setup lang="ts">
39
58
  import type { IRoundCard } from '../../types';
40
59
 
41
- defineProps<{
60
+ withDefaults(defineProps<{
42
61
  items: IRoundCard[]
43
62
  onlyGrid?: boolean
44
63
  allLink?: string
45
64
  allLinkText?: string
65
+ /** Вторая строка в плитке «все» — «16 models» */
66
+ allLinkNote?: string
67
+ /** Подпись ссылки в шапке («All models»); по умолчанию — как в плитке */
68
+ headerLinkText?: string
46
69
  title?: string
70
+ /** Счётчик в шапке; по умолчанию — число карточек */
71
+ count?: string | number
72
+ icon?: string
73
+ /** Размер заголовка секции — см. `UiSectionTitle` */
74
+ size?: 'lg' | 'xl'
47
75
  placeholderAvatar?: string
48
- }>();
76
+ /** Карточки только с аватаром и названием, без счётчика роликов */
77
+ withoutCount?: boolean
78
+ }>(), {
79
+ icon: 'user'
80
+ });
49
81
 
50
82
  const { t } = useI18n();
51
83
  const { generateLink } = useGenerateLink();
@@ -58,12 +90,22 @@ const { generateLink } = useGenerateLink();
58
90
  min-width: 0;
59
91
  }
60
92
 
93
+ .section-swiper-cards__header {
94
+ display: flex;
95
+ align-items: center;
96
+ justify-content: space-between;
97
+ column-gap: 16px;
98
+ margin-bottom: 16px;
99
+ }
100
+
101
+ /* text-transform здесь не нужен: капс и трекинг заголовку даёт его собственный
102
+ `._title`, а на корне он красил заодно и чип счётчика («6 UPDATES»). */
61
103
  .section-swiper-cards__title {
62
104
  @include font-lg;
105
+ }
63
106
 
64
- margin-bottom: 16px;
65
- text-transform: uppercase;
66
- letter-spacing: var(--tracking-wide);
107
+ .section-swiper-cards__link {
108
+ flex-shrink: 0;
67
109
  }
68
110
 
69
111
  .section-swiper-cards .section-swiper-cards__card {
@@ -74,24 +116,16 @@ const { generateLink } = useGenerateLink();
74
116
  min-width: 0;
75
117
  }
76
118
 
77
- .section-swiper-cards__all {
78
- @include font-sm;
79
-
80
- display: grid;
81
- place-content: center;
82
- width: $card-round-size;
83
- height: $card-round-size;
84
- aspect-ratio: 1/1;
85
- border-radius: $radius-full;
86
- object-fit: cover;
87
- border: 2px solid $card-round-border;
88
- background-color: $card-round-all-bg;
89
- color: $card-round-all-text;
90
- text-align: center;
119
+ /* Слайд ровно по круглой карточке: 80px на мобиле, 96px с sm */
120
+ .section-swiper-cards .ui-swiper__slide {
121
+ width: var(--card-round-size);
122
+ }
91
123
 
92
- @include hover(border-color, background-color) {
93
- border-color: $card-round-border--hover;
94
- background-color: $card-round-all-bg--hover;
124
+ /* Плитка «все» — только с sm, на мобиле её роль играет ссылка в шапке */
125
+ .section-swiper-cards .ui-swiper__slide--append {
126
+ @include to-br(sm) {
127
+ display: none;
95
128
  }
96
129
  }
130
+
97
131
  </style>
@@ -161,4 +161,19 @@ const slotText = computed(() => {
161
161
  background-color: var(--button-bg--hover, #{$button-warning-bg--hover});
162
162
  }
163
163
  }
164
+
165
+ /* Без подложки, рамки и паддингов — обычная текстовая ссылка с иконкой */
166
+ .ui-btn.--simple {
167
+ min-height: 0;
168
+ padding: 0;
169
+ column-gap: 4px;
170
+ color: var(--button-text, #{$button-simple-text});
171
+ background-color: transparent;
172
+ border-color: transparent;
173
+ border-radius: 0;
174
+
175
+ @include hover(color) {
176
+ color: var(--button-text--hover, #{$button-simple-text--hover});
177
+ }
178
+ }
164
179
  </style>
@@ -313,10 +313,6 @@ const { snackbarText, isSnackBarInPopup } = useSnackbar();
313
313
  .ui-popup__footer {
314
314
  padding: 16px 20px;
315
315
  border-top: 1px solid $popup-footer-border;
316
-
317
- @include from-br(sm) {
318
- padding: 16px 24px;
319
- }
320
316
  }
321
317
 
322
318
  .ui-popup[open].--drawer {
@@ -10,18 +10,19 @@
10
10
  >
11
11
  <h1
12
12
  v-if="titleTag === 'h1'"
13
- class="ui-section-title__title _title"
13
+ :class="titleClass"
14
14
  >{{ title }}</h1>
15
15
  <h2
16
16
  v-else-if="titleTag === 'h2'"
17
- class="ui-section-title__title _title"
17
+ :class="titleClass"
18
18
  >{{ title }}</h2>
19
19
  <h3
20
20
  v-else-if="titleTag === 'h3'"
21
- class="ui-section-title__title _title"
21
+ :class="titleClass"
22
22
  >{{ title }}</h3>
23
+ <!-- Без счётчика остаётся тот же чип, только с одной иконкой -->
23
24
  <UiCount
24
- v-if="count"
25
+ v-if="count || icon"
25
26
  :icon="icon"
26
27
  class="ui-section-title__count"
27
28
  >{{ count }}</UiCount>
@@ -29,15 +30,23 @@
29
30
  </template>
30
31
 
31
32
  <script setup lang="ts">
32
- withDefaults(defineProps<{
33
+ const props = withDefaults(defineProps<{
33
34
  count?: string | number
34
35
  titleTag?: string
35
36
  title: string
36
37
  icon?: string
37
38
  img?: string
39
+ /** `lg` — страничный заголовок (18px, капс); `xl` — заголовок секции (20px, без капса) */
40
+ size?: 'lg' | 'xl'
38
41
  }>(), {
39
- titleTag: 'h1'
42
+ titleTag: 'h1',
43
+ size: 'lg'
40
44
  });
45
+
46
+ const titleClass = computed(() => [
47
+ 'ui-section-title__title',
48
+ props.size === 'xl' ? '--xl' : '_title'
49
+ ]);
41
50
  </script>
42
51
 
43
52
  <style lang="scss">
@@ -48,6 +57,12 @@ withDefaults(defineProps<{
48
57
  vertical-align: middle;
49
58
  }
50
59
 
60
+ .ui-section-title__title.--xl {
61
+ @include font-xl;
62
+
63
+ font-weight: 600;
64
+ }
65
+
51
66
  .ui-section-title__flag,
52
67
  .ui-section-title__count.ui-count {
53
68
  display: inline-flex;
@@ -97,6 +97,10 @@ onMounted(() => {
97
97
  </script>
98
98
 
99
99
  <style lang="scss">
100
+ /* Запас под ring ховера карточек: у трека overflow-x: auto, а значит по спецификации
101
+ и overflow-y становится auto — без запаса тень обрезается по всем краям. */
102
+ $ring-room: 4px;
103
+
100
104
  .ui-swiper {
101
105
  position: relative;
102
106
 
@@ -113,8 +117,13 @@ onMounted(() => {
113
117
  scroll-snap-type: x mandatory;
114
118
  -webkit-overflow-scrolling: touch;
115
119
  scrollbar-width: none;
120
+ padding-block: $ring-room;
116
121
  padding-inline: var(--container-padding-inline);
117
122
 
123
+ /* Без scroll-padding mandatory-снап считает точкой привязки сам край скроллпорта
124
+ и на старте подтягивает первую карточку под padding — она липнет к краю вьюпорта. */
125
+ scroll-padding-inline: var(--container-padding-inline);
126
+
118
127
  &::-webkit-scrollbar {
119
128
  display: none;
120
129
  }
@@ -123,9 +132,12 @@ onMounted(() => {
123
132
  gap: 8px;
124
133
  }
125
134
 
135
+ /* Отрицательный margin гасит боковой запас, чтобы первая карточка осталась по краю */
126
136
  @include from-br(sm) {
127
137
  gap: 16px;
128
- padding-inline: 0;
138
+ padding-inline: $ring-room;
139
+ scroll-padding-inline: $ring-room;
140
+ margin-inline: -$ring-room;
129
141
  }
130
142
  }
131
143
 
@@ -144,7 +156,7 @@ $nav-gap: 4px;
144
156
  $size: 40px;
145
157
 
146
158
  position: absolute;
147
- top: calc(var(--slide-width) / 2);
159
+ top: calc(var(--slide-width) / 2 + #{$ring-room});
148
160
  transform: translateY(-50%);
149
161
  z-index: calc($z-index-plus + 2);
150
162
  display: grid;
@@ -165,15 +177,18 @@ $nav-gap: 4px;
165
177
  background-color: $swiper-nav-bg--hover;
166
178
  }
167
179
 
180
+ /* Трек вылезает за .ui-swiper на $ring-room, поэтому градиент уводим на
181
+ ($nav-gap + $ring-room) — иначе между затемнением и краем свайпера
182
+ остаётся полоска непокрытых карточек. */
168
183
  &::before {
169
184
  position: absolute;
170
185
  content: '';
171
186
  top: 50%;
172
- right: $nav-gap * -1;
187
+ right: -($nav-gap + $ring-room);
173
188
  transform: translateY(-50%);
174
189
  display: block;
175
190
  width: 64px;
176
- height: var(--slide-width);
191
+ height: calc(var(--slide-width) + #{$ring-room * 2});
177
192
  background: linear-gradient(270deg, $swiper-fade 0%, $swiper-fade-end 100%);
178
193
  }
179
194
 
@@ -0,0 +1,113 @@
1
+ <!-- Плитка-ссылка «смотреть все» в конце ленты: точечная текстура, стрелка и подпись. mini — круглая, под свайпер аватаров -->
2
+ <template>
3
+ <NuxtLink
4
+ :class="{'--mini': mini}"
5
+ :to="generateLink(to)"
6
+ class="ui-view-all"
7
+ >
8
+ <span
9
+ v-if="!mini"
10
+ class="ui-view-all__icon"
11
+ >
12
+ <UiIcon
13
+ name="arrow-right"
14
+ size="20"
15
+ />
16
+ </span>
17
+
18
+ <span class="ui-view-all__content">
19
+ <span class="ui-view-all__text">{{ text }}</span>
20
+ <span
21
+ v-if="note"
22
+ class="ui-view-all__note"
23
+ >{{ note }}</span>
24
+ </span>
25
+ </NuxtLink>
26
+ </template>
27
+
28
+ <script setup lang="ts">
29
+ defineProps<{
30
+ to: string
31
+ text: string
32
+ /** Вторая строка: «16 models» */
33
+ note?: string
34
+ /** Круглый вариант в размер аватар-карточки — для свайпера */
35
+ mini?: boolean
36
+ }>();
37
+
38
+ const { generateLink } = useGenerateLink();
39
+ </script>
40
+
41
+ <style lang="scss">
42
+ .ui-view-all {
43
+ display: grid;
44
+ place-content: center;
45
+ justify-items: center;
46
+ row-gap: 12px;
47
+ padding: 12px;
48
+ text-align: center;
49
+ border: 1px solid $view-all-border;
50
+ border-radius: $radius-xl;
51
+
52
+ /* точки поверх градиента — двумя слоями, без отдельной ноды-оверлея */
53
+ background-image:
54
+ radial-gradient(circle, $view-all-dots 1px, transparent 1px),
55
+ linear-gradient(to bottom right in oklab, $view-all-gradient-from, $view-all-gradient-via, $view-all-gradient-to);
56
+ background-size: 14px 14px, cover;
57
+
58
+ @include hover(border-color) {
59
+ border-color: $view-all-border--hover;
60
+ }
61
+ }
62
+
63
+ .ui-view-all__icon {
64
+ display: grid;
65
+ place-content: center;
66
+ width: 40px;
67
+ height: 40px;
68
+ color: $view-all-icon;
69
+ background-color: $view-all-icon-bg;
70
+ border: 1px solid $view-all-icon-border;
71
+ border-radius: $radius-full;
72
+ }
73
+
74
+ .ui-view-all__content {
75
+ display: grid;
76
+ row-gap: 2px;
77
+ }
78
+
79
+ .ui-view-all__text {
80
+ @include font-sm;
81
+
82
+ font-weight: 600;
83
+ color: $view-all-text;
84
+ }
85
+
86
+ .ui-view-all__note {
87
+ @include font-xs;
88
+
89
+ color: $view-all-note;
90
+ }
91
+
92
+ .ui-view-all.--mini {
93
+ width: var(--card-round-size);
94
+ height: var(--card-round-size);
95
+ padding: 8px;
96
+ row-gap: 0;
97
+ border-width: 2px;
98
+ border-color: $card-round-border;
99
+ border-radius: $radius-full;
100
+ background-size: 12px 12px, cover;
101
+
102
+ @include hover(border-color) {
103
+ border-color: $card-round-border--hover;
104
+ }
105
+ }
106
+
107
+ .ui-view-all.--mini .ui-view-all__text,
108
+ .ui-view-all.--mini .ui-view-all__note {
109
+ @include font-xs;
110
+
111
+ line-height: 1.2;
112
+ }
113
+ </style>
@@ -10,33 +10,6 @@ export const profileNavigationItems = [
10
10
  title: 'library',
11
11
  icon: 'library',
12
12
  group: 1,
13
- childs: [
14
- {
15
- value: '/user/profile?edit=true',
16
- title: 'videos',
17
- icon: 'video-camera',
18
- },
19
- {
20
- value: '/user/profile?edit=true',
21
- title: 'watch_later',
22
- icon: 'time',
23
- },
24
- {
25
- value: '/user/profile?edit=true',
26
- title: 'playlists',
27
- icon: 'list',
28
- },
29
- {
30
- value: '/user/profile?edit=true',
31
- title: 'models',
32
- icon: 'user',
33
- },
34
- {
35
- value: '/user/profile?edit=true',
36
- title: 'channels',
37
- icon: 'play-circle',
38
- },
39
- ]
40
13
  },
41
14
  {
42
15
  value: '/user/settings',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "itube-specs",
3
3
  "type": "module",
4
- "version": "0.0.897",
4
+ "version": "0.0.899",
5
5
  "main": "./nuxt.config.ts",
6
6
  "types": "./types/index.d.ts",
7
7
  "scripts": {