@platforma-sdk/ui-vue 1.45.42 → 1.46.0

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 (79) hide show
  1. package/.turbo/turbo-build.log +23 -39
  2. package/.turbo/turbo-type-check.log +1 -1
  3. package/CHANGELOG.md +22 -0
  4. package/dist/components/PlAdvancedFilter/FilterEditor.vue.d.ts +27 -0
  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 +302 -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 +29 -20
  14. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue2.js +184 -146
  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 +10 -2
  32. package/dist/components/PlAnnotations/components/FilterSidebar.vue2.js +98 -91
  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 +23 -23
  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 +19 -17
  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} +58 -62
  50. package/src/components/PlAdvancedFilter/OperandButton.vue +1 -1
  51. package/src/components/PlAdvancedFilter/PlAdvancedFilter.vue +152 -60
  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 +60 -69
  58. package/src/components/PlAnnotations/components/PlAnnotations.vue +3 -7
  59. package/src/components/PlAnnotations/components/PlAnnotationsModal.vue +4 -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.d.ts +0 -37
  63. package/dist/components/PlAdvancedFilter/SingleFilter.vue.js +0 -10
  64. package/dist/components/PlAdvancedFilter/SingleFilter.vue.js.map +0 -1
  65. package/dist/components/PlAdvancedFilter/SingleFilter.vue2.js +0 -306
  66. package/dist/components/PlAdvancedFilter/SingleFilter.vue2.js.map +0 -1
  67. package/dist/components/PlAdvancedFilter/SingleFilter.vue3.js +0 -35
  68. package/dist/components/PlAdvancedFilter/SingleFilter.vue3.js.map +0 -1
  69. package/dist/components/PlAdvancedFilter/types.js +0 -8
  70. package/dist/components/PlAdvancedFilter/types.js.map +0 -1
  71. package/dist/components/PlAnnotations/components/DynamicForm.vue.d.ts +0 -24
  72. package/dist/components/PlAnnotations/components/DynamicForm.vue.js +0 -10
  73. package/dist/components/PlAnnotations/components/DynamicForm.vue.js.map +0 -1
  74. package/dist/components/PlAnnotations/components/DynamicForm.vue2.js +0 -109
  75. package/dist/components/PlAnnotations/components/DynamicForm.vue2.js.map +0 -1
  76. package/dist/components/PlAnnotations/components/DynamicForm.vue3.js +0 -9
  77. package/dist/components/PlAnnotations/components/DynamicForm.vue3.js.map +0 -1
  78. package/dist/components/PlAnnotations/utils.js +0 -20
  79. package/dist/components/PlAnnotations/utils.js.map +0 -1
