@saasmakers/ui 2.0.68 → 2.0.69
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 +56 -211
- package/app/components/fields/FieldDate.vue +755 -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 +138 -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,19 @@ 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
|
|
71
|
+
const cursorAtEnd = input
|
|
72
|
+
? input.selectionStart === input.value.length && input.selectionEnd === input.value.length
|
|
73
|
+
: true
|
|
68
74
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
75
|
+
return searchRaw.value.length > 0
|
|
76
|
+
&& ((direction === 'left' && !cursorAtStart) || (direction === 'right' && !cursorAtEnd))
|
|
77
|
+
},
|
|
72
78
|
})
|
|
73
79
|
|
|
74
80
|
function getItemKey(item: Item) {
|
|
@@ -81,156 +87,6 @@ function getItemText(item: Item) {
|
|
|
81
87
|
: String(readField(item, props.itemLabel) ?? '')
|
|
82
88
|
}
|
|
83
89
|
|
|
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
90
|
function onBackspace() {
|
|
235
91
|
if (searchRaw.value === '' && modelValue.value.length > 0) {
|
|
236
92
|
modelValue.value = modelValue.value.slice(0, -1)
|
|
@@ -241,7 +97,7 @@ function onClose() {
|
|
|
241
97
|
if (opened.value) {
|
|
242
98
|
opened.value = false
|
|
243
99
|
searchRaw.value = ''
|
|
244
|
-
|
|
100
|
+
resetHighlight()
|
|
245
101
|
}
|
|
246
102
|
}
|
|
247
103
|
|
|
@@ -253,7 +109,7 @@ async function onCreate() {
|
|
|
253
109
|
emit('create', label)
|
|
254
110
|
|
|
255
111
|
searchRaw.value = ''
|
|
256
|
-
|
|
112
|
+
resetHighlight()
|
|
257
113
|
|
|
258
114
|
await nextTick()
|
|
259
115
|
|
|
@@ -276,14 +132,14 @@ function onEnter() {
|
|
|
276
132
|
}
|
|
277
133
|
}
|
|
278
134
|
|
|
279
|
-
function
|
|
280
|
-
|
|
135
|
+
function onChipMouseDown(event: MouseEvent) {
|
|
136
|
+
event.preventDefault()
|
|
281
137
|
}
|
|
282
138
|
|
|
283
139
|
async function onOpen() {
|
|
284
140
|
if (!props.disabled && !opened.value) {
|
|
285
141
|
opened.value = true
|
|
286
|
-
|
|
142
|
+
resetHighlight()
|
|
287
143
|
|
|
288
144
|
await nextTick()
|
|
289
145
|
|
|
@@ -301,7 +157,7 @@ function onRemove(item: Item) {
|
|
|
301
157
|
async function onSelect(item: Item) {
|
|
302
158
|
modelValue.value = [...modelValue.value, item]
|
|
303
159
|
searchRaw.value = ''
|
|
304
|
-
|
|
160
|
+
resetHighlight()
|
|
305
161
|
|
|
306
162
|
await nextTick()
|
|
307
163
|
|
|
@@ -349,39 +205,31 @@ function readField(item: Item, field: string) {
|
|
|
349
205
|
'min-h-14': size === 'lg',
|
|
350
206
|
}"
|
|
351
207
|
>
|
|
352
|
-
<
|
|
208
|
+
<BaseChip
|
|
353
209
|
v-for="item in modelValue"
|
|
354
210
|
:key="getItemKey(item)"
|
|
355
|
-
:
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
211
|
+
:ariaLabel="t('remove', { label: getItemText(item) })"
|
|
212
|
+
normal-case
|
|
213
|
+
removable
|
|
214
|
+
variant="filled"
|
|
215
|
+
@mousedown="onChipMouseDown"
|
|
359
216
|
@click="onRemove(item)"
|
|
360
217
|
>
|
|
361
|
-
|
|
218
|
+
{{ getItemText(item) }}
|
|
219
|
+
</BaseChip>
|
|
362
220
|
|
|
363
|
-
|
|
364
|
-
color="gray"
|
|
365
|
-
:icon="getLayerIcon('close')"
|
|
366
|
-
size="xs"
|
|
367
|
-
/>
|
|
368
|
-
</button>
|
|
369
|
-
|
|
370
|
-
<button
|
|
221
|
+
<BaseChip
|
|
371
222
|
v-if="!opened"
|
|
372
|
-
:
|
|
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"
|
|
223
|
+
:ariaLabel="t('add')"
|
|
374
224
|
:disabled="disabled"
|
|
375
|
-
|
|
225
|
+
:icon="getLayerIcon('plus')"
|
|
226
|
+
icon-leading
|
|
227
|
+
normal-case
|
|
228
|
+
variant="dashed"
|
|
376
229
|
@click="onOpen"
|
|
377
230
|
>
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
size="xs"
|
|
381
|
-
/>
|
|
382
|
-
|
|
383
|
-
<span>{{ t('add') }}</span>
|
|
384
|
-
</button>
|
|
231
|
+
{{ t('add') }}
|
|
232
|
+
</BaseChip>
|
|
385
233
|
|
|
386
234
|
<input
|
|
387
235
|
v-else
|
|
@@ -418,45 +266,42 @@ function readField(item: Item, field: string) {
|
|
|
418
266
|
class="max-h-[150px] flex flex-wrap gap-1.5 overflow-y-auto"
|
|
419
267
|
role="listbox"
|
|
420
268
|
>
|
|
421
|
-
<
|
|
269
|
+
<BaseChip
|
|
422
270
|
v-for="(item, index) in filteredItems"
|
|
423
271
|
:id="`${id}-option-${index}`"
|
|
424
272
|
:key="getItemKey(item)"
|
|
425
|
-
:
|
|
426
|
-
|
|
427
|
-
|
|
273
|
+
:active="index === highlightedIndex"
|
|
274
|
+
:ariaSelected="index === highlightedIndex"
|
|
275
|
+
as="div"
|
|
276
|
+
max-width
|
|
277
|
+
normal-case
|
|
428
278
|
role="option"
|
|
279
|
+
variant="outline"
|
|
429
280
|
@click="onSelect(item)"
|
|
430
|
-
@mousedown
|
|
281
|
+
@mousedown="onChipMouseDown"
|
|
431
282
|
@mouseenter="onHighlight(index)"
|
|
432
283
|
>
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
</span>
|
|
436
|
-
</div>
|
|
284
|
+
{{ getItemText(item) }}
|
|
285
|
+
</BaseChip>
|
|
437
286
|
|
|
438
|
-
<
|
|
287
|
+
<BaseChip
|
|
439
288
|
v-if="canCreate"
|
|
440
289
|
:id="`${id}-create`"
|
|
441
|
-
:
|
|
442
|
-
|
|
443
|
-
|
|
290
|
+
:active="highlightedIndex === createOptionIndex"
|
|
291
|
+
:ariaSelected="highlightedIndex === createOptionIndex"
|
|
292
|
+
as="div"
|
|
293
|
+
max-width
|
|
294
|
+
:icon="getLayerIcon('plus')"
|
|
295
|
+
icon-leading
|
|
296
|
+
normal-case
|
|
444
297
|
role="option"
|
|
298
|
+
variant="dashed"
|
|
445
299
|
@click="onCreate"
|
|
446
|
-
@mousedown
|
|
300
|
+
@mousedown="onChipMouseDown"
|
|
447
301
|
@mouseenter="onHighlight(createOptionIndex)"
|
|
448
302
|
>
|
|
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>
|
|
303
|
+
{{ t('create', { label: searchRaw.trim() }) }}
|
|
304
|
+
</BaseChip>
|
|
460
305
|
</div>
|
|
461
306
|
</div>
|
|
462
307
|
</div>
|