itube-specs 0.0.195
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/README.md +121 -0
- package/components/cards/f-video-mini-card.vue +49 -0
- package/components/grids/f-grid-categories.vue +20 -0
- package/components/grids/f-grid-channels.vue +23 -0
- package/components/grids/f-grid-models.vue +25 -0
- package/components/grids/f-grid-playlists.vue +21 -0
- package/components/grids/f-grid-videos.vue +33 -0
- package/components/page-components/f-breadcrumbs.vue +44 -0
- package/components/page-components/f-chips-panel.vue +101 -0
- package/components/page-components/f-pagination.vue +206 -0
- package/components/page-components/f-report.vue +221 -0
- package/components/page-components/f-share.vue +96 -0
- package/components/page-components/f-sort.vue +57 -0
- package/components/page-components/f-videos-title.vue +20 -0
- package/components/ui/f-button.vue +50 -0
- package/components/ui/f-checkbox.vue +55 -0
- package/components/ui/f-chips.vue +116 -0
- package/components/ui/f-count.vue +12 -0
- package/components/ui/f-country.vue +26 -0
- package/components/ui/f-dropdown.vue +122 -0
- package/components/ui/f-icon.vue +19 -0
- package/components/ui/f-img.vue +46 -0
- package/components/ui/f-input.vue +162 -0
- package/components/ui/f-label.vue +20 -0
- package/components/ui/f-link.vue +33 -0
- package/components/ui/f-model-tag.vue +28 -0
- package/components/ui/f-notification.vue +77 -0
- package/components/ui/f-popup.vue +136 -0
- package/components/ui/f-radio.vue +56 -0
- package/components/ui/f-select.vue +88 -0
- package/components/ui/f-slider.vue +55 -0
- package/components/ui/f-snackbar.vue +47 -0
- package/components/ui/f-timestamp.vue +51 -0
- package/components/ui/f-toggle.vue +29 -0
- package/composables/use-antiadblock-domains.ts +20 -0
- package/composables/use-auth-popup.ts +25 -0
- package/composables/use-convert-query-categories.ts +7 -0
- package/composables/use-generate-link.ts +30 -0
- package/composables/use-get-pure-route-name.ts +5 -0
- package/composables/use-get-videos-filter-request.ts +30 -0
- package/composables/use-meta.ts +42 -0
- package/composables/use-playlist-edit.ts +36 -0
- package/composables/use-report-popup.ts +21 -0
- package/composables/use-seo-links.ts +87 -0
- package/composables/use-share-popup.ts +23 -0
- package/composables/use-snackbar.ts +52 -0
- package/composables/use-test-composable.ts +3 -0
- package/lib/alphabet-items.ts +2 -0
- package/lib/contact-forms-scheme.ts +98 -0
- package/lib/contacts/report-issue-items.ts +5 -0
- package/lib/contacts/report-malware-items.ts +6 -0
- package/lib/contacts/report-reasons-items.ts +12 -0
- package/lib/contacts/report-wrong-items.ts +6 -0
- package/lib/index.ts +7 -0
- package/lib/report-forms-scheme.ts +205 -0
- package/nuxt.config.ts +20 -0
- package/package.json +53 -0
- package/runtime/enums/async-data.ts +48 -0
- package/runtime/enums/auth-step.ts +5 -0
- package/runtime/enums/contacts-subjects.ts +7 -0
- package/runtime/enums/languages.ts +9 -0
- package/runtime/enums/niche.ts +6 -0
- package/runtime/enums/playlist-step.ts +5 -0
- package/runtime/enums/playlist-type.ts +4 -0
- package/runtime/enums/report-forms-subjects.ts +7 -0
- package/runtime/index.ts +51 -0
- package/runtime/utils/cleaners/clean-category-card.ts +9 -0
- package/runtime/utils/cleaners/clean-category-info.ts +9 -0
- package/runtime/utils/cleaners/clean-channel-card.ts +12 -0
- package/runtime/utils/cleaners/clean-channel-info.ts +13 -0
- package/runtime/utils/cleaners/clean-model-card.ts +9 -0
- package/runtime/utils/cleaners/clean-model-info.ts +11 -0
- package/runtime/utils/cleaners/clean-playlist-card.ts +16 -0
- package/runtime/utils/cleaners/clean-playlist-data.ts +15 -0
- package/runtime/utils/cleaners/clean-playlist-video.ts +12 -0
- package/runtime/utils/cleaners/clean-profile-data.ts +11 -0
- package/runtime/utils/cleaners/clean-user-playlists-card.ts +11 -0
- package/runtime/utils/cleaners/clean-video-card.ts +19 -0
- package/runtime/utils/cleaners/clean-video-data.ts +27 -0
- package/runtime/utils/compress-image.ts +27 -0
- package/runtime/utils/converters/convert-categories-to-chips.ts +13 -0
- package/runtime/utils/converters/convert-categories-to-footer.ts +11 -0
- package/runtime/utils/converters/convert-date-to-timestamp.ts +37 -0
- package/runtime/utils/converters/convert-model-card-to-chips.ts +13 -0
- package/runtime/utils/converters/convert-string.ts +56 -0
- package/runtime/utils/converters/group-categories-by-first-letter.ts +24 -0
- package/runtime/utils/converters/group-objects-by-first-letter.ts +16 -0
- package/runtime/utils/format-date.ts +12 -0
- package/runtime/utils/format-number.ts +12 -0
- package/runtime/utils/format-time-ago.ts +21 -0
- package/runtime/utils/get-duration.ts +17 -0
- package/runtime/utils/get-month.ts +22 -0
- package/runtime/utils/get-multiple-query.ts +26 -0
- package/runtime/utils/get-selected-query.ts +6 -0
- package/runtime/utils/is-mobile.ts +15 -0
- package/runtime/utils/normalize-url.ts +43 -0
- package/runtime/utils/on-backdrop-click.ts +5 -0
- package/runtime/utils/scroll-lock.ts +28 -0
- package/runtime/utils/server/abort-controller.ts +14 -0
- package/runtime/utils/server/api-helper.ts +41 -0
- package/runtime/utils/server/get-url-with-proxied-params.ts +6 -0
- package/runtime/utils/server/parse-api-error.ts +14 -0
- package/runtime/utils/server/server-api-helper.ts +28 -0
- package/runtime/utils/validate-email.ts +4 -0
- package/runtime/utils/validate-password.ts +3 -0
- package/runtime/utils/validate-phone.ts +4 -0
- package/runtime/utils/validate-username.ts +4 -0
- package/runtime/utils/video-data-add-model-icon.ts +20 -0
- package/runtime/utils/vtt-helper.ts +86 -0
- package/types/authorization-forms.d.ts +16 -0
- package/types/breadcrumb-item.d.ts +4 -0
- package/types/button-sizes.d.ts +1 -0
- package/types/button-themes.d.ts +1 -0
- package/types/card-info.d.ts +22 -0
- package/types/category-card.d.ts +8 -0
- package/types/change-email-form.d.ts +3 -0
- package/types/change-password-form.d.ts +4 -0
- package/types/channel-card.d.ts +10 -0
- package/types/chips-item.d.ts +8 -0
- package/types/contacts-form.d.ts +10 -0
- package/types/contacts-scheme.d.ts +14 -0
- package/types/country.d.ts +5 -0
- package/types/css-breakpoints.d.ts +1 -0
- package/types/filter-scheme.d.ts +37 -0
- package/types/fluid-player.d.ts +226 -0
- package/types/gender.d.ts +5 -0
- package/types/group-categories.d.ts +15 -0
- package/types/index.d.ts +59 -0
- package/types/input-types.d.ts +1 -0
- package/types/link-item.d.ts +6 -0
- package/types/model-card.d.ts +7 -0
- package/types/model-filter-payload.d.ts +4 -0
- package/types/model-filter.d.ts +15 -0
- package/types/model-group.d.ts +5 -0
- package/types/model-tag.d.ts +5 -0
- package/types/multi-suggest.d.ts +105 -0
- package/types/navigation-items.d.ts +10 -0
- package/types/paginated-response.d.ts +8 -0
- package/types/parameter-model.d.ts +14 -0
- package/types/playlist-card.d.ts +16 -0
- package/types/playlist-data.d.ts +15 -0
- package/types/playlist-info-type.d.ts +28 -0
- package/types/playlist-video-form.d.ts +9 -0
- package/types/profile-data.d.ts +9 -0
- package/types/raw/raw-category-card.d.ts +23 -0
- package/types/raw/raw-category-info.d.ts +23 -0
- package/types/raw/raw-channel-card.d.ts +29 -0
- package/types/raw/raw-channel-info.d.ts +29 -0
- package/types/raw/raw-model-card.d.ts +53 -0
- package/types/raw/raw-model-info.d.ts +54 -0
- package/types/raw/raw-playlist-card.d.ts +27 -0
- package/types/raw/raw-playlist-data.d.ts +29 -0
- package/types/raw/raw-playlist-user.d.ts +24 -0
- package/types/raw/raw-playlist-video.d.ts +18 -0
- package/types/raw/raw-profile-data.d.ts +22 -0
- package/types/raw/raw-video-card.d.ts +78 -0
- package/types/raw/raw-video-data.d.ts +53 -0
- package/types/recovery-password-form.d.ts +4 -0
- package/types/related-phrases.d.ts +6 -0
- package/types/report-form.d.ts +9 -0
- package/types/report-scheme.d.ts +21 -0
- package/types/request-filters.d.ts +13 -0
- package/types/request-pagination.d.ts +5 -0
- package/types/search-top-models.d.ts +6 -0
- package/types/select-item.d.ts +10 -0
- package/types/tab-item.d.ts +6 -0
- package/types/thumbs-urls.d.ts +13 -0
- package/types/video-card.d.ts +18 -0
- package/types/video-data.d.ts +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Порядок действий:
|
|
2
|
+
1) `npm login` = логинимся к npm (bitbucket);
|
|
3
|
+
2) `npm version patch` - патчит версию (можно и минорить или мажорить по ситуации);
|
|
4
|
+
3) `npm publish --access public` - публикуем версию (или сразу `npm run public` - обьединяет два действия);
|
|
5
|
+
4) ставим пакет в проекте;
|
|
6
|
+
5) проекте самом в `nuxt.config.ts` добавляем;
|
|
7
|
+
```bash
|
|
8
|
+
extends: [
|
|
9
|
+
process.env.LAYER_PATH || '@test/test'
|
|
10
|
+
],
|
|
11
|
+
```
|
|
12
|
+
6) Если не первый раз то обновляем пакет `npm install @test/test@latest`, если пакет уже установлен можно в ручную поменять версию и `npm install` (если пакет недавно обновился, сразу npm install может не подхватить последнюю версию);
|
|
13
|
+
7) `npx nuxi prepare`
|
|
14
|
+
8) если есть проблемы с runtime запустить в пакете npm run dev он перегенерирует tsconfig и eslint.config.mjs
|
|
15
|
+
9) и запускаем либо `npm run dev`
|
|
16
|
+
|
|
17
|
+
`nuxt.config.ts`
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
import { fileURLToPath } from 'node:url'
|
|
21
|
+
import { defineNuxtConfig } from 'nuxt/config'
|
|
22
|
+
|
|
23
|
+
export default defineNuxtConfig({
|
|
24
|
+
modules: ['@nuxt/eslint'],
|
|
25
|
+
eslint: {
|
|
26
|
+
config: {
|
|
27
|
+
// Use the generated ESLint config for lint root project as well
|
|
28
|
+
rootDir: fileURLToPath(new URL('..', import.meta.url))
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
components: {
|
|
32
|
+
dirs: [
|
|
33
|
+
{
|
|
34
|
+
path: './components',
|
|
35
|
+
pathPrefix: false
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`package.json` - версии nuxt && vue должны быть идентичны проекту
|
|
43
|
+
```bash
|
|
44
|
+
{
|
|
45
|
+
"name": "@test/test", // свое
|
|
46
|
+
"type": "module",
|
|
47
|
+
"version": "0.0.1",
|
|
48
|
+
"main": "./nuxt.config.ts",
|
|
49
|
+
"types": "./types/index.d.ts",
|
|
50
|
+
"exports": {
|
|
51
|
+
".": {
|
|
52
|
+
"types": "./types/index.d.ts",
|
|
53
|
+
"import": "./nuxt.config.ts",
|
|
54
|
+
"default": "./nuxt.config.ts"
|
|
55
|
+
},
|
|
56
|
+
"./types": {
|
|
57
|
+
"types": "./types/index.d.ts",
|
|
58
|
+
"default": "./types/index.d.ts"
|
|
59
|
+
},
|
|
60
|
+
"./runtime": {
|
|
61
|
+
"import": "./runtime/index.ts",
|
|
62
|
+
"default": "./runtime/index.ts"
|
|
63
|
+
},
|
|
64
|
+
"./*": "./*"
|
|
65
|
+
},
|
|
66
|
+
"files": [
|
|
67
|
+
"components/",
|
|
68
|
+
"composables/",
|
|
69
|
+
"types/",
|
|
70
|
+
"nuxt.config.ts"
|
|
71
|
+
],
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"@nuxt/eslint": "latest",
|
|
74
|
+
"@types/node": "^20.19.19",
|
|
75
|
+
"eslint": "^9.37.0",
|
|
76
|
+
"nuxt": "3.17.6",
|
|
77
|
+
"typescript": "^5.9.3",
|
|
78
|
+
"vue": "3.5.17"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
Импорт typescript типов осуществляется за счет этих опций
|
|
83
|
+
```
|
|
84
|
+
"types": "./types/index.d.ts",
|
|
85
|
+
"exports": {
|
|
86
|
+
".": {
|
|
87
|
+
"types": "./types/index.d.ts",
|
|
88
|
+
"import": "./nuxt.config.ts",
|
|
89
|
+
"default": "./nuxt.config.ts"
|
|
90
|
+
},
|
|
91
|
+
"./types": {
|
|
92
|
+
"types": "./types/index.d.ts",
|
|
93
|
+
"default": "./types/index.d.ts"
|
|
94
|
+
},
|
|
95
|
+
"./runtime": {
|
|
96
|
+
"import": "./runtime/index.ts",
|
|
97
|
+
"default": "./runtime/index.ts"
|
|
98
|
+
},
|
|
99
|
+
"./*": "./*"
|
|
100
|
+
},
|
|
101
|
+
```
|
|
102
|
+
Где:
|
|
103
|
+
- "types" указывает TypeScript, где лежит корневой файл с типами;
|
|
104
|
+
- "exports" сообщает Node и TS, какие модули доступны для импорта;
|
|
105
|
+
- "./types" позволяет писать коротко:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
import type { IUser } from '@test/test/types'
|
|
109
|
+
```
|
|
110
|
+
Типы не импортируются автоматически, каждый тип в свою очередь экспортируется в index.d.ts (обязательно указывать расширение при эскпорте d.ts) общий файл откуда уже и берутся все типы в проект.
|
|
111
|
+
|
|
112
|
+
`tsconfig.json`
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
{
|
|
116
|
+
"extends": "./.nuxt/tsconfig.json",
|
|
117
|
+
"compilerOptions": {
|
|
118
|
+
"types": ["node"]
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component
|
|
3
|
+
class="f-video-mini-card"
|
|
4
|
+
:is="component"
|
|
5
|
+
:title="card.title"
|
|
6
|
+
:to="link ? generateLink(`/playlists/${prefix}/${card.id}`) : null"
|
|
7
|
+
>
|
|
8
|
+
<div class="f-video-mini-card__img-wrapper">
|
|
9
|
+
<FImg
|
|
10
|
+
class="f-video-mini-card__img"
|
|
11
|
+
sizes="96px"
|
|
12
|
+
:src="card.thumbUrl"
|
|
13
|
+
width="96"
|
|
14
|
+
height="64"
|
|
15
|
+
:alt="card.title"
|
|
16
|
+
/>
|
|
17
|
+
<FLabel
|
|
18
|
+
class="f-video-mini-card__duration"
|
|
19
|
+
theme="black"
|
|
20
|
+
:text="duration"
|
|
21
|
+
></FLabel>
|
|
22
|
+
</div>
|
|
23
|
+
<p class="f-video-mini-card__description _truncate">{{ card.title }}</p>
|
|
24
|
+
</component>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script setup lang="ts">
|
|
28
|
+
import type { IVideoCard } from '../../types';
|
|
29
|
+
import { getDuration } from '../../runtime';
|
|
30
|
+
|
|
31
|
+
const props = defineProps<{
|
|
32
|
+
card: IVideoCard
|
|
33
|
+
link?: boolean
|
|
34
|
+
prefix?: string
|
|
35
|
+
}>()
|
|
36
|
+
|
|
37
|
+
const duration = computed(() => {
|
|
38
|
+
return getDuration(props.card.duration)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const component = computed(() => {
|
|
42
|
+
if (props.link) {
|
|
43
|
+
return resolveComponent('NuxtLink');
|
|
44
|
+
}
|
|
45
|
+
return 'div';
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const { generateLink } = useGenerateLink();
|
|
49
|
+
</script>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="f-grid-categories">
|
|
3
|
+
<FCategoryCard
|
|
4
|
+
v-for="(item, index) in categories"
|
|
5
|
+
:card="item"
|
|
6
|
+
:key="item.guid"
|
|
7
|
+
:priority="priority && index === 0"
|
|
8
|
+
/>
|
|
9
|
+
<slot></slot>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import type { ICategoryCard } from '../../types';
|
|
15
|
+
|
|
16
|
+
defineProps<{
|
|
17
|
+
categories: Array<ICategoryCard>,
|
|
18
|
+
priority?: boolean
|
|
19
|
+
}>()
|
|
20
|
+
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="f-grid-channels"
|
|
4
|
+
id="anchor"
|
|
5
|
+
>
|
|
6
|
+
<FChannelCard
|
|
7
|
+
class="f-grid-channels__card"
|
|
8
|
+
v-for="(item, index) in items"
|
|
9
|
+
:card="item"
|
|
10
|
+
:key="item.guid"
|
|
11
|
+
:priority="priority && index === 0"
|
|
12
|
+
/>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup lang="ts">
|
|
17
|
+
import type { IChannelCard } from '../../types';
|
|
18
|
+
|
|
19
|
+
defineProps<{
|
|
20
|
+
items: IChannelCard[]
|
|
21
|
+
priority?: boolean
|
|
22
|
+
}>()
|
|
23
|
+
</script>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="f-grid-models"
|
|
4
|
+
:class="{'--footer': footer}"
|
|
5
|
+
>
|
|
6
|
+
<FModelCard
|
|
7
|
+
v-for="(item, index) in items"
|
|
8
|
+
:key="`model-card-${item.guid}`"
|
|
9
|
+
class="f-grid-models__item"
|
|
10
|
+
:card="item"
|
|
11
|
+
:loading="index > 5 ? 'lazy' : 'eager'"
|
|
12
|
+
:priority="priority && index === 0"
|
|
13
|
+
/>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script setup lang="ts">
|
|
18
|
+
import type { IModelCard } from '../../types';
|
|
19
|
+
|
|
20
|
+
defineProps<{
|
|
21
|
+
items: IModelCard[]
|
|
22
|
+
footer?: boolean
|
|
23
|
+
priority?: boolean
|
|
24
|
+
}>()
|
|
25
|
+
</script>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="f-grid-playlists">
|
|
3
|
+
<FPlaylistCard
|
|
4
|
+
v-for="(item, index) in items"
|
|
5
|
+
:card="item"
|
|
6
|
+
:key="`user-playlist-${index}`"
|
|
7
|
+
:top-playlists="topPlaylists"
|
|
8
|
+
:priority="priority && index === 0"
|
|
9
|
+
/>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import type { IPlaylistCard } from '../../types';
|
|
15
|
+
|
|
16
|
+
defineProps<{
|
|
17
|
+
items: IPlaylistCard[]
|
|
18
|
+
topPlaylists?: boolean
|
|
19
|
+
priority?: boolean
|
|
20
|
+
}>()
|
|
21
|
+
</script>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="f-grid-videos"
|
|
4
|
+
:class="[
|
|
5
|
+
{'--first-hidden': hideFirstRow},
|
|
6
|
+
]"
|
|
7
|
+
>
|
|
8
|
+
<FVideoCard
|
|
9
|
+
v-for="(item, index) in items"
|
|
10
|
+
class="f-grid-videos__card"
|
|
11
|
+
:card="item"
|
|
12
|
+
:priority="priority && index === 0"
|
|
13
|
+
:loading="index > 3 || related ? 'lazy' : 'eager'"
|
|
14
|
+
:key="`video-${item.guid}`"
|
|
15
|
+
:top-chips="topChips"
|
|
16
|
+
:playlist-id="playlistId"
|
|
17
|
+
/>
|
|
18
|
+
<slot></slot>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import type { IVideoCard } from '../../types';
|
|
24
|
+
|
|
25
|
+
defineProps<{
|
|
26
|
+
items: Array<IVideoCard>
|
|
27
|
+
hideFirstRow?: boolean
|
|
28
|
+
priority?: boolean
|
|
29
|
+
related?: boolean
|
|
30
|
+
playlistId?: string
|
|
31
|
+
topChips?: string[]
|
|
32
|
+
}>()
|
|
33
|
+
</script>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<nav class="f-breadcrumbs" aria-label="Breadcrumb" role="navigation" itemscope itemtype="https://schema.org/BreadcrumbList">
|
|
3
|
+
<ol>
|
|
4
|
+
<li
|
|
5
|
+
v-for="(item, index) in resultItems"
|
|
6
|
+
:key="`breadcrumb-${index}`"
|
|
7
|
+
itemscope
|
|
8
|
+
itemprop="itemListElement"
|
|
9
|
+
itemtype="https://schema.org/ListItem"
|
|
10
|
+
>
|
|
11
|
+
<NuxtLink
|
|
12
|
+
:to="generateLink(item.to || '/')"
|
|
13
|
+
class="f-breadcrumbs__item"
|
|
14
|
+
itemprop="item"
|
|
15
|
+
>
|
|
16
|
+
<span itemprop="name" v-html="item.name" />
|
|
17
|
+
</NuxtLink>
|
|
18
|
+
<meta itemprop="position" :content="String(index + 1)" />
|
|
19
|
+
</li>
|
|
20
|
+
<li
|
|
21
|
+
itemscope
|
|
22
|
+
itemprop="itemListElement"
|
|
23
|
+
itemtype="https://schema.org/ListItem"
|
|
24
|
+
>
|
|
25
|
+
<span class="f-breadcrumbs__item" aria-current="page" itemprop="name" v-html="items[items.length - 1].name" />
|
|
26
|
+
<meta itemprop="position" :content="String(items.length)" />
|
|
27
|
+
</li>
|
|
28
|
+
</ol>
|
|
29
|
+
</nav>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<script setup lang="ts">
|
|
33
|
+
import type { IBreadcrumbItem } from '../../types';
|
|
34
|
+
|
|
35
|
+
const props = defineProps<{
|
|
36
|
+
items: IBreadcrumbItem[]
|
|
37
|
+
}>()
|
|
38
|
+
|
|
39
|
+
const { generateLink } = useGenerateLink();
|
|
40
|
+
|
|
41
|
+
const resultItems = computed(() => {
|
|
42
|
+
return props.items.slice(0, props.items.length - 1)
|
|
43
|
+
})
|
|
44
|
+
</script>
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="f-chips-panel"
|
|
4
|
+
ref="containerRef"
|
|
5
|
+
:class="[
|
|
6
|
+
{ '--expand-show': isExpandOpened },
|
|
7
|
+
{ '--alphabet': alphabet },
|
|
8
|
+
{ '--mobile-expand': mobileExpand },
|
|
9
|
+
]"
|
|
10
|
+
>
|
|
11
|
+
<div
|
|
12
|
+
class="f-chips-panel__wrapper"
|
|
13
|
+
:class="[{ '--more-show': isElementsOverflow }]"
|
|
14
|
+
>
|
|
15
|
+
<slot name="prepend"></slot>
|
|
16
|
+
<slot>
|
|
17
|
+
<FChips
|
|
18
|
+
v-for="item in itemsResult"
|
|
19
|
+
:item="item"
|
|
20
|
+
:key="item.title"
|
|
21
|
+
:alphabet="alphabet"
|
|
22
|
+
is-link
|
|
23
|
+
:mini="mini"
|
|
24
|
+
/>
|
|
25
|
+
</slot>
|
|
26
|
+
<div
|
|
27
|
+
v-if="!hideMore"
|
|
28
|
+
v-show="isElementsOverflow"
|
|
29
|
+
class="f-chips-panel__more-wrapper"
|
|
30
|
+
:class="{'--small': mini}"
|
|
31
|
+
>
|
|
32
|
+
<FChips
|
|
33
|
+
class="f-chips-panel__button --more"
|
|
34
|
+
:item="{
|
|
35
|
+
title: moreButtonName,
|
|
36
|
+
icon: isExpandOpened ? 'minus' : 'plus',
|
|
37
|
+
}"
|
|
38
|
+
:mini="mini"
|
|
39
|
+
@click="onMoreClick"
|
|
40
|
+
/>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<script setup lang="ts">
|
|
47
|
+
import { IChipsItem } from '@arkadykid/test/types';
|
|
48
|
+
|
|
49
|
+
const props = defineProps<{
|
|
50
|
+
items?: IChipsItem[];
|
|
51
|
+
alphabet?: boolean;
|
|
52
|
+
hideMore?: boolean;
|
|
53
|
+
mini?: boolean;
|
|
54
|
+
mobileExpand?: boolean;
|
|
55
|
+
}>();
|
|
56
|
+
|
|
57
|
+
const { t } = useI18n()
|
|
58
|
+
|
|
59
|
+
const emit = defineEmits<{
|
|
60
|
+
(eventName: 'more', eventValue: boolean): void
|
|
61
|
+
}>()
|
|
62
|
+
|
|
63
|
+
const containerRef = ref();
|
|
64
|
+
|
|
65
|
+
const isExpandOpened = ref(false);
|
|
66
|
+
|
|
67
|
+
const MAXIMUM_BUTTONS = 20; // вычислено на глаз, приблизительно
|
|
68
|
+
|
|
69
|
+
const itemsResult = computed(() => {
|
|
70
|
+
return props.items?.slice(
|
|
71
|
+
0,
|
|
72
|
+
!props.alphabet && !isExpandOpened.value
|
|
73
|
+
? MAXIMUM_BUTTONS
|
|
74
|
+
: props.items.length,
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const moreButtonName = computed(() => isExpandOpened.value ? t('less') : t('more'));
|
|
79
|
+
|
|
80
|
+
const isElementsOverflow = ref(false);
|
|
81
|
+
|
|
82
|
+
function checkIsElementOverflow() {
|
|
83
|
+
isElementsOverflow.value = !!(
|
|
84
|
+
containerRef.value &&
|
|
85
|
+
containerRef.value.scrollHeight > containerRef.value.clientHeight
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function onMoreClick() {
|
|
90
|
+
isExpandOpened.value = !isExpandOpened.value
|
|
91
|
+
emit('more', isExpandOpened.value)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
onMounted(() => {
|
|
95
|
+
checkIsElementOverflow();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
watch(() => useRoute().query, () => {
|
|
99
|
+
setTimeout(() => checkIsElementOverflow(), 100)
|
|
100
|
+
})
|
|
101
|
+
</script>
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<nav
|
|
3
|
+
v-if="length > 1"
|
|
4
|
+
class="f-pagination"
|
|
5
|
+
>
|
|
6
|
+
<div class="f-pagination__items">
|
|
7
|
+
|
|
8
|
+
<template v-if="length <= 6">
|
|
9
|
+
<NuxtLink
|
|
10
|
+
class="f-pagination__item"
|
|
11
|
+
:class="[
|
|
12
|
+
number === activeNumber && 'f-pagination__item--active',
|
|
13
|
+
theme && `f-button --${theme}`
|
|
14
|
+
]"
|
|
15
|
+
v-for="number in length"
|
|
16
|
+
:key="`pagination-${number}`"
|
|
17
|
+
:to="linkTo(number)"
|
|
18
|
+
>
|
|
19
|
+
{{ number }}
|
|
20
|
+
</NuxtLink>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<template v-else>
|
|
24
|
+
<NuxtLink
|
|
25
|
+
class="f-pagination__item"
|
|
26
|
+
:class="[
|
|
27
|
+
1 === activeNumber && 'f-pagination__item--active',
|
|
28
|
+
theme && `f-button --${theme}`
|
|
29
|
+
]"
|
|
30
|
+
:to="linkTo(1)"
|
|
31
|
+
>1
|
|
32
|
+
</NuxtLink>
|
|
33
|
+
<span
|
|
34
|
+
v-if="activeNumber > 4"
|
|
35
|
+
class="f-pagination__item f-pagination__item--empty"
|
|
36
|
+
:class="[
|
|
37
|
+
theme && `f-button --${theme}`
|
|
38
|
+
]"
|
|
39
|
+
>...</span>
|
|
40
|
+
<NuxtLink
|
|
41
|
+
v-else
|
|
42
|
+
class="f-pagination__item"
|
|
43
|
+
:class="[
|
|
44
|
+
2 === activeNumber && 'f-pagination__item--active',
|
|
45
|
+
theme && `f-button --${theme}`
|
|
46
|
+
]"
|
|
47
|
+
:to="linkTo(2)"
|
|
48
|
+
>2
|
|
49
|
+
</NuxtLink>
|
|
50
|
+
<NuxtLink
|
|
51
|
+
v-for="number in activeNumbers"
|
|
52
|
+
:class="[
|
|
53
|
+
number === activeNumber && 'f-pagination__item--active',
|
|
54
|
+
theme && `f-button --${theme}`
|
|
55
|
+
]"
|
|
56
|
+
class="f-pagination__item"
|
|
57
|
+
:key="`pagination-${number}`"
|
|
58
|
+
:to="linkTo(number)"
|
|
59
|
+
>
|
|
60
|
+
{{ number }}
|
|
61
|
+
</NuxtLink>
|
|
62
|
+
<span
|
|
63
|
+
v-if="activeNumber < length - 3"
|
|
64
|
+
class="f-pagination__item f-pagination__item--empty"
|
|
65
|
+
:class="[
|
|
66
|
+
theme && `f-button --${theme}`
|
|
67
|
+
]"
|
|
68
|
+
>...</span>
|
|
69
|
+
<NuxtLink
|
|
70
|
+
v-else
|
|
71
|
+
class="f-pagination__item"
|
|
72
|
+
:class="[
|
|
73
|
+
length - 1 === activeNumber && 'f-pagination__item--active',
|
|
74
|
+
theme && `f-button --${theme}`
|
|
75
|
+
]"
|
|
76
|
+
:to="linkTo(length - 1)"
|
|
77
|
+
>{{ length - 1 }}
|
|
78
|
+
</NuxtLink>
|
|
79
|
+
<NuxtLink
|
|
80
|
+
class="f-pagination__item"
|
|
81
|
+
:class="[
|
|
82
|
+
length === activeNumber && 'f-pagination__item--active',
|
|
83
|
+
theme && `f-button --${theme}`
|
|
84
|
+
]"
|
|
85
|
+
:to="linkTo(length)"
|
|
86
|
+
>
|
|
87
|
+
{{ length }}
|
|
88
|
+
</NuxtLink>
|
|
89
|
+
</template>
|
|
90
|
+
</div>
|
|
91
|
+
<FLink
|
|
92
|
+
wide
|
|
93
|
+
class="f-pagination__prev"
|
|
94
|
+
:to="linkTo(previousValue)"
|
|
95
|
+
:class="{'--disabled': activeNumber <= 1}"
|
|
96
|
+
>
|
|
97
|
+
<FIcon
|
|
98
|
+
v-if="arrowIcon"
|
|
99
|
+
name="arrow-left"
|
|
100
|
+
size="24"
|
|
101
|
+
/>
|
|
102
|
+
{{ t('previous' )}}
|
|
103
|
+
</FLink>
|
|
104
|
+
<FLink
|
|
105
|
+
wide
|
|
106
|
+
class="f-pagination__next"
|
|
107
|
+
:class="{'--disabled': activeNumber >= length}"
|
|
108
|
+
:to="linkTo(nextValue)"
|
|
109
|
+
>
|
|
110
|
+
{{ t('next') }}
|
|
111
|
+
<FIcon
|
|
112
|
+
v-if="arrowIcon"
|
|
113
|
+
name="arrow-right"
|
|
114
|
+
size="24"
|
|
115
|
+
/>
|
|
116
|
+
</FLink>
|
|
117
|
+
</nav>
|
|
118
|
+
</template>
|
|
119
|
+
|
|
120
|
+
<script setup lang="ts">
|
|
121
|
+
// не забыват проставлять key на компоненте для переинициализации и отслеживания props.total чтобы пересчитывалось правильное сео
|
|
122
|
+
import type { ButtonThemes } from '../../types';
|
|
123
|
+
|
|
124
|
+
const props = withDefaults(defineProps<{
|
|
125
|
+
prefix?: string
|
|
126
|
+
theme?: ButtonThemes
|
|
127
|
+
total: number
|
|
128
|
+
perPage?: number | string
|
|
129
|
+
limit?: number
|
|
130
|
+
arrowIcon?: boolean
|
|
131
|
+
}>(), {
|
|
132
|
+
prefix: 'page',
|
|
133
|
+
perPage: 48,
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
const {t} = useI18n();
|
|
137
|
+
const route = useRoute()
|
|
138
|
+
|
|
139
|
+
const length = computed(() => {
|
|
140
|
+
return props.limit ? Math.min(props.total, props.limit) : props.total;
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
const activeNumber = computed(() => {
|
|
144
|
+
const n = Number(route.query[props.prefix]);
|
|
145
|
+
return !n || n < 1 ? 1 : n;
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
const previousValue = computed(() => {
|
|
149
|
+
return activeNumber.value > 1 ? activeNumber.value - 1 : activeNumber.value
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const nextValue = computed(() => {
|
|
153
|
+
return activeNumber.value !== length.value ? activeNumber.value + 1 : activeNumber.value
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
const activeNumbers = computed(() => {
|
|
157
|
+
if (activeNumber.value < 5) {
|
|
158
|
+
return [3, 4, 5]
|
|
159
|
+
} else if (activeNumber.value > length.value - 3) {
|
|
160
|
+
return [length.value - 4, length.value - 3, length.value - 2]
|
|
161
|
+
}
|
|
162
|
+
return [activeNumber.value - 1, activeNumber.value, activeNumber.value + 1]
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
watchEffect(() => {
|
|
166
|
+
if (activeNumber.value > length.value && props.total > 0) {
|
|
167
|
+
navigateTo(linkTo(length.value));
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
function linkTo(value: string | number) {
|
|
172
|
+
const safeValue = Math.max(1, Number(value));
|
|
173
|
+
|
|
174
|
+
// Если это первая страница — ведём в корень без query
|
|
175
|
+
if (safeValue === 1) {
|
|
176
|
+
return {
|
|
177
|
+
path: route.path
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
query: {
|
|
183
|
+
...route.query,
|
|
184
|
+
[props.prefix]: safeValue
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
watch(() => route.query['page'], (value) => {
|
|
190
|
+
if (value) {
|
|
191
|
+
document.documentElement.querySelector('#anchor')?.scrollIntoView({ behavior: 'smooth' });
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
const i18n = useI18n();
|
|
196
|
+
const localePath = useLocalePath();
|
|
197
|
+
const siteUrl = useAppConfig().siteUrl as string;
|
|
198
|
+
const { canonicalUrl, alternateLinks } = useSeoLinks(siteUrl, props.total === 1, i18n, localePath);
|
|
199
|
+
|
|
200
|
+
useHead({
|
|
201
|
+
link: computed(() => [
|
|
202
|
+
{ rel: 'canonical', href: canonicalUrl.value, key: `canonical-${route.path}` },
|
|
203
|
+
...alternateLinks.value,
|
|
204
|
+
]),
|
|
205
|
+
});
|
|
206
|
+
</script>
|