@@ -1,59 +1,128 @@
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>[]);
19
- /** 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 });
23
+ getSuggestOptions: (params: { columnId: PlAdvancedFilterColumnId; axisIdx?: number; searchStr: string; searchType: 'value' | 'label' }) =>
24
+ ListOptionBase<string | number>[] | Promise<ListOptionBase<string | number>[]>;
25
+ }>(), {
26
+ supportedFilters: () => SUPPORTED_FILTER_TYPES,
27
+ getSuggestModel: undefined,
23
28
 
24
- const model = defineModel<CommonFilterSpec>({ required: true });
25
- const innerModel = useInnerModel<CommonFilterSpec, PlAdvancedFilterUI>(toInnerModel, toOuterModel, model);
29
+ enableDnd: false,
30
+ enableAddGroupButton: false,
31
+ });
26
32
 
27
33
  const firstColumnId = computed(() => props.items[0]?.id);
28
- const emptyGroup: Group[] = [{
29
- id: 'empty',
30
- not: false,
31
- operand: 'and',
34
+ const emptyGroup: NodeFilter[] = [{
35
+ id: -1,
36
+ type: 'and',
32
37
  filters: [],
33
- expanded: true,
38
+ isExpanded: true,
34
39
  }];
35
40
 
41
+ function getRootGroups() {
42
+ if (model.value.type !== 'or' && model.value.type !== 'and') {
43
+ throw new Error('Invalid model structure, expected root to be "or" or "and" group');
44
+ }
45
+ return model.value.filters;
46
+ }
47
+
48
+ function getRootGroup(idx: number): NodeFilter {
49
+ const groups = getRootGroups();
50
+ const group = groups[idx];
51
+ if (group.type !== 'and' && group.type !== 'or' && group.type !== 'not') {
52
+ throw new Error('Invalid group structure, expected "and", "or" or "not" group');
53
+ }
54
+ return group;
55
+ }
56
+
57
+ function getRootGroupContent(idx: number): Exclude<NodeFilter, { type: 'not' }> {
58
+ const group = getRootGroup(idx);
59
+
60
+ if (group.type !== 'not') {
61
+ return group;
62
+ }
63
+
64
+ if (group.filter.type !== 'and' && group.filter.type !== 'or') {
65
+ throw new Error('Invalid group structure, expected "and" or "or" group inside "not"');
66
+ }
67
+
68
+ return group.filter;
69
+ }
70
+
36
71
  function addColumnToGroup(groupIdx: number, selectedSourceId: PlAdvancedFilterColumnId) {
37
- innerModel.value.groups[groupIdx].filters.push({
72
+ const group = getRootGroupContent(groupIdx);
73
+
74
+ group.filters.push({
38
75
  ...DEFAULT_FILTERS[DEFAULT_FILTER_TYPE],
39
76
  column: selectedSourceId,
40
- });
77
+ id: getNewId(),
78
+ isExpanded: true,
79
+ } as CommonFilter);
41
80
  }
42
81
 
43
82
  function removeFilterFromGroup(groupIdx: number, filterIdx: number) {
44
- if (innerModel.value.groups[groupIdx].filters.length === 1 && filterIdx === 0) {
83
+ const group = getRootGroupContent(groupIdx);
84
+
85
+ if (group.filters.length === 1 && filterIdx === 0) {
45
86
  removeGroup(groupIdx);
46
87
  } else {
47
- innerModel.value.groups[groupIdx].filters = innerModel.value.groups[groupIdx].filters.filter((_v, idx) => idx !== filterIdx);
88
+ group.filters.splice(filterIdx, 1);
89
+ }
90
+ }
91
+ function inverseRootNode(groupIdx: number) {
92
+ const groups = getRootGroups();
93
+ const group = groups[groupIdx];
94
+ if (group.type === 'not') {
95
+ if (group.filter.type !== 'and' && group.filter.type !== 'or') {
96
+ throw new Error('Invalid group structure, expected "and" or "or" group inside "not"');
97
+ }
98
+ groups[groupIdx] = group.filter;
99
+ } else {
100
+ const type = groups[groupIdx].type;
101
+ if (type !== 'and' && type !== 'or' && type !== 'not') {
102
+ throw new Error('Invalid group structure, expected "and", "or" or "not" group');
103
+ }
104
+
105
+ groups[groupIdx] = {
106
+ id: getNewId(),
107
+ isExpanded: true,
108
+ type: 'not',
109
+ filter: groups[groupIdx],
110
+ };
48
111
  }
49
112
  }
50
113
 
114
+ function getNotContent<T extends CommonFilter>(item: T): Exclude<T, { type: 'not' }> {
115
+ return item.type === 'not' ? item.filter as Exclude<T, { type: 'not' }> : item as Exclude<T, { type: 'not' }>;
116
+ }
117
+
51
118
  function removeGroup(groupIdx: number) {
52
- innerModel.value.groups = innerModel.value.groups.filter((v, idx) => idx !== groupIdx);
119
+ const groups = getRootGroups();
120
+ groups.splice(groupIdx, 1);
53
121
  }
54
122
  function addGroup(selectedSourceId: PlAdvancedFilterColumnId) {
55
123
  const newGroup = createNewGroup(selectedSourceId);
56
- innerModel.value.groups.push(newGroup);
124
+ const groups = getRootGroups();
125
+ groups.push(newGroup);
57
126
  }
58
127
 
59
128
  function handleDropToExistingGroup(groupIdx: number, event: DragEvent) {
@@ -77,70 +146,85 @@ function handleDropToNewGroup(event: DragEvent) {
77
146
  function dragOver(event: DragEvent) {
78
147
  event.preventDefault();
79
148
  }
149
+
150
+ function validateFilter<T extends CommonFilter>(item: T): EditableFilter {
151
+ if (item.type === 'and' || item.type === 'or' || item.type === 'not') {
152
+ throw new Error('Invalid filter structure, expected leaf filter');
153
+ }
154
+
155
+ return item as EditableFilter;
156
+ }
157
+
158
+ function updateFilter(filters: CommonFilter[], idx: number, updatedFilter: EditableFilter) {
159
+ filters[idx] = toRaw(updatedFilter as CommonFilter);
160
+ }
80
161
  </script>
81
162
  <template>
82
163
  <div>
83
164
  <PlElementList
84
- v-model:items="innerModel.groups"
85
- :get-item-key="(group) => group.id"
165
+ v-model:items="model.filters"
166
+ :get-item-key="(filter) => filter.id"
86
167
 
87
168
  :item-class="$style.filterGroup"
88
169
  :item-class-content="$style.filterGroupContent"
89
170
  :item-class-title="$style.filterGroupTitle"
90
171
 
91
- :is-expanded="(group) => group.expanded"
172
+ :is-expanded="(filter) => filter.isExpanded === true"
173
+ :on-expand="(group) => { group.isExpanded = !group.isExpanded}"
92
174
 
93
175
  :disableDragging="false"
94
176
  :disableRemoving="false"
95
177
  :disableToggling="true"
96
178
  :disablePinning="true"
97
-
98
- :on-expand="(group) => { group.expanded = !group.expanded}"
99
179
  >
100
180
  <template #item-title>
101
181
  Filter group
102
182
  </template>
103
183
  <template #item-content="{ item, index }">
104
184
  <div
105
- :class="$style.groupContent" dropzone="true"
185
+ :class="$style.groupContent"
186
+ dropzone="true"
106
187
  @drop="(event) => handleDropToExistingGroup(index, event)"
107
188
  @dragover="dragOver"
108
189
  >
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">
190
+ <PlCheckbox :model-value="item.type === 'not'" :class="$style.notCheckbox" @update:model-value="inverseRootNode(index)">NOT</PlCheckbox>
191
+ <template v-for="(_, filterIdx) in getNotContent(item).filters" :key="filterIdx">
192
+ <FilterEditor
193
+ :filter="validateFilter(getNotContent(item).filters[filterIdx])"
194
+ :operand="getNotContent(item).type"
195
+ :column-options="items"
196
+ :supported-filters="props.supportedFilters"
197
+ :get-suggest-options="props.getSuggestOptions"
198
+ :enable-dnd="Boolean(props.enableDnd)"
199
+ :is-last="filterIdx === getNotContent(item).filters.length - 1"
200
+ :on-change-operand="(v) => getNotContent(item).type = v"
201
+ :on-delete="() => removeFilterFromGroup(index, filterIdx)"
202
+ @update:filter="(value) => updateFilter(getNotContent(item).filters, filterIdx, value)"
203
+ />
204
+ </template>
205
+ <div v-if="props.enableDnd" :class="$style.dropzone">
124
206
  <div>Drop dimensions here</div>
125
207
  </div>
126
- <PlBtnSecondary v-else @click="addColumnToGroup(index, firstColumnId)">
127
- <PlIcon16 name="add" style="margin-right: 8px"/>Add column
208
+ <PlBtnSecondary v-else icon="add" @click="addColumnToGroup(index, firstColumnId)">
209
+ Add filter
128
210
  </PlBtnSecondary>
129
211
  </div>
130
212
  </template>
131
213
  <template #item-after="{ index }">
132
214
  <OperandButton
215
+ v-if="props.enableAddGroupButton || index < getRootGroups().length - 1"
133
216
  :class="$style.buttonWrapper"
134
- :active="innerModel.operand"
135
- :disabled="index === innerModel.groups.length - 1"
136
- :on-select="(v) => innerModel.operand = v"
217
+ :active="model.type"
218
+ :disabled="index === getRootGroups().length - 1"
219
+ :on-select="(v) => model.type = v"
137
220
  />
138
221
  </template>
139
222
  </PlElementList>
140
223
 
141
- <!-- Last group - always exists, always empty, just for adding new groups -->
224
+ <!-- Last group - always empty, just for adding new groups -->
142
225
  <PlElementList
143
- v-model:items="emptyGroup"
226
+ v-if="props.enableAddGroupButton"
227
+ :items="emptyGroup"
144
228
  :get-item-key="(group) => group.id"
145
229
  :item-class="$style.filterGroup"
146
230
  :item-class-content="$style.filterGroupContent"
@@ -157,14 +241,15 @@ function dragOver(event: DragEvent) {
157
241
  @dragover="dragOver"
158
242
  >
159
243
  <template #item-title>Filter group</template>
160
- <template #item-content="{item}">
161
- <PlCheckbox v-model="item.not" disabled >NOT</PlCheckbox>
244
+ <template #item-content>
162
245
  <div v-if="enableDnd" :class="$style.dropzone">
163
246
  <div>Drop dimensions here</div>
164
247
  </div>
165
- <PlBtnSecondary v-else @click="addGroup(firstColumnId)">
166
- <PlIcon16 name="add" style="margin-right: 8px"/>Add column
167
- </PlBtnSecondary>
248
+ <slot v-else name="add-group-buttons">
249
+ <PlBtnSecondary icon="add" @click="addGroup(firstColumnId)">
250
+ Add filter
251
+ </PlBtnSecondary>
252
+ </slot>
168
253
  </template>
169
254
  </PlElementList>
170
255
  </div>
@@ -179,11 +264,17 @@ function dragOver(event: DragEvent) {
179
264
  .filterGroupTitle {
180
265
  background: none;
181
266
  }
267
+ .filterGroupContent {
268
+ padding: 4px 24px 24px 24px;
269
+ }
182
270
  .groupContent {
183
271
  display: flex;
184
272
  flex-direction: column;
185
273
  gap: 12px;
186
274
  }
275
+ .notCheckbox {
276
+ margin: 4px 0;
277
+ }
187
278
  .dropzone {
188
279
  border-radius: 6px;
189
280
  border: 1.5px dashed var(--color-div-grey);
@@ -199,7 +290,8 @@ function dragOver(event: DragEvent) {
199
290
  align-items: center;
200
291
  }
201
292
  .buttonWrapper {
202
- height: 72px;
293
+ margin-top: 8px;
294
+ height: 56px;
203
295
  display: flex;
204
296
  align-items: center;
205
297
  }
@@ -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
  };