@saasmakers/ui 2.0.68 → 2.0.70
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/app/components/bases/BaseChip.vue +125 -0
- package/app/components/fields/FieldCombobox.vue +59 -209
- package/app/components/fields/FieldDate.vue +758 -0
- package/app/components/fields/FieldSelect.vue +2 -2
- package/app/composables/useChipNavigation.ts +178 -0
- package/app/composables/useRelativeDate.ts +97 -0
- package/app/i18n/relativeDate.ts +144 -0
- package/app/types/bases.d.ts +26 -0
- package/app/types/fields.d.ts +24 -0
- package/app/types/global.d.ts +6 -0
- package/app/utils/dateField.ts +104 -0
- package/app/utils/layerIcons.ts +1 -0
- package/package.json +1 -1
- package/uno.config.ts +2 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { BaseChip } from '../../types/bases'
|
|
3
|
+
|
|
4
|
+
const props = withDefaults(defineProps<BaseChip>(), {
|
|
5
|
+
active: false,
|
|
6
|
+
activeHighlight: 'gray',
|
|
7
|
+
ariaLabel: undefined,
|
|
8
|
+
ariaSelected: undefined,
|
|
9
|
+
as: 'button',
|
|
10
|
+
disabled: false,
|
|
11
|
+
hoverable: false,
|
|
12
|
+
icon: undefined,
|
|
13
|
+
iconLeading: false,
|
|
14
|
+
iconSize: undefined,
|
|
15
|
+
id: undefined,
|
|
16
|
+
maxWidth: false,
|
|
17
|
+
normalCase: false,
|
|
18
|
+
removable: false,
|
|
19
|
+
role: undefined,
|
|
20
|
+
text: '',
|
|
21
|
+
variant: 'outline',
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const emit = defineEmits<{
|
|
25
|
+
click: [event: MouseEvent]
|
|
26
|
+
mousedown: [event: MouseEvent]
|
|
27
|
+
mouseenter: [event: MouseEvent]
|
|
28
|
+
}>()
|
|
29
|
+
|
|
30
|
+
defineSlots<{
|
|
31
|
+
default?: () => VNode[]
|
|
32
|
+
}>()
|
|
33
|
+
|
|
34
|
+
const hasLeadingIcon = computed(() => {
|
|
35
|
+
return props.iconLeading && !!props.icon
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const hasTrailingIcon = computed(() => {
|
|
39
|
+
return props.removable || (!props.iconLeading && !!props.icon)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const resolvedIconSize = computed<BaseSize>(() => {
|
|
43
|
+
if (props.iconSize) {
|
|
44
|
+
return props.iconSize
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (props.maxWidth && props.iconLeading) {
|
|
48
|
+
return '2xs'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return 'xs'
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
function onClick(event: MouseEvent) {
|
|
55
|
+
emit('click', event)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function onMouseDown(event: MouseEvent) {
|
|
59
|
+
emit('mousedown', event)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function onMouseEnter(event: MouseEvent) {
|
|
63
|
+
emit('mouseenter', event)
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<template>
|
|
68
|
+
<component
|
|
69
|
+
:is="as"
|
|
70
|
+
:id="id"
|
|
71
|
+
class="min-h-8 inline-flex items-center border rounded-md font-normal tracking-tight outline-none transition-colors"
|
|
72
|
+
:class="{
|
|
73
|
+
'max-w-full': maxWidth,
|
|
74
|
+
'gap-1': hasLeadingIcon || hasTrailingIcon,
|
|
75
|
+
'cursor-pointer': !disabled,
|
|
76
|
+
'normal-case': normalCase,
|
|
77
|
+
'field-disabled': disabled,
|
|
78
|
+
'base-border bg-gray-100 py-1 pl-2 pr-1.5 text-gray-700 dark:bg-gray-800 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700': variant === 'filled',
|
|
79
|
+
'base-border px-2 py-1 text-gray-600 dark:text-gray-400': variant === 'outline' && !active,
|
|
80
|
+
'base-border-hover': variant === 'outline' && !active && hoverable,
|
|
81
|
+
'base-border px-2 py-1 bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300': variant === 'outline' && active && activeHighlight === 'gray',
|
|
82
|
+
'base-highlight-indigo border-indigo-600/35 px-2 py-1 text-gray-900 dark:text-gray-100': variant === 'outline' && active && activeHighlight === 'indigo',
|
|
83
|
+
'base-border base-border-hover border-dashed py-1 text-gray-600 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300': variant === 'dashed' && !active,
|
|
84
|
+
'pl-1.5 pr-2': variant === 'dashed' && !active && iconLeading,
|
|
85
|
+
'px-2 py-1': variant === 'dashed' && !active && !iconLeading,
|
|
86
|
+
'base-border border-dashed px-2 py-1 bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300': variant === 'dashed' && active && activeHighlight === 'gray',
|
|
87
|
+
'base-highlight-indigo border-indigo-600/35 border-dashed px-2 py-1 text-gray-900 dark:text-gray-100': variant === 'dashed' && active && activeHighlight === 'indigo',
|
|
88
|
+
}"
|
|
89
|
+
:aria-label="ariaLabel"
|
|
90
|
+
:aria-selected="ariaSelected"
|
|
91
|
+
:disabled="as === 'button' ? disabled : undefined"
|
|
92
|
+
:role="role"
|
|
93
|
+
:type="as === 'button' ? 'button' : undefined"
|
|
94
|
+
@click="onClick"
|
|
95
|
+
@mousedown="onMouseDown"
|
|
96
|
+
@mouseenter="onMouseEnter"
|
|
97
|
+
>
|
|
98
|
+
<BaseIcon
|
|
99
|
+
v-if="hasLeadingIcon"
|
|
100
|
+
class="shrink-0"
|
|
101
|
+
:color="maxWidth ? 'gray' : undefined"
|
|
102
|
+
:icon="icon"
|
|
103
|
+
:size="resolvedIconSize"
|
|
104
|
+
/>
|
|
105
|
+
|
|
106
|
+
<span class="truncate">
|
|
107
|
+
<slot>{{ text }}</slot>
|
|
108
|
+
</span>
|
|
109
|
+
|
|
110
|
+
<BaseIcon
|
|
111
|
+
v-if="removable"
|
|
112
|
+
color="gray"
|
|
113
|
+
:icon="getLayerIcon('close')"
|
|
114
|
+
size="xs"
|
|
115
|
+
/>
|
|
116
|
+
|
|
117
|
+
<BaseIcon
|
|
118
|
+
v-else-if="hasTrailingIcon"
|
|
119
|
+
class="shrink-0"
|
|
120
|
+
color="gray"
|
|
121
|
+
:icon="icon"
|
|
122
|
+
:size="resolvedIconSize"
|
|
123
|
+
/>
|
|
124
|
+
</component>
|
|
125
|
+
</template>
|
|
@@ -31,7 +31,6 @@ const inputRef = ref<HTMLInputElement>()
|
|
|
31
31
|
const listRef = ref<HTMLElement>()
|
|
32
32
|
const opened = ref(false)
|
|
33
33
|
const searchRaw = ref('')
|
|
34
|
-
const highlightedIndex = ref(0)
|
|
35
34
|
|
|
36
35
|
const filteredItems = computed(() => {
|
|
37
36
|
const selectedKeys = new Set(modelValue.value.map(item => getItemKey(item)))
|
|
@@ -63,12 +62,20 @@ const optionsCount = computed(() => {
|
|
|
63
62
|
return filteredItems.value.length + (canCreate.value ? 1 : 0)
|
|
64
63
|
})
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
const { highlightedIndex, onArrowKey, onHighlight, resetHighlight } = useChipNavigation({
|
|
66
|
+
containerRef: listRef,
|
|
67
|
+
count: optionsCount,
|
|
68
|
+
shouldIgnoreHorizontal: (direction) => {
|
|
69
|
+
const input = inputRef.value
|
|
70
|
+
const cursorAtStart = input ? input.selectionStart === 0 && input.selectionEnd === 0 : true
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
const cursorAtEnd = input
|
|
73
|
+
? input.selectionStart === input.value.length && input.selectionEnd === input.value.length
|
|
74
|
+
: true
|
|
75
|
+
|
|
76
|
+
return searchRaw.value.length > 0
|
|
77
|
+
&& ((direction === 'left' && !cursorAtStart) || (direction === 'right' && !cursorAtEnd))
|
|
78
|
+
},
|
|
72
79
|
})
|
|
73
80
|
|
|
74
81
|
function getItemKey(item: Item) {
|
|
@@ -81,167 +88,22 @@ function getItemText(item: Item) {
|
|
|
81
88
|
: String(readField(item, props.itemLabel) ?? '')
|
|
82
89
|
}
|
|
83
90
|
|
|
84
|
-
function getOptionElements() {
|
|
85
|
-
if (!listRef.value) {
|
|
86
|
-
return []
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return Array.from(listRef.value.querySelectorAll<HTMLElement>('[role="option"]'))
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function getOptionRects() {
|
|
93
|
-
return getOptionElements().map((element, index) => ({
|
|
94
|
-
index,
|
|
95
|
-
rect: element.getBoundingClientRect(),
|
|
96
|
-
}))
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async function moveHighlight(direction: 'down' | 'left' | 'right' | 'up') {
|
|
100
|
-
const optionRects = getOptionRects()
|
|
101
|
-
|
|
102
|
-
if (optionRects.length === 0) {
|
|
103
|
-
return
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const currentIndex = Math.min(highlightedIndex.value, optionRects.length - 1)
|
|
107
|
-
const current = optionRects[currentIndex]!
|
|
108
|
-
|
|
109
|
-
if (direction === 'left' || direction === 'right') {
|
|
110
|
-
const sameRowOptions = optionRects.filter((option) => {
|
|
111
|
-
return Math.abs(option.rect.top - current.rect.top) <= Math.min(option.rect.height, current.rect.height) / 2
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
if (direction === 'right') {
|
|
115
|
-
const rightOptions = sameRowOptions
|
|
116
|
-
.filter(option => option.rect.left > current.rect.left + 1)
|
|
117
|
-
.sort((first, second) => first.rect.left - second.rect.left)
|
|
118
|
-
|
|
119
|
-
if (rightOptions.length > 0) {
|
|
120
|
-
highlightedIndex.value = rightOptions[0]!.index
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
const nextRowOptions = optionRects
|
|
124
|
-
.filter(option => option.rect.top > current.rect.bottom - 1)
|
|
125
|
-
.sort((first, second) => first.rect.top - second.rect.top || first.rect.left - second.rect.left)
|
|
126
|
-
|
|
127
|
-
if (nextRowOptions.length > 0) {
|
|
128
|
-
const nextRowTop = nextRowOptions[0]!.rect.top
|
|
129
|
-
const firstInNextRow = nextRowOptions.find(option => Math.abs(option.rect.top - nextRowTop) <= 4)
|
|
130
|
-
|
|
131
|
-
if (firstInNextRow) {
|
|
132
|
-
highlightedIndex.value = firstInNextRow.index
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
const leftOptions = sameRowOptions
|
|
139
|
-
.filter(option => option.rect.right < current.rect.right - 1)
|
|
140
|
-
.sort((first, second) => second.rect.left - first.rect.left)
|
|
141
|
-
|
|
142
|
-
if (leftOptions.length > 0) {
|
|
143
|
-
highlightedIndex.value = leftOptions[0]!.index
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
const previousRowOptions = optionRects
|
|
147
|
-
.filter(option => option.rect.bottom < current.rect.top + 1)
|
|
148
|
-
.sort((first, second) => second.rect.top - first.rect.top || second.rect.left - first.rect.left)
|
|
149
|
-
|
|
150
|
-
if (previousRowOptions.length > 0) {
|
|
151
|
-
const previousRowTop = previousRowOptions[0]!.rect.top
|
|
152
|
-
|
|
153
|
-
const lastInPreviousRow = previousRowOptions
|
|
154
|
-
.filter(option => Math.abs(option.rect.top - previousRowTop) <= 4)
|
|
155
|
-
.sort((first, second) => second.rect.left - first.rect.left)[0]
|
|
156
|
-
|
|
157
|
-
if (lastInPreviousRow) {
|
|
158
|
-
highlightedIndex.value = lastInPreviousRow.index
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
const currentCenterX = current.rect.left + current.rect.width / 2
|
|
166
|
-
|
|
167
|
-
const rowOptions = optionRects.filter((option) => {
|
|
168
|
-
if (direction === 'down') {
|
|
169
|
-
return option.rect.top > current.rect.top + 1
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return option.rect.bottom < current.rect.bottom - 1
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
if (rowOptions.length === 0) {
|
|
176
|
-
return
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const targetRowTop = direction === 'down'
|
|
180
|
-
? Math.min(...rowOptions.map(option => option.rect.top))
|
|
181
|
-
: Math.max(...rowOptions.map(option => option.rect.top))
|
|
182
|
-
|
|
183
|
-
const targetRowOptions = rowOptions.filter(option => Math.abs(option.rect.top - targetRowTop) <= 4)
|
|
184
|
-
|
|
185
|
-
let closestIndex: number | undefined
|
|
186
|
-
let closestDist: number | undefined
|
|
187
|
-
|
|
188
|
-
for (const option of targetRowOptions) {
|
|
189
|
-
const centerX = option.rect.left + option.rect.width / 2
|
|
190
|
-
const dist = Math.abs(centerX - currentCenterX)
|
|
191
|
-
|
|
192
|
-
if (closestDist === undefined || dist < closestDist) {
|
|
193
|
-
closestDist = dist
|
|
194
|
-
closestIndex = option.index
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (closestIndex !== undefined) {
|
|
199
|
-
highlightedIndex.value = closestIndex
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
await nextTick()
|
|
204
|
-
|
|
205
|
-
getOptionElements()[highlightedIndex.value]?.scrollIntoView({
|
|
206
|
-
block: 'nearest',
|
|
207
|
-
inline: 'nearest',
|
|
208
|
-
})
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
async function onArrowKey(event: KeyboardEvent, direction: 'down' | 'left' | 'right' | 'up') {
|
|
212
|
-
if (optionsCount.value === 0) {
|
|
213
|
-
return
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (direction === 'left' || direction === 'right') {
|
|
217
|
-
const input = inputRef.value
|
|
218
|
-
const cursorAtStart = input ? input.selectionStart === 0 && input.selectionEnd === 0 : true
|
|
219
|
-
|
|
220
|
-
const cursorAtEnd = input
|
|
221
|
-
? input.selectionStart === input.value.length && input.selectionEnd === input.value.length
|
|
222
|
-
: true
|
|
223
|
-
|
|
224
|
-
if (searchRaw.value.length > 0 && ((direction === 'left' && !cursorAtStart) || (direction === 'right' && !cursorAtEnd))) {
|
|
225
|
-
return
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
event.preventDefault()
|
|
230
|
-
|
|
231
|
-
await moveHighlight(direction)
|
|
232
|
-
}
|
|
233
|
-
|
|
234
91
|
function onBackspace() {
|
|
235
92
|
if (searchRaw.value === '' && modelValue.value.length > 0) {
|
|
236
93
|
modelValue.value = modelValue.value.slice(0, -1)
|
|
237
94
|
}
|
|
238
95
|
}
|
|
239
96
|
|
|
97
|
+
function onChipMouseDown(event: MouseEvent) {
|
|
98
|
+
event.preventDefault()
|
|
99
|
+
}
|
|
100
|
+
|
|
240
101
|
function onClose() {
|
|
241
102
|
if (opened.value) {
|
|
242
103
|
opened.value = false
|
|
243
104
|
searchRaw.value = ''
|
|
244
|
-
|
|
105
|
+
|
|
106
|
+
resetHighlight()
|
|
245
107
|
}
|
|
246
108
|
}
|
|
247
109
|
|
|
@@ -253,7 +115,8 @@ async function onCreate() {
|
|
|
253
115
|
emit('create', label)
|
|
254
116
|
|
|
255
117
|
searchRaw.value = ''
|
|
256
|
-
|
|
118
|
+
|
|
119
|
+
resetHighlight()
|
|
257
120
|
|
|
258
121
|
await nextTick()
|
|
259
122
|
|
|
@@ -276,14 +139,11 @@ function onEnter() {
|
|
|
276
139
|
}
|
|
277
140
|
}
|
|
278
141
|
|
|
279
|
-
function onHighlight(index: number) {
|
|
280
|
-
highlightedIndex.value = index
|
|
281
|
-
}
|
|
282
|
-
|
|
283
142
|
async function onOpen() {
|
|
284
143
|
if (!props.disabled && !opened.value) {
|
|
285
144
|
opened.value = true
|
|
286
|
-
|
|
145
|
+
|
|
146
|
+
resetHighlight()
|
|
287
147
|
|
|
288
148
|
await nextTick()
|
|
289
149
|
|
|
@@ -301,7 +161,8 @@ function onRemove(item: Item) {
|
|
|
301
161
|
async function onSelect(item: Item) {
|
|
302
162
|
modelValue.value = [...modelValue.value, item]
|
|
303
163
|
searchRaw.value = ''
|
|
304
|
-
|
|
164
|
+
|
|
165
|
+
resetHighlight()
|
|
305
166
|
|
|
306
167
|
await nextTick()
|
|
307
168
|
|
|
@@ -349,39 +210,31 @@ function readField(item: Item, field: string) {
|
|
|
349
210
|
'min-h-14': size === 'lg',
|
|
350
211
|
}"
|
|
351
212
|
>
|
|
352
|
-
<
|
|
213
|
+
<BaseChip
|
|
353
214
|
v-for="item in modelValue"
|
|
354
215
|
:key="getItemKey(item)"
|
|
355
216
|
:aria-label="t('remove', { label: getItemText(item) })"
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
217
|
+
|
|
218
|
+
removable normal-case
|
|
219
|
+
variant="filled"
|
|
220
|
+
@mousedown="onChipMouseDown"
|
|
359
221
|
@click="onRemove(item)"
|
|
360
222
|
>
|
|
361
|
-
|
|
223
|
+
{{ getItemText(item) }}
|
|
224
|
+
</BaseChip>
|
|
362
225
|
|
|
363
|
-
|
|
364
|
-
color="gray"
|
|
365
|
-
:icon="getLayerIcon('close')"
|
|
366
|
-
size="xs"
|
|
367
|
-
/>
|
|
368
|
-
</button>
|
|
369
|
-
|
|
370
|
-
<button
|
|
226
|
+
<BaseChip
|
|
371
227
|
v-if="!opened"
|
|
372
228
|
:aria-label="t('add')"
|
|
373
|
-
class="min-h-8 inline-flex items-center gap-1 border base-border base-border-hover rounded-md border-dashed py-1 pl-1.5 pr-2 text-gray-600 font-normal tracking-tight normal-case dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
|
|
374
229
|
:disabled="disabled"
|
|
375
|
-
|
|
230
|
+
:icon="getLayerIcon('plus')"
|
|
231
|
+
icon-leading
|
|
232
|
+
normal-case
|
|
233
|
+
variant="dashed"
|
|
376
234
|
@click="onOpen"
|
|
377
235
|
>
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
size="xs"
|
|
381
|
-
/>
|
|
382
|
-
|
|
383
|
-
<span>{{ t('add') }}</span>
|
|
384
|
-
</button>
|
|
236
|
+
{{ t('add') }}
|
|
237
|
+
</BaseChip>
|
|
385
238
|
|
|
386
239
|
<input
|
|
387
240
|
v-else
|
|
@@ -418,45 +271,42 @@ function readField(item: Item, field: string) {
|
|
|
418
271
|
class="max-h-[150px] flex flex-wrap gap-1.5 overflow-y-auto"
|
|
419
272
|
role="listbox"
|
|
420
273
|
>
|
|
421
|
-
<
|
|
274
|
+
<BaseChip
|
|
422
275
|
v-for="(item, index) in filteredItems"
|
|
423
276
|
:id="`${id}-option-${index}`"
|
|
424
277
|
:key="getItemKey(item)"
|
|
278
|
+
:active="index === highlightedIndex"
|
|
425
279
|
:aria-selected="index === highlightedIndex"
|
|
426
|
-
|
|
427
|
-
|
|
280
|
+
as="div"
|
|
281
|
+
max-width
|
|
282
|
+
normal-case
|
|
428
283
|
role="option"
|
|
284
|
+
variant="outline"
|
|
429
285
|
@click="onSelect(item)"
|
|
430
|
-
@mousedown
|
|
286
|
+
@mousedown="onChipMouseDown"
|
|
431
287
|
@mouseenter="onHighlight(index)"
|
|
432
288
|
>
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
</span>
|
|
436
|
-
</div>
|
|
289
|
+
{{ getItemText(item) }}
|
|
290
|
+
</BaseChip>
|
|
437
291
|
|
|
438
|
-
<
|
|
292
|
+
<BaseChip
|
|
439
293
|
v-if="canCreate"
|
|
440
294
|
:id="`${id}-create`"
|
|
295
|
+
:active="highlightedIndex === createOptionIndex"
|
|
441
296
|
:aria-selected="highlightedIndex === createOptionIndex"
|
|
442
|
-
|
|
443
|
-
|
|
297
|
+
as="div"
|
|
298
|
+
max-width
|
|
299
|
+
:icon="getLayerIcon('plus')"
|
|
300
|
+
icon-leading
|
|
301
|
+
normal-case
|
|
444
302
|
role="option"
|
|
303
|
+
variant="dashed"
|
|
445
304
|
@click="onCreate"
|
|
446
|
-
@mousedown
|
|
305
|
+
@mousedown="onChipMouseDown"
|
|
447
306
|
@mouseenter="onHighlight(createOptionIndex)"
|
|
448
307
|
>
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
color="gray"
|
|
452
|
-
:icon="getLayerIcon('plus')"
|
|
453
|
-
size="2xs"
|
|
454
|
-
/>
|
|
455
|
-
|
|
456
|
-
<span class="truncate">
|
|
457
|
-
{{ t('create', { label: searchRaw.trim() }) }}
|
|
458
|
-
</span>
|
|
459
|
-
</div>
|
|
308
|
+
{{ t('create', { label: searchRaw.trim() }) }}
|
|
309
|
+
</BaseChip>
|
|
460
310
|
</div>
|
|
461
311
|
</div>
|
|
462
312
|
</div>
|