itube-specs 0.0.866 → 0.0.868
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.
- package/assets/scss/themes/dark/_theme.scss +12 -0
- package/assets/scss/themes/light/_theme.scss +12 -0
- package/components/layout/burger.vue +9 -0
- package/components/layout/header.vue +15 -3
- package/components/layout/niche.vue +179 -0
- package/composables/use-niches.ts +47 -0
- package/package.json +1 -1
|
@@ -338,6 +338,18 @@ $language-value-text: $color-white;
|
|
|
338
338
|
$language-description: $color-zinc-500;
|
|
339
339
|
$language-empty-text: $color-zinc-300;
|
|
340
340
|
|
|
341
|
+
// ─── Niche ───
|
|
342
|
+
$niche-trigger-text: $color-zinc-400;
|
|
343
|
+
$niche-angle--hover: $color-white;
|
|
344
|
+
$niche-title: $color-zinc-500;
|
|
345
|
+
$niche-value-text: $color-white;
|
|
346
|
+
$niche-menu-bg: $color-night;
|
|
347
|
+
$niche-menu-border: $color-zinc-800;
|
|
348
|
+
$niche-item-text: $color-zinc-300;
|
|
349
|
+
$niche-item-bg--hover: $color-zinc-800;
|
|
350
|
+
$niche-item-text--current: $color-white;
|
|
351
|
+
$niche-item-bg--current: $color-zinc-700;
|
|
352
|
+
|
|
341
353
|
// ─── Footer language ───
|
|
342
354
|
$footer-language-bg: $color-zinc-800-50;
|
|
343
355
|
$footer-language-bg--hover: $color-zinc-700;
|
|
@@ -395,6 +395,18 @@ $language-value-text: $color-white;
|
|
|
395
395
|
$language-description: $color-gray-500;
|
|
396
396
|
$language-empty-text: $color-gray-400;
|
|
397
397
|
|
|
398
|
+
// ─── Niche ───
|
|
399
|
+
$niche-trigger-text: $color-gray-500;
|
|
400
|
+
$niche-angle--hover: $color-gray-300;
|
|
401
|
+
$niche-title: $color-gray-400;
|
|
402
|
+
$niche-value-text: $color-white;
|
|
403
|
+
$niche-menu-bg: $color-slate-850;
|
|
404
|
+
$niche-menu-border: $color-white-10;
|
|
405
|
+
$niche-item-text: $color-gray-300;
|
|
406
|
+
$niche-item-bg--hover: $color-slate-750;
|
|
407
|
+
$niche-item-text--current: $color-white;
|
|
408
|
+
$niche-item-bg--current: $color-indigo-soft;
|
|
409
|
+
|
|
398
410
|
// ─── Footer language ───
|
|
399
411
|
$footer-language-bg: $color-slate-750;
|
|
400
412
|
$footer-language-bg--hover: $color-slate-700;
|
|
@@ -68,6 +68,11 @@
|
|
|
68
68
|
</div>
|
|
69
69
|
</nav>
|
|
70
70
|
|
|
71
|
+
<LayoutNiche
|
|
72
|
+
class="layout-burger__niche"
|
|
73
|
+
icon-size="20"
|
|
74
|
+
/>
|
|
75
|
+
|
|
71
76
|
<LayoutLanguage
|
|
72
77
|
class="layout-burger__language"
|
|
73
78
|
icon-size="20"
|
|
@@ -257,6 +262,10 @@ router.afterEach(() => {
|
|
|
257
262
|
padding-bottom: 12px;
|
|
258
263
|
}
|
|
259
264
|
|
|
265
|
+
.layout-burger__niche {
|
|
266
|
+
margin-bottom: 12px;
|
|
267
|
+
}
|
|
268
|
+
|
|
260
269
|
.layout-burger__close {
|
|
261
270
|
position: fixed;
|
|
262
271
|
top: 16px;
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
glow
|
|
11
11
|
class="layout-header__logo"
|
|
12
12
|
/>
|
|
13
|
+
<LayoutNiche
|
|
14
|
+
class="layout-header__niche _from-sm"
|
|
15
|
+
icon-size="20"
|
|
16
|
+
/>
|
|
17
|
+
|
|
13
18
|
<div
|
|
14
19
|
ref="languageRef"
|
|
15
20
|
class="layout-header__language _from-sm"
|
|
@@ -135,7 +140,7 @@ $breakpoint: sm;
|
|
|
135
140
|
width: 100%;
|
|
136
141
|
|
|
137
142
|
@include from-br(sm) {
|
|
138
|
-
grid-template-columns: auto 1fr auto auto;
|
|
143
|
+
grid-template-columns: auto 1fr auto auto auto;
|
|
139
144
|
}
|
|
140
145
|
}
|
|
141
146
|
|
|
@@ -180,7 +185,14 @@ $breakpoint: sm;
|
|
|
180
185
|
.layout-header__user,
|
|
181
186
|
.layout-header__sign-up {
|
|
182
187
|
@include from-br(sm) {
|
|
183
|
-
grid-column:
|
|
188
|
+
grid-column: 5;
|
|
189
|
+
grid-row: 1;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.layout-header__niche {
|
|
194
|
+
@include from-br(sm) {
|
|
195
|
+
grid-column: 3;
|
|
184
196
|
grid-row: 1;
|
|
185
197
|
}
|
|
186
198
|
}
|
|
@@ -194,7 +206,7 @@ $breakpoint: sm;
|
|
|
194
206
|
.layout-header__language {
|
|
195
207
|
@include from-br(sm) {
|
|
196
208
|
position: relative;
|
|
197
|
-
grid-column:
|
|
209
|
+
grid-column: 4;
|
|
198
210
|
grid-row: 1;
|
|
199
211
|
}
|
|
200
212
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="niches.length > 1"
|
|
4
|
+
ref="nicheRef"
|
|
5
|
+
class="layout-niche"
|
|
6
|
+
@keydown.esc="isOpen = false"
|
|
7
|
+
>
|
|
8
|
+
<button
|
|
9
|
+
:class="{'--open': isOpen}"
|
|
10
|
+
:aria-expanded="isOpen"
|
|
11
|
+
:aria-label="$t('niche')"
|
|
12
|
+
class="layout-niche__trigger"
|
|
13
|
+
type="button"
|
|
14
|
+
@click="isOpen = !isOpen"
|
|
15
|
+
>
|
|
16
|
+
<UiIcon
|
|
17
|
+
:name="currentNiche.icon"
|
|
18
|
+
:size="iconSize"
|
|
19
|
+
class="layout-niche__icon"
|
|
20
|
+
/>
|
|
21
|
+
<span class="layout-niche__current _to-sm">
|
|
22
|
+
<span>{{ $t('niche') }}: </span>
|
|
23
|
+
<span class="layout-niche__text">{{ $t(currentNiche.label) }}</span>
|
|
24
|
+
</span>
|
|
25
|
+
<UiIcon
|
|
26
|
+
class="layout-niche__angle"
|
|
27
|
+
name="angle-down"
|
|
28
|
+
size="12"
|
|
29
|
+
/>
|
|
30
|
+
</button>
|
|
31
|
+
<ul
|
|
32
|
+
v-if="isOpen"
|
|
33
|
+
class="layout-niche__list"
|
|
34
|
+
>
|
|
35
|
+
<li
|
|
36
|
+
v-for="item in niches"
|
|
37
|
+
:key="`niche-${item.name}`"
|
|
38
|
+
>
|
|
39
|
+
<button
|
|
40
|
+
:class="{'--current': item.name === currentNiche.name}"
|
|
41
|
+
:aria-current="item.name === currentNiche.name ? 'true' : undefined"
|
|
42
|
+
class="layout-niche__item"
|
|
43
|
+
type="button"
|
|
44
|
+
@click="select(item.name)"
|
|
45
|
+
>
|
|
46
|
+
<UiIcon
|
|
47
|
+
:name="item.icon"
|
|
48
|
+
class="layout-niche__item-icon"
|
|
49
|
+
size="20"
|
|
50
|
+
/>
|
|
51
|
+
{{ $t(item.label) }}
|
|
52
|
+
</button>
|
|
53
|
+
</li>
|
|
54
|
+
</ul>
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<script setup lang="ts">
|
|
59
|
+
import { onClickOutside } from '@vueuse/core';
|
|
60
|
+
import type { Niche } from '../../runtime';
|
|
61
|
+
|
|
62
|
+
withDefaults(defineProps<{
|
|
63
|
+
iconSize?: string
|
|
64
|
+
}>(), {
|
|
65
|
+
iconSize: '20'
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const { niches, currentNiche, changeNiche } = useNiches();
|
|
70
|
+
|
|
71
|
+
const nicheRef = ref<HTMLElement>();
|
|
72
|
+
const isOpen = ref(false);
|
|
73
|
+
|
|
74
|
+
onClickOutside(nicheRef, () => {
|
|
75
|
+
isOpen.value = false;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
function select(name: Niche) {
|
|
79
|
+
isOpen.value = false;
|
|
80
|
+
changeNiche(name);
|
|
81
|
+
}
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<style lang="scss">
|
|
85
|
+
.layout-niche {
|
|
86
|
+
display: grid;
|
|
87
|
+
|
|
88
|
+
@include from-br(sm) {
|
|
89
|
+
position: relative;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.layout-niche__trigger {
|
|
94
|
+
display: grid;
|
|
95
|
+
grid-auto-flow: column;
|
|
96
|
+
align-items: center;
|
|
97
|
+
justify-content: start;
|
|
98
|
+
column-gap: 6px;
|
|
99
|
+
padding: 6px 8px;
|
|
100
|
+
color: $niche-trigger-text;
|
|
101
|
+
|
|
102
|
+
@include to-br(sm) {
|
|
103
|
+
grid-template-columns: auto 1fr auto;
|
|
104
|
+
width: 100%;
|
|
105
|
+
padding: 10px 16px;
|
|
106
|
+
column-gap: 16px;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.layout-niche__current {
|
|
111
|
+
@include font-sm;
|
|
112
|
+
|
|
113
|
+
display: grid;
|
|
114
|
+
grid-template-columns: auto 1fr;
|
|
115
|
+
color: $niche-title;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.layout-niche__text {
|
|
119
|
+
color: $niche-value-text;
|
|
120
|
+
font-weight: 500;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.layout-niche__angle {
|
|
124
|
+
pointer-events: none;
|
|
125
|
+
transition: transform var(--transition), color var(--transition);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.layout-niche__trigger:hover .layout-niche__angle {
|
|
129
|
+
color: $niche-angle--hover;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.layout-niche__trigger.--open .layout-niche__angle {
|
|
133
|
+
transform: rotate(180deg);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.layout-niche__list {
|
|
137
|
+
display: grid;
|
|
138
|
+
|
|
139
|
+
@include from-br(sm) {
|
|
140
|
+
position: absolute;
|
|
141
|
+
right: 0;
|
|
142
|
+
bottom: -8px;
|
|
143
|
+
z-index: $z-index-plus;
|
|
144
|
+
min-width: 180px;
|
|
145
|
+
padding: 8px;
|
|
146
|
+
background-color: $niche-menu-bg;
|
|
147
|
+
border: 1px solid $niche-menu-border;
|
|
148
|
+
border-radius: $radius-xl;
|
|
149
|
+
transform: translateY(100%);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.layout-niche__item {
|
|
154
|
+
@include font-sm;
|
|
155
|
+
|
|
156
|
+
display: grid;
|
|
157
|
+
grid-template-columns: 20px 1fr;
|
|
158
|
+
align-items: center;
|
|
159
|
+
column-gap: 12px;
|
|
160
|
+
width: 100%;
|
|
161
|
+
padding: 10px 16px;
|
|
162
|
+
color: $niche-item-text;
|
|
163
|
+
text-align: start;
|
|
164
|
+
border-radius: $radius-lg;
|
|
165
|
+
|
|
166
|
+
@include hover(background-color) {
|
|
167
|
+
background-color: $niche-item-bg--hover;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@include from-br(sm) {
|
|
171
|
+
padding: 8px 10px;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.layout-niche__item.--current {
|
|
176
|
+
color: $niche-item-text--current;
|
|
177
|
+
background-color: $niche-item-bg--current;
|
|
178
|
+
}
|
|
179
|
+
</style>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Niche } from '../runtime';
|
|
2
|
+
import { NicheHelper } from '../utils/niche-helper';
|
|
3
|
+
|
|
4
|
+
/** Иконка из assets/icons/common под каждую нишу. */
|
|
5
|
+
const NICHE_ICONS: Record<Niche, string> = {
|
|
6
|
+
straight: 'hetero',
|
|
7
|
+
gay: 'gay',
|
|
8
|
+
shemale: 'shemale',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface INicheItem {
|
|
12
|
+
name: Niche,
|
|
13
|
+
icon: string,
|
|
14
|
+
/** Ключ i18n с названием ниши */
|
|
15
|
+
label: string,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Ниши сайта для переключателя: список из per-site конфига проекта (appConfig.niches),
|
|
20
|
+
* текущая — из useState('niche') с фолбэком на дефолтную нишу окружения.
|
|
21
|
+
*/
|
|
22
|
+
export const useNiches = () => {
|
|
23
|
+
const defaultNiche = NicheHelper.getSiteDefaultNiche();
|
|
24
|
+
const current = useState<Niche>('niche', () => defaultNiche);
|
|
25
|
+
const localePath = useLocalePath();
|
|
26
|
+
|
|
27
|
+
const niches = computed<INicheItem[]>(() =>
|
|
28
|
+
((useAppConfig().niches as Niche[]) || []).map(name => ({
|
|
29
|
+
name,
|
|
30
|
+
icon: NICHE_ICONS[name] || 'earth',
|
|
31
|
+
label: `niches.${name}`,
|
|
32
|
+
})));
|
|
33
|
+
|
|
34
|
+
const currentNiche = computed(() => niches.value.find(item => item.name === current.value) || niches.value[0]);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Ниша влияет и на роутинг (префикс пути), и на заголовок X-Niche в SSR-запросах,
|
|
38
|
+
* поэтому переключение — полной перезагрузкой на корень выбранной ниши.
|
|
39
|
+
*/
|
|
40
|
+
function changeNiche(name: Niche) {
|
|
41
|
+
if (name === current.value) return;
|
|
42
|
+
current.value = name;
|
|
43
|
+
window.location.href = localePath(name === defaultNiche ? '/' : `/${name}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { niches, currentNiche, changeNiche };
|
|
47
|
+
};
|