@platforma-sdk/ui-vue 1.45.37 → 1.45.45

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.
Files changed (78) hide show
  1. package/.turbo/turbo-build.log +23 -39
  2. package/.turbo/turbo-type-check.log +1 -1
  3. package/CHANGELOG.md +19 -0
  4. package/dist/components/PlAdvancedFilter/{SingleFilter.vue.d.ts → FilterEditor.vue.d.ts} +12 -18
  5. package/dist/components/PlAdvancedFilter/FilterEditor.vue.js +10 -0
  6. package/dist/components/PlAdvancedFilter/FilterEditor.vue.js.map +1 -0
  7. package/dist/components/PlAdvancedFilter/FilterEditor.vue2.js +305 -0
  8. package/dist/components/PlAdvancedFilter/FilterEditor.vue2.js.map +1 -0
  9. package/dist/components/PlAdvancedFilter/FilterEditor.vue3.js +33 -0
  10. package/dist/components/PlAdvancedFilter/FilterEditor.vue3.js.map +1 -0
  11. package/dist/components/PlAdvancedFilter/OperandButton.vue2.js.map +1 -1
  12. package/dist/components/PlAdvancedFilter/OperandButton.vue3.js +9 -9
  13. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue.d.ts +35 -16
  14. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue2.js +185 -145
  15. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue2.js.map +1 -1
  16. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue3.js +13 -9
  17. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue3.js.map +1 -1
  18. package/dist/components/PlAdvancedFilter/constants.d.ts +4 -4
  19. package/dist/components/PlAdvancedFilter/constants.js +26 -22
  20. package/dist/components/PlAdvancedFilter/constants.js.map +1 -1
  21. package/dist/components/PlAdvancedFilter/index.d.ts +4 -0
  22. package/dist/components/PlAdvancedFilter/index.js +9 -0
  23. package/dist/components/PlAdvancedFilter/index.js.map +1 -0
  24. package/dist/components/PlAdvancedFilter/types.d.ts +30 -34
  25. package/dist/components/PlAdvancedFilter/utils.d.ts +26 -0
  26. package/dist/components/PlAdvancedFilter/utils.js +32 -131
  27. package/dist/components/PlAdvancedFilter/utils.js.map +1 -1
  28. package/dist/components/PlAnnotations/components/AnnotationsSidebar.vue2.js +43 -41
  29. package/dist/components/PlAnnotations/components/AnnotationsSidebar.vue2.js.map +1 -1
  30. package/dist/components/PlAnnotations/components/AnnotationsSidebar.vue3.js +2 -2
  31. package/dist/components/PlAnnotations/components/FilterSidebar.vue.d.ts +14 -2
  32. package/dist/components/PlAnnotations/components/FilterSidebar.vue2.js +99 -90
  33. package/dist/components/PlAnnotations/components/FilterSidebar.vue2.js.map +1 -1
  34. package/dist/components/PlAnnotations/components/FilterSidebar.vue3.js +5 -5
  35. package/dist/components/PlAnnotations/components/PlAnnotations.vue2.js +32 -30
  36. package/dist/components/PlAnnotations/components/PlAnnotations.vue2.js.map +1 -1
  37. package/dist/components/PlAnnotations/components/PlAnnotations.vue3.js +5 -7
  38. package/dist/components/PlAnnotations/components/PlAnnotations.vue3.js.map +1 -1
  39. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue2.js +26 -22
  40. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue2.js.map +1 -1
  41. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue3.js +7 -9
  42. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue3.js.map +1 -1
  43. package/dist/components/PlAnnotations/components/style.module.css.js +13 -0
  44. package/dist/components/PlAnnotations/components/style.module.css.js.map +1 -0
  45. package/dist/components/PlAnnotations/index.d.ts +1 -0
  46. package/dist/index.js +35 -33
  47. package/dist/index.js.map +1 -1
  48. package/package.json +9 -8
  49. package/src/components/PlAdvancedFilter/{SingleFilter.vue → FilterEditor.vue} +53 -53
  50. package/src/components/PlAdvancedFilter/OperandButton.vue +1 -1
  51. package/src/components/PlAdvancedFilter/PlAdvancedFilter.vue +155 -59
  52. package/src/components/PlAdvancedFilter/constants.ts +17 -11
  53. package/src/components/PlAdvancedFilter/index.ts +6 -0
  54. package/src/components/PlAdvancedFilter/types.ts +23 -54
  55. package/src/components/PlAdvancedFilter/utils.ts +51 -163
  56. package/src/components/PlAnnotations/components/AnnotationsSidebar.vue +8 -8
  57. package/src/components/PlAnnotations/components/FilterSidebar.vue +64 -69
  58. package/src/components/PlAnnotations/components/PlAnnotations.vue +4 -7
  59. package/src/components/PlAnnotations/components/PlAnnotationsModal.vue +5 -8
  60. package/src/components/PlAnnotations/components/style.module.css +16 -0
  61. package/src/components/PlAnnotations/index.ts +2 -0
  62. package/dist/components/PlAdvancedFilter/SingleFilter.vue.js +0 -10
  63. package/dist/components/PlAdvancedFilter/SingleFilter.vue.js.map +0 -1
  64. package/dist/components/PlAdvancedFilter/SingleFilter.vue2.js +0 -306
  65. package/dist/components/PlAdvancedFilter/SingleFilter.vue2.js.map +0 -1
  66. package/dist/components/PlAdvancedFilter/SingleFilter.vue3.js +0 -35
  67. package/dist/components/PlAdvancedFilter/SingleFilter.vue3.js.map +0 -1
  68. package/dist/components/PlAdvancedFilter/types.js +0 -8
  69. package/dist/components/PlAdvancedFilter/types.js.map +0 -1
  70. package/dist/components/PlAnnotations/components/DynamicForm.vue.d.ts +0 -24
  71. package/dist/components/PlAnnotations/components/DynamicForm.vue.js +0 -10
  72. package/dist/components/PlAnnotations/components/DynamicForm.vue.js.map +0 -1
  73. package/dist/components/PlAnnotations/components/DynamicForm.vue2.js +0 -109
  74. package/dist/components/PlAnnotations/components/DynamicForm.vue2.js.map +0 -1
  75. package/dist/components/PlAnnotations/components/DynamicForm.vue3.js +0 -9
  76. package/dist/components/PlAnnotations/components/DynamicForm.vue3.js.map +0 -1
  77. package/dist/components/PlAnnotations/utils.js +0 -20
  78. package/dist/components/PlAnnotations/utils.js.map +0 -1
