@dataloop-ai/components 0.20.26 → 0.20.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/compound/DlSearches/DlSmartSearch/components/DlSmartSearchInput.vue +9 -2
- package/src/components/compound/DlSearches/DlSmartSearch/components/SuggestionsDropdown.vue +1 -1
- package/src/components/compound/DlSelect/DlSelect.vue +9 -19
- package/src/components/compound/DlSelect/utils.ts +4 -4
- package/src/demos/DlSelectDemo.vue +22 -1
- package/src/demos/SmartSearchDemo/DlSmartSearchDemo.vue +1 -1
- package/src/hooks/use-suggestions.ts +24 -9
- package/src/utils/parse-smart-query.ts +45 -26
- package/src/utils/splitByQuotes.ts +15 -6
package/package.json
CHANGED
|
@@ -445,8 +445,15 @@ export default defineComponent({
|
|
|
445
445
|
queryRightSide = queryRightSide.trimStart()
|
|
446
446
|
} else {
|
|
447
447
|
// this|situation: replace whatever is there on both sides with the value
|
|
448
|
-
if (
|
|
449
|
-
|
|
448
|
+
if (
|
|
449
|
+
/^[^']+((?<!\\)'.*(?<!\\)'[^']+)*(?<!\\)'([^']+\\')*[^']*((?<!\\)')?$/.test(
|
|
450
|
+
queryLeftSide
|
|
451
|
+
)
|
|
452
|
+
) {
|
|
453
|
+
queryLeftSide = queryLeftSide.replace(
|
|
454
|
+
/(?<!\\)'([^']+\\')*[^']*((?<!\\)')?$/,
|
|
455
|
+
''
|
|
456
|
+
)
|
|
450
457
|
} else {
|
|
451
458
|
queryLeftSide = queryLeftSide.replace(/[^'\s]+$/, '')
|
|
452
459
|
}
|
|
@@ -159,7 +159,7 @@ export default defineComponent({
|
|
|
159
159
|
const removeQuotes = (item: any) => {
|
|
160
160
|
const str = '' + item
|
|
161
161
|
const match = str.match(/^'(.*)'$/)
|
|
162
|
-
return match ? match[1] : str
|
|
162
|
+
return match ? match[1].replace(/\\'/g, "'") : str
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
const updatePosition = () => {
|
|
@@ -82,37 +82,27 @@
|
|
|
82
82
|
name="selected"
|
|
83
83
|
>
|
|
84
84
|
<span class="root-container--placeholder">
|
|
85
|
-
<dl-ellipsis
|
|
86
|
-
{{ filterSelectLabel }}
|
|
87
|
-
</dl-ellipsis>
|
|
85
|
+
<dl-ellipsis :text="filterSelectLabel" />
|
|
88
86
|
</span>
|
|
89
87
|
</slot>
|
|
90
88
|
</div>
|
|
91
89
|
<template v-else>
|
|
92
90
|
<span
|
|
93
|
-
v-show="
|
|
94
|
-
(multiselect && !searchable) ||
|
|
95
|
-
(multiselect && searchable && !isExpanded)
|
|
96
|
-
"
|
|
91
|
+
v-show="multiselect && (!searchable || !isExpanded)"
|
|
97
92
|
class="root-container--placeholder"
|
|
98
93
|
>
|
|
99
94
|
<template v-if="allFiltersModel">
|
|
100
|
-
|
|
95
|
+
<dl-ellipsis :text="computedAllItemsLabel" />
|
|
101
96
|
</template>
|
|
102
97
|
<template v-else>
|
|
103
|
-
<dl-ellipsis
|
|
98
|
+
<dl-ellipsis :text="filterSelectLabel" />
|
|
104
99
|
</template>
|
|
105
100
|
</span>
|
|
106
101
|
<span
|
|
107
|
-
v-show="
|
|
108
|
-
(!multiselect && !searchable) ||
|
|
109
|
-
(!multiselect && searchable && !isExpanded)
|
|
110
|
-
"
|
|
102
|
+
v-show="!multiselect && (!searchable || !isExpanded)"
|
|
111
103
|
class="selected-label"
|
|
112
104
|
>
|
|
113
|
-
<dl-ellipsis
|
|
114
|
-
{{ getLabel(selectedOption) }}
|
|
115
|
-
</dl-ellipsis>
|
|
105
|
+
<dl-ellipsis :text="getLabel(selectedOption)" />
|
|
116
106
|
</span>
|
|
117
107
|
</template>
|
|
118
108
|
<div
|
|
@@ -635,10 +625,10 @@ export default defineComponent({
|
|
|
635
625
|
}
|
|
636
626
|
return this.modelValueLength > 0
|
|
637
627
|
? `${this.modelValueLength} ${this.selectedResourceLabel}`
|
|
638
|
-
: this.computedPlaceholder
|
|
628
|
+
: String(this.computedPlaceholder)
|
|
639
629
|
},
|
|
640
630
|
computedAllItemsLabel(): string {
|
|
641
|
-
return this.allItemsOptionLabel
|
|
631
|
+
return String(this.allItemsOptionLabel ?? 'All Items')
|
|
642
632
|
},
|
|
643
633
|
isModelValuePrimitiveType(): boolean {
|
|
644
634
|
return this.isPrimitiveValue(this.modelValue)
|
|
@@ -674,7 +664,7 @@ export default defineComponent({
|
|
|
674
664
|
return this.searchable && this.isExpanded
|
|
675
665
|
},
|
|
676
666
|
computedPlaceholder(): string {
|
|
677
|
-
return this.placeholder
|
|
667
|
+
return String(this.placeholder ?? 'Select option')
|
|
678
668
|
},
|
|
679
669
|
placeholderStyles(): Record<string, string> {
|
|
680
670
|
if (this.disabled) {
|
|
@@ -3,10 +3,10 @@ import { DlSelectOptionType } from './types'
|
|
|
3
3
|
|
|
4
4
|
export const getLabel = (option: any) => {
|
|
5
5
|
if (typeof option === 'object' && 'label' in option) {
|
|
6
|
-
return option.label
|
|
6
|
+
return String(option.label)
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
return option
|
|
9
|
+
return String(option)
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const ICON_SIZES = {
|
|
@@ -45,12 +45,12 @@ export const getLabelOfSelectedOption = (
|
|
|
45
45
|
typeof selected === 'string' &&
|
|
46
46
|
option === selected
|
|
47
47
|
) {
|
|
48
|
-
return option
|
|
48
|
+
return String(option)
|
|
49
49
|
} else if (
|
|
50
50
|
typeof option === 'object' &&
|
|
51
51
|
isValueSelected(option, selected)
|
|
52
52
|
) {
|
|
53
|
-
return option.label
|
|
53
|
+
return String(option.label)
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
return '1 Option Selected'
|
|
@@ -697,6 +697,19 @@
|
|
|
697
697
|
<div>{{ scope.opt.label }}</div>
|
|
698
698
|
</template>
|
|
699
699
|
</dl-select>
|
|
700
|
+
|
|
701
|
+
Select numbers as labels
|
|
702
|
+
<dl-select
|
|
703
|
+
v-model="selectedNumbers"
|
|
704
|
+
:options="optionsAsNumbers"
|
|
705
|
+
multiselect
|
|
706
|
+
searchable
|
|
707
|
+
style="margin-bottom: 150px; width: 200px"
|
|
708
|
+
>
|
|
709
|
+
<template #selected="scope">
|
|
710
|
+
<div>{{ scope.opt.label }}</div>
|
|
711
|
+
</template>
|
|
712
|
+
</dl-select>
|
|
700
713
|
</div>
|
|
701
714
|
</template>
|
|
702
715
|
|
|
@@ -712,6 +725,12 @@ const defaultOptions = [
|
|
|
712
725
|
{ label: 'Contributor 3', value: 'c3' }
|
|
713
726
|
]
|
|
714
727
|
|
|
728
|
+
const optionsAsNumbers = [
|
|
729
|
+
{ label: 1, value: 1 },
|
|
730
|
+
{ label: 2, value: 2 },
|
|
731
|
+
{ label: 3, value: 3 }
|
|
732
|
+
]
|
|
733
|
+
|
|
715
734
|
const defaultOptions1 = [
|
|
716
735
|
{
|
|
717
736
|
label: 'Contributor 1 Contributor 1 Contributor 1 Contributor 1 Contributor 1 Contributor 1',
|
|
@@ -980,7 +999,9 @@ export default defineComponent({
|
|
|
980
999
|
tasksFilter: [],
|
|
981
1000
|
showAllOption: true,
|
|
982
1001
|
disabledSelected: 'disabled option',
|
|
983
|
-
preSelectedValue: null
|
|
1002
|
+
preSelectedValue: null,
|
|
1003
|
+
optionsAsNumbers,
|
|
1004
|
+
selectedNumbers: []
|
|
984
1005
|
}
|
|
985
1006
|
},
|
|
986
1007
|
computed: {
|
|
@@ -136,7 +136,7 @@ export default defineComponent({
|
|
|
136
136
|
'*': 'any'
|
|
137
137
|
},
|
|
138
138
|
type: ['dir', 'file'],
|
|
139
|
-
test0: ['why wont', 'this work', 123],
|
|
139
|
+
test0: ['why wont', 'this work', 123, '1\' = 12"'],
|
|
140
140
|
test1: [...numbersArr, 'number'],
|
|
141
141
|
test2: ['true', 'false']
|
|
142
142
|
}
|
|
@@ -333,10 +333,12 @@ export const useSuggestions = (
|
|
|
333
333
|
const mappedTypes: string[] = []
|
|
334
334
|
for (const type of filteredTypes) {
|
|
335
335
|
if (typeof type === 'string') {
|
|
336
|
-
mappedTypes.push(
|
|
336
|
+
mappedTypes.push(enquoteString(type))
|
|
337
337
|
} else if (typeof type === 'object') {
|
|
338
338
|
mappedTypes.push(
|
|
339
|
-
...Object.keys(type).map((key) =>
|
|
339
|
+
...Object.keys(type).map((key) =>
|
|
340
|
+
enquoteString(key)
|
|
341
|
+
)
|
|
340
342
|
)
|
|
341
343
|
} else {
|
|
342
344
|
mappedTypes.push(type)
|
|
@@ -614,10 +616,19 @@ const isValidBoolean = (str: string) => {
|
|
|
614
616
|
}
|
|
615
617
|
|
|
616
618
|
const isValidString = (str: string) => {
|
|
617
|
-
const
|
|
618
|
-
if (
|
|
619
|
-
|
|
620
|
-
|
|
619
|
+
const matchSingleQuotes = str.match(/^\s*(?<!\\)'(.*)(?<!\\)'\s*$/)
|
|
620
|
+
if (matchSingleQuotes) {
|
|
621
|
+
return !/(?<!\\)'/.test(matchSingleQuotes[1])
|
|
622
|
+
}
|
|
623
|
+
const matchDoubleQuotes = str.match(/^\s*(?<!\\)"(.*)(?<!\\)"\s*$/)
|
|
624
|
+
if (matchDoubleQuotes) {
|
|
625
|
+
return !/(?<!\\)"/.test(matchDoubleQuotes[1])
|
|
626
|
+
}
|
|
627
|
+
return false
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const enquoteString = (str: string) => {
|
|
631
|
+
return `'${str.replace(/'/g, "\\'")}'`
|
|
621
632
|
}
|
|
622
633
|
|
|
623
634
|
const getOperatorByDataType = (dataType: string) => {
|
|
@@ -728,7 +739,9 @@ const getValueMatch = (
|
|
|
728
739
|
let serach = s
|
|
729
740
|
|
|
730
741
|
if (typeof str === 'string') {
|
|
731
|
-
term = insensitive(
|
|
742
|
+
term = insensitive(
|
|
743
|
+
str.replace(/^["'](.*)["']$/, '$1').replace(/\\'/g, "'")
|
|
744
|
+
)
|
|
732
745
|
}
|
|
733
746
|
if (typeof s === 'string') {
|
|
734
747
|
serach = insensitive(s)
|
|
@@ -844,7 +857,9 @@ const getValueSuggestions = (
|
|
|
844
857
|
!knownDataTypes.includes(type as string) &&
|
|
845
858
|
typeof type !== 'object'
|
|
846
859
|
) {
|
|
847
|
-
suggestion.push(
|
|
860
|
+
suggestion.push(
|
|
861
|
+
typeof type === 'string' ? enquoteString(type) : type
|
|
862
|
+
)
|
|
848
863
|
}
|
|
849
864
|
}
|
|
850
865
|
}
|
|
@@ -863,7 +878,7 @@ const getValueSuggestions = (
|
|
|
863
878
|
default:
|
|
864
879
|
if (typeof type === 'object') {
|
|
865
880
|
// value aliases: key is the alias, value is the actual value
|
|
866
|
-
for (const key in type) suggestion.push(
|
|
881
|
+
for (const key in type) suggestion.push(enquoteString(key))
|
|
867
882
|
}
|
|
868
883
|
break
|
|
869
884
|
}
|
|
@@ -29,6 +29,17 @@ const GeneratePureValue = (value: any) => {
|
|
|
29
29
|
if (isFinite(num)) {
|
|
30
30
|
return num
|
|
31
31
|
}
|
|
32
|
+
|
|
33
|
+
let unquoted = value.replace(/^[^']*'(.*)'[^']*$/, '$1')
|
|
34
|
+
if (unquoted !== value) {
|
|
35
|
+
return unquoted.replace(/\\'/g, "'")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
unquoted = value.replace(/^[^"]*"(.*)"[^"]*$/, '$1')
|
|
39
|
+
if (unquoted !== value) {
|
|
40
|
+
return unquoted.replace(/\\"/g, '"')
|
|
41
|
+
}
|
|
42
|
+
|
|
32
43
|
return value.replaceAll('"', '').replaceAll("'", '')
|
|
33
44
|
} catch (e) {}
|
|
34
45
|
}
|
|
@@ -48,6 +59,14 @@ const Operators: string[] = [
|
|
|
48
59
|
'DOESNT-EXIST'
|
|
49
60
|
]
|
|
50
61
|
|
|
62
|
+
const SplitFirst = (what: string, by: string): string[] => {
|
|
63
|
+
const result = what.split(by)
|
|
64
|
+
if (result.length > 2) {
|
|
65
|
+
return [result.shift(), result.join(by)]
|
|
66
|
+
}
|
|
67
|
+
return result
|
|
68
|
+
}
|
|
69
|
+
|
|
51
70
|
/**
|
|
52
71
|
* Method to convert a DlSmartSearch query string to Mongo based JSON query
|
|
53
72
|
*
|
|
@@ -112,66 +131,67 @@ export const parseSmartQuery = (
|
|
|
112
131
|
for (const term of andTerms) {
|
|
113
132
|
pureValue = null
|
|
114
133
|
|
|
134
|
+
const termUpToQuote = term.replace(/^([^'"]+)['"].*$/, '$1')
|
|
135
|
+
|
|
115
136
|
switch (true) {
|
|
116
|
-
case
|
|
117
|
-
;[key, value] = term
|
|
137
|
+
case termUpToQuote.includes('>='):
|
|
138
|
+
;[key, value] = SplitFirst(term, '>=').map((x) => x.trim())
|
|
118
139
|
pureValue = GeneratePureValue(value)
|
|
119
140
|
if (pureValue !== null) {
|
|
120
141
|
andQuery.push({ [key]: { $gte: pureValue } })
|
|
121
142
|
}
|
|
122
143
|
break
|
|
123
|
-
case
|
|
124
|
-
;[key, value] = term
|
|
144
|
+
case termUpToQuote.includes('<='):
|
|
145
|
+
;[key, value] = SplitFirst(term, '<=').map((x) => x.trim())
|
|
125
146
|
pureValue = GeneratePureValue(value)
|
|
126
147
|
if (pureValue !== null) {
|
|
127
148
|
andQuery.push({ [key]: { $lte: pureValue } })
|
|
128
149
|
}
|
|
129
150
|
break
|
|
130
|
-
case
|
|
131
|
-
;[key, value] = term
|
|
151
|
+
case termUpToQuote.includes('>'):
|
|
152
|
+
;[key, value] = SplitFirst(term, '>').map((x) => x.trim())
|
|
132
153
|
pureValue = GeneratePureValue(value)
|
|
133
154
|
if (pureValue !== null) {
|
|
134
155
|
andQuery.push({ [key]: { $gt: pureValue } })
|
|
135
156
|
}
|
|
136
157
|
break
|
|
137
|
-
case
|
|
138
|
-
;[key, value] = term
|
|
158
|
+
case termUpToQuote.includes('<'):
|
|
159
|
+
;[key, value] = SplitFirst(term, '<').map((x) => x.trim())
|
|
139
160
|
pureValue = GeneratePureValue(value)
|
|
140
161
|
if (pureValue !== null) {
|
|
141
162
|
andQuery.push({ [key]: { $lt: pureValue } })
|
|
142
163
|
}
|
|
143
164
|
break
|
|
144
|
-
case
|
|
145
|
-
;[key, value] = term
|
|
165
|
+
case termUpToQuote.includes('!='):
|
|
166
|
+
;[key, value] = SplitFirst(term, '!=').map((x) => x.trim())
|
|
146
167
|
pureValue = GeneratePureValue(value)
|
|
147
168
|
if (pureValue !== null) {
|
|
148
169
|
andQuery.push({ [key]: { $ne: pureValue } })
|
|
149
170
|
}
|
|
150
171
|
break
|
|
151
|
-
case
|
|
152
|
-
;[key, value] = term
|
|
172
|
+
case termUpToQuote.includes('='):
|
|
173
|
+
;[key, value] = SplitFirst(term, '=').map((x) => x.trim())
|
|
153
174
|
pureValue = GeneratePureValue(value)
|
|
154
175
|
if (pureValue !== null) {
|
|
155
176
|
andQuery.push({ [key]: pureValue })
|
|
156
177
|
}
|
|
157
178
|
break
|
|
158
|
-
case
|
|
179
|
+
case termUpToQuote.includes('EXISTS'):
|
|
159
180
|
key = term.split('EXISTS')[0].trim()
|
|
160
181
|
andQuery.push({ [key]: { $exists: true } })
|
|
161
182
|
break
|
|
162
|
-
case
|
|
183
|
+
case termUpToQuote.includes('DOESNT-EXIST'):
|
|
163
184
|
key = term.split('DOESNT-EXIST')[0].trim()
|
|
164
185
|
andQuery.push({ [key]: { $exists: false } })
|
|
165
186
|
break
|
|
166
|
-
case
|
|
167
|
-
;[key, value] = term
|
|
187
|
+
case termUpToQuote.includes('IN'):
|
|
188
|
+
;[key, value] = SplitFirst(term, 'IN').map((x) => x.trim())
|
|
168
189
|
if (key.includes('NOT-')) {
|
|
169
|
-
;[key, value] = term
|
|
170
|
-
.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
.map((x) => x.trim())[1]
|
|
190
|
+
;[key, value] = SplitFirst(term, 'NOT-IN').map((x) =>
|
|
191
|
+
x.trim()
|
|
192
|
+
)
|
|
193
|
+
// TODO make , inside quotes work
|
|
194
|
+
queryValue = value
|
|
175
195
|
.split(',')
|
|
176
196
|
.map((x) => GeneratePureValue(x.trim()))
|
|
177
197
|
.filter((x) => x)
|
|
@@ -181,9 +201,8 @@ export const parseSmartQuery = (
|
|
|
181
201
|
andQuery.push({ [key]: { $nin: pureValue } })
|
|
182
202
|
}
|
|
183
203
|
} else {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
.map((x) => x.trim())[1]
|
|
204
|
+
// TODO make , inside quotes work
|
|
205
|
+
queryValue = value
|
|
187
206
|
.split(',')
|
|
188
207
|
.map((x) => GeneratePureValue(x.trim()))
|
|
189
208
|
.filter((x) => x)
|
|
@@ -344,7 +363,7 @@ export const stringifySmartQuery = (query: { [key: string]: any }): string => {
|
|
|
344
363
|
} else if (isDatePattern(value)) {
|
|
345
364
|
result += `${key} = (${value})`
|
|
346
365
|
} else {
|
|
347
|
-
result += `${key} = '${value}'`
|
|
366
|
+
result += `${key} = '${value.toString().replace(/'/g, "\\'")}'`
|
|
348
367
|
}
|
|
349
368
|
}
|
|
350
369
|
|
|
@@ -3,7 +3,7 @@ export function splitByQuotes(input: string, split: string) {
|
|
|
3
3
|
.replace('sp', split)
|
|
4
4
|
.replace('o', '[\\(\\{\\[]')
|
|
5
5
|
.replace('c', '[\\)\\}\\]]')
|
|
6
|
-
.replace('t', '[\'"]')
|
|
6
|
+
.replace('t', '(?<!\\\\)[\'"]')
|
|
7
7
|
.replace('e', '[\\\\]')
|
|
8
8
|
const r = new RegExp(pattern, 'gi')
|
|
9
9
|
const stack: string[] = []
|
|
@@ -13,11 +13,20 @@ export function splitByQuotes(input: string, split: string) {
|
|
|
13
13
|
if ($e) {
|
|
14
14
|
buffer.push($1, $s || $o || $c || $t)
|
|
15
15
|
return
|
|
16
|
-
} else if ($o)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
} else if ($o) {
|
|
17
|
+
if (!stack.includes("'") && !stack.includes('"')) {
|
|
18
|
+
stack.push($o)
|
|
19
|
+
}
|
|
20
|
+
} else if ($c) {
|
|
21
|
+
if (!stack.includes("'") && !stack.includes('"')) {
|
|
22
|
+
stack.pop()
|
|
23
|
+
}
|
|
24
|
+
} else if ($t) {
|
|
25
|
+
const otherQuote = $t === '"' ? "'" : '"'
|
|
26
|
+
if (!stack.includes(otherQuote)) {
|
|
27
|
+
if (stack[stack.length - 1] !== $t) stack.push($t)
|
|
28
|
+
else stack.pop()
|
|
29
|
+
}
|
|
21
30
|
} else {
|
|
22
31
|
if ($s ? !stack.length : !$1) {
|
|
23
32
|
buffer.push($1)
|