@webitel/ui-sdk 24.10.31 → 24.10.33
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/dist/ui-sdk.js +1 -1
- package/dist/ui-sdk.umd.cjs +1 -1
- package/package.json +1 -1
- package/src/components/wt-intersection-observer/__tests__/WtIntersectionObserver.spec.js +12 -0
- package/src/components/wt-intersection-observer/wt-intersection-observer.vue +56 -0
- package/src/modules/Filters/classes/BaseFilterSchema.js +15 -6
- package/src/modules/Filters/components/filter-select.vue +118 -0
- package/src/modules/Filters/store/FiltersStoreModule.js +27 -35
package/package.json
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import WtIntersectionObserver from '../wt-intersection-observer.vue';
|
|
3
|
+
|
|
4
|
+
describe('WtIntersectionObserver', () => {
|
|
5
|
+
const next = () => true;
|
|
6
|
+
it('renders a component', () => {
|
|
7
|
+
const wrapper = shallowMount(WtIntersectionObserver, {
|
|
8
|
+
props: { next }
|
|
9
|
+
});
|
|
10
|
+
expect(wrapper.exists()).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<wt-loader
|
|
3
|
+
v-if="loading"
|
|
4
|
+
size="sm"
|
|
5
|
+
/>
|
|
6
|
+
<div ref="intersectionTarget" />
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script setup>
|
|
10
|
+
import { useIntersectionObserver } from '@vueuse/core';
|
|
11
|
+
import { onMounted, onUnmounted, ref } from 'vue';
|
|
12
|
+
|
|
13
|
+
const props = defineProps({
|
|
14
|
+
next: {
|
|
15
|
+
type: Function,
|
|
16
|
+
required: true,
|
|
17
|
+
},
|
|
18
|
+
loading: {
|
|
19
|
+
type: Boolean,
|
|
20
|
+
default: false,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const emit = defineEmits([
|
|
25
|
+
'next',
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const intersectionTarget = ref(null);
|
|
29
|
+
|
|
30
|
+
let stopObs;
|
|
31
|
+
|
|
32
|
+
onMounted(() => {
|
|
33
|
+
/**
|
|
34
|
+
*
|
|
35
|
+
* Note, observer triggers at init, so it should be used also as init function
|
|
36
|
+
* however, current filters module version is initializing list by itself, so we need to refactor filters ASAP
|
|
37
|
+
*/
|
|
38
|
+
const { stop } = useIntersectionObserver(intersectionTarget.value, ([{ isIntersecting }]) => {
|
|
39
|
+
if (isIntersecting && props.next) {
|
|
40
|
+
emit('next');
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
stopObs = stop;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
onUnmounted(() => {
|
|
48
|
+
stopObs();
|
|
49
|
+
});
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<style scoped lang="scss">
|
|
53
|
+
.wt-loader {
|
|
54
|
+
margin: var(--spacing-lg) auto;
|
|
55
|
+
}
|
|
56
|
+
</style>
|
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
localStorageGetter,
|
|
3
|
+
queryGetter,
|
|
4
|
+
valueGetter,
|
|
5
|
+
} from '../scripts/getters/index.js';
|
|
6
|
+
import {
|
|
7
|
+
localStorageRestore,
|
|
8
|
+
queryRestore,
|
|
9
|
+
} from '../scripts/restores/index.js';
|
|
10
|
+
import {
|
|
11
|
+
localStorageSetter,
|
|
12
|
+
querySetter,
|
|
13
|
+
valueSetter,
|
|
14
|
+
} from '../scripts/setters/index.js';
|
|
4
15
|
|
|
5
16
|
const convertGetterArray = (context) => (getters) => {
|
|
6
17
|
const availableGetters = ['value', 'query', 'localStorage'];
|
|
@@ -88,8 +99,8 @@ export default class BaseFilterSchema {
|
|
|
88
99
|
{
|
|
89
100
|
name,
|
|
90
101
|
value,
|
|
91
|
-
defaultValue,
|
|
92
102
|
multiple,
|
|
103
|
+
defaultValue: defaultValue || value,
|
|
93
104
|
},
|
|
94
105
|
rest,
|
|
95
106
|
);
|
|
@@ -97,8 +108,6 @@ export default class BaseFilterSchema {
|
|
|
97
108
|
this.setupGetters(get);
|
|
98
109
|
this.setupSetters(set);
|
|
99
110
|
this.setupRestores(restore);
|
|
100
|
-
|
|
101
|
-
return this;
|
|
102
111
|
}
|
|
103
112
|
|
|
104
113
|
setupGetters(getters) {
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<wt-select
|
|
3
|
+
:close-on-select="!filterSchema.multiple"
|
|
4
|
+
:multiple="filterSchema.multiple"
|
|
5
|
+
:options="options"
|
|
6
|
+
:search-method="search"
|
|
7
|
+
:track-by="trackBy"
|
|
8
|
+
:value="value"
|
|
9
|
+
v-bind="attrs"
|
|
10
|
+
@input="setValue"
|
|
11
|
+
/>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup>
|
|
15
|
+
|
|
16
|
+
import { computed, reactive, useAttrs } from 'vue';
|
|
17
|
+
import { useI18n } from 'vue-i18n';
|
|
18
|
+
import { useStore } from 'vuex';
|
|
19
|
+
import isEmpty from '../../../scripts/isEmpty.js';
|
|
20
|
+
import getNamespacedState from '../../../store/helpers/getNamespacedState.js';
|
|
21
|
+
|
|
22
|
+
const props = defineProps({
|
|
23
|
+
namespace: {
|
|
24
|
+
type: String,
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
filterQuery: {
|
|
28
|
+
type: String,
|
|
29
|
+
required: true,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const attrs = useAttrs();
|
|
34
|
+
const store = useStore();
|
|
35
|
+
const { t } = useI18n();
|
|
36
|
+
|
|
37
|
+
const filterSchema = computed(() => getNamespacedState(store.state, props.namespace)[props.filterQuery]);
|
|
38
|
+
|
|
39
|
+
const trackBy = computed(() => {
|
|
40
|
+
if (filterSchema.value.storedProp !== undefined) {
|
|
41
|
+
return filterSchema.value.storedProp;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (filterSchema.value.search) {
|
|
45
|
+
return 'id';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (filterSchema.value.options) {
|
|
49
|
+
return 'value';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return 'id';
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const rawValue = computed(() => store.getters[`${props.namespace}/FILTER_${props.filterQuery}`]);
|
|
56
|
+
|
|
57
|
+
const cachedSearchOpts = reactive({});
|
|
58
|
+
|
|
59
|
+
const search = filterSchema.value.search && (async (selectParams) => {
|
|
60
|
+
const params = {
|
|
61
|
+
...selectParams,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
if (trackBy.value === 'id') {
|
|
65
|
+
params.ids = Array.isArray(rawValue.value) ? rawValue.value : [rawValue.value];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const { items, ...rest } = await filterSchema.value.search(params);
|
|
69
|
+
|
|
70
|
+
items.forEach((item) => {
|
|
71
|
+
cachedSearchOpts[item.id] = item;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
items,
|
|
76
|
+
...rest,
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const options = computed(() => {
|
|
81
|
+
const options = filterSchema.value.options;
|
|
82
|
+
|
|
83
|
+
return options;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const value = computed(() => {
|
|
87
|
+
if (options.value) {
|
|
88
|
+
if (filterSchema.value.multiple) {
|
|
89
|
+
return options.value.filter((option) => rawValue.value.includes(option[trackBy.value]));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return options.value.find((option) => option[trackBy.value] === rawValue.value);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (filterSchema.value.search) {
|
|
96
|
+
if (filterSchema.value.multiple) {
|
|
97
|
+
return rawValue.value.map((value) => cachedSearchOpts[value]);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return cachedSearchOpts[rawValue.value];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return rawValue.value;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const setValue = (value) => {
|
|
107
|
+
const payload = {
|
|
108
|
+
value: isEmpty(value) ? value : value[trackBy.value],
|
|
109
|
+
name: props.filterQuery,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return store.dispatch(`${props.namespace}/SET_FILTER`, payload);
|
|
113
|
+
};
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<style scoped>
|
|
117
|
+
|
|
118
|
+
</style>
|
|
@@ -8,14 +8,12 @@ import FilterEvent from '../enums/FilterEvent.enum.js';
|
|
|
8
8
|
export default class FiltersStoreModule extends BaseStoreModule {
|
|
9
9
|
state = {
|
|
10
10
|
_emitter: mitt(),
|
|
11
|
-
_requireRouter: false,
|
|
12
11
|
};
|
|
13
12
|
|
|
14
13
|
getters = {
|
|
15
14
|
ROUTER: (state, g, rootState) => {
|
|
16
|
-
if (!state._requireRouter) return null;
|
|
17
15
|
if (rootState.router === undefined) {
|
|
18
|
-
|
|
16
|
+
console.warn(
|
|
19
17
|
'"rootState.router" is needed for filters to work properly.' +
|
|
20
18
|
' Please, provide to root state, or setup it in filters module as getter "ROUTER"',
|
|
21
19
|
);
|
|
@@ -36,10 +34,6 @@ export default class FiltersStoreModule extends BaseStoreModule {
|
|
|
36
34
|
|
|
37
35
|
if (!filter) throw new Error(`Unknown filter: ${filterName}`);
|
|
38
36
|
|
|
39
|
-
if (state._requireRouter && !getters.ROUTER) {
|
|
40
|
-
throw new Error(`Router is required for filter: ${filterName}`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
37
|
return filter.get({
|
|
44
38
|
router: getters.ROUTER,
|
|
45
39
|
});
|
|
@@ -137,13 +131,15 @@ export default class FiltersStoreModule extends BaseStoreModule {
|
|
|
137
131
|
|
|
138
132
|
// clean up query params
|
|
139
133
|
const router = context.getters.ROUTER;
|
|
140
|
-
const query = Object.entries(router.currentRoute.query)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
134
|
+
const query = Object.entries(router.currentRoute.query).reduce(
|
|
135
|
+
(filteredQuery, [qKey, qValue]) => {
|
|
136
|
+
if (context.state[qKey]) {
|
|
137
|
+
return filteredQuery;
|
|
138
|
+
}
|
|
139
|
+
return { ...filteredQuery, [qKey]: qValue };
|
|
140
|
+
},
|
|
141
|
+
{},
|
|
142
|
+
);
|
|
147
143
|
|
|
148
144
|
await router.push({
|
|
149
145
|
...router.currentRoute,
|
|
@@ -167,26 +163,22 @@ export default class FiltersStoreModule extends BaseStoreModule {
|
|
|
167
163
|
},
|
|
168
164
|
|
|
169
165
|
EMIT: async (context, { event, payload }) => {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const eventListeners = context.state._emitter.all.get(event);
|
|
166
|
+
const wildcardListeners = context.state._emitter.all.get('*');
|
|
167
|
+
const eventListeners = context.state._emitter.all.get(event);
|
|
173
168
|
|
|
174
|
-
|
|
169
|
+
const listeners = [
|
|
170
|
+
...(wildcardListeners || []),
|
|
171
|
+
...(eventListeners || []),
|
|
172
|
+
];
|
|
175
173
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
174
|
+
if (!listeners) {
|
|
175
|
+
console.info(`No listeners for ${event} event`);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
180
178
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
resolve();
|
|
186
|
-
} catch (err) {
|
|
187
|
-
reject(err);
|
|
188
|
-
}
|
|
189
|
-
});
|
|
179
|
+
for (const listener of listeners) {
|
|
180
|
+
await listener({ event, payload });
|
|
181
|
+
}
|
|
190
182
|
},
|
|
191
183
|
};
|
|
192
184
|
|
|
@@ -198,13 +190,13 @@ export default class FiltersStoreModule extends BaseStoreModule {
|
|
|
198
190
|
|
|
199
191
|
addFilter(filter) {
|
|
200
192
|
const setup = (filter) => {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
this.state[filter.name] = stateFilter;
|
|
193
|
+
this.state[filter.name] = new BaseFilterSchema(filter);
|
|
194
|
+
|
|
204
195
|
this.getters[`FILTER_${filter.name}`] = (state, getters) => {
|
|
205
196
|
// if is used as watcher for getter to recompute,
|
|
206
197
|
// because GET_FILTER is function ==> it's not reactive
|
|
207
|
-
if (state[filter.name].value) {
|
|
198
|
+
if (state[filter.name].value) {
|
|
199
|
+
}
|
|
208
200
|
return getters.GET_FILTER(filter.name);
|
|
209
201
|
};
|
|
210
202
|
};
|