@@ -1,59 +1,131 @@
1
1
  <script lang="ts" setup>
2
- import SingleFilter from './SingleFilter.vue';
3
- import { PlBtnSecondary, PlElementList, PlCheckbox, PlIcon16 } from '@milaboratories/uikit';
4
- import type { PlAdvancedFilterColumnId, CommonFilterSpec, Group, PlAdvancedFilterUI, SourceOptionInfo } from './types';
5
- import { computed } from 'vue';
6
- import OperandButton from './OperandButton.vue';
7
- import { DEFAULT_FILTER_TYPE, DEFAULT_FILTERS } from './constants';
2
+ import { PlBtnSecondary, PlCheckbox, PlElementList } from '@milaboratories/uikit';
8
3
  import type { ListOptionBase } from '@platforma-sdk/model';
9
- import { createNewGroup, isValidColumnId, toInnerModel, toOuterModel, useInnerModel } from './utils';
4
+ import { computed, toRaw } from 'vue';
5
+ import FilterEditor from './FilterEditor.vue';
6
+ import OperandButton from './OperandButton.vue';
7
+ import { DEFAULT_FILTER_TYPE, DEFAULT_FILTERS, SUPPORTED_FILTER_TYPES } from './constants';
8
+ import type { CommonFilter, EditableFilter, NodeFilter, PlAdvancedFilterColumnId, RootFilter, SourceOptionInfo } from './types';
9
+ import { createNewGroup, getNewId, isValidColumnId } from './utils';
10
+
11
+ const model = defineModel<RootFilter>('filters', { required: true });
10
12
 
