@webitel/ui-datalist 26.8.24 → 26.8.26
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/package.json +1 -1
- package/src/modules/filters/composables/useVariableColumnFilters.ts +69 -37
- package/src/modules/filters/modules/filterConfig/components/variable/variable-filter-value-preview.vue +5 -1
- package/src/modules/filters/modules/filterConfig/components/variable/variable-key-filter-value-field.vue +99 -0
- package/src/modules/filters/modules/filterConfig/components/variable/variable-key-filter-value-preview.vue +24 -0
- package/src/modules/filters/modules/filterConfig/components/variable/variableKeyFilterConfig.ts +48 -0
- package/src/modules/table/__tests__/createTableStore.spec.ts +59 -0
- package/src/modules/table/createTableStore.store.ts +20 -2
- package/src/modules/types/tableStore.types.ts +3 -1
- package/types/.tsbuildinfo +1 -1
- package/types/modules/filter-presets/stores/createFilterPresetsStore.d.ts +1 -0
- package/types/modules/filters/composables/useVariableColumnFilters.d.ts +3 -0
- package/types/modules/filters/modules/filterConfig/components/variable/variable-key-filter-value-field.vue.d.ts +24 -0
- package/types/modules/filters/modules/filterConfig/components/variable/variable-key-filter-value-preview.vue.d.ts +8 -0
- package/types/modules/filters/modules/filterConfig/components/variable/variableKeyFilterConfig.d.ts +21 -0
- package/types/modules/permissions-page/stores/createPermissionsStore.d.ts +2 -0
- package/types/modules/table/createTableStore.store.d.ts +2 -0
- package/types/modules/types/tableStore.types.d.ts +3 -1
package/package.json
CHANGED
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
import type { DataField } from '@webitel/api-services/gen/models';
|
|
2
2
|
import type { WtTableHeader } from '@webitel/ui-sdk/components/wt-table/types/WtTable';
|
|
3
|
-
import { WtTypeExtensionFieldKind } from '@webitel/ui-sdk/enums';
|
|
4
3
|
import { isVariableHeader } from '@webitel/ui-sdk/modules/TableVariableColumnSelect';
|
|
5
4
|
import { computed, type MaybeRefOrGetter, toValue } from 'vue';
|
|
6
5
|
import { useI18n } from 'vue-i18n';
|
|
7
6
|
|
|
8
7
|
import type { FilterInitParams, FilterName, IFilter } from '../classes/Filter';
|
|
9
8
|
import type { IFiltersManager } from '../classes/FiltersManager';
|
|
9
|
+
import {
|
|
10
|
+
isVariableKeyFilterValue,
|
|
11
|
+
VariableKeyFilterConfig,
|
|
12
|
+
} from '../modules/filterConfig/components/variable/variableKeyFilterConfig';
|
|
10
13
|
import { FilterOption } from '../modules/filterConfig/enums/FilterOption';
|
|
11
14
|
import {
|
|
12
15
|
isVariableFilterName,
|
|
13
16
|
toVariableFilterFields,
|
|
14
17
|
VARIABLE_FIELD_PREFIX,
|
|
15
|
-
variableKeyFromFilterName,
|
|
16
18
|
} from '../scripts/variableFilters';
|
|
17
19
|
|
|
18
20
|
const parseVariableFilterValue = (raw: string) =>
|
|
19
21
|
raw.split('&').reduce<Record<string, string>>((vars, pair) => {
|
|
20
|
-
const [key,
|
|
21
|
-
if (key) vars[key] =
|
|
22
|
+
const [key, ...valueParts] = pair.split('=');
|
|
23
|
+
if (key) vars[key] = valueParts.join('=');
|
|
22
24
|
return vars;
|
|
23
25
|
}, {});
|
|
24
26
|
|
|
@@ -37,49 +39,39 @@ export const useVariableColumnFilters = ({
|
|
|
37
39
|
}) => {
|
|
38
40
|
const { t } = useI18n();
|
|
39
41
|
|
|
40
|
-
const withVariableFilterLabel = (
|
|
41
|
-
params: FilterInitParams,
|
|
42
|
-
): FilterInitParams =>
|
|
43
|
-
isVariableFilterName(params.name)
|
|
44
|
-
? {
|
|
45
|
-
...params,
|
|
46
|
-
label: t('webitelUI.filters.variable'),
|
|
47
|
-
}
|
|
48
|
-
: params;
|
|
49
|
-
|
|
50
42
|
const variableFilterFields = computed<DataField[]>(() => {
|
|
51
43
|
const headers = toValue(shownHeaders) || [];
|
|
52
44
|
const columnHeaders = headers.filter(
|
|
53
45
|
(header) => isVariableHeader(header) && header.filtered,
|
|
54
46
|
);
|
|
55
|
-
const columnFields = toVariableFilterFields(columnHeaders);
|
|
56
47
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
.filter((name) => isVariableFilterName(name) && !knownNames.has(name))
|
|
61
|
-
.map((name) => ({
|
|
62
|
-
id: name,
|
|
63
|
-
name: variableKeyFromFilterName(name),
|
|
64
|
-
kind: WtTypeExtensionFieldKind.Text,
|
|
65
|
-
}));
|
|
66
|
-
|
|
67
|
-
return [
|
|
68
|
-
...columnFields,
|
|
69
|
-
...appliedOnlyFields,
|
|
70
|
-
];
|
|
48
|
+
return toVariableFilterFields(columnHeaders).filter(
|
|
49
|
+
(field) => !field.id || !toValue(filtersManager).hasFilter(field.id),
|
|
50
|
+
);
|
|
71
51
|
});
|
|
72
52
|
|
|
53
|
+
const variableFilterConfigs = computed(() =>
|
|
54
|
+
toValue(filtersManager)
|
|
55
|
+
.getAllKeys()
|
|
56
|
+
.filter(isVariableFilterName)
|
|
57
|
+
.map(
|
|
58
|
+
(name) =>
|
|
59
|
+
new VariableKeyFilterConfig({
|
|
60
|
+
name,
|
|
61
|
+
label: t('webitelUI.filters.variable'),
|
|
62
|
+
}),
|
|
63
|
+
),
|
|
64
|
+
);
|
|
65
|
+
|
|
73
66
|
const splitVariableFilter = (params: FilterInitParams) => {
|
|
74
67
|
const variables = parseVariableFilterValue(String(params.value ?? ''));
|
|
75
68
|
|
|
76
69
|
Object.entries(variables).forEach(([key, value]) => {
|
|
77
|
-
addFilter(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
70
|
+
addFilter({
|
|
71
|
+
name: `${VARIABLE_FIELD_PREFIX}${key}`,
|
|
72
|
+
value,
|
|
73
|
+
label: params.label,
|
|
74
|
+
});
|
|
83
75
|
});
|
|
84
76
|
|
|
85
77
|
if (toValue(filtersManager).hasFilter(FilterOption.Variable)) {
|
|
@@ -95,7 +87,7 @@ export const useVariableColumnFilters = ({
|
|
|
95
87
|
return;
|
|
96
88
|
}
|
|
97
89
|
|
|
98
|
-
addFilter(
|
|
90
|
+
addFilter(params);
|
|
99
91
|
};
|
|
100
92
|
|
|
101
93
|
const handleUpdateFilter = (params: FilterInitParams) => {
|
|
@@ -104,12 +96,52 @@ export const useVariableColumnFilters = ({
|
|
|
104
96
|
return;
|
|
105
97
|
}
|
|
106
98
|
|
|
107
|
-
updateFilter(
|
|
99
|
+
updateFilter(params);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const updateVariableKeyFilter = ({
|
|
103
|
+
name,
|
|
104
|
+
value,
|
|
105
|
+
label,
|
|
106
|
+
}: FilterInitParams) => {
|
|
107
|
+
if (!isVariableKeyFilterValue(value)) {
|
|
108
|
+
updateFilter({
|
|
109
|
+
name,
|
|
110
|
+
value,
|
|
111
|
+
label,
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const newName = `${VARIABLE_FIELD_PREFIX}${value.key}`;
|
|
117
|
+
|
|
118
|
+
if (newName !== name) {
|
|
119
|
+
deleteFilter({
|
|
120
|
+
name,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
addFilter({
|
|
125
|
+
name: newName,
|
|
126
|
+
value: value.value,
|
|
127
|
+
label,
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const handlePanelUpdateFilter = (params: FilterInitParams) => {
|
|
132
|
+
if (isVariableFilterName(params.name)) {
|
|
133
|
+
updateVariableKeyFilter(params);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
handleUpdateFilter(params);
|
|
108
138
|
};
|
|
109
139
|
|
|
110
140
|
return {
|
|
111
141
|
variableFilterFields,
|
|
142
|
+
variableFilterConfigs,
|
|
112
143
|
handleAddFilter,
|
|
113
144
|
handleUpdateFilter,
|
|
145
|
+
handlePanelUpdateFilter,
|
|
114
146
|
};
|
|
115
147
|
};
|
|
@@ -25,7 +25,11 @@ const props = defineProps<{
|
|
|
25
25
|
|
|
26
26
|
const { t } = useI18n();
|
|
27
27
|
|
|
28
|
-
const
|
|
28
|
+
const [key, ...valueParts] = props.value.split('=');
|
|
29
|
+
const variable = [
|
|
30
|
+
key,
|
|
31
|
+
valueParts.join('='),
|
|
32
|
+
];
|
|
29
33
|
</script>
|
|
30
34
|
|
|
31
35
|
<style lang="scss" scoped>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="variable-key-filter-value-field">
|
|
3
|
+
<wt-input-text
|
|
4
|
+
:model-value="variable.key"
|
|
5
|
+
:label="t('vocabulary.keys')"
|
|
6
|
+
:v="!disableValidation && v$.variable.key"
|
|
7
|
+
@update:model-value="updateVariable({ key: $event })"
|
|
8
|
+
/>
|
|
9
|
+
<wt-input-text
|
|
10
|
+
:model-value="variable.value"
|
|
11
|
+
:label="t('vocabulary.values')"
|
|
12
|
+
:v="!disableValidation && v$.variable.value"
|
|
13
|
+
@update:model-value="updateVariable({ value: $event })"
|
|
14
|
+
/>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script lang="ts" setup>
|
|
19
|
+
import { useVuelidate } from '@vuelidate/core';
|
|
20
|
+
import { required } from '@vuelidate/validators';
|
|
21
|
+
import { computed, watch } from 'vue';
|
|
22
|
+
import { useI18n } from 'vue-i18n';
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
type IVariableKeyFilterConfig,
|
|
26
|
+
isVariableKeyFilterValue,
|
|
27
|
+
type VariableKeyFilterValue,
|
|
28
|
+
} from './variableKeyFilterConfig';
|
|
29
|
+
|
|
30
|
+
const props = defineProps<{
|
|
31
|
+
filterConfig: IVariableKeyFilterConfig;
|
|
32
|
+
disableValidation?: boolean;
|
|
33
|
+
}>();
|
|
34
|
+
|
|
35
|
+
const emit = defineEmits<{
|
|
36
|
+
'update:invalid': [
|
|
37
|
+
boolean,
|
|
38
|
+
];
|
|
39
|
+
}>();
|
|
40
|
+
|
|
41
|
+
const model = defineModel<string | VariableKeyFilterValue>();
|
|
42
|
+
|
|
43
|
+
const { t } = useI18n();
|
|
44
|
+
|
|
45
|
+
const variable = computed<VariableKeyFilterValue>(() =>
|
|
46
|
+
isVariableKeyFilterValue(model.value)
|
|
47
|
+
? model.value
|
|
48
|
+
: {
|
|
49
|
+
key: props.filterConfig.variableKey,
|
|
50
|
+
value: model.value ?? '',
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const updateVariable = (patch: Partial<VariableKeyFilterValue>) => {
|
|
55
|
+
model.value = {
|
|
56
|
+
...variable.value,
|
|
57
|
+
...patch,
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const v$ = useVuelidate(
|
|
62
|
+
computed(() => ({
|
|
63
|
+
variable: {
|
|
64
|
+
key: {
|
|
65
|
+
required,
|
|
66
|
+
},
|
|
67
|
+
value: {
|
|
68
|
+
required,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
})),
|
|
72
|
+
{
|
|
73
|
+
variable,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
$autoDirty: true,
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if (!props.disableValidation) v$.value.$touch();
|
|
81
|
+
|
|
82
|
+
watch(
|
|
83
|
+
() => v$.value.$invalid,
|
|
84
|
+
(invalid) => {
|
|
85
|
+
emit('update:invalid', invalid);
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
immediate: true,
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
<style lang="scss" scoped>
|
|
94
|
+
.variable-key-filter-value-field {
|
|
95
|
+
display: flex;
|
|
96
|
+
flex-direction: column;
|
|
97
|
+
gap: var(--spacing-xs);
|
|
98
|
+
}
|
|
99
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<variable-filter-value-preview
|
|
3
|
+
:key="variableValue"
|
|
4
|
+
:value="variableValue"
|
|
5
|
+
/>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script lang="ts" setup>
|
|
9
|
+
import { computed } from 'vue';
|
|
10
|
+
|
|
11
|
+
import VariableFilterValuePreview from './variable-filter-value-preview.vue';
|
|
12
|
+
import type { IVariableKeyFilterConfig } from './variableKeyFilterConfig';
|
|
13
|
+
|
|
14
|
+
const props = defineProps<{
|
|
15
|
+
filterConfig: IVariableKeyFilterConfig;
|
|
16
|
+
value?: unknown;
|
|
17
|
+
}>();
|
|
18
|
+
|
|
19
|
+
const variableValue = computed(
|
|
20
|
+
() => `${props.filterConfig.variableKey}=${props.value ?? ''}`,
|
|
21
|
+
);
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<style scoped></style>
|
package/src/modules/filters/modules/filterConfig/components/variable/variableKeyFilterConfig.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
|
|
3
|
+
import type { FilterName, FilterValue } from '../../../../classes/Filter';
|
|
4
|
+
import { variableKeyFromFilterName } from '../../../../scripts/variableFilters';
|
|
5
|
+
import type { BaseFilterConfig } from '../../classes/FilterConfig';
|
|
6
|
+
import { FilterConfig } from '../../classes/FilterConfig';
|
|
7
|
+
import VariableKeyFilterValueField from './variable-key-filter-value-field.vue';
|
|
8
|
+
import VariableKeyFilterValuePreview from './variable-key-filter-value-preview.vue';
|
|
9
|
+
|
|
10
|
+
export interface IVariableKeyFilterConfig extends BaseFilterConfig {
|
|
11
|
+
readonly variableKey: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const variableKeyValueSchema = z.object({
|
|
15
|
+
key: z.string(),
|
|
16
|
+
value: z.string(),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export type VariableKeyFilterValue = z.infer<typeof variableKeyValueSchema>;
|
|
20
|
+
|
|
21
|
+
export const isVariableKeyFilterValue = (
|
|
22
|
+
value: FilterValue,
|
|
23
|
+
): value is VariableKeyFilterValue =>
|
|
24
|
+
variableKeyValueSchema.safeParse(value).success;
|
|
25
|
+
|
|
26
|
+
export class VariableKeyFilterConfig
|
|
27
|
+
extends FilterConfig
|
|
28
|
+
implements IVariableKeyFilterConfig
|
|
29
|
+
{
|
|
30
|
+
readonly variableKey: string;
|
|
31
|
+
|
|
32
|
+
constructor({
|
|
33
|
+
name,
|
|
34
|
+
label,
|
|
35
|
+
}: {
|
|
36
|
+
name: FilterName;
|
|
37
|
+
label: string;
|
|
38
|
+
}) {
|
|
39
|
+
super({
|
|
40
|
+
name,
|
|
41
|
+
valueInputComponent: VariableKeyFilterValueField,
|
|
42
|
+
valuePreviewComponent: VariableKeyFilterValuePreview,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.label = label;
|
|
46
|
+
this.variableKey = variableKeyFromFilterName(name);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -260,6 +260,65 @@ describe('tableStoreBody', () => {
|
|
|
260
260
|
});
|
|
261
261
|
});
|
|
262
262
|
|
|
263
|
+
/*
|
|
264
|
+
An endpoint may answer with sibling data alongside `items` — e.g. per-page
|
|
265
|
+
totals for a footer row — which has no home on the store otherwise;
|
|
266
|
+
without this, a table needing it had to bolt a bespoke wrapper onto
|
|
267
|
+
`createTableStore` just to surface one extra reactive field.
|
|
268
|
+
*/
|
|
269
|
+
describe('aggs', () => {
|
|
270
|
+
let useStore: ReturnType<typeof createTableStore>;
|
|
271
|
+
|
|
272
|
+
beforeEach(async () => {
|
|
273
|
+
getList.mockResolvedValue({
|
|
274
|
+
items: [],
|
|
275
|
+
next: false,
|
|
276
|
+
aggs: {
|
|
277
|
+
total: 3,
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
useStore = createTableStore('cases-aggs/datalist', {
|
|
282
|
+
apiModule: {
|
|
283
|
+
getList,
|
|
284
|
+
},
|
|
285
|
+
headers,
|
|
286
|
+
disablePersistence: true,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
mountTab(useStore);
|
|
290
|
+
await flush();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('picks up aggs from the getList response', () => {
|
|
294
|
+
const store = useStore();
|
|
295
|
+
|
|
296
|
+
expect(store.aggs).toEqual({
|
|
297
|
+
total: 3,
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('stays undefined when the response has none', async () => {
|
|
302
|
+
getList.mockResolvedValue({
|
|
303
|
+
items: [],
|
|
304
|
+
next: false,
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const store = useStore();
|
|
308
|
+
await store.loadDataList();
|
|
309
|
+
|
|
310
|
+
expect(store.aggs).toBeUndefined();
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it('drops on $reset', () => {
|
|
314
|
+
const store = useStore();
|
|
315
|
+
|
|
316
|
+
store.$reset();
|
|
317
|
+
|
|
318
|
+
expect(store.aggs).toBeUndefined();
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
263
322
|
describe('filters persistence of a nested list', () => {
|
|
264
323
|
it('keeps an added filter out of the route query, in sessionStorage instead', async () => {
|
|
265
324
|
const useStore = createTableStore('cases-nested-filters/datalist', {
|
|
@@ -126,6 +126,12 @@ export const tableStoreBody = <Entity extends Identifiable>(
|
|
|
126
126
|
const selected: Ref<Entity[]> = ref([]);
|
|
127
127
|
const error = ref<unknown>(null);
|
|
128
128
|
const isLoading = ref(false);
|
|
129
|
+
/**
|
|
130
|
+
* Sibling data an endpoint answers alongside the paginated `items` — e.g.
|
|
131
|
+
* per-page totals for a footer row. Not every `getList` response has one;
|
|
132
|
+
* absent, it stays `undefined` and callers that do not need it can ignore it.
|
|
133
|
+
*/
|
|
134
|
+
const aggs = ref<unknown>(undefined);
|
|
129
135
|
|
|
130
136
|
const updateSelected = (value: Entity[]) => {
|
|
131
137
|
selected.value = value;
|
|
@@ -177,9 +183,14 @@ export const tableStoreBody = <Entity extends Identifiable>(
|
|
|
177
183
|
const params = getLoadDataParams();
|
|
178
184
|
|
|
179
185
|
try {
|
|
180
|
-
const {
|
|
186
|
+
const {
|
|
187
|
+
items,
|
|
188
|
+
next,
|
|
189
|
+
aggs: responseAggs,
|
|
190
|
+
} = await apiModule.getList(params);
|
|
181
191
|
|
|
182
192
|
dataList.value = items ?? [];
|
|
193
|
+
aggs.value = responseAggs;
|
|
183
194
|
|
|
184
195
|
/**
|
|
185
196
|
* @author: @Oleksandr Palonnyi
|
|
@@ -221,9 +232,14 @@ export const tableStoreBody = <Entity extends Identifiable>(
|
|
|
221
232
|
const params = getLoadDataParams();
|
|
222
233
|
|
|
223
234
|
try {
|
|
224
|
-
const {
|
|
235
|
+
const {
|
|
236
|
+
items,
|
|
237
|
+
next,
|
|
238
|
+
aggs: responseAggs,
|
|
239
|
+
} = await apiModule.getList(params);
|
|
225
240
|
|
|
226
241
|
dataList.value.push(...(items ?? []));
|
|
242
|
+
aggs.value = responseAggs;
|
|
227
243
|
$patchPaginationStore({
|
|
228
244
|
next,
|
|
229
245
|
});
|
|
@@ -413,6 +429,7 @@ export const tableStoreBody = <Entity extends Identifiable>(
|
|
|
413
429
|
selected.value = [];
|
|
414
430
|
error.value = null;
|
|
415
431
|
isLoading.value = false;
|
|
432
|
+
aggs.value = undefined;
|
|
416
433
|
parentId.value = undefined;
|
|
417
434
|
|
|
418
435
|
paginationStore.$reset();
|
|
@@ -433,6 +450,7 @@ export const tableStoreBody = <Entity extends Identifiable>(
|
|
|
433
450
|
selected,
|
|
434
451
|
error,
|
|
435
452
|
isLoading,
|
|
453
|
+
aggs,
|
|
436
454
|
|
|
437
455
|
page,
|
|
438
456
|
size,
|
|
@@ -68,12 +68,14 @@ export interface LoadDataListOptions {
|
|
|
68
68
|
withLoading?: boolean;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
export interface TableStore<Entity> {
|
|
71
|
+
export interface TableStore<Entity, Aggs = unknown> {
|
|
72
72
|
// tableStore
|
|
73
73
|
dataList: Ref<Entity[]>;
|
|
74
74
|
selected: Ref<Entity[]>;
|
|
75
75
|
error: Ref<Error | null>;
|
|
76
76
|
isLoading: Ref<boolean>;
|
|
77
|
+
/** Sibling data alongside `dataList` — e.g. per-page totals for a footer row. */
|
|
78
|
+
aggs: Ref<Aggs | undefined>;
|
|
77
79
|
|
|
78
80
|
// paginationStore
|
|
79
81
|
page: Ref<number>;
|