@bagelink/vue 1.7.80 → 1.7.86
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 +28 -0
- package/dist/components/Filter.vue.d.ts.map +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/index.cjs +25 -25
- package/dist/index.mjs +4529 -4233
- package/dist/style.css +1 -1
- package/dist/utils/index.d.ts +3 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/queryFilter.d.ts +177 -0
- package/dist/utils/queryFilter.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/Filter.vue +267 -0
- package/src/components/index.ts +1 -0
- package/src/styles/appearance.css +24 -0
- package/src/utils/index.ts +4 -1
- package/src/utils/queryFilter.ts +366 -0
package/dist/utils/index.d.ts
CHANGED
|
@@ -25,10 +25,12 @@ export declare function normalizeDimension(value: string | number | undefined, d
|
|
|
25
25
|
export * as bagelFormUtils from './BagelFormUtils';
|
|
26
26
|
export { useForm } from './BagelFormUtils';
|
|
27
27
|
export { useLang } from './lang';
|
|
28
|
+
export type { ComparisonOperator, FilterCondition, LogicalOperator, QueryConditions, QueryFilter } from './queryFilter';
|
|
29
|
+
export { anyOf, query, range, search } from './queryFilter';
|
|
28
30
|
export { formatString } from './strings';
|
|
29
|
-
export { useDebounceFn } from '@vueuse/core';
|
|
30
31
|
export declare function pathKeyToURL(pathKey?: string): string | undefined;
|
|
31
32
|
export declare function getNestedValue(obj: any, path?: string, defaultValue?: any): any;
|
|
32
33
|
export declare function tryRun<T>(func: () => T, callback?: (error: Error) => void): T | undefined;
|
|
33
34
|
export declare function downloadFile(source: string | Blob, fileName?: string): void;
|
|
35
|
+
export { useDebounceFn } from '@vueuse/core';
|
|
34
36
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAI1E,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,IAAI,GAAE,MAAY,QAO1D;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,UAMlC;AAED,wBAAgB,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ3D;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,iBAGtE;AAED,wBAAgB,QAAQ,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,UAG3C;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,IAAI,QAIrE;AAED,wBAAgB,QAAQ,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,UAOpE;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,EAC7C,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EACxB,QAAQ,CAAC,EAAE,GAAG,EACd,GAAG,CAAC,EAAE,CAAC;;EAwBP;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,OAO9C;AAED,wBAAgB,SAAS,CAAC,QAAQ,CAAC,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,EAAE,OAAO,CAAC,EAAE,MAAM,OAG5E;AAED,eAAO,MAAM,MAAM,GAAI,YAAY,GAAG,YAA0C,CAAA;AAEhF,wBAAgB,iBAAiB,CAAC,CAAC,EAClC,IAAI,CAAC,EAAE,GAAG,EAAE,EACZ,OAAO,CAAC,EAAE,MAAM,EAAE,GAChB,cAAc,CAAC,CAAC,CAAC,CAgBnB;AAED,wBAAgB,KAAK,CAAC,EAAE,GAAE,MAAY,oBAErC;AAKD,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2CxF;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAatD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,UAIvC;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAE,aAAa,SAAO,GAAG,MAAM,GAAG,SAAS,CAG/G;AAED,OAAO,KAAK,cAAc,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAE1C,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAEhC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCIM 2.0 Query Builder
|
|
3
|
+
*
|
|
4
|
+
* Type-safe query builder for SCIM 2.0 filtering.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* // Array-based (recommended for UI)
|
|
9
|
+
* const filters: QueryConditions<Person> = [
|
|
10
|
+
* { field: 'first_name', op: 'eq', value: 'John' },
|
|
11
|
+
* { field: 'age', op: 'gt', value: 18, connector: 'and' },
|
|
12
|
+
* ]
|
|
13
|
+
* buildQuery(filters) // → 'first_name eq "John" and age gt 18'
|
|
14
|
+
*
|
|
15
|
+
* // Range query (same field twice)
|
|
16
|
+
* const rangeFilters: QueryConditions<Donation> = [
|
|
17
|
+
* { field: 'amount', op: 'ge', value: 100 },
|
|
18
|
+
* { field: 'amount', op: 'le', value: 500, connector: 'and' },
|
|
19
|
+
* ]
|
|
20
|
+
* buildQuery(rangeFilters) // → 'amount ge 100 and amount le 500'
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
type Primitive = string | number | boolean | null;
|
|
24
|
+
type DeepKeyOf<T> = T extends Primitive ? never : {
|
|
25
|
+
[K in keyof T & string]: K | `${K}.${DeepKeyOf<T[K]>}`;
|
|
26
|
+
}[keyof T & string];
|
|
27
|
+
/**
|
|
28
|
+
* SCIM 2.0 comparison operators
|
|
29
|
+
*/
|
|
30
|
+
export type ComparisonOperator = 'eq' | 'ne' | 'gt' | 'ge' | 'lt' | 'le' | 'co' | 'sw' | 'ew' | 'pr';
|
|
31
|
+
/**
|
|
32
|
+
* SCIM 2.0 logical operators
|
|
33
|
+
*/
|
|
34
|
+
export type LogicalOperator = 'and' | 'or';
|
|
35
|
+
/**
|
|
36
|
+
* A single filter condition
|
|
37
|
+
*/
|
|
38
|
+
export interface FilterCondition<T extends Record<string, any> = Record<string, any>> {
|
|
39
|
+
field: DeepKeyOf<T> | string;
|
|
40
|
+
op: ComparisonOperator;
|
|
41
|
+
value?: Primitive;
|
|
42
|
+
connector?: LogicalOperator;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Array of filter conditions - the primary way to define queries
|
|
46
|
+
*/
|
|
47
|
+
export type QueryConditions<T extends Record<string, any> = Record<string, any>> = FilterCondition<T>[];
|
|
48
|
+
/**
|
|
49
|
+
* Build a SCIM 2.0 query string from conditions array
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* buildQuery<Person>([
|
|
54
|
+
* { field: 'name', op: 'eq', value: 'John' },
|
|
55
|
+
* { field: 'age', op: 'gt', value: 18, connector: 'and' },
|
|
56
|
+
* ])
|
|
57
|
+
* // → 'name eq "John" and age gt 18'
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare function buildQuery<T extends Record<string, any>>(conditions: QueryConditions<T>): string;
|
|
61
|
+
/**
|
|
62
|
+
* Query Builder for SCIM 2.0 filtering
|
|
63
|
+
*/
|
|
64
|
+
export declare class QueryFilter<T extends Record<string, any>> {
|
|
65
|
+
private parts;
|
|
66
|
+
/**
|
|
67
|
+
* Equal comparison
|
|
68
|
+
*/
|
|
69
|
+
eq<K extends DeepKeyOf<T>>(field: K, value: Primitive): this;
|
|
70
|
+
/**
|
|
71
|
+
* Not equal comparison
|
|
72
|
+
*/
|
|
73
|
+
ne<K extends DeepKeyOf<T>>(field: K, value: Primitive): this;
|
|
74
|
+
/**
|
|
75
|
+
* Greater than comparison
|
|
76
|
+
*/
|
|
77
|
+
gt<K extends DeepKeyOf<T>>(field: K, value: number | string): this;
|
|
78
|
+
/**
|
|
79
|
+
* Greater than or equal comparison
|
|
80
|
+
*/
|
|
81
|
+
ge<K extends DeepKeyOf<T>>(field: K, value: number | string): this;
|
|
82
|
+
/**
|
|
83
|
+
* Less than comparison
|
|
84
|
+
*/
|
|
85
|
+
lt<K extends DeepKeyOf<T>>(field: K, value: number | string): this;
|
|
86
|
+
/**
|
|
87
|
+
* Less than or equal comparison
|
|
88
|
+
*/
|
|
89
|
+
le<K extends DeepKeyOf<T>>(field: K, value: number | string): this;
|
|
90
|
+
/**
|
|
91
|
+
* Contains comparison (for strings)
|
|
92
|
+
*/
|
|
93
|
+
co<K extends DeepKeyOf<T>>(field: K, value: string): this;
|
|
94
|
+
/**
|
|
95
|
+
* Starts with comparison (for strings)
|
|
96
|
+
*/
|
|
97
|
+
sw<K extends DeepKeyOf<T>>(field: K, value: string): this;
|
|
98
|
+
/**
|
|
99
|
+
* Ends with comparison (for strings)
|
|
100
|
+
*/
|
|
101
|
+
ew<K extends DeepKeyOf<T>>(field: K, value: string): this;
|
|
102
|
+
/**
|
|
103
|
+
* Present check (field has a value)
|
|
104
|
+
*/
|
|
105
|
+
pr<K extends DeepKeyOf<T>>(field: K): this;
|
|
106
|
+
/**
|
|
107
|
+
* AND logical operator
|
|
108
|
+
*/
|
|
109
|
+
and(): this;
|
|
110
|
+
/**
|
|
111
|
+
* OR logical operator
|
|
112
|
+
*/
|
|
113
|
+
or(): this;
|
|
114
|
+
/**
|
|
115
|
+
* NOT logical operator
|
|
116
|
+
*/
|
|
117
|
+
not(): this;
|
|
118
|
+
/**
|
|
119
|
+
* Group expressions with parentheses
|
|
120
|
+
*/
|
|
121
|
+
group(builderFn: (qb: QueryFilter<T>) => QueryFilter<T>): this;
|
|
122
|
+
/**
|
|
123
|
+
* Add raw query string (use with caution)
|
|
124
|
+
*/
|
|
125
|
+
raw(query: string): this;
|
|
126
|
+
/**
|
|
127
|
+
* Clear the query
|
|
128
|
+
*/
|
|
129
|
+
clear(): this;
|
|
130
|
+
/**
|
|
131
|
+
* Build the final query string
|
|
132
|
+
*/
|
|
133
|
+
build(): string;
|
|
134
|
+
/**
|
|
135
|
+
* Convert to string automatically (allows using builder without .build())
|
|
136
|
+
*/
|
|
137
|
+
toString(): string;
|
|
138
|
+
/**
|
|
139
|
+
* Allow implicit string conversion
|
|
140
|
+
*/
|
|
141
|
+
[Symbol.toPrimitive](hint: string): string | number;
|
|
142
|
+
/**
|
|
143
|
+
* Format value for SCIM 2.0 query
|
|
144
|
+
*/
|
|
145
|
+
private formatValue;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Build a query string from conditions
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* // From conditions array
|
|
153
|
+
* query<Person>([
|
|
154
|
+
* { field: 'first_name', op: 'eq', value: 'John' },
|
|
155
|
+
* { field: 'age', op: 'gt', value: 18, connector: 'and' },
|
|
156
|
+
* ])
|
|
157
|
+
* // → 'first_name eq "John" and age gt 18'
|
|
158
|
+
*
|
|
159
|
+
* // Empty returns empty string
|
|
160
|
+
* query([]) // → ''
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
export declare function query<T extends Record<string, any>>(conditions: QueryConditions<T>): string;
|
|
164
|
+
/**
|
|
165
|
+
* Create a query that checks if any of the fields match the value
|
|
166
|
+
*/
|
|
167
|
+
export declare function anyOf<T extends Record<string, any>>(fields: Array<DeepKeyOf<T> | string>, value: Primitive): string;
|
|
168
|
+
/**
|
|
169
|
+
* Create a range query (field >= min and field <= max)
|
|
170
|
+
*/
|
|
171
|
+
export declare function range<T extends Record<string, any>>(field: DeepKeyOf<T> | string, min: number | string, max: number | string): string;
|
|
172
|
+
/**
|
|
173
|
+
* Create a search query across multiple string fields (OR)
|
|
174
|
+
*/
|
|
175
|
+
export declare function search<T extends Record<string, any>>(fields: Array<DeepKeyOf<T> | string>, searchTerm: string): string;
|
|
176
|
+
export {};
|
|
177
|
+
//# sourceMappingURL=queryFilter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryFilter.d.ts","sourceRoot":"","sources":["../../src/utils/queryFilter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
<script setup lang="ts" generic="T extends Record<string, any>">
|
|
2
|
+
import type { ComparisonOperator, QueryConditions, FilterCondition, LogicalOperator } from '../utils/queryFilter'
|
|
3
|
+
import { Btn, DateInput, Dropdown, SelectInput, TextInput, type Option } from '@bagelink/vue'
|
|
4
|
+
import { computed } from 'vue'
|
|
5
|
+
|
|
6
|
+
type OptionsSource = Option[] | ((query: string) => Promise<Option[]>)
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
9
|
+
fields: Array<{
|
|
10
|
+
label: string
|
|
11
|
+
value: string
|
|
12
|
+
type?: 'string' | 'number' | 'boolean' | 'date'
|
|
13
|
+
options?: OptionsSource
|
|
14
|
+
}>
|
|
15
|
+
}>()
|
|
16
|
+
|
|
17
|
+
const emit = defineEmits<{
|
|
18
|
+
change: [value: QueryConditions<T>]
|
|
19
|
+
}>()
|
|
20
|
+
|
|
21
|
+
const model = defineModel<QueryConditions<T>>({ default: () => [] })
|
|
22
|
+
|
|
23
|
+
const allOperatorOptions: { label: string, value: ComparisonOperator }[] = [
|
|
24
|
+
{ label: 'שווה ל', value: 'eq' },
|
|
25
|
+
{ label: 'לא שווה ל', value: 'ne' },
|
|
26
|
+
{ label: 'גדול מ', value: 'gt' },
|
|
27
|
+
{ label: 'גדול או שווה ל', value: 'ge' },
|
|
28
|
+
{ label: 'קטן מ', value: 'lt' },
|
|
29
|
+
{ label: 'קטן או שווה ל', value: 'le' },
|
|
30
|
+
{ label: 'מכיל', value: 'co' },
|
|
31
|
+
{ label: 'מתחיל ב', value: 'sw' },
|
|
32
|
+
{ label: 'מסתיים ב', value: 'ew' },
|
|
33
|
+
{ label: 'קיים', value: 'pr' },
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
const connectorOptions: { label: string, value: LogicalOperator }[] = [
|
|
37
|
+
{ label: 'וגם', value: 'and' },
|
|
38
|
+
{ label: 'או', value: 'or' },
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
function getOperatorsForType(fieldType: string, hasOptions: boolean) {
|
|
42
|
+
if (hasOptions) {
|
|
43
|
+
return allOperatorOptions.filter(o => ['eq', 'ne', 'pr'].includes(o.value))
|
|
44
|
+
}
|
|
45
|
+
switch (fieldType) {
|
|
46
|
+
case 'boolean':
|
|
47
|
+
return allOperatorOptions.filter(o => ['eq', 'ne', 'pr'].includes(o.value))
|
|
48
|
+
case 'number':
|
|
49
|
+
case 'date':
|
|
50
|
+
return allOperatorOptions.filter(o => ['eq', 'ne', 'gt', 'ge', 'lt', 'le', 'pr'].includes(o.value))
|
|
51
|
+
default:
|
|
52
|
+
return allOperatorOptions.filter(o => ['eq', 'ne', 'co', 'sw', 'ew', 'pr'].includes(o.value))
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getFieldOptions(fieldValue: string): OptionsSource | undefined {
|
|
57
|
+
return props.fields.find(f => f.value === fieldValue)?.options
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const booleanOptions = [
|
|
61
|
+
{ label: 'כן', value: 'true' },
|
|
62
|
+
{ label: 'לא', value: 'false' },
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
function parseValue(value: any): any {
|
|
66
|
+
const str = String(value ?? '')
|
|
67
|
+
if (str === 'true') return true
|
|
68
|
+
if (str === 'false') return false
|
|
69
|
+
if (str === 'null') return null
|
|
70
|
+
if (str !== '' && !Number.isNaN(Number(str))) return Number(str)
|
|
71
|
+
return value
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function updateCondition(index: number, updates: Partial<FilterCondition<T>>) {
|
|
75
|
+
const newConditions = [...model.value]
|
|
76
|
+
newConditions[index] = { ...newConditions[index], ...updates }
|
|
77
|
+
|
|
78
|
+
// Parse value if provided
|
|
79
|
+
if ('value' in updates) {
|
|
80
|
+
newConditions[index].value = parseValue(updates.value)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
model.value = newConditions
|
|
84
|
+
emit('change', newConditions)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function addCondition() {
|
|
88
|
+
const field = props.fields[0]?.value || ''
|
|
89
|
+
const newCondition: FilterCondition<T> = {
|
|
90
|
+
field,
|
|
91
|
+
op: 'eq',
|
|
92
|
+
value: '',
|
|
93
|
+
connector: model.value.length > 0 ? 'and' : undefined,
|
|
94
|
+
}
|
|
95
|
+
const newConditions = [...model.value, newCondition]
|
|
96
|
+
model.value = newConditions
|
|
97
|
+
emit('change', newConditions)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function removeCondition(index: number) {
|
|
101
|
+
const newConditions = model.value.filter((_, i) => i !== index)
|
|
102
|
+
// Clear connector on first item
|
|
103
|
+
if (newConditions.length > 0 && newConditions[0].connector) {
|
|
104
|
+
newConditions[0] = { ...newConditions[0], connector: undefined }
|
|
105
|
+
}
|
|
106
|
+
model.value = newConditions
|
|
107
|
+
emit('change', newConditions)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function clearAll() {
|
|
111
|
+
model.value = []
|
|
112
|
+
emit('change', [])
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function getFieldType(fieldValue: string): string {
|
|
116
|
+
return props.fields.find(f => f.value === fieldValue)?.type || 'string'
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function needsValue(op: ComparisonOperator): boolean {
|
|
120
|
+
return op !== 'pr'
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function displayValue(value: any): string {
|
|
124
|
+
if (value === null) return 'null'
|
|
125
|
+
if (value === true) return 'true'
|
|
126
|
+
if (value === false) return 'false'
|
|
127
|
+
return String(value ?? '')
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const activeFiltersCount = computed(() => {
|
|
131
|
+
return model.value.filter(c => c.field !== '' && (c.op === 'pr' || (c.value !== undefined && c.value !== ''))).length
|
|
132
|
+
})
|
|
133
|
+
</script>
|
|
134
|
+
|
|
135
|
+
<template>
|
|
136
|
+
<Dropdown
|
|
137
|
+
flat placement="bottom-start" icon="filter_alt"
|
|
138
|
+
:value="activeFiltersCount > 0 ? `פילטר (${activeFiltersCount})` : 'פילטר'" thin :auto-hide="false"
|
|
139
|
+
>
|
|
140
|
+
<div class="filter-builder">
|
|
141
|
+
<TransitionGroup name="condition">
|
|
142
|
+
<div
|
|
143
|
+
v-for="(condition, index) in model" :key="index" class="flex gap-05 bg-gray-20 rounded p-025"
|
|
144
|
+
:class="condition.connector === 'or' ? 'mt-05' : ''"
|
|
145
|
+
>
|
|
146
|
+
<!-- Connector (AND/OR) -->
|
|
147
|
+
<SelectInput
|
|
148
|
+
v-if="index > 0" :model-value="condition.connector" :options="connectorOptions"
|
|
149
|
+
style="width: 75px"
|
|
150
|
+
@update:model-value="(v: LogicalOperator) => updateCondition(index, { connector: v })"
|
|
151
|
+
/>
|
|
152
|
+
<div v-else-if="model.length > 1" style="width: 100px" />
|
|
153
|
+
|
|
154
|
+
<!-- Field selector -->
|
|
155
|
+
<SelectInput
|
|
156
|
+
:model-value="condition.field" :options="fields" placeholder="בחר שדה" class="field-select"
|
|
157
|
+
@update:model-value="(v: string) => updateCondition(index, { field: v })"
|
|
158
|
+
/>
|
|
159
|
+
|
|
160
|
+
<!-- Operator selector -->
|
|
161
|
+
<SelectInput
|
|
162
|
+
:model-value="condition.op"
|
|
163
|
+
:options="getOperatorsForType(getFieldType(condition.field as string), !!getFieldOptions(condition.field as string))"
|
|
164
|
+
class="operator-select"
|
|
165
|
+
@update:model-value="(v: ComparisonOperator) => updateCondition(index, { op: v })"
|
|
166
|
+
/>
|
|
167
|
+
|
|
168
|
+
<!-- Value input - type-specific -->
|
|
169
|
+
<template v-if="needsValue(condition.op)">
|
|
170
|
+
<SelectInput
|
|
171
|
+
v-if="getFieldOptions(condition.field as string)" :model-value="displayValue(condition.value)"
|
|
172
|
+
:options="getFieldOptions(condition.field as string)!" placeholder="בחר" class="value-input" searchable
|
|
173
|
+
@update:model-value="(v: string) => updateCondition(index, { value: v })"
|
|
174
|
+
/>
|
|
175
|
+
<SelectInput
|
|
176
|
+
v-else-if="getFieldType(condition.field as string) === 'boolean'"
|
|
177
|
+
:model-value="displayValue(condition.value)" :options="booleanOptions" placeholder="בחר"
|
|
178
|
+
class="value-input" @update:model-value="(v: string) => updateCondition(index, { value: v })"
|
|
179
|
+
/>
|
|
180
|
+
<DateInput
|
|
181
|
+
v-else-if="getFieldType(condition.field as string) === 'date'"
|
|
182
|
+
:model-value="displayValue(condition.value)" placeholder="בחר תאריך" class="value-input"
|
|
183
|
+
@update:model-value="(v: any) => updateCondition(index, { value: String(v) })"
|
|
184
|
+
/>
|
|
185
|
+
<TextInput
|
|
186
|
+
v-else-if="getFieldType(condition.field as string) === 'number'"
|
|
187
|
+
:model-value="displayValue(condition.value)" placeholder="0" type="number" class="value-input"
|
|
188
|
+
@update:model-value="(v: string) => updateCondition(index, { value: v })"
|
|
189
|
+
/>
|
|
190
|
+
<TextInput
|
|
191
|
+
v-else :model-value="displayValue(condition.value)" placeholder="ערך" class="value-input"
|
|
192
|
+
@update:model-value="(v: string) => updateCondition(index, { value: v })"
|
|
193
|
+
/>
|
|
194
|
+
</template>
|
|
195
|
+
<div v-else class="value-placeholder" />
|
|
196
|
+
|
|
197
|
+
<!-- Remove button -->
|
|
198
|
+
<Btn icon="close" flat round thin color="gray" @click="removeCondition(index)" />
|
|
199
|
+
</div>
|
|
200
|
+
</TransitionGroup>
|
|
201
|
+
|
|
202
|
+
<div class="flex gap-05 mt-075">
|
|
203
|
+
<Btn icon="add" flat thin value="הוסף תנאי" @click="addCondition" />
|
|
204
|
+
<Btn v-if="model.length > 0" icon="delete_sweep" flat thin color="gray" value="נקה הכל" @click="clearAll" />
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
</Dropdown>
|
|
208
|
+
</template>
|
|
209
|
+
|
|
210
|
+
<style scoped>
|
|
211
|
+
.filter-builder {
|
|
212
|
+
--input-font-size: 12px;
|
|
213
|
+
--input-height: 30px;
|
|
214
|
+
/* --input-border-radius: 100px; */
|
|
215
|
+
/* --input-bg: var(--bgl-primary-light); */
|
|
216
|
+
/* --input-color: var(--bgl-primary); */
|
|
217
|
+
min-width: 520px;
|
|
218
|
+
max-width: 90vw;
|
|
219
|
+
padding: 0.5rem;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.field-select {
|
|
223
|
+
flex: 1;
|
|
224
|
+
min-width: 120px;
|
|
225
|
+
margin: 0;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.operator-select {
|
|
229
|
+
min-width: 120px;
|
|
230
|
+
margin: 0;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.value-input {
|
|
234
|
+
flex: 1;
|
|
235
|
+
min-width: 100px;
|
|
236
|
+
margin: 0;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.value-placeholder {
|
|
240
|
+
flex: 1;
|
|
241
|
+
min-width: 100px;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* Transition animations */
|
|
245
|
+
.condition-enter-active,
|
|
246
|
+
.condition-leave-active {
|
|
247
|
+
transition: all 0.25s ease;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.condition-enter-from {
|
|
251
|
+
opacity: 0;
|
|
252
|
+
transform: translateX(-10px);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.condition-leave-to {
|
|
256
|
+
opacity: 0;
|
|
257
|
+
transform: translateX(10px);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
:dir(rtl) .condition-enter-from {
|
|
261
|
+
transform: translateX(10px);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
:dir(rtl) .condition-leave-to {
|
|
265
|
+
transform: translateX(-10px);
|
|
266
|
+
}
|
|
267
|
+
</style>
|
package/src/components/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ export { Draggable, useDraggable, vDraggable } from './draggable'
|
|
|
19
19
|
export { default as DragOver } from './DragOver.vue'
|
|
20
20
|
export { default as Dropdown } from './Dropdown.vue'
|
|
21
21
|
export { default as FieldSetVue } from './FieldSetVue.vue'
|
|
22
|
+
export { default as Filter } from './Filter.vue'
|
|
22
23
|
export { default as Flag } from './Flag.vue'
|
|
23
24
|
export * from './form'
|
|
24
25
|
export { default as Icon } from './Icon/Icon.vue'
|
|
@@ -120,6 +120,18 @@
|
|
|
120
120
|
object-fit: scale-down;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
.object-position-top {
|
|
124
|
+
object-position: top;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.object-position-center {
|
|
128
|
+
object-position: center;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.object-position-bottom {
|
|
132
|
+
object-position: bottom;
|
|
133
|
+
}
|
|
134
|
+
|
|
123
135
|
.shadow-10 {
|
|
124
136
|
box-shadow: 0 0 10px 0 var(--bgl-shadow) !important;
|
|
125
137
|
}
|
|
@@ -435,6 +447,18 @@
|
|
|
435
447
|
object-fit: scale-down;
|
|
436
448
|
}
|
|
437
449
|
|
|
450
|
+
.m_object-position-top {
|
|
451
|
+
object-position: top;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.m_object-position-center {
|
|
455
|
+
object-position: center;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.m_object-position-bottom {
|
|
459
|
+
object-position: bottom;
|
|
460
|
+
}
|
|
461
|
+
|
|
438
462
|
.m_shadow-light {
|
|
439
463
|
box-shadow: 0 1px 5px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1) !important;
|
|
440
464
|
}
|
package/src/utils/index.ts
CHANGED
|
@@ -203,8 +203,9 @@ export { useForm } from './BagelFormUtils'
|
|
|
203
203
|
|
|
204
204
|
export { useLang } from './lang'
|
|
205
205
|
|
|
206
|
+
export type { ComparisonOperator, FilterCondition, LogicalOperator, QueryConditions, QueryFilter } from './queryFilter'
|
|
207
|
+
export { anyOf, query, range, search } from './queryFilter'
|
|
206
208
|
export { formatString } from './strings'
|
|
207
|
-
export { useDebounceFn } from '@vueuse/core'
|
|
208
209
|
|
|
209
210
|
const URL_REGEX = /^https?:\/\/|^\/\//
|
|
210
211
|
|
|
@@ -281,3 +282,5 @@ export function downloadFile(source: string | Blob, fileName?: string) {
|
|
|
281
282
|
link.click()
|
|
282
283
|
document.body.removeChild(link)
|
|
283
284
|
}
|
|
285
|
+
|
|
286
|
+
export { useDebounceFn } from '@vueuse/core'
|