11
13
  const props = withDefaults(defineProps<{
12
14
  /** List of ids of sources (columns, axes) that can be selected in filters */
13
15
  items: SourceOptionInfo[];
16
+ /** List of supported filter types */
17
+ supportedFilters?: typeof SUPPORTED_FILTER_TYPES;
14
18
  /** If true - new filter can be added by droppind element into filter group; else new column is added by button click */
15
19
  enableDnd?: boolean;
20
+ /** If true - "Add group" button is shown below the filter groups */
21
+ enableAddGroupButton?: boolean;
16
22
  /** Loading function for unique values for Equal/InSet filters and fixed axes options. */
17
- getSuggestOptions: (params: { columnId: PlAdvancedFilterColumnId; searchStr: string; axisIdx?: number }) => (Promise<ListOptionBase<string | number>[]>) |
18
- ((params: { columnId: PlAdvancedFilterColumnId; searchStr: string; axisIdx?: number }) => ListOptionBase<string | number>[]);
23
+ getSuggestOptions: (params: { columnId: PlAdvancedFilterColumnId; searchStr: string; axisIdx?: number }) =>
24
+ ListOptionBase<string | number>[] | Promise<ListOptionBase<string | number>[]>;
19
25
  /** Loading function for label of selected value for Equal/InSet filters and fixed axes options. */
20
- getSuggestModel: (params: { columnId: PlAdvancedFilterColumnId; searchStr: string; axisIdx?: number }) => (Promise<ListOptionBase<string | number>>) |
21
- ((params: { columnId: PlAdvancedFilterColumnId; searchStr: string; axisIdx?: number }) => ListOptionBase<string | number>);
22
- }>(), { enableDnd: false });
26
+ getSuggestModel?: (params: { columnId: PlAdvancedFilterColumnId; searchStr: string; axisIdx?: number }) =>
27
+ ListOptionBase<string | number> | Promise<ListOptionBase<string | number>>;
28
+ }>(), {
29
+ supportedFilters: () => SUPPORTED_FILTER_TYPES,
30
+ getSuggestModel: undefined,
23
31
 
24
- const model = defineModel<CommonFilterSpec>({ required: true });
25
- const innerModel = useInnerModel<CommonFilterSpec, PlAdvancedFilterUI>(toInnerModel, toOuterModel, model);
32
+ enableDnd: false,
33
+ enableAddGroupButton: false,
34
+ });
26
35
 
27
36
  const firstColumnId = computed(() => props.items[0]?.id);
