@sewadah/selaras 0.3.2 → 0.5.0
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/README.md +6 -0
- package/agent/catalog.mjs +73 -0
- package/agent/mcp.mjs +15 -0
- package/agent/server.mjs +58 -0
- package/dist/agent/catalog.json +1 -0
- package/dist/module.json +1 -1
- package/dist/runtime/components/DashboardResizeHandle.vue +2 -1
- package/dist/runtime/internal/ComboboxSelectBase.d.vue.ts +29 -21
- package/dist/runtime/internal/ComboboxSelectBase.vue +138 -61
- package/dist/runtime/internal/ComboboxSelectBase.vue.d.ts +29 -21
- package/dist/runtime/internal/ComboboxSelectBody.d.vue.ts +1 -0
- package/dist/runtime/internal/ComboboxSelectBody.vue +2 -2
- package/dist/runtime/internal/ComboboxSelectBody.vue.d.ts +1 -0
- package/dist/runtime/theme/dashboard-resize-handle.d.ts +6 -0
- package/dist/runtime/theme/dashboard-resize-handle.js +4 -12
- package/dist/runtime/theme/select.js +1 -4
- package/package.json +15 -5
- package/skills/selaras/SKILL.md +40 -0
- package/skills/selaras/agents/openai.yaml +4 -0
package/dist/module.json
CHANGED
|
@@ -12,8 +12,9 @@ const isMobile = computed(() => dashboard?.isMobile.value ?? false);
|
|
|
12
12
|
const theme = useComponentTheme("dashboardResizeHandle", dashboardResizeHandleTheme);
|
|
13
13
|
const ui = computed(() => theme.value());
|
|
14
14
|
const rootUi = computed(() => resolveSlot(ui.value.root, props.ui?.root));
|
|
15
|
+
const lineUi = computed(() => resolveSlot(ui.value.line, props.ui?.line));
|
|
15
16
|
</script>
|
|
16
17
|
|
|
17
18
|
<template>
|
|
18
|
-
<SplitterResizeHandle v-if="!isMobile" direction="horizontal" :ui="{ root: rootUi, line:
|
|
19
|
+
<SplitterResizeHandle v-if="!isMobile" direction="horizontal" :ui="{ root: rootUi, line: lineUi }" />
|
|
19
20
|
</template>
|
|
@@ -60,21 +60,21 @@ export interface ComboboxSelectBaseEmits {
|
|
|
60
60
|
}
|
|
61
61
|
declare var __VLS_43: {
|
|
62
62
|
item: SelectModelValue | SelectOption<SelectModelValue>;
|
|
63
|
-
}, __VLS_98: {}, __VLS_105: {}, __VLS_120: {},
|
|
63
|
+
}, __VLS_98: {}, __VLS_105: {}, __VLS_120: {}, __VLS_136: {}, __VLS_143: {}, __VLS_150: {}, __VLS_174: {
|
|
64
64
|
item: SelectModelValue | SelectOption<SelectModelValue>;
|
|
65
|
-
},
|
|
65
|
+
}, __VLS_194: {
|
|
66
66
|
selected: import("./combobox-select.js").ResolvedOption | undefined;
|
|
67
|
-
},
|
|
67
|
+
}, __VLS_205: {}, __VLS_212: {}, __VLS_219: {}, __VLS_249: {}, __VLS_252: {}, __VLS_255: {}, __VLS_258: {}, __VLS_261: {
|
|
68
68
|
item: SelectModelValue | SelectOption<SelectModelValue>;
|
|
69
|
-
},
|
|
69
|
+
}, __VLS_264: {
|
|
70
70
|
group: import("./combobox-select.js").SelectOptionGroup<SelectModelValue>;
|
|
71
|
-
},
|
|
71
|
+
}, __VLS_267: {}, __VLS_305: string, __VLS_306: {}, __VLS_309: {}, __VLS_319: {
|
|
72
72
|
item: SelectModelValue | SelectOption<SelectModelValue>;
|
|
73
|
-
},
|
|
73
|
+
}, __VLS_322: {
|
|
74
74
|
group: import("./combobox-select.js").SelectOptionGroup<SelectModelValue>;
|
|
75
75
|
};
|
|
76
76
|
type __VLS_Slots = {} & {
|
|
77
|
-
[K in NonNullable<typeof
|
|
77
|
+
[K in NonNullable<typeof __VLS_305>]?: (props: typeof __VLS_306) => any;
|
|
78
78
|
} & {
|
|
79
79
|
item?: (props: typeof __VLS_43) => any;
|
|
80
80
|
} & {
|
|
@@ -84,33 +84,41 @@ type __VLS_Slots = {} & {
|
|
|
84
84
|
} & {
|
|
85
85
|
'dropdown-icon'?: (props: typeof __VLS_120) => any;
|
|
86
86
|
} & {
|
|
87
|
-
|
|
87
|
+
'clear-icon'?: (props: typeof __VLS_136) => any;
|
|
88
88
|
} & {
|
|
89
|
-
|
|
89
|
+
'loading-icon'?: (props: typeof __VLS_143) => any;
|
|
90
90
|
} & {
|
|
91
|
-
'
|
|
91
|
+
'dropdown-icon'?: (props: typeof __VLS_150) => any;
|
|
92
92
|
} & {
|
|
93
|
-
|
|
93
|
+
item?: (props: typeof __VLS_174) => any;
|
|
94
94
|
} & {
|
|
95
|
-
|
|
95
|
+
value?: (props: typeof __VLS_194) => any;
|
|
96
96
|
} & {
|
|
97
|
-
|
|
97
|
+
'clear-icon'?: (props: typeof __VLS_205) => any;
|
|
98
98
|
} & {
|
|
99
|
-
'
|
|
99
|
+
'loading-icon'?: (props: typeof __VLS_212) => any;
|
|
100
100
|
} & {
|
|
101
|
-
|
|
101
|
+
'dropdown-icon'?: (props: typeof __VLS_219) => any;
|
|
102
102
|
} & {
|
|
103
|
-
|
|
103
|
+
header?: (props: typeof __VLS_249) => any;
|
|
104
104
|
} & {
|
|
105
|
-
|
|
105
|
+
'filter-icon'?: (props: typeof __VLS_252) => any;
|
|
106
106
|
} & {
|
|
107
|
-
|
|
107
|
+
empty?: (props: typeof __VLS_255) => any;
|
|
108
108
|
} & {
|
|
109
|
-
|
|
109
|
+
'empty-filter'?: (props: typeof __VLS_258) => any;
|
|
110
110
|
} & {
|
|
111
|
-
item?: (props: typeof
|
|
111
|
+
item?: (props: typeof __VLS_261) => any;
|
|
112
112
|
} & {
|
|
113
|
-
group?: (props: typeof
|
|
113
|
+
group?: (props: typeof __VLS_264) => any;
|
|
114
|
+
} & {
|
|
115
|
+
footer?: (props: typeof __VLS_267) => any;
|
|
116
|
+
} & {
|
|
117
|
+
header?: (props: typeof __VLS_309) => any;
|
|
118
|
+
} & {
|
|
119
|
+
item?: (props: typeof __VLS_319) => any;
|
|
120
|
+
} & {
|
|
121
|
+
group?: (props: typeof __VLS_322) => any;
|
|
114
122
|
};
|
|
115
123
|
declare const __VLS_base: import("vue").DefineComponent<ComboboxSelectBaseProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
116
124
|
"update:modelValue": (value: SelectModelValue | SelectModelValue[] | undefined) => any;
|
|
@@ -117,7 +117,16 @@ function updateSelection(event, value) {
|
|
|
117
117
|
localValue.value = value;
|
|
118
118
|
emit(event, value);
|
|
119
119
|
}
|
|
120
|
+
const localOpen = ref(props.defaultOpen ?? false);
|
|
121
|
+
const isOpenControlled = () => Object.hasOwn(instance.vnode.props ?? {}, "open");
|
|
122
|
+
const open = computed(() => isOpenControlled() ? props.open ?? false : localOpen.value);
|
|
123
|
+
const isMobile = useIsMobile();
|
|
124
|
+
const mobilePresentation = ref(false);
|
|
125
|
+
const showMobileModal = computed(() => mobilePresentation.value);
|
|
126
|
+
const showMobileAutocompleteModal = computed(() => mobilePresentation.value && !!props.creatable);
|
|
127
|
+
const modalBody = ref();
|
|
120
128
|
const editableInput = ref();
|
|
129
|
+
const activeEditableInput = computed(() => showMobileAutocompleteModal.value ? modalBody.value?.searchElement : editableInput.value);
|
|
121
130
|
const comboboxRootKey = ref(0);
|
|
122
131
|
function setEditableInput(element) {
|
|
123
132
|
const candidate = typeof HTMLInputElement !== "undefined" && element instanceof HTMLInputElement ? element : element?.$el;
|
|
@@ -241,13 +250,13 @@ function revertUnmatchedText() {
|
|
|
241
250
|
return true;
|
|
242
251
|
}
|
|
243
252
|
function onSearchKeydown(event) {
|
|
244
|
-
if (event.target !==
|
|
253
|
+
if (event.target !== activeEditableInput.value)
|
|
245
254
|
return;
|
|
246
255
|
if (["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key) && !event.isComposing && !isSearchComposing.value) {
|
|
247
256
|
keyboardNavigatedOptions.value = true;
|
|
248
257
|
return;
|
|
249
258
|
}
|
|
250
|
-
if (event.key === "Backspace" && props.creatable && props.multiple && props.displayMode === "comma" && !event.isComposing && !isSearchComposing.value && event.keyCode !== 229 && !searchText.value && !
|
|
259
|
+
if (event.key === "Backspace" && props.creatable && props.multiple && props.displayMode === "comma" && !event.isComposing && !isSearchComposing.value && event.keyCode !== 229 && !searchText.value && !activeEditableInput.value?.value && selectedValues.value.length) {
|
|
251
260
|
removeValue(selectedValues.value.at(-1));
|
|
252
261
|
event.preventDefault();
|
|
253
262
|
return;
|
|
@@ -266,18 +275,20 @@ function onSearchKeydown(event) {
|
|
|
266
275
|
if (props.forceSelection)
|
|
267
276
|
return;
|
|
268
277
|
if (props.creatable && !searchText.value.trim()) {
|
|
269
|
-
const activeId2 =
|
|
278
|
+
const activeId2 = activeEditableInput.value?.getAttribute("aria-activedescendant");
|
|
270
279
|
const activeOption2 = activeId2 ? document.getElementById(activeId2) : null;
|
|
271
280
|
if (keyboardNavigatedOptions.value && activeOption2?.getAttribute("aria-selected") === "false")
|
|
272
281
|
return;
|
|
273
282
|
event.stopPropagation();
|
|
274
283
|
return;
|
|
275
284
|
}
|
|
276
|
-
const activeId =
|
|
285
|
+
const activeId = activeEditableInput.value?.getAttribute("aria-activedescendant");
|
|
277
286
|
const activeOption = activeId ? document.getElementById(activeId) : null;
|
|
278
287
|
if ((!hasMatchingOption(searchText.value) || !activeOption || activeOption.hasAttribute("data-selaras-create-option")) && commitCreatableText(searchText.value)) {
|
|
279
288
|
if (props.multiple)
|
|
280
289
|
searchText.value = "";
|
|
290
|
+
else if (showMobileAutocompleteModal.value)
|
|
291
|
+
updateOpen(false);
|
|
281
292
|
event.preventDefault();
|
|
282
293
|
event.stopPropagation();
|
|
283
294
|
}
|
|
@@ -297,7 +308,9 @@ onUnmounted(() => {
|
|
|
297
308
|
if (typeof window !== "undefined")
|
|
298
309
|
endSuggestionPointer();
|
|
299
310
|
});
|
|
300
|
-
function onSearchBlur() {
|
|
311
|
+
function onSearchBlur(event) {
|
|
312
|
+
if (props.creatable && event?.target !== editableInput.value)
|
|
313
|
+
return;
|
|
301
314
|
if (pointerInSuggestionContent.value)
|
|
302
315
|
return;
|
|
303
316
|
if (revertUnmatchedText())
|
|
@@ -329,7 +342,7 @@ watch(
|
|
|
329
342
|
([multiple, currentValue], [previousMultiple, previousValue]) => {
|
|
330
343
|
if (multiple === previousMultiple)
|
|
331
344
|
return;
|
|
332
|
-
const wasEditableFocused = props.creatable && typeof document !== "undefined" && document.activeElement ===
|
|
345
|
+
const wasEditableFocused = props.creatable && typeof document !== "undefined" && document.activeElement === activeEditableInput.value;
|
|
333
346
|
const abortingComposition = !!props.creatable && isSearchComposing.value;
|
|
334
347
|
const previouslyIdle = props.creatable && props.searchTerm === void 0 && searchText.value === (previousMultiple || !props.resetSearchTermOnSelect ? "" : singleSelectionLabel(previousValue));
|
|
335
348
|
const savedSearchText = searchText.value;
|
|
@@ -354,7 +367,7 @@ watch(
|
|
|
354
367
|
if (props.searchTerm === void 0)
|
|
355
368
|
internalSearchText.value = nextSearchText;
|
|
356
369
|
}
|
|
357
|
-
const priorInput =
|
|
370
|
+
const priorInput = activeEditableInput.value;
|
|
358
371
|
nextTick(() => {
|
|
359
372
|
if (props.creatable || props.searchable) {
|
|
360
373
|
if (props.searchTerm === void 0)
|
|
@@ -369,7 +382,7 @@ watch(
|
|
|
369
382
|
return;
|
|
370
383
|
if (document.activeElement !== document.body && document.activeElement !== priorInput)
|
|
371
384
|
return;
|
|
372
|
-
|
|
385
|
+
activeEditableInput.value?.focus();
|
|
373
386
|
});
|
|
374
387
|
},
|
|
375
388
|
{ flush: "pre" }
|
|
@@ -441,20 +454,10 @@ const groupProps = computed(() => resolveSlot(ui.value.group, props.ui?.group));
|
|
|
441
454
|
const itemProps = computed(() => resolveSlot(ui.value.item, props.ui?.item));
|
|
442
455
|
const itemIndicatorProps = computed(() => resolveSlot(ui.value.itemIndicator, props.ui?.itemIndicator));
|
|
443
456
|
const emptyProps = computed(() => resolveSlot(ui.value.empty, props.ui?.empty));
|
|
444
|
-
const mobileContentProps = computed(() =>
|
|
445
|
-
|
|
446
|
-
{
|
|
447
|
-
style: {
|
|
448
|
-
width: "min(calc(100vw - 2rem), 36rem)",
|
|
449
|
-
minWidth: "0",
|
|
450
|
-
maxHeight: "min(50vh, 28rem)"
|
|
451
|
-
}
|
|
452
|
-
},
|
|
453
|
-
resolveSlot(ui.value.mobilePanel, props.ui?.mobilePanel)
|
|
457
|
+
const mobileContentProps = computed(() => mergeProps(
|
|
458
|
+
resolveSlot(ui.value.mobileContent, props.ui?.mobileContent),
|
|
459
|
+
props.creatable ? resolveSlot(ui.value.mobilePanel, props.ui?.mobilePanel) : {}
|
|
454
460
|
));
|
|
455
|
-
const localOpen = ref(props.defaultOpen ?? false);
|
|
456
|
-
const isOpenControlled = () => Object.hasOwn(instance.vnode.props ?? {}, "open");
|
|
457
|
-
const open = computed(() => isOpenControlled() ? props.open ?? false : localOpen.value);
|
|
458
461
|
watch(flatOptions, (options, previousOptions) => {
|
|
459
462
|
const value = selection.value;
|
|
460
463
|
if (!props.creatable || props.multiple || open.value || props.searchTerm !== void 0 || !props.resetSearchTermOnSelect || value === void 0 || Array.isArray(value))
|
|
@@ -479,34 +482,48 @@ function resetSelection(event) {
|
|
|
479
482
|
updateOpen(false);
|
|
480
483
|
});
|
|
481
484
|
}
|
|
482
|
-
const isMobile = useIsMobile();
|
|
483
|
-
const mobilePresentation = ref(false);
|
|
484
485
|
watch(open, (open2) => {
|
|
485
|
-
if (!open2 && mobilePresentation.value &&
|
|
486
|
-
searchText.value = "";
|
|
486
|
+
if (!open2 && mobilePresentation.value && props.resetSearchTermOnBlur)
|
|
487
|
+
searchText.value = props.creatable && !props.multiple ? singleSelectionLabel(selection.value) : "";
|
|
487
488
|
mobilePresentation.value = open2 && !!props.adaptive && isMobile.value;
|
|
488
489
|
});
|
|
489
490
|
onMounted(() => {
|
|
490
491
|
if (!open.value)
|
|
491
492
|
return;
|
|
492
493
|
mobilePresentation.value = !!props.adaptive && isMobile.value;
|
|
493
|
-
if (mobilePresentation.value && props.creatable && !props.disabled) {
|
|
494
|
-
nextTick(() => {
|
|
495
|
-
editableInput.value?.focus();
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
494
|
});
|
|
499
|
-
const
|
|
500
|
-
const showMobileAutocompletePanel = computed(() => mobilePresentation.value && !!props.creatable);
|
|
495
|
+
const useMobileAutocompleteTrigger = computed(() => props.creatable && (open.value ? showMobileAutocompleteModal.value : props.adaptive && isMobile.value));
|
|
501
496
|
const modalId = `selaras-select-modal-${useId()}`;
|
|
502
497
|
const selectTrigger = ref();
|
|
503
|
-
const
|
|
504
|
-
const modalTriggerAttrs = computed(() => !props.creatable && (open.value ? showMobileSelectModal.value : props.adaptive && isMobile.value) ? { "aria-haspopup": "dialog", "aria-controls": modalId } : {});
|
|
498
|
+
const modalTriggerAttrs = computed(() => !props.creatable && (open.value ? showMobileModal.value : props.adaptive && isMobile.value) ? { "aria-haspopup": "dialog", "aria-controls": modalId } : {});
|
|
505
499
|
function onModalSelection(value) {
|
|
506
500
|
setValue(value == null ? props.multiple ? [] : void 0 : value);
|
|
501
|
+
if (props.creatable) {
|
|
502
|
+
nextTick(() => {
|
|
503
|
+
if (props.resetSearchTermOnSelect)
|
|
504
|
+
searchText.value = props.multiple ? "" : singleSelectionLabel(selection.value);
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
507
|
if (!props.multiple)
|
|
508
508
|
updateOpen(false);
|
|
509
509
|
}
|
|
510
|
+
watch(selection, () => {
|
|
511
|
+
if (useMobileAutocompleteTrigger.value && !open.value && props.resetSearchTermOnSelect && props.searchTerm === void 0)
|
|
512
|
+
internalSearchText.value = props.multiple ? "" : singleSelectionLabel(selection.value);
|
|
513
|
+
});
|
|
514
|
+
function openMobileAutocomplete(event) {
|
|
515
|
+
if (props.disabled || event && !["ArrowDown", "ArrowUp", "Enter", " "].includes(event.key))
|
|
516
|
+
return;
|
|
517
|
+
event?.preventDefault();
|
|
518
|
+
updateOpen(true);
|
|
519
|
+
}
|
|
520
|
+
function finishMobileAutocomplete() {
|
|
521
|
+
if (isSearchComposing.value)
|
|
522
|
+
return;
|
|
523
|
+
if (!revertUnmatchedText() && !props.forceSelection)
|
|
524
|
+
commitCreatableText(searchText.value);
|
|
525
|
+
updateOpen(false);
|
|
526
|
+
}
|
|
510
527
|
function serializeFormValue(value) {
|
|
511
528
|
if (typeof value !== "object")
|
|
512
529
|
return String(value);
|
|
@@ -518,22 +535,26 @@ function serializeFormValue(value) {
|
|
|
518
535
|
}
|
|
519
536
|
const adaptiveUi = computed(() => ({
|
|
520
537
|
content: {
|
|
521
|
-
class: "rounded-[var(--selaras-resolved-radius-md)]",
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
if (props.searchable && modalBody.value?.focusSearch())
|
|
527
|
-
event.preventDefault();
|
|
528
|
-
},
|
|
529
|
-
"onCloseAutoFocus": (event) => {
|
|
538
|
+
"class": "rounded-[var(--selaras-resolved-radius-md)]",
|
|
539
|
+
"id": modalId,
|
|
540
|
+
"aria-modal": true,
|
|
541
|
+
"onOpenAutoFocus": (event) => {
|
|
542
|
+
if (props.searchable && modalBody.value?.focusSearch())
|
|
530
543
|
event.preventDefault();
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
544
|
+
},
|
|
545
|
+
"onCloseAutoFocus": (event) => {
|
|
546
|
+
event.preventDefault();
|
|
547
|
+
if (props.disabled)
|
|
548
|
+
return;
|
|
549
|
+
nextTick(() => {
|
|
550
|
+
const target = props.creatable ? editableInput.value : selectTrigger.value?.$el;
|
|
551
|
+
if (target?.isConnected)
|
|
552
|
+
target.focus();
|
|
553
|
+
});
|
|
554
|
+
}
|
|
535
555
|
}
|
|
536
556
|
}));
|
|
557
|
+
const modalSearchInputProps = computed(() => mergeProps(searchInputProps.value, props.creatable ? { "id": `${selectId.value ?? modalId}-input`, "aria-label": nativeSearchInputAttrs.value["aria-label"] ?? props.placeholder ?? messages.value.search } : {}));
|
|
537
558
|
const bodyProps = computed(() => ({
|
|
538
559
|
searchable: props.searchable,
|
|
539
560
|
creatable: props.creatable,
|
|
@@ -563,7 +584,7 @@ const bodyProps = computed(() => ({
|
|
|
563
584
|
<template>
|
|
564
585
|
<ComboboxRoot
|
|
565
586
|
:key="comboboxRootKey"
|
|
566
|
-
:open="open"
|
|
587
|
+
:open="useMobileAutocompleteTrigger ? false : open"
|
|
567
588
|
:model-value="rekaSelection"
|
|
568
589
|
:multiple="multiple"
|
|
569
590
|
:disabled="disabled"
|
|
@@ -583,7 +604,7 @@ const bodyProps = computed(() => ({
|
|
|
583
604
|
typing is always available without opening anything first.
|
|
584
605
|
-->
|
|
585
606
|
<div
|
|
586
|
-
v-if="creatable"
|
|
607
|
+
v-if="creatable && !useMobileAutocompleteTrigger"
|
|
587
608
|
data-ui-group-item
|
|
588
609
|
:data-selaras-field-filled="selectedValues.length ? '' : void 0"
|
|
589
610
|
:data-selaras-field-active="open ? '' : void 0"
|
|
@@ -719,6 +740,50 @@ const bodyProps = computed(() => ({
|
|
|
719
740
|
</ComboboxTrigger>
|
|
720
741
|
</div>
|
|
721
742
|
|
|
743
|
+
<div
|
|
744
|
+
v-else-if="creatable"
|
|
745
|
+
data-ui-group-item
|
|
746
|
+
:data-selaras-field-filled="selectedValues.length ? '' : void 0"
|
|
747
|
+
:data-selaras-field-active="open ? '' : void 0"
|
|
748
|
+
v-bind="triggerProps"
|
|
749
|
+
@click="openMobileAutocomplete()"
|
|
750
|
+
>
|
|
751
|
+
<span v-if="multiple && selectedOptions.length" v-bind="commaValueProps">{{ commaText }}</span>
|
|
752
|
+
<input
|
|
753
|
+
:id="selectId"
|
|
754
|
+
:ref="setEditableInput"
|
|
755
|
+
:value="multiple ? '' : searchText"
|
|
756
|
+
:placeholder="multiple && selectedOptions.length ? void 0 : placeholder"
|
|
757
|
+
:disabled="disabled"
|
|
758
|
+
:aria-invalid="selectInvalid || void 0"
|
|
759
|
+
:aria-describedby="searchDescribedBy"
|
|
760
|
+
:aria-busy="loading || void 0"
|
|
761
|
+
v-bind="searchInputProps"
|
|
762
|
+
readonly
|
|
763
|
+
aria-haspopup="dialog"
|
|
764
|
+
:aria-expanded="open"
|
|
765
|
+
:aria-controls="modalId"
|
|
766
|
+
@keydown="openMobileAutocomplete"
|
|
767
|
+
>
|
|
768
|
+
<Button v-if="clearable && !disabled && selectedOptions.length" :size="clearSize" variant="text" color="neutral" tabindex="-1" :aria-label="messages.clear" v-bind="clearProps" @click.stop="clear">
|
|
769
|
+
<template #icon>
|
|
770
|
+
<slot name="clear-icon">
|
|
771
|
+
<Icon :name="icons.close" class="size-4" />
|
|
772
|
+
</slot>
|
|
773
|
+
</template>
|
|
774
|
+
</Button>
|
|
775
|
+
<slot v-if="loading" name="loading-icon">
|
|
776
|
+
<Icon :name="icons.loading" class="size-4 animate-spin" v-bind="iconProps" />
|
|
777
|
+
</slot>
|
|
778
|
+
<span v-if="loading" class="sr-only">{{ messages.loading }}</span>
|
|
779
|
+
<button v-if="dropdown" type="button" tabindex="-1" :disabled="disabled" :aria-label="messages.search" v-bind="dropdownProps" @click.stop="onDropdownClick();
|
|
780
|
+
openMobileAutocomplete()">
|
|
781
|
+
<slot name="dropdown-icon">
|
|
782
|
+
<Icon :name="icons.chevronDown" class="size-4" />
|
|
783
|
+
</slot>
|
|
784
|
+
</button>
|
|
785
|
+
</div>
|
|
786
|
+
|
|
722
787
|
<ComboboxTrigger
|
|
723
788
|
v-else
|
|
724
789
|
:id="selectId"
|
|
@@ -831,12 +896,12 @@ const bodyProps = computed(() => ({
|
|
|
831
896
|
</ComboboxTrigger>
|
|
832
897
|
</ComboboxAnchor>
|
|
833
898
|
|
|
834
|
-
<ComboboxPortal v-if="!
|
|
899
|
+
<ComboboxPortal v-if="!showMobileModal" v-bind="portalProps">
|
|
835
900
|
<ComboboxContent
|
|
836
901
|
position="popper" :side-offset="4"
|
|
837
902
|
:hide-when-empty="creatable && !multiple && !forceSelection"
|
|
838
903
|
:data-selaras-theme="themeBindings['data-selaras-theme']" :data-selaras-mode="themeBindings['data-selaras-mode']" :style="themeBindings.style" :data-selaras-color="colorRoleMarker"
|
|
839
|
-
v-bind="
|
|
904
|
+
v-bind="contentProps"
|
|
840
905
|
@pointerdown.capture="startSuggestionPointer"
|
|
841
906
|
>
|
|
842
907
|
<ComboboxSelectBody v-bind="bodyProps" @update:search-text="searchText = $event">
|
|
@@ -866,10 +931,8 @@ const bodyProps = computed(() => ({
|
|
|
866
931
|
</ComboboxContent>
|
|
867
932
|
</ComboboxPortal>
|
|
868
933
|
<!--
|
|
869
|
-
Modal owns
|
|
870
|
-
|
|
871
|
-
Autocomplete stays in the nonmodal ComboboxContent branch above: its
|
|
872
|
-
editable input is the one focus and dismissal owner for that surface.
|
|
934
|
+
Modal owns focus and dismissal; Listbox supplies selection and the
|
|
935
|
+
Autocomplete editor inside the same accessible dialog surface.
|
|
873
936
|
-->
|
|
874
937
|
<Modal
|
|
875
938
|
v-else :open="open" :title="placeholder || messages.search" :description="messages.searchDescription"
|
|
@@ -878,15 +941,26 @@ const bodyProps = computed(() => ({
|
|
|
878
941
|
>
|
|
879
942
|
<template #content>
|
|
880
943
|
<ListboxRoot
|
|
881
|
-
v-if="!creatable"
|
|
882
944
|
:model-value="rekaSelection" :multiple="multiple" :disabled="disabled"
|
|
883
945
|
:data-selaras-theme="themeBindings['data-selaras-theme']" :data-selaras-mode="themeBindings['data-selaras-mode']" :style="themeBindings.style" :data-selaras-color="colorRoleMarker" v-bind="mobileContentProps"
|
|
946
|
+
@pointerdown.capture="startSuggestionPointer"
|
|
947
|
+
@keydown.capture="onSearchKeydown"
|
|
884
948
|
@update:model-value="onModalSelection"
|
|
885
949
|
>
|
|
886
|
-
<ComboboxSelectBody ref="modalBody" v-bind="bodyProps" listbox @update:search-text="searchText = $event">
|
|
887
|
-
<template v-for="slotName in ['
|
|
950
|
+
<ComboboxSelectBody ref="modalBody" v-bind="bodyProps" :search-input-props="modalSearchInputProps" listbox @update:search-text="searchText = $event">
|
|
951
|
+
<template v-for="slotName in ['filter-icon', 'empty', 'empty-filter', 'footer'].filter((slotName) => $slots[slotName])" #[slotName]>
|
|
888
952
|
<slot :name="slotName" />
|
|
889
953
|
</template>
|
|
954
|
+
<template #header>
|
|
955
|
+
<slot name="header" />
|
|
956
|
+
<div v-if="creatable && multiple && selectedOptions.length" class="flex flex-wrap gap-1.5 px-3 pt-3">
|
|
957
|
+
<template v-if="displayMode === 'chip'">
|
|
958
|
+
<Chip v-for="option in visibleOptions" :key="option.key" :label="option.label" size="sm" removable @remove="removeValue(option.value)" />
|
|
959
|
+
<span v-if="overflowOptions.length" v-bind="chipOverflowProps">{{ messages.moreItems(overflowOptions.length) }}</span>
|
|
960
|
+
</template>
|
|
961
|
+
<span v-else v-bind="valueProps">{{ commaText }}</span>
|
|
962
|
+
</div>
|
|
963
|
+
</template>
|
|
890
964
|
<template v-if="$slots.item" #item="scope">
|
|
891
965
|
<slot name="item" v-bind="scope" />
|
|
892
966
|
</template>
|
|
@@ -895,10 +969,13 @@ const bodyProps = computed(() => ({
|
|
|
895
969
|
</template>
|
|
896
970
|
</ComboboxSelectBody>
|
|
897
971
|
</ListboxRoot>
|
|
898
|
-
<div v-if="
|
|
899
|
-
<
|
|
972
|
+
<div v-if="creatable || multiple" class="flex justify-end p-2">
|
|
973
|
+
<Button v-if="creatable" variant="ghost" color="neutral" @click="finishMobileAutocomplete">
|
|
974
|
+
{{ messages.done }}
|
|
975
|
+
</Button>
|
|
976
|
+
<DialogClose v-else as-child>
|
|
900
977
|
<Button variant="ghost" color="neutral">
|
|
901
|
-
{{
|
|
978
|
+
{{ messages.done }}
|
|
902
979
|
</Button>
|
|
903
980
|
</DialogClose>
|
|
904
981
|
</div>
|
|
@@ -60,21 +60,21 @@ export interface ComboboxSelectBaseEmits {
|
|
|
60
60
|
}
|
|
61
61
|
declare var __VLS_43: {
|
|
62
62
|
item: SelectModelValue | SelectOption<SelectModelValue>;
|
|
63
|
-
}, __VLS_98: {}, __VLS_105: {}, __VLS_120: {},
|
|
63
|
+
}, __VLS_98: {}, __VLS_105: {}, __VLS_120: {}, __VLS_136: {}, __VLS_143: {}, __VLS_150: {}, __VLS_174: {
|
|
64
64
|
item: SelectModelValue | SelectOption<SelectModelValue>;
|
|
65
|
-
},
|
|
65
|
+
}, __VLS_194: {
|
|
66
66
|
selected: import("./combobox-select.js").ResolvedOption | undefined;
|
|
67
|
-
},
|
|
67
|
+
}, __VLS_205: {}, __VLS_212: {}, __VLS_219: {}, __VLS_249: {}, __VLS_252: {}, __VLS_255: {}, __VLS_258: {}, __VLS_261: {
|
|
68
68
|
item: SelectModelValue | SelectOption<SelectModelValue>;
|
|
69
|
-
},
|
|
69
|
+
}, __VLS_264: {
|
|
70
70
|
group: import("./combobox-select.js").SelectOptionGroup<SelectModelValue>;
|
|
71
|
-
},
|
|
71
|
+
}, __VLS_267: {}, __VLS_305: string, __VLS_306: {}, __VLS_309: {}, __VLS_319: {
|
|
72
72
|
item: SelectModelValue | SelectOption<SelectModelValue>;
|
|
73
|
-
},
|
|
73
|
+
}, __VLS_322: {
|
|
74
74
|
group: import("./combobox-select.js").SelectOptionGroup<SelectModelValue>;
|
|
75
75
|
};
|
|
76
76
|
type __VLS_Slots = {} & {
|
|
77
|
-
[K in NonNullable<typeof
|
|
77
|
+
[K in NonNullable<typeof __VLS_305>]?: (props: typeof __VLS_306) => any;
|
|
78
78
|
} & {
|
|
79
79
|
item?: (props: typeof __VLS_43) => any;
|
|
80
80
|
} & {
|
|
@@ -84,33 +84,41 @@ type __VLS_Slots = {} & {
|
|
|
84
84
|
} & {
|
|
85
85
|
'dropdown-icon'?: (props: typeof __VLS_120) => any;
|
|
86
86
|
} & {
|
|
87
|
-
|
|
87
|
+
'clear-icon'?: (props: typeof __VLS_136) => any;
|
|
88
88
|
} & {
|
|
89
|
-
|
|
89
|
+
'loading-icon'?: (props: typeof __VLS_143) => any;
|
|
90
90
|
} & {
|
|
91
|
-
'
|
|
91
|
+
'dropdown-icon'?: (props: typeof __VLS_150) => any;
|
|
92
92
|
} & {
|
|
93
|
-
|
|
93
|
+
item?: (props: typeof __VLS_174) => any;
|
|
94
94
|
} & {
|
|
95
|
-
|
|
95
|
+
value?: (props: typeof __VLS_194) => any;
|
|
96
96
|
} & {
|
|
97
|
-
|
|
97
|
+
'clear-icon'?: (props: typeof __VLS_205) => any;
|
|
98
98
|
} & {
|
|
99
|
-
'
|
|
99
|
+
'loading-icon'?: (props: typeof __VLS_212) => any;
|
|
100
100
|
} & {
|
|
101
|
-
|
|
101
|
+
'dropdown-icon'?: (props: typeof __VLS_219) => any;
|
|
102
102
|
} & {
|
|
103
|
-
|
|
103
|
+
header?: (props: typeof __VLS_249) => any;
|
|
104
104
|
} & {
|
|
105
|
-
|
|
105
|
+
'filter-icon'?: (props: typeof __VLS_252) => any;
|
|
106
106
|
} & {
|
|
107
|
-
|
|
107
|
+
empty?: (props: typeof __VLS_255) => any;
|
|
108
108
|
} & {
|
|
109
|
-
|
|
109
|
+
'empty-filter'?: (props: typeof __VLS_258) => any;
|
|
110
110
|
} & {
|
|
111
|
-
item?: (props: typeof
|
|
111
|
+
item?: (props: typeof __VLS_261) => any;
|
|
112
112
|
} & {
|
|
113
|
-
group?: (props: typeof
|
|
113
|
+
group?: (props: typeof __VLS_264) => any;
|
|
114
|
+
} & {
|
|
115
|
+
footer?: (props: typeof __VLS_267) => any;
|
|
116
|
+
} & {
|
|
117
|
+
header?: (props: typeof __VLS_309) => any;
|
|
118
|
+
} & {
|
|
119
|
+
item?: (props: typeof __VLS_319) => any;
|
|
120
|
+
} & {
|
|
121
|
+
group?: (props: typeof __VLS_322) => any;
|
|
114
122
|
};
|
|
115
123
|
declare const __VLS_base: import("vue").DefineComponent<ComboboxSelectBaseProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
116
124
|
"update:modelValue": (value: SelectModelValue | SelectModelValue[] | undefined) => any;
|
|
@@ -68,6 +68,7 @@ type __VLS_Slots = {} & {
|
|
|
68
68
|
};
|
|
69
69
|
declare const __VLS_base: import("vue").DefineComponent<ComboboxSelectBodyProps, {
|
|
70
70
|
focusSearch: typeof focusSearch;
|
|
71
|
+
searchElement: import("vue").ComputedRef<HTMLInputElement | undefined>;
|
|
71
72
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
72
73
|
"update:searchText": (value: string) => any;
|
|
73
74
|
}, string, import("vue").PublicProps, Readonly<ComboboxSelectBodyProps> & Readonly<{
|
|
@@ -90,13 +90,13 @@ function focusSearch() {
|
|
|
90
90
|
element.focus();
|
|
91
91
|
return true;
|
|
92
92
|
}
|
|
93
|
-
defineExpose({ focusSearch });
|
|
93
|
+
defineExpose({ focusSearch, searchElement: computed(() => searchInput.value?.$el) });
|
|
94
94
|
</script>
|
|
95
95
|
|
|
96
96
|
<template>
|
|
97
97
|
<slot name="header" />
|
|
98
98
|
|
|
99
|
-
<div v-if="searchable && !creatable" v-bind="searchWrapperProps">
|
|
99
|
+
<div v-if="searchable && (!creatable || listbox)" v-bind="searchWrapperProps">
|
|
100
100
|
<slot name="filter-icon">
|
|
101
101
|
<Icon :name="icons.search" class="size-4 text-[var(--selaras-resolved-text-muted)]" />
|
|
102
102
|
</slot>
|
|
@@ -68,6 +68,7 @@ type __VLS_Slots = {} & {
|
|
|
68
68
|
};
|
|
69
69
|
declare const __VLS_base: import("vue").DefineComponent<ComboboxSelectBodyProps, {
|
|
70
70
|
focusSearch: typeof focusSearch;
|
|
71
|
+
searchElement: import("vue").ComputedRef<HTMLInputElement | undefined>;
|
|
71
72
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
72
73
|
"update:searchText": (value: string) => any;
|
|
73
74
|
}, string, import("vue").PublicProps, Readonly<ComboboxSelectBodyProps> & Readonly<{
|
|
@@ -2,25 +2,31 @@ export declare const dashboardResizeHandleTheme: import("tailwind-variants").TVR
|
|
|
2
2
|
[key: string]: {
|
|
3
3
|
[key: string]: import("tailwind-variants").ClassValue | {
|
|
4
4
|
root?: import("tailwind-variants").ClassValue;
|
|
5
|
+
line?: import("tailwind-variants").ClassValue;
|
|
5
6
|
};
|
|
6
7
|
};
|
|
7
8
|
} | {
|
|
8
9
|
[x: string]: {
|
|
9
10
|
[x: string]: import("tailwind-variants").ClassValue | {
|
|
10
11
|
root?: import("tailwind-variants").ClassValue;
|
|
12
|
+
line?: import("tailwind-variants").ClassValue;
|
|
11
13
|
};
|
|
12
14
|
};
|
|
13
15
|
} | {}, {
|
|
14
16
|
root: string;
|
|
17
|
+
line: string;
|
|
15
18
|
}, undefined, {
|
|
16
19
|
[key: string]: {
|
|
17
20
|
[key: string]: import("tailwind-variants").ClassValue | {
|
|
18
21
|
root?: import("tailwind-variants").ClassValue;
|
|
22
|
+
line?: import("tailwind-variants").ClassValue;
|
|
19
23
|
};
|
|
20
24
|
};
|
|
21
25
|
} | {}, {
|
|
22
26
|
root: string;
|
|
27
|
+
line: string;
|
|
23
28
|
}, import("tailwind-variants").TVReturnTypeLike<unknown, {
|
|
24
29
|
root: string;
|
|
30
|
+
line: string;
|
|
25
31
|
}>>;
|
|
26
32
|
export type DashboardResizeHandleThemeSlots = keyof (typeof dashboardResizeHandleTheme)['slots'];
|
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
import { tv } from "tailwind-variants";
|
|
2
2
|
export const dashboardResizeHandleTheme = tv({
|
|
3
3
|
slots: {
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// --selaras-resolved-surface-elevated (a step apart from DashboardPanel's own plain
|
|
9
|
-
// --selaras-resolved-surface-default), that "invisible" padding stops being invisible: it reads
|
|
10
|
-
// as a sliver of the wrong background bleeding through right at the
|
|
11
|
-
// sidebar's own edge. A background split exactly down the middle -
|
|
12
|
-
// matching each side's real background - makes the hit area blend
|
|
13
|
-
// into whichever panel it happens to be sitting over instead of
|
|
14
|
-
// showing through as its own separate strip.
|
|
15
|
-
root: "bg-[linear-gradient(to_right,var(--selaras-resolved-surface-elevated)_50%,var(--selaras-resolved-surface-default)_50%)]"
|
|
4
|
+
// Overlap the panel boundary without consuming layout space or painting
|
|
5
|
+
// either surface. Both panels keep their own backgrounds beneath the target.
|
|
6
|
+
root: "z-10 -mx-1 w-2 bg-transparent",
|
|
7
|
+
line: "rtl:translate-x-1/2 group-focus-visible:bg-[var(--_selaras-color-indicator)]"
|
|
16
8
|
}
|
|
17
9
|
});
|