ketekny-ui-kit 1.0.138 → 1.0.139
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/ui/kSelect.vue +45 -5
package/package.json
CHANGED
package/src/ui/kSelect.vue
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
{{ label }}
|
|
5
5
|
</label>
|
|
6
6
|
|
|
7
|
-
<SelectRoot v-model="internalValue" v-model:open="open" :disabled="disabled">
|
|
7
|
+
<SelectRoot v-model="internalValue" v-model:open="open" :disabled="disabled" :multiple="multiple">
|
|
8
8
|
<SelectTrigger as-child>
|
|
9
9
|
<div
|
|
10
10
|
:id="computedId"
|
|
@@ -13,9 +13,32 @@
|
|
|
13
13
|
:aria-label="label || 'Select option'"
|
|
14
14
|
>
|
|
15
15
|
<SelectValue
|
|
16
|
+
v-if="!multiple"
|
|
16
17
|
:placeholder="placeholder"
|
|
17
18
|
class="block pr-10 truncate text-gray-700 data-[placeholder]:text-gray-400 dark:text-slate-100 dark:data-[placeholder]:text-slate-500"
|
|
18
19
|
/>
|
|
20
|
+
<div v-else class="flex flex-wrap items-center gap-1 pr-10">
|
|
21
|
+
<span v-if="selectedOptions.length === 0" class="text-gray-400 dark:text-slate-500">
|
|
22
|
+
{{ placeholder }}
|
|
23
|
+
</span>
|
|
24
|
+
<kChip
|
|
25
|
+
v-for="option in selectedOptions"
|
|
26
|
+
:key="option[optionValue]"
|
|
27
|
+
size="small"
|
|
28
|
+
>
|
|
29
|
+
<span>{{ option[optionLabel] }}</span>
|
|
30
|
+
<button
|
|
31
|
+
v-if="!disabled"
|
|
32
|
+
type="button"
|
|
33
|
+
class="ml-1 text-primary/70 transition-colors hover:text-primary"
|
|
34
|
+
:aria-label="`Remove ${option[optionLabel]}`"
|
|
35
|
+
@pointerdown.stop.prevent
|
|
36
|
+
@click.stop.prevent="removeSelection(option[optionValue])"
|
|
37
|
+
>
|
|
38
|
+
<X class="w-3 h-3" />
|
|
39
|
+
</button>
|
|
40
|
+
</kChip>
|
|
41
|
+
</div>
|
|
19
42
|
|
|
20
43
|
<button
|
|
21
44
|
v-if="clearable && isSelectionSet && !disabled"
|
|
@@ -113,10 +136,12 @@ import {
|
|
|
113
136
|
SelectViewport,
|
|
114
137
|
} from "reka-ui";
|
|
115
138
|
import { X, ChevronDown, Check, Search, SearchX } from "@lucide/vue";
|
|
139
|
+
import kChip from "./kChip.vue";
|
|
116
140
|
|
|
117
141
|
const props = defineProps({
|
|
118
142
|
options: { type: Array, required: true },
|
|
119
|
-
modelValue: [String, Number],
|
|
143
|
+
modelValue: [String, Number, Array],
|
|
144
|
+
multiple: { type: Boolean, default: false },
|
|
120
145
|
optionValue: { type: String, default: "value" },
|
|
121
146
|
optionLabel: { type: String, default: "label" },
|
|
122
147
|
label: String,
|
|
@@ -177,15 +202,25 @@ function normalizeDropdownHeight(value) {
|
|
|
177
202
|
|
|
178
203
|
const computedId = computed(() => (props.id != null && props.id !== "" ? props.id : generatedId));
|
|
179
204
|
const hasError = computed(() => props.error != null && props.error !== false);
|
|
180
|
-
const isSelectionSet = computed(() =>
|
|
205
|
+
const isSelectionSet = computed(() => {
|
|
206
|
+
if (props.multiple) return Array.isArray(props.modelValue) && props.modelValue.length > 0;
|
|
207
|
+
return props.modelValue !== null && props.modelValue !== undefined && props.modelValue !== "";
|
|
208
|
+
});
|
|
181
209
|
const resolvedDropdownHeight = computed(() => normalizeDropdownHeight(props.dropdownHeight));
|
|
210
|
+
const selectedOptions = computed(() => {
|
|
211
|
+
if (!props.multiple || !Array.isArray(props.modelValue)) return [];
|
|
212
|
+
return props.modelValue
|
|
213
|
+
.map((value) => props.options.find((option) => option?.[props.optionValue] === value))
|
|
214
|
+
.filter(Boolean);
|
|
215
|
+
});
|
|
182
216
|
|
|
183
217
|
const internalValue = computed({
|
|
184
218
|
get() {
|
|
219
|
+
if (props.multiple) return Array.isArray(props.modelValue) ? props.modelValue : [];
|
|
185
220
|
return props.modelValue === null ? undefined : props.modelValue;
|
|
186
221
|
},
|
|
187
222
|
set(val) {
|
|
188
|
-
emit("update:modelValue", val ?? null);
|
|
223
|
+
emit("update:modelValue", props.multiple ? (Array.isArray(val) ? val : []) : (val ?? null));
|
|
189
224
|
},
|
|
190
225
|
});
|
|
191
226
|
|
|
@@ -219,10 +254,15 @@ watch(open, (val) => {
|
|
|
219
254
|
});
|
|
220
255
|
|
|
221
256
|
function clearSelection() {
|
|
222
|
-
emit("update:modelValue", null);
|
|
257
|
+
emit("update:modelValue", props.multiple ? [] : null);
|
|
223
258
|
open.value = false;
|
|
224
259
|
}
|
|
225
260
|
|
|
261
|
+
function removeSelection(value) {
|
|
262
|
+
if (props.disabled || !Array.isArray(props.modelValue)) return;
|
|
263
|
+
emit("update:modelValue", props.modelValue.filter((selectedValue) => selectedValue !== value));
|
|
264
|
+
}
|
|
265
|
+
|
|
226
266
|
function getOptionNodes() {
|
|
227
267
|
const el = viewportRef.value?.$el ?? viewportRef.value;
|
|
228
268
|
if (!el) return [];
|