28
- const emptyGroup: Group[] = [{
29
- id: 'empty',
30
- not: false,
31
- operand: 'and',
37
+ const emptyGroup: NodeFilter[] = [{
38
+ id: -1,
39
+ type: 'and',
32
40
  filters: [],
33
- expanded: true,
41
+ isExpanded: true,
34
42
  }];
35
43
 
44
+ function getRootGroups() {
45
+ if (model.value.type !== 'or' && model.value.type !== 'and') {
46
+ throw new Error('Invalid model structure, expected root to be "or" or "and" group');
47
+ }
48
+ return model.value.filters;
49
+ }
50
+
51
+ function getRootGroup(idx: number): NodeFilter {
52
+ const groups = getRootGroups();
53
+ const group = groups[idx];
54
+ if (group.type !== 'and' && group.type !== 'or' && group.type !== 'not') {
55
+ throw new Error('Invalid group structure, expected "and", "or" or "not" group');
56
+ }
57
+ return group;
58
+ }
59
+
60
+ function getRootGroupContent(idx: number): Exclude<NodeFilter, { type: 'not' }> {
61
+ const group = getRootGroup(idx);
62
+
63
+ if (group.type !== 'not') {
64
+ return group;
65
+ }
66
+
67
+ if (group.filter.type !== 'and' && group.filter.type !== 'or') {
68
+ throw new Error('Invalid group structure, expected "and" or "or" group inside "not"');
69
+ }
70
+
71
+ return group.filter;
72
+ }
73
+
36
74
  function addColumnToGroup(groupIdx: number, selectedSourceId: PlAdvancedFilterColumnId) {
37
- innerModel.value.groups[groupIdx].filters.push({
75
+ const group = getRootGroupContent(groupIdx);
76
+
77
+ group.filters.push({
38
78
  ...DEFAULT_FILTERS[DEFAULT_FILTER_TYPE],
39
79
  column: selectedSourceId,
40
- });
80
+ id: getNewId(),
81
+ isExpanded: true,
82
+ } as CommonFilter);
41
83
  }
42
84
 
43
85
  function removeFilterFromGroup(groupIdx: number, filterIdx: number) {
44
- if (innerModel.value.groups[groupIdx].filters.length === 1 && filterIdx === 0) {
86
+ const group = getRootGroupContent(groupIdx);
87
+
88
+ if (group.filters.length === 1 && filterIdx === 0) {
45
89
  removeGroup(groupIdx);
46
90
  } else {
47
- innerModel.value.groups[groupIdx].filters = innerModel.value.groups[groupIdx].filters.filter((_v, idx) => idx !== filterIdx);
91
+ group.filters.splice(filterIdx, 1);
92
+ }
93
+ }
94
+ function inverseRootNode(groupIdx: number) {
95
+ const groups = getRootGroups();
96
+ const group = groups[groupIdx];
97
+ if (group.type === 'not') {
98
+ if (group.filter.type !== 'and' && group.filter.type !== 'or') {
99
+ throw new Error('Invalid group structure, expected "and" or "or" group inside "not"');
100
+ }
101
+ groups[groupIdx] = group.filter;
102
+ } else {
103
+ const type = groups[groupIdx].type;
104
+ if (type !== 'and' && type !== 'or' && type !== 'not') {
105
+ throw new Error('Invalid group structure, expected "and", "or" or "not" group');
106
+ }
107
+
108
+ groups[groupIdx] = {
109
+ id: getNewId(),
110
+ isExpanded: true,
111
+ type: 'not',
112
+ filter: groups[groupIdx],
113
+ };
48
114
  }
49
115
  }
50
116
 
117
+ function getNotContent<T extends CommonFilter>(item: T): Exclude<T, { type: 'not' }> {
118
+ return item.type === 'not' ? item.filter as Exclude<T, { type: 'not' }> : item as Exclude<T, { type: 'not' }>;
119
+ }
120
+
51
121
  function removeGroup(groupIdx: number) {
52
- innerModel.value.groups = innerModel.value.groups.filter((v, idx) => idx !== groupIdx);
122
+ const groups = getRootGroups();
123
+ groups.splice(groupIdx, 1);
53
124
  }
54
125
  function addGroup(selectedSourceId: PlAdvancedFilterColumnId) {
55
126
  const newGroup = createNewGroup(selectedSourceId);
56
- innerModel.value.groups.push(newGroup);
127
+ const groups = getRootGroups();
128
+ groups.push(newGroup);
57
129
  }
58
130
 
59
131
  function handleDropToExistingGroup(groupIdx: number, event: DragEvent) {
@@ -77,70 +149,86 @@ function handleDropToNewGroup(event: DragEvent) {
77
149
  function dragOver(event: DragEvent) {
78
150
  event.preventDefault();
79
151
  }
152
+
153
+ function validateFilter<T extends CommonFilter>(item: T): EditableFilter {
154
+ if (item.type === 'and' || item.type === 'or' || item.type === 'not') {
155
+ throw new Error('Invalid filter structure, expected leaf filter');
156
+ }
157
+
158
+ return item as EditableFilter;
159
+ }
160
+
161
+ function updateFilter(filters: CommonFilter[], idx: number, updatedFilter: EditableFilter) {
162
+ filters[idx] = toRaw(updatedFilter as CommonFilter);
163
+ }
80
164
  </script>
81
165
  <template>
82
166
  <div>
83
167
  <PlElementList
84
- v-model:items="innerModel.groups"
85
- :get-item-key="(group) => group.id"
168
+ v-model:items="model.filters"
169
+ :get-item-key="(filter) => filter.id"
86
170
 
87
171
  :item-class="$style.filterGroup"
88
172
  :item-class-content="$style.filterGroupContent"
89
173
  :item-class-title="$style.filterGroupTitle"
90
174
 
91
- :is-expanded="(group) => group.expanded"
175
+ :is-expanded="(filter) => filter.isExpanded === true"
176
+ :on-expand="(group) => { group.isExpanded = !group.isExpanded}"
92
177
 
93
178
  :disableDragging="false"
94
179
  :disableRemoving="false"
95
180
  :disableToggling="true"
96
181
  :disablePinning="true"
97
-
98
- :on-expand="(group) => { group.expanded = !group.expanded}"
99
182
  >
100
183
  <template #item-title>
101
184
  Filter group
102
185
  </template>
103
186
  <template #item-content="{ item, index }">
104
187
  <div
105
- :class="$style.groupContent" dropzone="true"
188
+ :class="$style.groupContent"
189
+ dropzone="true"
106
190
  @drop="(event) => handleDropToExistingGroup(index, event)"
107
191
  @dragover="dragOver"
108
192
  >
109
- <PlCheckbox v-model="item.not">NOT</PlCheckbox>
110
- <SingleFilter
111
- v-for="(filter, filterIdx) in item.filters"
112
- :key="filterIdx"
113
- v-model="item.filters[filterIdx]"
114
- :operand="item.operand"
115
- :column-options="items"
116
- :get-suggest-model="getSuggestModel"
117
- :get-suggest-options="getSuggestOptions"
118
- :enable-dnd="enableDnd"
119
- :is-last="filterIdx === item.filters.length - 1"
120
- :on-change-operand="(v) => item.operand = v"
121
- :on-delete="() => removeFilterFromGroup(index, filterIdx)"
122
- />
123
- <div v-if="enableDnd" :class="$style.dropzone">
193
+ <PlCheckbox :model-value="item.type === 'not'" :class="$style.notCheckbox" @update:model-value="inverseRootNode(index)">NOT</PlCheckbox>
194
+ <template v-for="(_, filterIdx) in getNotContent(item).filters" :key="filterIdx">
195
+ <FilterEditor
196
+ :filter="validateFilter(getNotContent(item).filters[filterIdx])"
197
+ :operand="getNotContent(item).type"
198
+ :column-options="items"
199
+ :supported-filters="props.supportedFilters"
200
+ :get-suggest-model="props.getSuggestModel"
201
+ :get-suggest-options="props.getSuggestOptions"
202
+ :enable-dnd="Boolean(props.enableDnd)"
203
+ :is-last="filterIdx === getNotContent(item).filters.length - 1"
204
+ :on-change-operand="(v) => getNotContent(item).type = v"
205
+ :on-delete="() => removeFilterFromGroup(index, filterIdx)"
206
+ @update:filter="(value) => updateFilter(getNotContent(item).filters, filterIdx, value)"
207
+ />
208
+ </template>
209
+ <div v-if="props.enableDnd" :class="$style.dropzone">
124
210
  <div>Drop dimensions here</div>
125
211
  </div>
126
- <PlBtnSecondary v-else @click="addColumnToGroup(index, firstColumnId)">
127
- <PlIcon16 name="add" style="margin-right: 8px"/>Add column
212
+ <PlBtnSecondary v-else icon="add" @click="addColumnToGroup(index, firstColumnId)">
213
+ Add filter
128
214
  </PlBtnSecondary>
129
215
  </div>
130
216
  </template>
131
217
  <template #item-after="{ index }">
132
218
  <OperandButton
219
+ v-if="props.enableAddGroupButton || index < getRootGroups().length - 1"
133
220
  :class="$style.buttonWrapper"
134
- :active="innerModel.operand"
135
- :disabled="index === innerModel.groups.length - 1"
136
- :on-select="(v) => innerModel.operand = v"
221
+ :active="model.type"
222
+ :disabled="index === getRootGroups().length - 1"
223
+ :on-select="(v) => model.type = v"
137
224
  />
138
225
  </template>
139
226
  </PlElementList>
140
227
 
141
- <!-- Last group - always exists, always empty, just for adding new groups -->
228
+ <!-- Last group - always empty, just for adding new groups -->
142
229
  <PlElementList
143
- v-model:items="emptyGroup"
230
+ v-if="props.enableAddGroupButton"
231
+ :items="emptyGroup"
144
232
  :get-item-key="(group) => group.id"
145
233
  :item-class="$style.filterGroup"
146
234
  :item-class-content="$style.filterGroupContent"
@@ -157,14 +245,15 @@ function dragOver(event: DragEvent) {
157
245
  @dragover="dragOver"
158
246
  >
159
247
  <template #item-title>Filter group</template>
160
- <template #item-content="{item}">
161
- <PlCheckbox v-model="item.not" disabled >NOT</PlCheckbox>
248
+ <template #item-content>
162
249
  <div v-if="enableDnd" :class="$style.dropzone">
163
250
  <div>Drop dimensions here</div>
164
251
  </div>
165
- <PlBtnSecondary v-else @click="addGroup(firstColumnId)">
166
- <PlIcon16 name="add" style="margin-right: 8px"/>Add column
167
- </PlBtnSecondary>
252
+ <slot v-else name="add-group-buttons">
253
+ <PlBtnSecondary icon="add" @click="addGroup(firstColumnId)">
254
+ Add filter
255
+ </PlBtnSecondary>
256
+ </slot>
168
257
  </template>
169
258
  </PlElementList>
170
259
  </div>
@@ -179,11 +268,17 @@ function dragOver(event: DragEvent) {
179
268
  .filterGroupTitle {
180
269
  background: none;
181
270
  }
271
+ .filterGroupContent {
272
+ padding: 4px 24px 24px 24px;
273
+ }
182
274
  .groupContent {
183
275
  display: flex;
184
276
  flex-direction: column;
185
277
  gap: 12px;
186
278
  }
279
+ .notCheckbox {
280
+ margin: 4px 0;
281
+ }
187
282
  .dropzone {
188
283
  border-radius: 6px;
189
284
  border: 1.5px dashed var(--color-div-grey);
@@ -199,7 +294,8 @@ function dragOver(event: DragEvent) {
199
294
  align-items: center;
200
295
  }
201
296
  .buttonWrapper {
202
- height: 72px;
297
+ margin-top: 8px;
298
+ height: 56px;
203
299
  display: flex;
204
300
  align-items: center;
205
301
  }
@@ -1,6 +1,8 @@
1
- import type { PlAdvancedFilterColumnId, Filter, FilterType, SupportedFilterTypes } from './types';
1
+ import type { FilterSpecType } from '@platforma-sdk/model';
2
+ import type { EditableFilter, PlAdvancedFilterColumnId, SupportedFilterTypes } from './types';
2
3
 
3
- export const SUPPORTED_FILTER_TYPES = new Set<SupportedFilterTypes>([
4
+ // List of ALL supported filter types in Advanced Filter component
5
+ export const SUPPORTED_FILTER_TYPES = [
4
6
  'isNA',
5
7
  'isNotNA',
6
8
  'greaterThan',
@@ -17,26 +19,30 @@ export const SUPPORTED_FILTER_TYPES = new Set<SupportedFilterTypes>([
17
19
  'patternMatchesRegularExpression',
18
20
  'inSet',
19
21
  'notInSet',
20
- ]);
22
+ 'topN',
23
+ 'bottomN',
24
+ ] satisfies FilterSpecType[];
21
25
 
22
- export const DEFAULT_FILTER_TYPE: FilterType = 'isNA';
26
+ export const DEFAULT_FILTER_TYPE: SupportedFilterTypes = 'isNA';
23
27
 
24
28
  const emptyCommonPart = { column: '' as PlAdvancedFilterColumnId };
25
- export const DEFAULT_FILTERS: Record<SupportedFilterTypes, Filter> = {
29
+ export const DEFAULT_FILTERS: Record<SupportedFilterTypes, EditableFilter> = {
26
30
  isNA: { type: 'isNA', ...emptyCommonPart },
27
31
  isNotNA: { type: 'isNotNA', ...emptyCommonPart },
28
- lessThan: { type: 'lessThan', x: undefined, ...emptyCommonPart },
29
- lessThanOrEqual: { type: 'lessThanOrEqual', x: undefined, ...emptyCommonPart },
32
+ lessThan: { type: 'lessThan', x: 0, ...emptyCommonPart },
33
+ lessThanOrEqual: { type: 'lessThanOrEqual', x: 0, ...emptyCommonPart },
30
34
  patternEquals: { type: 'patternEquals', value: undefined, ...emptyCommonPart },
31
35
  patternNotEquals: { type: 'patternNotEquals', value: undefined, ...emptyCommonPart },
32
- greaterThan: { type: 'greaterThan', x: undefined, ...emptyCommonPart },
33
- greaterThanOrEqual: { type: 'greaterThanOrEqual', x: undefined, ...emptyCommonPart },
36
+ greaterThan: { type: 'greaterThan', x: 0, ...emptyCommonPart },
37
+ greaterThanOrEqual: { type: 'greaterThanOrEqual', x: 0, ...emptyCommonPart },
34
38
  patternContainSubsequence: { type: 'patternContainSubsequence', value: '', ...emptyCommonPart },
35
39
  patternNotContainSubsequence: { type: 'patternNotContainSubsequence', value: '', ...emptyCommonPart },
36
40
  patternFuzzyContainSubsequence: { type: 'patternFuzzyContainSubsequence', maxEdits: 2, substitutionsOnly: false, wildcard: undefined, value: '', ...emptyCommonPart },
37
41
  patternMatchesRegularExpression: { type: 'patternMatchesRegularExpression', value: '', ...emptyCommonPart },
38
- equal: { type: 'equal', x: undefined, ...emptyCommonPart },
39
- notEqual: { type: 'notEqual', x: undefined, ...emptyCommonPart },
42
+ equal: { type: 'equal', x: 0, ...emptyCommonPart },
43
+ notEqual: { type: 'notEqual', x: 0, ...emptyCommonPart },
40
44
  inSet: { type: 'inSet', value: [], ...emptyCommonPart },
41
45
  notInSet: { type: 'notInSet', value: [], ...emptyCommonPart },
46
+ topN: { type: 'topN', n: 10, ...emptyCommonPart },
47
+ bottomN: { type: 'bottomN', n: 10, ...emptyCommonPart },
42
48
  };
@@ -1 +1,7 @@
1
+ import { SUPPORTED_FILTER_TYPES } from './constants.ts';
2
+ import type { RootFilter, SourceOptionInfo } from './types.ts';
3
+
1
4
  export { default as PlAdvancedFilter } from './PlAdvancedFilter.vue';
5
+ export const PlAdvancedFilterSupportedFilters = SUPPORTED_FILTER_TYPES;
6
+ export type PlAdvancedFilterItem = SourceOptionInfo;
7
+ export type PlAdvancedFilterFilter = RootFilter;
@@ -1,67 +1,36 @@
1
- import type { AxisSpec, FilterSpec, FilterSpecLeaf, FilterSpecType, ListOptionBase, PColumnSpec, SUniversalPColumnId } from '@platforma-sdk/model';
2
- import { SUPPORTED_FILTER_TYPES } from './constants';
3
- import type { CanonicalizedJson } from '@platforma-sdk/model';
4
- import type { AxisId } from '@platforma-sdk/model';
1
+ import type { AxisId, AxisSpec, CanonicalizedJson, FilterSpec, FilterSpecLeaf, PColumnSpec, SUniversalPColumnId } from '@platforma-sdk/model';
2
+ import type { SUPPORTED_FILTER_TYPES } from './constants';
5
3
 
6
- export type PlAdvancedFilterColumnId = SUniversalPColumnId | CanonicalizedJson<AxisId>;
7
- export type CommonFilterSpec = FilterSpec<FilterSpecLeaf<PlAdvancedFilterColumnId>, { expanded?: boolean }>;
8
-
9
- // Not supported: topN, bottomN, lessThanColumn, lessThanColumnOrEqual
10
- // or, and, not - in groups
11
- export type SupportedFilterTypes = FilterSpecType &
12
- 'isNA' | 'isNotNA' |
13
- 'patternEquals' | 'patternNotEquals' |
14
- 'patternContainSubsequence' | 'patternNotContainSubsequence' |
15
- 'patternMatchesRegularExpression' |
16
- 'patternFuzzyContainSubsequence' |
17
- 'inSet' | 'notInSet' |
18
- 'equal' | 'notEqual' |
19
- 'lessThan' | 'lessThanOrEqual' |
20
- 'greaterThan' | 'greaterThanOrEqual';
21
-
22
- export type FilterType = SupportedFilterTypes;
4
+ export type Operand = 'or' | 'and';
23
5
 
24
- export function isSupportedFilterType(type: FilterSpecType | undefined): type is SupportedFilterTypes {
25
- if (!type) {
26
- return false;
27
- }
28
- return SUPPORTED_FILTER_TYPES.has(type as SupportedFilterTypes);
29
- }
6
+ export type PlAdvancedFilterColumnId = SUniversalPColumnId | CanonicalizedJson<AxisId>;
30
7
 
31
- export type Operand = 'or' | 'and';
8
+ export type FilterLeafContent = Extract<FilterSpecLeaf<PlAdvancedFilterColumnId>, { type: SupportedFilterTypes }>;
9
+ export type CommonFilter = FilterSpec<FilterLeafContent, { id: number; isExpanded?: boolean }>;
10
+ export type FilterLeaf = Exclude<CommonFilter, { type: Operand | 'not' }>;
11
+ export type NodeFilter = Extract<CommonFilter, { type: Operand | 'not' }>;
12
+ export type RootFilter = Omit<Extract<NodeFilter, { type: Operand }>, 'filters'> & { filters: NodeFilter[] };
32
13
 
33
- type FilterUiBase = FilterSpecLeaf<PlAdvancedFilterColumnId> & {
34
- type: SupportedFilterTypes;
35
- column: PlAdvancedFilterColumnId;
36
- };
14
+ // Not supported: less(/greater)ThanColumn, less(/greater)ThanColumnOrEqual
15
+ // or, and, not - in groups
16
+ export type SupportedFilterTypes = typeof SUPPORTED_FILTER_TYPES[number];
37
17
 
38
18
  type RequireFields<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
39
19
  type OptionalFields<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
40
20
 
21
+ type NumericalWithOptionalN = 'topN' | 'bottomN';
41
22
  type NumericalWithOptionalX = 'lessThan' | 'lessThanOrEqual' | 'greaterThan' | 'greaterThanOrEqual' | 'equal' | 'notEqual';
42
23
  type StringWithOptionalValue = 'patternEquals' | 'patternNotEquals';
43
- type EditedTypes = SupportedFilterTypes & ('patternFuzzyContainSubsequence' | NumericalWithOptionalX | StringWithOptionalValue); // types from ui with some changed by optionality fields
44
- export type Filter = Exclude<FilterUiBase, { type: EditedTypes }> |
45
- RequireFields<Extract<FilterUiBase, { type: 'patternFuzzyContainSubsequence' }>, 'maxEdits' | 'substitutionsOnly'> |
46
- OptionalFields<Extract<FilterUiBase, { type: NumericalWithOptionalX }>, 'x'> |
47
- OptionalFields<Extract<FilterUiBase, { type: StringWithOptionalValue }>, 'value'>
24
+ // types from ui with some changed by optionality fields
25
+ type EditedTypes = 'patternFuzzyContainSubsequence' | NumericalWithOptionalX | StringWithOptionalValue | NumericalWithOptionalN;
26
+ export type EditableFilter =
27
+ | Exclude<FilterLeafContent, { type: EditedTypes }>
28
+ | RequireFields<Extract<FilterLeafContent, { type: 'patternFuzzyContainSubsequence' }>, 'maxEdits' | 'substitutionsOnly'>
29
+ | OptionalFields<Extract<FilterLeafContent, { type: NumericalWithOptionalN }>, 'n'>
30
+ | OptionalFields<Extract<FilterLeafContent, { type: NumericalWithOptionalX }>, 'x'>
31
+ | OptionalFields<Extract<FilterLeafContent, { type: StringWithOptionalValue }>, 'value'>
48
32
  ;
49
33
 
50
- export type Group = {
51
- id: string;
52
- not: boolean;
53
- filters: Filter[];
54
- operand: Operand;
55
- expanded: boolean;
56
- };
57
-
58
- export type PlAdvancedFilterUI = {
59
- groups: Group[];
60
- operand: Operand;
61
- };
62
-
63
- export type UniqueValuesList = ListOptionBase<string | number>[];
64
- export type OptionInfo = { error: boolean; label: string; spec: PColumnSpec | AxisSpec };
65
34
  export type FixedAxisInfo = {
66
35
  idx: number;
67
36
  label: string;
@@ -70,8 +39,8 @@ export type FixedAxisInfo = {
70
39
  export type SourceOptionInfo = {
71
40
  id: PlAdvancedFilterColumnId;
72
41
  label: string;
73
- error: boolean;
74
42
  spec: PColumnSpec | AxisSpec;
75
- axesToBeFixed?: FixedAxisInfo[];
43
+ error?: boolean;
76
44
  alphabet?: 'nucleotide' | 'aminoacid' | string;
45
+ axesToBeFixed?: FixedAxisInfo[];
77
46
  };