@bagelink/vue 1.9.79 → 1.9.81
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/components/Filter.vue.d.ts +1 -0
- package/dist/components/Filter.vue.d.ts.map +1 -1
- package/dist/i18n/index.d.ts +18 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/index.cjs +41 -41
- package/dist/index.mjs +4446 -4377
- package/dist/style.css +1 -1
- package/dist/utils/queryFilter.d.ts +2 -2
- package/dist/utils/queryFilter.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Filter.vue +65 -23
- package/src/i18n/locales/en.json +5 -2
- package/src/i18n/locales/es.json +5 -2
- package/src/i18n/locales/fr.json +5 -2
- package/src/i18n/locales/he.json +5 -2
- package/src/i18n/locales/it.json +5 -2
- package/src/i18n/locales/ru.json +5 -2
- package/src/utils/queryFilter.ts +12 -3
|
@@ -27,7 +27,7 @@ type DeepKeyOf<T> = T extends Primitive ? never : {
|
|
|
27
27
|
/**
|
|
28
28
|
* SCIM 2.0 comparison operators
|
|
29
29
|
*/
|
|
30
|
-
export type ComparisonOperator = 'eq' | 'ne' | 'gt' | 'ge' | 'lt' | 'le' | 'co' | 'sw' | 'ew' | 'pr';
|
|
30
|
+
export type ComparisonOperator = 'eq' | 'ne' | 'gt' | 'ge' | 'lt' | 'le' | 'co' | 'sw' | 'ew' | 'pr' | 'in' | 'nin';
|
|
31
31
|
/**
|
|
32
32
|
* SCIM 2.0 logical operators
|
|
33
33
|
*/
|
|
@@ -38,7 +38,7 @@ export type LogicalOperator = 'and' | 'or';
|
|
|
38
38
|
export interface FilterCondition<T extends Record<string, any> = Record<string, any>> {
|
|
39
39
|
field: DeepKeyOf<T> | string;
|
|
40
40
|
op: ComparisonOperator;
|
|
41
|
-
value?: Primitive;
|
|
41
|
+
value?: Primitive | Primitive[];
|
|
42
42
|
connector?: LogicalOperator;
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queryFilter.d.ts","sourceRoot":"","sources":["../../src/utils/queryFilter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAA;AAEjD,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,GACpC,KAAK,GACL;KACA,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;CACtD,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAA;AAEpB;;GAEG;AACH,MAAM,MAAM,kBAAkB,GACzB,IAAI,GACL,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"queryFilter.d.ts","sourceRoot":"","sources":["../../src/utils/queryFilter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { Option } from '@bagelink/vue'
|
|
3
3
|
import type { ComparisonOperator, LogicalOperator, QueryConditions } from '../utils/queryFilter'
|
|
4
4
|
import { Btn, DateInput, Dropdown, Icon, SelectInput, TextInput, useI18n, useDebounceFn } from '@bagelink/vue'
|
|
5
|
-
import { computed,
|
|
5
|
+
import { computed, getCurrentInstance, ref } from 'vue'
|
|
6
6
|
|
|
7
7
|
type OptionsSource = Option[] | ((query: string) => Promise<Option[]>)
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ interface UICondition {
|
|
|
10
10
|
id: string
|
|
11
11
|
field: string
|
|
12
12
|
operator: ComparisonOperator
|
|
13
|
-
value: string
|
|
13
|
+
value: string | string[]
|
|
14
14
|
connector: LogicalOperator | ''
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -20,6 +20,7 @@ const props = defineProps<{
|
|
|
20
20
|
value: string
|
|
21
21
|
type?: 'string' | 'number' | 'boolean' | 'date'
|
|
22
22
|
options?: OptionsSource
|
|
23
|
+
multiple?: boolean
|
|
23
24
|
}>
|
|
24
25
|
}>()
|
|
25
26
|
|
|
@@ -34,11 +35,14 @@ const model = defineModel<QueryConditions<T>>({ default: () => [] })
|
|
|
34
35
|
const internalModel = ref<QueryConditions<T>>([...model.value] as QueryConditions<T>)
|
|
35
36
|
|
|
36
37
|
const { $t } = useI18n()
|
|
37
|
-
const
|
|
38
|
-
const hasSaveListener = computed(() => !!
|
|
39
|
-
|
|
40
|
-
function isComplete(c: { field: string, op: ComparisonOperator, value
|
|
41
|
-
|
|
38
|
+
const instance = getCurrentInstance()
|
|
39
|
+
const hasSaveListener = computed(() => !!instance?.vnode.props?.onSave)
|
|
40
|
+
|
|
41
|
+
function isComplete(c: { field: string, op: ComparisonOperator, value?: any }) {
|
|
42
|
+
if (!c.field || !c.op) return false
|
|
43
|
+
if (c.op === 'pr') return true
|
|
44
|
+
if (c.op === 'in' || c.op === 'nin') return Array.isArray(c.value) && c.value.length > 0
|
|
45
|
+
return c.value !== '' && c.value !== null && c.value !== undefined
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
const debouncedFlush = useDebounceFn(() => {
|
|
@@ -72,6 +76,8 @@ const currentTexts = computed(() => ({
|
|
|
72
76
|
sw: $t('filter.operators.sw'),
|
|
73
77
|
ew: $t('filter.operators.ew'),
|
|
74
78
|
pr: $t('filter.operators.pr'),
|
|
79
|
+
in: $t('filter.operators.in'),
|
|
80
|
+
nin: $t('filter.operators.nin'),
|
|
75
81
|
},
|
|
76
82
|
placeholders: {
|
|
77
83
|
selectField: $t('filter.placeholders.selectField'),
|
|
@@ -105,7 +111,9 @@ const conditions = computed(() => {
|
|
|
105
111
|
id,
|
|
106
112
|
field: cond.field as string,
|
|
107
113
|
operator: cond.op,
|
|
108
|
-
value:
|
|
114
|
+
value: Array.isArray(cond.value)
|
|
115
|
+
? cond.value.map(String)
|
|
116
|
+
: (cond.value === null ? 'null' : cond.value === undefined ? '' : String(cond.value)),
|
|
109
117
|
connector: index > 0 ? (cond.connector || 'and') : '',
|
|
110
118
|
}
|
|
111
119
|
return uiCond
|
|
@@ -123,9 +131,14 @@ const allOperatorOptions = computed(() => [
|
|
|
123
131
|
{ label: currentTexts.value.operators.sw, value: 'sw' as ComparisonOperator },
|
|
124
132
|
{ label: currentTexts.value.operators.ew, value: 'ew' as ComparisonOperator },
|
|
125
133
|
{ label: currentTexts.value.operators.pr, value: 'pr' as ComparisonOperator },
|
|
134
|
+
{ label: currentTexts.value.operators.in, value: 'in' as ComparisonOperator },
|
|
135
|
+
{ label: currentTexts.value.operators.nin, value: 'nin' as ComparisonOperator },
|
|
126
136
|
])
|
|
127
137
|
|
|
128
|
-
function getOperatorsForType(fieldType: string, hasOptions: boolean) {
|
|
138
|
+
function getOperatorsForType(fieldType: string, hasOptions: boolean, isMultiple: boolean) {
|
|
139
|
+
if (isMultiple && hasOptions) {
|
|
140
|
+
return allOperatorOptions.value.filter(o => ['in', 'nin', 'pr'].includes(o.value))
|
|
141
|
+
}
|
|
129
142
|
if (hasOptions) {
|
|
130
143
|
return allOperatorOptions.value.filter(o => ['eq', 'ne', 'pr'].includes(o.value))
|
|
131
144
|
}
|
|
@@ -140,8 +153,17 @@ function getOperatorsForType(fieldType: string, hasOptions: boolean) {
|
|
|
140
153
|
}
|
|
141
154
|
}
|
|
142
155
|
|
|
156
|
+
function getFieldDef(fieldValue: string) {
|
|
157
|
+
return props.fields.find(f => f.value === fieldValue)
|
|
158
|
+
}
|
|
159
|
+
|
|
143
160
|
function getFieldOptions(fieldValue: string): OptionsSource | undefined {
|
|
144
|
-
return
|
|
161
|
+
return getFieldDef(fieldValue)?.options
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function isMultipleField(fieldValue: string): boolean {
|
|
165
|
+
const def = getFieldDef(fieldValue)
|
|
166
|
+
return !!(def?.multiple && def?.options)
|
|
145
167
|
}
|
|
146
168
|
|
|
147
169
|
const booleanOptions = computed(() => [
|
|
@@ -158,7 +180,7 @@ function parseValue(value: string | number | boolean | null): string | number |
|
|
|
158
180
|
return value
|
|
159
181
|
}
|
|
160
182
|
|
|
161
|
-
function updateConditionAtIndex(index: number, updates: Partial<{ field: string, op: ComparisonOperator, value: string | number | boolean | null, connector: LogicalOperator }>) {
|
|
183
|
+
function updateConditionAtIndex(index: number, updates: Partial<{ field: string, op: ComparisonOperator, value: string | number | boolean | null | string[], connector: LogicalOperator }>) {
|
|
162
184
|
const newModel = [...internalModel.value]
|
|
163
185
|
const existing = newModel[index]
|
|
164
186
|
if (!existing) return
|
|
@@ -166,7 +188,7 @@ function updateConditionAtIndex(index: number, updates: Partial<{ field: string,
|
|
|
166
188
|
const updated = { ...existing }
|
|
167
189
|
if (updates.field !== undefined) updated.field = updates.field
|
|
168
190
|
if (updates.op !== undefined) updated.op = updates.op
|
|
169
|
-
if (updates.value !== undefined) updated.value = parseValue(updates.value)
|
|
191
|
+
if (updates.value !== undefined) updated.value = Array.isArray(updates.value) ? updates.value : parseValue(updates.value)
|
|
170
192
|
if (updates.connector !== undefined) updated.connector = updates.connector
|
|
171
193
|
|
|
172
194
|
newModel[index] = updated
|
|
@@ -176,10 +198,11 @@ function updateConditionAtIndex(index: number, updates: Partial<{ field: string,
|
|
|
176
198
|
|
|
177
199
|
function addCondition() {
|
|
178
200
|
const field = props.fields[0]?.value || 'field'
|
|
201
|
+
const multi = isMultipleField(field)
|
|
179
202
|
const newCondition = {
|
|
180
203
|
field,
|
|
181
|
-
op: 'eq' as ComparisonOperator,
|
|
182
|
-
value: '' as any,
|
|
204
|
+
op: (multi ? 'in' : 'eq') as ComparisonOperator,
|
|
205
|
+
value: (multi ? [] : '') as any,
|
|
183
206
|
connector: model.value.length > 0 ? 'and' as LogicalOperator : undefined,
|
|
184
207
|
}
|
|
185
208
|
const newModel = [...internalModel.value, newCondition]
|
|
@@ -214,6 +237,10 @@ function needsValue(operator: ComparisonOperator): boolean {
|
|
|
214
237
|
return operator !== 'pr'
|
|
215
238
|
}
|
|
216
239
|
|
|
240
|
+
function isMultiOperator(operator: ComparisonOperator): boolean {
|
|
241
|
+
return operator === 'in' || operator === 'nin'
|
|
242
|
+
}
|
|
243
|
+
|
|
217
244
|
const activeFiltersCount = computed(() => {
|
|
218
245
|
return conditions.value.filter((c: UICondition) => Boolean(c.field) && (c.operator === 'pr' || Boolean(c.value))).length
|
|
219
246
|
})
|
|
@@ -229,10 +256,17 @@ function onFieldChange(id: string, newField: string) {
|
|
|
229
256
|
|
|
230
257
|
function onOperatorChange(id: string, operator: ComparisonOperator) {
|
|
231
258
|
const index = getConditionIndex(id)
|
|
232
|
-
if (index
|
|
259
|
+
if (index < 0) return
|
|
260
|
+
const current = conditions.value[index]
|
|
261
|
+
const wasMulti = current?.operator === 'in' || current?.operator === 'nin'
|
|
262
|
+
const isMulti = operator === 'in' || operator === 'nin'
|
|
263
|
+
const updates: Parameters<typeof updateConditionAtIndex>[1] = { op: operator }
|
|
264
|
+
if (wasMulti && !isMulti) updates.value = ''
|
|
265
|
+
else if (!wasMulti && isMulti) updates.value = []
|
|
266
|
+
updateConditionAtIndex(index, updates)
|
|
233
267
|
}
|
|
234
268
|
|
|
235
|
-
function onValueChange(id: string, value: string) {
|
|
269
|
+
function onValueChange(id: string, value: string | string[]) {
|
|
236
270
|
const index = getConditionIndex(id)
|
|
237
271
|
if (index >= 0) updateConditionAtIndex(index, { value })
|
|
238
272
|
}
|
|
@@ -258,7 +292,6 @@ function onConnectorChange(id: string, connector: LogicalOperator) {
|
|
|
258
292
|
{{ currentTexts.noActiveFilters }}
|
|
259
293
|
</div>
|
|
260
294
|
</div>
|
|
261
|
-
|
|
262
295
|
<TransitionGroup name="condition">
|
|
263
296
|
<div
|
|
264
297
|
v-for="(condition, index) in conditions" :key="condition.id"
|
|
@@ -291,7 +324,7 @@ function onConnectorChange(id: string, connector: LogicalOperator) {
|
|
|
291
324
|
<!-- Operator selector -->
|
|
292
325
|
<SelectInput
|
|
293
326
|
:model-value="condition.operator"
|
|
294
|
-
:options="getOperatorsForType(getFieldType(condition.field), !!getFieldOptions(condition.field))"
|
|
327
|
+
:options="getOperatorsForType(getFieldType(condition.field), !!getFieldOptions(condition.field), isMultipleField(condition.field))"
|
|
295
328
|
class="m-0 light-input borderHover"
|
|
296
329
|
@update:model-value="(v: ComparisonOperator) => onOperatorChange(condition.id, v)"
|
|
297
330
|
/>
|
|
@@ -299,7 +332,15 @@ function onConnectorChange(id: string, connector: LogicalOperator) {
|
|
|
299
332
|
<!-- Value input - type-specific -->
|
|
300
333
|
<template v-if="needsValue(condition.operator)">
|
|
301
334
|
<SelectInput
|
|
302
|
-
v-if="getFieldOptions(condition.field)
|
|
335
|
+
v-if="getFieldOptions(condition.field) && isMultiOperator(condition.operator)"
|
|
336
|
+
:model-value="Array.isArray(condition.value) ? condition.value : []"
|
|
337
|
+
:options="getFieldOptions(condition.field)!"
|
|
338
|
+
:placeholder="currentTexts.placeholders.selectOption"
|
|
339
|
+
class="m-0 light-input borderHover" searchable multiselect
|
|
340
|
+
@update:model-value="(v: string[]) => onValueChange(condition.id, v)"
|
|
341
|
+
/>
|
|
342
|
+
<SelectInput
|
|
343
|
+
v-else-if="getFieldOptions(condition.field)" :model-value="condition.value"
|
|
303
344
|
:options="getFieldOptions(condition.field)!"
|
|
304
345
|
:placeholder="currentTexts.placeholders.selectOption"
|
|
305
346
|
class="m-0 light-input borderHover" searchable
|
|
@@ -314,18 +355,18 @@ function onConnectorChange(id: string, connector: LogicalOperator) {
|
|
|
314
355
|
/>
|
|
315
356
|
<DateInput
|
|
316
357
|
v-else-if="getFieldType(condition.field) === 'date'"
|
|
317
|
-
:model-value="condition.value" :placeholder="currentTexts.placeholders.selectDate"
|
|
358
|
+
:model-value="(condition.value as string)" :placeholder="currentTexts.placeholders.selectDate"
|
|
318
359
|
class="m-0 light-input borderHover"
|
|
319
360
|
@update:model-value="(v: any) => onValueChange(condition.id, String(v))"
|
|
320
361
|
/>
|
|
321
362
|
<TextInput
|
|
322
363
|
v-else-if="getFieldType(condition.field) === 'number'"
|
|
323
|
-
:model-value="condition.value" placeholder="0" type="number"
|
|
364
|
+
:model-value="(condition.value as string)" placeholder="0" type="number"
|
|
324
365
|
class="m-0 light-input borderHover"
|
|
325
366
|
@update:model-value="(v: string) => onValueChange(condition.id, v)"
|
|
326
367
|
/>
|
|
327
368
|
<TextInput
|
|
328
|
-
v-else :model-value="condition.value"
|
|
369
|
+
v-else :model-value="(condition.value as string)"
|
|
329
370
|
:placeholder="currentTexts.placeholders.enterValue" class="m-0 light-input borderHover"
|
|
330
371
|
@update:model-value="(v: string) => onValueChange(condition.id, v)"
|
|
331
372
|
/>
|
|
@@ -349,7 +390,8 @@ function onConnectorChange(id: string, connector: LogicalOperator) {
|
|
|
349
390
|
:value="currentTexts.buttons.clearAll" @click="clearAll"
|
|
350
391
|
/>
|
|
351
392
|
<Btn
|
|
352
|
-
v-if="hasSaveListener"
|
|
393
|
+
v-if="hasSaveListener" :disabled="conditions.length === 0" class="color-green" icon="save" thin
|
|
394
|
+
color="green-light" :value="$t('filter.buttons.save')"
|
|
353
395
|
@click="emit('save', internalModel.filter(isComplete))"
|
|
354
396
|
/>
|
|
355
397
|
</div>
|
package/src/i18n/locales/en.json
CHANGED
|
@@ -72,7 +72,9 @@
|
|
|
72
72
|
"co": "Contains",
|
|
73
73
|
"sw": "Starts with",
|
|
74
74
|
"ew": "Ends with",
|
|
75
|
-
"pr": "Present"
|
|
75
|
+
"pr": "Present",
|
|
76
|
+
"in": "In list",
|
|
77
|
+
"nin": "Not in list"
|
|
76
78
|
},
|
|
77
79
|
"placeholders": {
|
|
78
80
|
"selectField": "Select field",
|
|
@@ -82,7 +84,8 @@
|
|
|
82
84
|
},
|
|
83
85
|
"buttons": {
|
|
84
86
|
"addCondition": "Add condition",
|
|
85
|
-
"clearAll": "Clear all"
|
|
87
|
+
"clearAll": "Clear all",
|
|
88
|
+
"save": "Save"
|
|
86
89
|
}
|
|
87
90
|
},
|
|
88
91
|
"multiStep": {
|
package/src/i18n/locales/es.json
CHANGED
|
@@ -72,7 +72,9 @@
|
|
|
72
72
|
"co": "Contiene",
|
|
73
73
|
"sw": "Empieza con",
|
|
74
74
|
"ew": "Termina con",
|
|
75
|
-
"pr": "Presente"
|
|
75
|
+
"pr": "Presente",
|
|
76
|
+
"in": "En lista",
|
|
77
|
+
"nin": "No en lista"
|
|
76
78
|
},
|
|
77
79
|
"placeholders": {
|
|
78
80
|
"selectField": "Seleccionar campo",
|
|
@@ -82,7 +84,8 @@
|
|
|
82
84
|
},
|
|
83
85
|
"buttons": {
|
|
84
86
|
"addCondition": "Añadir condición",
|
|
85
|
-
"clearAll": "Limpiar todo"
|
|
87
|
+
"clearAll": "Limpiar todo",
|
|
88
|
+
"save": "Guardar"
|
|
86
89
|
}
|
|
87
90
|
},
|
|
88
91
|
"multiStep": {
|
package/src/i18n/locales/fr.json
CHANGED
|
@@ -72,7 +72,9 @@
|
|
|
72
72
|
"co": "Contient",
|
|
73
73
|
"sw": "Commence par",
|
|
74
74
|
"ew": "Se termine par",
|
|
75
|
-
"pr": "Présent"
|
|
75
|
+
"pr": "Présent",
|
|
76
|
+
"in": "Dans la liste",
|
|
77
|
+
"nin": "Pas dans la liste"
|
|
76
78
|
},
|
|
77
79
|
"placeholders": {
|
|
78
80
|
"selectField": "Sélectionner un champ",
|
|
@@ -82,7 +84,8 @@
|
|
|
82
84
|
},
|
|
83
85
|
"buttons": {
|
|
84
86
|
"addCondition": "Ajouter une condition",
|
|
85
|
-
"clearAll": "Tout effacer"
|
|
87
|
+
"clearAll": "Tout effacer",
|
|
88
|
+
"save": "Enregistrer"
|
|
86
89
|
}
|
|
87
90
|
},
|
|
88
91
|
"multiStep": {
|
package/src/i18n/locales/he.json
CHANGED
|
@@ -72,7 +72,9 @@
|
|
|
72
72
|
"co": "מכיל",
|
|
73
73
|
"sw": "מתחיל ב",
|
|
74
74
|
"ew": "מסתיים ב",
|
|
75
|
-
"pr": "קיים"
|
|
75
|
+
"pr": "קיים",
|
|
76
|
+
"in": "ברשימה",
|
|
77
|
+
"nin": "לא ברשימה"
|
|
76
78
|
},
|
|
77
79
|
"placeholders": {
|
|
78
80
|
"selectField": "בחר שדה",
|
|
@@ -82,7 +84,8 @@
|
|
|
82
84
|
},
|
|
83
85
|
"buttons": {
|
|
84
86
|
"addCondition": "הוספת תנאי",
|
|
85
|
-
"clearAll": "נקה הכל"
|
|
87
|
+
"clearAll": "נקה הכל",
|
|
88
|
+
"save": "שמירה"
|
|
86
89
|
}
|
|
87
90
|
},
|
|
88
91
|
"multiStep": {
|
package/src/i18n/locales/it.json
CHANGED
|
@@ -72,7 +72,9 @@
|
|
|
72
72
|
"co": "Contiene",
|
|
73
73
|
"sw": "Inizia con",
|
|
74
74
|
"ew": "Finisce con",
|
|
75
|
-
"pr": "Presente"
|
|
75
|
+
"pr": "Presente",
|
|
76
|
+
"in": "Nella lista",
|
|
77
|
+
"nin": "Non nella lista"
|
|
76
78
|
},
|
|
77
79
|
"placeholders": {
|
|
78
80
|
"selectField": "Seleziona campo",
|
|
@@ -82,7 +84,8 @@
|
|
|
82
84
|
},
|
|
83
85
|
"buttons": {
|
|
84
86
|
"addCondition": "Aggiungi condizione",
|
|
85
|
-
"clearAll": "Cancella tutto"
|
|
87
|
+
"clearAll": "Cancella tutto",
|
|
88
|
+
"save": "Salva"
|
|
86
89
|
}
|
|
87
90
|
},
|
|
88
91
|
"multiStep": {
|
package/src/i18n/locales/ru.json
CHANGED
|
@@ -72,7 +72,9 @@
|
|
|
72
72
|
"co": "Содержит",
|
|
73
73
|
"sw": "Начинается с",
|
|
74
74
|
"ew": "Заканчивается на",
|
|
75
|
-
"pr": "Присутствует"
|
|
75
|
+
"pr": "Присутствует",
|
|
76
|
+
"in": "В списке",
|
|
77
|
+
"nin": "Не в списке"
|
|
76
78
|
},
|
|
77
79
|
"placeholders": {
|
|
78
80
|
"selectField": "Выберите поле",
|
|
@@ -82,7 +84,8 @@
|
|
|
82
84
|
},
|
|
83
85
|
"buttons": {
|
|
84
86
|
"addCondition": "Добавить условие",
|
|
85
|
-
"clearAll": "Очистить всё"
|
|
87
|
+
"clearAll": "Очистить всё",
|
|
88
|
+
"save": "Сохранить"
|
|
86
89
|
}
|
|
87
90
|
},
|
|
88
91
|
"multiStep": {
|
package/src/utils/queryFilter.ts
CHANGED
|
@@ -43,6 +43,8 @@ export type ComparisonOperator
|
|
|
43
43
|
| 'sw' // starts with
|
|
44
44
|
| 'ew' // ends with
|
|
45
45
|
| 'pr' // present (has value)
|
|
46
|
+
| 'in' // in list — expands to (field eq "a" or field eq "b" …)
|
|
47
|
+
| 'nin' // not in list — expands to (field ne "a" and field ne "b" …)
|
|
46
48
|
|
|
47
49
|
/**
|
|
48
50
|
* SCIM 2.0 logical operators
|
|
@@ -55,7 +57,7 @@ export type LogicalOperator = 'and' | 'or'
|
|
|
55
57
|
export interface FilterCondition<T extends Record<string, any> = Record<string, any>> {
|
|
56
58
|
field: DeepKeyOf<T> | string
|
|
57
59
|
op: ComparisonOperator
|
|
58
|
-
value?: Primitive //
|
|
60
|
+
value?: Primitive | Primitive[] // array only used with 'in'/'nin'
|
|
59
61
|
connector?: LogicalOperator // before this condition, ignored for first
|
|
60
62
|
}
|
|
61
63
|
|
|
@@ -107,8 +109,15 @@ export function buildQuery<T extends Record<string, any>>(
|
|
|
107
109
|
// Build the condition
|
|
108
110
|
if (op === 'pr') {
|
|
109
111
|
parts.push(`${field} pr`)
|
|
112
|
+
} else if (op === 'in' || op === 'nin') {
|
|
113
|
+
const values = Array.isArray(value) ? value : (value != null ? [value] : [])
|
|
114
|
+
if (values.length === 0) continue
|
|
115
|
+
const innerOp = op === 'in' ? 'eq' : 'ne'
|
|
116
|
+
const innerConn = op === 'in' ? 'or' : 'and'
|
|
117
|
+
const inner = values.map(v => `${field} ${innerOp} ${formatValue(v)}`).join(` ${innerConn} `)
|
|
118
|
+
parts.push(values.length > 1 ? `(${inner})` : inner)
|
|
110
119
|
} else {
|
|
111
|
-
parts.push(`${field} ${op} ${formatValue(value ?? null)}`)
|
|
120
|
+
parts.push(`${field} ${op} ${formatValue(value as Primitive ?? null)}`)
|
|
112
121
|
}
|
|
113
122
|
}
|
|
114
123
|
|
|
@@ -373,7 +382,7 @@ export function search<T extends Record<string, any>>(
|
|
|
373
382
|
return buildQuery(conditions)
|
|
374
383
|
}
|
|
375
384
|
|
|
376
|
-
const OPERATORS: ComparisonOperator[] = ['eq', 'ne', 'gt', 'ge', 'lt', 'le', 'co', 'sw', 'ew', 'pr']
|
|
385
|
+
const OPERATORS: ComparisonOperator[] = ['eq', 'ne', 'gt', 'ge', 'lt', 'le', 'co', 'sw', 'ew', 'pr', 'in', 'nin']
|
|
377
386
|
const CONNECTORS: LogicalOperator[] = ['and', 'or']
|
|
378
387
|
|
|
379
388
|
/**
|