itube-specs 0.0.723 → 0.0.726
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.
|
@@ -22,19 +22,25 @@
|
|
|
22
22
|
icon="date"
|
|
23
23
|
/>
|
|
24
24
|
</div>
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
25
|
+
<template v-if="scheme.length">
|
|
26
|
+
<SSelect
|
|
27
|
+
v-for="(item, index) in scheme"
|
|
28
|
+
:key="`scheme-${index}`"
|
|
29
|
+
v-model="filterValue[item.name.toLowerCase()]"
|
|
30
|
+
:name="item.title"
|
|
31
|
+
:items="item.items"
|
|
32
|
+
:label="item?.label"
|
|
33
|
+
:label-icon="{
|
|
34
|
+
icon: item.icon,
|
|
35
|
+
prefix: 'categories'
|
|
36
|
+
}"
|
|
37
|
+
:placeholder="item.placeholder"
|
|
38
|
+
/>
|
|
39
|
+
</template>
|
|
40
|
+
<div
|
|
41
|
+
v-else
|
|
42
|
+
class="s-filter-popup__loading _loading"
|
|
43
|
+
></div>
|
|
38
44
|
</div>
|
|
39
45
|
|
|
40
46
|
<template #footer>
|
|
@@ -62,7 +68,7 @@
|
|
|
62
68
|
<script setup lang="ts">
|
|
63
69
|
import type { IFilterScheme, ISelectItem } from '../../types';
|
|
64
70
|
|
|
65
|
-
const { t } = useI18n()
|
|
71
|
+
const { t } = useI18n();
|
|
66
72
|
|
|
67
73
|
const props = defineProps<{
|
|
68
74
|
addedItems: ISelectItem[]
|
|
@@ -70,7 +76,7 @@ const props = defineProps<{
|
|
|
70
76
|
modelValue: boolean
|
|
71
77
|
scheme: IFilterScheme[]
|
|
72
78
|
count: number
|
|
73
|
-
}>()
|
|
79
|
+
}>();
|
|
74
80
|
|
|
75
81
|
const durationValue = ref('');
|
|
76
82
|
const addedValue = ref('');
|
|
@@ -87,39 +93,39 @@ const categoriesFromQuery = route.query.categories
|
|
|
87
93
|
categoriesFromQuery.forEach(str => {
|
|
88
94
|
const [key, ...rest] = str.split('_');
|
|
89
95
|
if (key && rest.length > 0) {
|
|
90
|
-
filterValue.value[
|
|
96
|
+
filterValue.value[key] = rest.join('_');
|
|
91
97
|
}
|
|
92
98
|
});
|
|
93
99
|
|
|
94
|
-
durationValue.value = route.query[
|
|
95
|
-
addedValue.value = route.query[
|
|
100
|
+
durationValue.value = route.query['duration'] as string || '';
|
|
101
|
+
addedValue.value = route.query['added'] as string || '';
|
|
96
102
|
|
|
97
103
|
const emit = defineEmits<{
|
|
98
104
|
(eventName: 'update:modelValue', value: boolean): void
|
|
99
|
-
}>()
|
|
105
|
+
}>();
|
|
100
106
|
|
|
101
|
-
const isOpen = ref(props.modelValue)
|
|
107
|
+
const isOpen = ref(props.modelValue);
|
|
102
108
|
|
|
103
109
|
function closePopup() {
|
|
104
110
|
isOpen.value = false;
|
|
105
|
-
emit('update:modelValue', isOpen.value)
|
|
111
|
+
emit('update:modelValue', isOpen.value);
|
|
106
112
|
}
|
|
107
113
|
|
|
108
114
|
const router = useRouter();
|
|
109
115
|
|
|
110
116
|
function saveResult() {
|
|
111
117
|
const oldQuery = { ...route.query };
|
|
112
|
-
delete oldQuery[
|
|
118
|
+
delete oldQuery['page'];
|
|
113
119
|
|
|
114
120
|
const mainFiltersValue = {
|
|
115
121
|
...(durationValue.value ? { duration: durationValue.value } : {}),
|
|
116
|
-
...(addedValue.value ? { added: addedValue.value } : {})
|
|
122
|
+
...(addedValue.value ? { added: addedValue.value } : {}),
|
|
117
123
|
};
|
|
118
124
|
|
|
119
125
|
const preserved = {
|
|
120
126
|
...mainFiltersValue,
|
|
121
127
|
...(oldQuery.sort ? { sort: oldQuery.sort } : {}),
|
|
122
|
-
...(oldQuery.page ? { page: oldQuery.page } : {})
|
|
128
|
+
...(oldQuery.page ? { page: oldQuery.page } : {}),
|
|
123
129
|
};
|
|
124
130
|
|
|
125
131
|
const categories = Object.entries(filterValue.value)
|
|
@@ -132,7 +138,7 @@ function saveResult() {
|
|
|
132
138
|
query: {
|
|
133
139
|
...preserved,
|
|
134
140
|
categories: categories.length ? categoriesString : undefined,
|
|
135
|
-
}
|
|
141
|
+
},
|
|
136
142
|
});
|
|
137
143
|
|
|
138
144
|
closePopup();
|
|
@@ -145,7 +151,7 @@ function reset() {
|
|
|
145
151
|
router.push({
|
|
146
152
|
query: {
|
|
147
153
|
categories: null,
|
|
148
|
-
}
|
|
154
|
+
},
|
|
149
155
|
});
|
|
150
156
|
}
|
|
151
157
|
|
|
@@ -154,16 +160,16 @@ const filterTitle = computed(() => {
|
|
|
154
160
|
const hasAdded = addedValue.value ? 1 : 0;
|
|
155
161
|
const count = Object.keys(filterValue.value).length + hasAdded + hasDuration;
|
|
156
162
|
const filterText = count > 1 ? t('filters') : t('filter');
|
|
157
|
-
return count > 0 ? `${filterText}: ${count}` : t('filter')
|
|
158
|
-
})
|
|
163
|
+
return count > 0 ? `${filterText}: ${count}` : t('filter');
|
|
164
|
+
});
|
|
159
165
|
|
|
160
|
-
const isFiltered = computed(() => !!route.query[
|
|
166
|
+
const isFiltered = computed(() => !!route.query['categories'] || Object.keys(filterValue.value).length > 0 || durationValue.value || addedValue.value);
|
|
161
167
|
|
|
162
168
|
function translateTitle(array: ISelectItem[]) {
|
|
163
169
|
return array.map((item: ISelectItem) => ({
|
|
164
170
|
title: t(item.title),
|
|
165
171
|
key: item.key,
|
|
166
|
-
value: item.value
|
|
167
|
-
}))
|
|
172
|
+
value: item.value,
|
|
173
|
+
}));
|
|
168
174
|
}
|
|
169
175
|
</script>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
</template>
|
|
22
22
|
|
|
23
23
|
<script setup lang="ts">
|
|
24
|
-
import type { ButtonSizes, ButtonThemes, IFilterScheme, ISelectItem
|
|
24
|
+
import type { ButtonSizes, ButtonThemes, IFilterScheme, ISelectItem } from '../../types';
|
|
25
25
|
import type { LocationQuery } from '#vue-router';
|
|
26
26
|
|
|
27
27
|
const route = useRoute();
|
|
@@ -33,12 +33,20 @@ defineProps<{
|
|
|
33
33
|
durationItems: ISelectItem[]
|
|
34
34
|
buttonSize?: ButtonSizes
|
|
35
35
|
buttonTheme?: ButtonThemes
|
|
36
|
-
}>()
|
|
36
|
+
}>();
|
|
37
|
+
|
|
38
|
+
const emit = defineEmits<{
|
|
39
|
+
(e: 'open'): void
|
|
40
|
+
}>();
|
|
41
|
+
|
|
42
|
+
watch(filterOpen, (value) => {
|
|
43
|
+
if (value) emit('open');
|
|
44
|
+
});
|
|
37
45
|
|
|
38
46
|
function getFilteredQuery(query: LocationQuery) {
|
|
39
47
|
const result: string[] = [];
|
|
40
48
|
|
|
41
|
-
const categoriesRaw = query[
|
|
49
|
+
const categoriesRaw = query['categories'];
|
|
42
50
|
if (categoriesRaw) {
|
|
43
51
|
if (Array.isArray(categoriesRaw)) {
|
|
44
52
|
categoriesRaw.forEach(item => {
|
|
@@ -52,7 +60,7 @@ function getFilteredQuery(query: LocationQuery) {
|
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
['duration', 'added'].forEach(key => {
|
|
55
|
-
const val = query[
|
|
63
|
+
const val = query[key];
|
|
56
64
|
if (typeof val === 'string' && val.length > 0) {
|
|
57
65
|
result.push(val);
|
|
58
66
|
}
|
|
@@ -63,9 +71,9 @@ function getFilteredQuery(query: LocationQuery) {
|
|
|
63
71
|
|
|
64
72
|
const filterCount = ref(0);
|
|
65
73
|
|
|
66
|
-
filterCount.value = getFilteredQuery(route.query).length
|
|
74
|
+
filterCount.value = getFilteredQuery(route.query).length;
|
|
67
75
|
|
|
68
76
|
watch(() => route.query, (value) => {
|
|
69
|
-
filterCount.value = getFilteredQuery(value).length
|
|
70
|
-
})
|
|
77
|
+
filterCount.value = getFilteredQuery(value).length;
|
|
78
|
+
});
|
|
71
79
|
</script>
|
package/components/ui/s-img.vue
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
:loading="loading"
|
|
7
7
|
:width="width"
|
|
8
8
|
:height="height"
|
|
9
|
-
:sizes="
|
|
9
|
+
:sizes="computedSizes"
|
|
10
10
|
:alt="alt"
|
|
11
11
|
:decoding="priority ? 'sync' : 'async'"
|
|
12
12
|
:fetchpriority="priority ? 'high' : 'auto'"
|
|
@@ -22,7 +22,7 @@ const props = withDefaults(defineProps<{
|
|
|
22
22
|
src?: string,
|
|
23
23
|
width: string | number,
|
|
24
24
|
height: string | number,
|
|
25
|
-
sizes
|
|
25
|
+
sizes?: string,
|
|
26
26
|
urls?: IThumbUrls,
|
|
27
27
|
alt?: string,
|
|
28
28
|
loading?: 'lazy' | 'eager',
|
|
@@ -33,6 +33,11 @@ const props = withDefaults(defineProps<{
|
|
|
33
33
|
fit: 'cover',
|
|
34
34
|
})
|
|
35
35
|
|
|
36
|
+
const computedSizes = computed(() => {
|
|
37
|
+
if (props.loading === 'lazy') return 'auto';
|
|
38
|
+
return props.sizes;
|
|
39
|
+
});
|
|
40
|
+
|
|
36
41
|
const imgSrc = computed(() => {
|
|
37
42
|
if (props.src) return props.src
|
|
38
43
|
if (props.urls) return props.urls.webp[ThumbSize.Medium]
|
package/package.json
CHANGED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
// @vitest-environment nuxt
|
|
2
|
-
import { describe, it, expect } from 'vitest';
|
|
3
|
-
import { useFilterScheme } from '../use-filter-scheme';
|
|
4
|
-
|
|
5
|
-
const t = (key: string) => key;
|
|
6
|
-
|
|
7
|
-
describe('useFilterScheme', () => {
|
|
8
|
-
it('пустой массив для null', () => {
|
|
9
|
-
const { filterScheme } = useFilterScheme(t, null);
|
|
10
|
-
expect(filterScheme.value).toEqual([]);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('трансформирует группы в схему', () => {
|
|
14
|
-
const groups = [{
|
|
15
|
-
title: 'Age', name: 'age', icon: 'age-icon',
|
|
16
|
-
categories: [
|
|
17
|
-
{ title: 'teen', name: 'teen' },
|
|
18
|
-
{ title: 'milf', name: 'milf' },
|
|
19
|
-
],
|
|
20
|
-
}];
|
|
21
|
-
const { filterScheme } = useFilterScheme(t, groups);
|
|
22
|
-
expect(filterScheme.value).toHaveLength(1);
|
|
23
|
-
expect(filterScheme.value[0].label).toBe('Age');
|
|
24
|
-
expect(filterScheme.value[0].items).toEqual([
|
|
25
|
-
{ title: 'Teen', value: 'teen' },
|
|
26
|
-
{ title: 'Milf', value: 'milf' },
|
|
27
|
-
]);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('capitalize для multi-word', () => {
|
|
31
|
-
const groups = [{
|
|
32
|
-
title: 'Body', name: 'body', icon: '',
|
|
33
|
-
categories: [{ title: 'big tits', name: 'big-tits' }],
|
|
34
|
-
}];
|
|
35
|
-
const { filterScheme } = useFilterScheme(t, groups);
|
|
36
|
-
expect(filterScheme.value[0].items[0].title).toBe('Big Tits');
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('фильтрует пустые группы', () => {
|
|
40
|
-
const groups = [
|
|
41
|
-
{ title: 'Empty', name: 'empty', icon: '', categories: [] },
|
|
42
|
-
{ title: 'Body', name: 'body', icon: '', categories: [{ title: 'slim', name: 'slim' }] },
|
|
43
|
-
];
|
|
44
|
-
const { filterScheme } = useFilterScheme(t, groups);
|
|
45
|
-
expect(filterScheme.value).toHaveLength(1);
|
|
46
|
-
expect(filterScheme.value[0].name).toBe('body');
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('placeholder из t()', () => {
|
|
50
|
-
const groups = [{
|
|
51
|
-
title: 'Age', name: 'age', icon: '',
|
|
52
|
-
categories: [{ title: 'teen', name: 'teen' }],
|
|
53
|
-
}];
|
|
54
|
-
const { filterScheme } = useFilterScheme(t, groups);
|
|
55
|
-
expect(filterScheme.value[0].placeholder).toBe('choose_category');
|
|
56
|
-
});
|
|
57
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { IFilterScheme, IGroupCategories } from '../types';
|
|
2
|
-
import { convertString } from '../runtime';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Строит вычисляемую схему фильтров из группированных категорий.
|
|
6
|
-
* @param t - функция перевода (i18n)
|
|
7
|
-
* @param groupsCategories - массив групп категорий
|
|
8
|
-
*/
|
|
9
|
-
export const useFilterScheme = (t, groupsCategories: IGroupCategories[] | null) => {
|
|
10
|
-
const getFilterSchemeCategories = (): IFilterScheme[] => {
|
|
11
|
-
return groupsCategories?.map((item) => ({
|
|
12
|
-
label: item.title,
|
|
13
|
-
name: item.name,
|
|
14
|
-
title: item.title,
|
|
15
|
-
icon: item.icon,
|
|
16
|
-
placeholder: t('choose_category'),
|
|
17
|
-
items: item.categories?.map((subItem) => ({
|
|
18
|
-
title: convertString().toCapitalize(subItem.title),
|
|
19
|
-
value: subItem.name,
|
|
20
|
-
})),
|
|
21
|
-
})) || [];
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const filterScheme = computed((): IFilterScheme[] => {
|
|
25
|
-
if (!groupsCategories) {
|
|
26
|
-
return [];
|
|
27
|
-
}
|
|
28
|
-
return getFilterSchemeCategories().filter(item => item?.items.length > 0);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
return { filterScheme };
|
|
32
|
-
};
|