@sewadah/selaras 0.7.1 → 0.9.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 +1 -1
- package/dist/agent/catalog.json +1 -1
- package/dist/module.json +1 -1
- package/dist/runtime/components/Autocomplete.vue +6 -0
- package/dist/runtime/components/AvatarGroup.vue +3 -2
- package/dist/runtime/components/CodeGroup.vue +3 -15
- package/dist/runtime/components/ColorModeSwitch.vue +5 -4
- package/dist/runtime/components/ColorModeToggle.vue +5 -4
- package/dist/runtime/components/Input.d.vue.ts +7 -4
- package/dist/runtime/components/Input.vue +22 -5
- package/dist/runtime/components/Input.vue.d.ts +7 -4
- package/dist/runtime/components/Listbox.d.vue.ts +28 -0
- package/dist/runtime/components/Listbox.vue +53 -0
- package/dist/runtime/components/Listbox.vue.d.ts +28 -0
- package/dist/runtime/components/Select.vue +6 -0
- package/dist/runtime/components/Textarea.d.vue.ts +7 -4
- package/dist/runtime/components/Textarea.vue +24 -7
- package/dist/runtime/components/Textarea.vue.d.ts +7 -4
- package/dist/runtime/components/TransferList.d.vue.ts +47 -0
- package/dist/runtime/components/TransferList.vue +67 -0
- package/dist/runtime/components/TransferList.vue.d.ts +47 -0
- package/dist/runtime/internal/ComboboxSelectBase.d.vue.ts +7 -63
- package/dist/runtime/internal/ComboboxSelectBase.vue +18 -5
- package/dist/runtime/internal/ComboboxSelectBase.vue.d.ts +7 -63
- package/dist/runtime/internal/ListSelectionForm.d.vue.ts +15 -0
- package/dist/runtime/internal/ListSelectionForm.vue +33 -0
- package/dist/runtime/internal/ListSelectionForm.vue.d.ts +15 -0
- package/dist/runtime/internal/ListboxBase.d.vue.ts +49 -0
- package/dist/runtime/internal/ListboxBase.vue +177 -0
- package/dist/runtime/internal/ListboxBase.vue.d.ts +49 -0
- package/dist/runtime/internal/ListboxOption.d.vue.ts +31 -0
- package/dist/runtime/internal/ListboxOption.vue +37 -0
- package/dist/runtime/internal/ListboxOption.vue.d.ts +31 -0
- package/dist/runtime/internal/TransferListBase.d.vue.ts +63 -0
- package/dist/runtime/internal/TransferListBase.vue +206 -0
- package/dist/runtime/internal/TransferListBase.vue.d.ts +63 -0
- package/dist/runtime/internal/color-mode-control.d.ts +7 -0
- package/dist/runtime/internal/color-mode-control.js +12 -0
- package/dist/runtime/internal/list-selection.d.ts +15 -0
- package/dist/runtime/internal/list-selection.js +43 -0
- package/dist/runtime/theme/input.d.ts +6 -0
- package/dist/runtime/theme/input.js +2 -0
- package/dist/runtime/theme/listbox.d.ts +131 -0
- package/dist/runtime/theme/listbox.js +27 -0
- package/dist/runtime/theme/select.d.ts +6 -0
- package/dist/runtime/theme/select.js +3 -1
- package/dist/runtime/theme/textarea.d.ts +6 -0
- package/dist/runtime/theme/textarea.js +2 -0
- package/dist/runtime/theme/transfer-list.d.ts +74 -0
- package/dist/runtime/theme/transfer-list.js +21 -0
- package/dist/runtime/theme-config.d.ts +10 -0
- package/dist/runtime/types/index.d.ts +3 -0
- package/dist/runtime/utils/field-adornments.d.ts +14 -0
- package/dist/runtime/utils/field-adornments.js +36 -0
- package/dist/runtime/utils/listbox-contracts.d.ts +31 -0
- package/dist/runtime/utils/listbox-contracts.js +0 -0
- package/dist/runtime/utils/messages.d.ts +7 -0
- package/dist/runtime/utils/messages.js +7 -0
- package/dist/runtime/utils/select-contracts.d.ts +3 -0
- package/dist/runtime/utils/slot-children.d.ts +2 -0
- package/dist/runtime/utils/slot-children.js +15 -0
- package/dist/runtime/utils/transfer-list-contracts.d.ts +48 -0
- package/dist/runtime/utils/transfer-list-contracts.js +0 -0
- package/package.json +5 -4
- package/skills/selaras/SKILL.md +7 -7
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ListboxContent, ListboxFilter, ListboxGroup, ListboxGroupLabel, ListboxRoot, ListboxVirtualizer, useFilter } from "reka-ui";
|
|
3
|
+
import { computed, ref, toRaw, useId } from "vue";
|
|
4
|
+
import Checkbox from "../components/Checkbox.vue";
|
|
5
|
+
import Icon from "../components/Icon.vue";
|
|
6
|
+
import { useFormField } from "../composables/use-form-field";
|
|
7
|
+
import { useIcons } from "../composables/use-icons";
|
|
8
|
+
import { useMessages } from "../composables/use-messages";
|
|
9
|
+
import { listboxTheme } from "../theme/listbox";
|
|
10
|
+
import { resolveRegisteredColorRole } from "../utils/registered-colors";
|
|
11
|
+
import { resolveSlot, useComponentTheme, useFallthroughAttrs, useRootProps } from "../utils/ui";
|
|
12
|
+
import { isOptionGroup, useComboboxSelect } from "./combobox-select";
|
|
13
|
+
import { useListSelection } from "./list-selection";
|
|
14
|
+
import ListboxOption from "./ListboxOption.vue";
|
|
15
|
+
import ListSelectionForm from "./ListSelectionForm.vue";
|
|
16
|
+
defineOptions({ inheritAttrs: false });
|
|
17
|
+
const props = defineProps({
|
|
18
|
+
items: { type: null, required: true },
|
|
19
|
+
valueKey: { type: String, required: false },
|
|
20
|
+
labelKey: { type: String, required: false },
|
|
21
|
+
descriptionKey: { type: String, required: false },
|
|
22
|
+
multiple: { type: Boolean, required: false },
|
|
23
|
+
modelValue: { type: [String, Number, Object, Array], required: false },
|
|
24
|
+
defaultValue: { type: [String, Number, Object, Array], required: false },
|
|
25
|
+
formField: { type: Boolean, required: false, default: true },
|
|
26
|
+
label: { type: String, required: false },
|
|
27
|
+
selectionBehavior: { type: String, required: false },
|
|
28
|
+
checkbox: { type: Boolean, required: false },
|
|
29
|
+
selectAll: { type: Boolean, required: false },
|
|
30
|
+
ui: { type: Object, required: false },
|
|
31
|
+
searchable: { type: Boolean, required: false },
|
|
32
|
+
searchTerm: { type: String, required: false },
|
|
33
|
+
virtualize: { type: [Boolean, Object], required: false },
|
|
34
|
+
disabled: { type: Boolean, required: false },
|
|
35
|
+
loading: { type: Boolean, required: false },
|
|
36
|
+
invalid: { type: Boolean, required: false },
|
|
37
|
+
size: { type: null, required: false },
|
|
38
|
+
color: { type: null, required: false },
|
|
39
|
+
id: { type: String, required: false },
|
|
40
|
+
name: { type: String, required: false },
|
|
41
|
+
form: { type: String, required: false },
|
|
42
|
+
required: { type: Boolean, required: false }
|
|
43
|
+
});
|
|
44
|
+
const emit = defineEmits(["update:modelValue", "update:searchTerm"]);
|
|
45
|
+
const slots = defineSlots();
|
|
46
|
+
const parentField = useFormField();
|
|
47
|
+
const field = props.formField === false ? void 0 : parentField;
|
|
48
|
+
const icons = useIcons();
|
|
49
|
+
const messages = useMessages();
|
|
50
|
+
const { selection, values, update, reset } = useListSelection(props, (value) => emit("update:modelValue", value));
|
|
51
|
+
const { flatOptions } = useComboboxSelect({
|
|
52
|
+
get items() {
|
|
53
|
+
return props.items;
|
|
54
|
+
},
|
|
55
|
+
get valueKey() {
|
|
56
|
+
return props.valueKey;
|
|
57
|
+
},
|
|
58
|
+
get labelKey() {
|
|
59
|
+
return props.labelKey;
|
|
60
|
+
},
|
|
61
|
+
get modelValue() {
|
|
62
|
+
return selection.value;
|
|
63
|
+
}
|
|
64
|
+
}, (_, value) => update(value));
|
|
65
|
+
const localSearch = ref(props.searchTerm ?? "");
|
|
66
|
+
const search = computed({
|
|
67
|
+
get: () => props.searchTerm ?? localSearch.value,
|
|
68
|
+
set: (value) => {
|
|
69
|
+
localSearch.value = value;
|
|
70
|
+
emit("update:searchTerm", value);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
const { contains } = useFilter({ sensitivity: "base" });
|
|
74
|
+
function matches(option) {
|
|
75
|
+
return !props.searchable || contains(option.label, search.value);
|
|
76
|
+
}
|
|
77
|
+
const visible = computed(() => flatOptions.value.filter(matches));
|
|
78
|
+
const visibleValues = computed(() => visible.value.map((option) => option.value));
|
|
79
|
+
const optionsByValue = computed(() => new Map(flatOptions.value.map((option) => [option.value, option])));
|
|
80
|
+
const indices = computed(() => new Map(flatOptions.value.map((option, index) => [option, index])));
|
|
81
|
+
const groups = computed(() => {
|
|
82
|
+
let offset = 0;
|
|
83
|
+
return props.items.map((entry) => {
|
|
84
|
+
const group = isOptionGroup(entry) ? entry : void 0;
|
|
85
|
+
const count = group ? group.items.length : 1;
|
|
86
|
+
const options = flatOptions.value.slice(offset, offset + count).map((option, index) => ({ option, index: offset + index })).filter(({ option }) => matches(option));
|
|
87
|
+
offset += count;
|
|
88
|
+
return { group, options };
|
|
89
|
+
}).filter((group) => group.options.length);
|
|
90
|
+
});
|
|
91
|
+
const eligible = computed(() => visible.value.filter((option) => !option.disabled).map((option) => option.value));
|
|
92
|
+
const allChecked = computed(() => eligible.value.length > 0 && eligible.value.every((value) => values.value.includes(value)));
|
|
93
|
+
const someChecked = computed(() => eligible.value.some((value) => values.value.includes(value)));
|
|
94
|
+
function selectVisible(checked) {
|
|
95
|
+
update(checked === true ? [...values.value, ...eligible.value.filter((value) => !values.value.includes(value))] : values.value.filter((value) => !eligible.value.includes(value)));
|
|
96
|
+
}
|
|
97
|
+
function onListKeydown(event) {
|
|
98
|
+
if (props.multiple && (event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "a") {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
event.stopPropagation();
|
|
101
|
+
selectVisible(true);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function updateSelection(value) {
|
|
105
|
+
update(Array.isArray(value) ? value.filter((item) => !optionsByValue.value.get(item)?.disabled || values.value.includes(item)) : value);
|
|
106
|
+
}
|
|
107
|
+
const generatedId = useId();
|
|
108
|
+
const id = computed(() => props.id ?? field?.id ?? `selaras-listbox-${generatedId}`);
|
|
109
|
+
const content = ref();
|
|
110
|
+
defineExpose({ focus: () => content.value?.$el.focus() });
|
|
111
|
+
const size = computed(() => props.size ?? field?.size ?? "md");
|
|
112
|
+
const invalid = computed(() => props.invalid || field?.invalid.value || false);
|
|
113
|
+
const color = computed(() => resolveRegisteredColorRole(invalid.value ? "danger" : props.color ?? "primary", "primary"));
|
|
114
|
+
const theme = useComponentTheme("listbox", listboxTheme);
|
|
115
|
+
const ui = computed(() => theme.value({ size: size.value, color: color.value, invalid: invalid.value }));
|
|
116
|
+
const rootProps = useRootProps(() => ui.value.root, () => props.ui?.root, { exclude: (key) => key.startsWith("aria-") });
|
|
117
|
+
const a11yAttrs = useFallthroughAttrs((key) => key.startsWith("aria-"));
|
|
118
|
+
const parts = computed(() => Object.fromEntries(Object.keys(listboxTheme.slots).map((key) => [key, resolveSlot(ui.value[key], props.ui?.[key])])));
|
|
119
|
+
const virtual = computed(() => typeof props.virtualize === "object" ? { estimateSize: 40, overscan: 8, ...props.virtualize } : { estimateSize: 40, overscan: 8 });
|
|
120
|
+
const rowProps = computed(() => ({ parts: parts.value, disabled: props.disabled, checkbox: !!props.multiple && props.checkbox, descriptionKey: props.descriptionKey ?? "description", color: color.value, size: size.value }));
|
|
121
|
+
function sameValue(a, b) {
|
|
122
|
+
return toRaw(a) === toRaw(b);
|
|
123
|
+
}
|
|
124
|
+
function resetForm() {
|
|
125
|
+
reset();
|
|
126
|
+
search.value = "";
|
|
127
|
+
}
|
|
128
|
+
</script>
|
|
129
|
+
|
|
130
|
+
<template>
|
|
131
|
+
<ListboxRoot :model-value="selection ?? (multiple ? [] : null)" :multiple="multiple" :disabled="disabled" :selection-behavior="selectionBehavior ?? 'toggle'" :by="sameValue" highlight-on-hover :data-selaras-color="color" v-bind="rootProps" @update:model-value="updateSelection($event)">
|
|
132
|
+
<div v-if="slots.header || multiple && selectAll || loading" v-bind="parts.header">
|
|
133
|
+
<slot name="header" />
|
|
134
|
+
<Checkbox v-if="multiple && selectAll" :id="`${generatedId}-select-all`" name="" :model-value="allChecked ? true : someChecked ? 'indeterminate' : false" :disabled="disabled || !eligible.length" :color="color" :size="size" :label="messages.selectAll" @update:model-value="selectVisible" />
|
|
135
|
+
<Icon v-if="loading" :name="icons.loading" :aria-label="messages.loading" class="animate-spin" v-bind="parts.icon" />
|
|
136
|
+
</div>
|
|
137
|
+
<div v-if="searchable" v-bind="parts.searchWrapper">
|
|
138
|
+
<Icon :name="icons.search" v-bind="parts.icon" />
|
|
139
|
+
<ListboxFilter v-model="search" :disabled="disabled" :placeholder="messages.search" :aria-label="messages.search" role="combobox" aria-autocomplete="list" :aria-expanded="true" :aria-controls="id" v-bind="parts.searchInput" />
|
|
140
|
+
</div>
|
|
141
|
+
<ListboxContent :id="id" ref="content" :aria-label="label ?? (field?.labelId ? void 0 : messages.select)" :aria-labelledby="field?.labelId" :aria-describedby="field?.describedBy.value" :aria-invalid="invalid || void 0" :aria-busy="loading || void 0" v-bind="{ ...parts.content, ...a11yAttrs }" @keydown.capture="onListKeydown">
|
|
142
|
+
<div v-if="!visible.length" v-bind="parts.empty">
|
|
143
|
+
<slot v-if="!flatOptions.length" name="empty">
|
|
144
|
+
{{ messages.noOptions }}
|
|
145
|
+
</slot>
|
|
146
|
+
<slot v-else name="empty-filter">
|
|
147
|
+
{{ messages.noResultsFound }}
|
|
148
|
+
</slot>
|
|
149
|
+
</div>
|
|
150
|
+
<ListboxVirtualizer v-else-if="virtualize" v-slot="{ option: value, virtualItem }" :options="visibleValues" :text-content="(value2) => optionsByValue.get(value2).label" v-bind="virtual">
|
|
151
|
+
<ListboxOption :option="optionsByValue.get(value)" :index="indices.get(optionsByValue.get(value)) ?? virtualItem.index" :selected="values.includes(value)" v-bind="rowProps">
|
|
152
|
+
<template v-if="slots.item" #default="data">
|
|
153
|
+
<slot name="item" v-bind="data" />
|
|
154
|
+
</template>
|
|
155
|
+
</ListboxOption>
|
|
156
|
+
</ListboxVirtualizer>
|
|
157
|
+
<template v-else>
|
|
158
|
+
<ListboxGroup v-for="(group, groupIndex) in groups" :key="groupIndex" v-bind="parts.group">
|
|
159
|
+
<ListboxGroupLabel v-if="group.group" v-bind="parts.groupLabel">
|
|
160
|
+
<slot name="group" :group="group.group">
|
|
161
|
+
{{ group.group.label }}
|
|
162
|
+
</slot>
|
|
163
|
+
</ListboxGroupLabel>
|
|
164
|
+
<ListboxOption v-for="{ option, index } in group.options" :key="option.key" :option="option" :index="index" :selected="values.includes(option.value)" v-bind="rowProps">
|
|
165
|
+
<template v-if="slots.item" #default="data">
|
|
166
|
+
<slot name="item" v-bind="data" />
|
|
167
|
+
</template>
|
|
168
|
+
</ListboxOption>
|
|
169
|
+
</ListboxGroup>
|
|
170
|
+
</template>
|
|
171
|
+
</ListboxContent>
|
|
172
|
+
<div v-if="slots.footer" v-bind="parts.footer">
|
|
173
|
+
<slot name="footer" />
|
|
174
|
+
</div>
|
|
175
|
+
<ListSelectionForm v-if="formField !== false" :values="values" :name="name ?? field?.name" :form="form" :required="required" :disabled="disabled" @reset="resetForm" />
|
|
176
|
+
</ListboxRoot>
|
|
177
|
+
</template>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ListboxProps } from '../utils/listbox-contracts.js';
|
|
2
|
+
import type { SelectModelValue } from '../utils/select-contracts.js';
|
|
3
|
+
import type { SelectItems } from './combobox-select.js';
|
|
4
|
+
export interface ListboxBaseProps extends Omit<ListboxProps, 'items' | 'valueKey' | 'labelKey' | 'descriptionKey' | 'multiple' | 'modelValue' | 'defaultValue'> {
|
|
5
|
+
items: SelectItems;
|
|
6
|
+
valueKey?: string;
|
|
7
|
+
labelKey?: string;
|
|
8
|
+
descriptionKey?: string;
|
|
9
|
+
multiple?: boolean;
|
|
10
|
+
modelValue?: SelectModelValue | SelectModelValue[];
|
|
11
|
+
defaultValue?: SelectModelValue | SelectModelValue[];
|
|
12
|
+
/** Composite panels must not inherit or submit their parent's form value. */
|
|
13
|
+
formField?: boolean;
|
|
14
|
+
}
|
|
15
|
+
type __VLS_Slots = {
|
|
16
|
+
'item'?: (props: {
|
|
17
|
+
item: SelectModelValue;
|
|
18
|
+
index: number;
|
|
19
|
+
selected: boolean;
|
|
20
|
+
disabled: boolean;
|
|
21
|
+
}) => any;
|
|
22
|
+
'group'?: (props: {
|
|
23
|
+
group: {
|
|
24
|
+
label: string;
|
|
25
|
+
items: readonly SelectModelValue[];
|
|
26
|
+
};
|
|
27
|
+
}) => any;
|
|
28
|
+
'header'?: () => any;
|
|
29
|
+
'footer'?: () => any;
|
|
30
|
+
'empty'?: () => any;
|
|
31
|
+
'empty-filter'?: () => any;
|
|
32
|
+
};
|
|
33
|
+
declare const __VLS_base: import("vue").DefineComponent<ListboxBaseProps, {
|
|
34
|
+
focus: () => void | undefined;
|
|
35
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
36
|
+
"update:modelValue": (value: SelectModelValue | SelectModelValue[] | undefined) => any;
|
|
37
|
+
"update:searchTerm": (value: string) => any;
|
|
38
|
+
}, string, import("vue").PublicProps, Readonly<ListboxBaseProps> & Readonly<{
|
|
39
|
+
"onUpdate:modelValue"?: ((value: SelectModelValue | SelectModelValue[] | undefined) => any) | undefined;
|
|
40
|
+
"onUpdate:searchTerm"?: ((value: string) => any) | undefined;
|
|
41
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
42
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
43
|
+
declare const _default: typeof __VLS_export;
|
|
44
|
+
export default _default;
|
|
45
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
46
|
+
new (): {
|
|
47
|
+
$slots: S;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ListboxThemeSlots } from '../theme/listbox.js';
|
|
2
|
+
import type { ResolvedItemOption } from './combobox-select.js';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
option: ResolvedItemOption;
|
|
5
|
+
index: number;
|
|
6
|
+
selected: boolean;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
checkbox?: boolean;
|
|
9
|
+
descriptionKey: string;
|
|
10
|
+
color: string;
|
|
11
|
+
size?: 'sm' | 'md' | 'lg';
|
|
12
|
+
parts: Record<ListboxThemeSlots, Record<string, unknown>>;
|
|
13
|
+
};
|
|
14
|
+
declare var __VLS_13: {
|
|
15
|
+
item: import("../utils/select-contracts.js").SelectModelValue | import("./combobox-select.js").SelectOption<import("../utils/select-contracts.js").SelectModelValue>;
|
|
16
|
+
index: number;
|
|
17
|
+
selected: boolean;
|
|
18
|
+
disabled: boolean;
|
|
19
|
+
};
|
|
20
|
+
type __VLS_Slots = {} & {
|
|
21
|
+
default?: (props: typeof __VLS_13) => any;
|
|
22
|
+
};
|
|
23
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
24
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
25
|
+
declare const _default: typeof __VLS_export;
|
|
26
|
+
export default _default;
|
|
27
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
28
|
+
new (): {
|
|
29
|
+
$slots: S;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ListboxItem, ListboxItemIndicator } from "reka-ui";
|
|
3
|
+
import { computed } from "vue";
|
|
4
|
+
import Icon from "../components/Icon.vue";
|
|
5
|
+
import { useIcons } from "../composables/use-icons";
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
option: { type: Object, required: true },
|
|
8
|
+
index: { type: Number, required: true },
|
|
9
|
+
selected: { type: Boolean, required: true },
|
|
10
|
+
disabled: { type: Boolean, required: false },
|
|
11
|
+
checkbox: { type: Boolean, required: false },
|
|
12
|
+
descriptionKey: { type: String, required: true },
|
|
13
|
+
color: { type: String, required: true },
|
|
14
|
+
size: { type: String, required: false },
|
|
15
|
+
parts: { type: Object, required: true }
|
|
16
|
+
});
|
|
17
|
+
const icons = useIcons();
|
|
18
|
+
const record = computed(() => typeof props.option.raw === "object" ? props.option.raw : void 0);
|
|
19
|
+
const description = computed(() => record.value?.[props.descriptionKey]);
|
|
20
|
+
const icon = computed(() => typeof record.value?.icon === "string" ? record.value.icon : void 0);
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<ListboxItem :value="option.value" :text-value="option.label" :disabled="disabled || option.disabled" v-bind="parts.item">
|
|
25
|
+
<span v-if="checkbox" aria-hidden="true" :data-state="selected ? 'checked' : 'unchecked'" v-bind="parts.checkbox"><Icon v-if="selected" :name="icons.check" class="size-3" /></span>
|
|
26
|
+
<slot :item="option.raw" :index="index" :selected="selected" :disabled="!!(disabled || option.disabled)">
|
|
27
|
+
<Icon v-if="icon" :name="icon" v-bind="parts.icon" />
|
|
28
|
+
<span v-bind="parts.itemContent">
|
|
29
|
+
<span v-bind="parts.label">{{ option.label }}</span>
|
|
30
|
+
<span v-if="description" v-bind="parts.description">{{ description }}</span>
|
|
31
|
+
</span>
|
|
32
|
+
</slot>
|
|
33
|
+
<ListboxItemIndicator v-if="!checkbox" v-bind="parts.indicator">
|
|
34
|
+
<Icon :name="icons.check" v-bind="parts.icon" />
|
|
35
|
+
</ListboxItemIndicator>
|
|
36
|
+
</ListboxItem>
|
|
37
|
+
</template>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ListboxThemeSlots } from '../theme/listbox.js';
|
|
2
|
+
import type { ResolvedItemOption } from './combobox-select.js';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
option: ResolvedItemOption;
|
|
5
|
+
index: number;
|
|
6
|
+
selected: boolean;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
checkbox?: boolean;
|
|
9
|
+
descriptionKey: string;
|
|
10
|
+
color: string;
|
|
11
|
+
size?: 'sm' | 'md' | 'lg';
|
|
12
|
+
parts: Record<ListboxThemeSlots, Record<string, unknown>>;
|
|
13
|
+
};
|
|
14
|
+
declare var __VLS_13: {
|
|
15
|
+
item: import("../utils/select-contracts.js").SelectModelValue | import("./combobox-select.js").SelectOption<import("../utils/select-contracts.js").SelectModelValue>;
|
|
16
|
+
index: number;
|
|
17
|
+
selected: boolean;
|
|
18
|
+
disabled: boolean;
|
|
19
|
+
};
|
|
20
|
+
type __VLS_Slots = {} & {
|
|
21
|
+
default?: (props: typeof __VLS_13) => any;
|
|
22
|
+
};
|
|
23
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
24
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
25
|
+
declare const _default: typeof __VLS_export;
|
|
26
|
+
export default _default;
|
|
27
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
28
|
+
new (): {
|
|
29
|
+
$slots: S;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { SelectModelValue } from '../utils/select-contracts.js';
|
|
2
|
+
import type { TransferListProps, TransferSide } from '../utils/transfer-list-contracts.js';
|
|
3
|
+
import type { SelectItems } from './combobox-select.js';
|
|
4
|
+
export interface TransferListBaseProps extends Omit<TransferListProps, 'items' | 'valueKey' | 'labelKey' | 'descriptionKey'> {
|
|
5
|
+
items: SelectItems;
|
|
6
|
+
valueKey?: string;
|
|
7
|
+
labelKey?: string;
|
|
8
|
+
descriptionKey?: string;
|
|
9
|
+
}
|
|
10
|
+
declare var __VLS_2: "source-header" | "target-header", __VLS_3: {
|
|
11
|
+
count: number;
|
|
12
|
+
checkedCount: number;
|
|
13
|
+
}, __VLS_15: {
|
|
14
|
+
side: TransferSide;
|
|
15
|
+
item: SelectModelValue;
|
|
16
|
+
index: number;
|
|
17
|
+
selected: boolean;
|
|
18
|
+
disabled: boolean;
|
|
19
|
+
}, __VLS_18: {
|
|
20
|
+
side: TransferSide;
|
|
21
|
+
}, __VLS_21: {
|
|
22
|
+
side: TransferSide;
|
|
23
|
+
}, __VLS_25: "source-footer" | "target-footer", __VLS_26: {};
|
|
24
|
+
type __VLS_Slots = {} & {
|
|
25
|
+
[K in NonNullable<typeof __VLS_2>]?: (props: typeof __VLS_3) => any;
|
|
26
|
+
} & {
|
|
27
|
+
[K in NonNullable<typeof __VLS_25>]?: (props: typeof __VLS_26) => any;
|
|
28
|
+
} & {
|
|
29
|
+
item?: (props: typeof __VLS_15) => any;
|
|
30
|
+
} & {
|
|
31
|
+
empty?: (props: typeof __VLS_18) => any;
|
|
32
|
+
} & {
|
|
33
|
+
'empty-filter'?: (props: typeof __VLS_21) => any;
|
|
34
|
+
};
|
|
35
|
+
declare const __VLS_base: import("vue").DefineComponent<TransferListBaseProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
36
|
+
"update:modelValue": (value: SelectModelValue[]) => any;
|
|
37
|
+
"update:checked": (value: SelectModelValue[]) => any;
|
|
38
|
+
"update:sourceSearchTerm": (value: string) => any;
|
|
39
|
+
"update:targetSearchTerm": (value: string) => any;
|
|
40
|
+
transfer: (payload: {
|
|
41
|
+
direction: "to-target" | "to-source";
|
|
42
|
+
items: SelectModelValue[];
|
|
43
|
+
values: SelectModelValue[];
|
|
44
|
+
}) => any;
|
|
45
|
+
}, string, import("vue").PublicProps, Readonly<TransferListBaseProps> & Readonly<{
|
|
46
|
+
"onUpdate:modelValue"?: ((value: SelectModelValue[]) => any) | undefined;
|
|
47
|
+
"onUpdate:checked"?: ((value: SelectModelValue[]) => any) | undefined;
|
|
48
|
+
"onUpdate:sourceSearchTerm"?: ((value: string) => any) | undefined;
|
|
49
|
+
"onUpdate:targetSearchTerm"?: ((value: string) => any) | undefined;
|
|
50
|
+
onTransfer?: ((payload: {
|
|
51
|
+
direction: "to-target" | "to-source";
|
|
52
|
+
items: SelectModelValue[];
|
|
53
|
+
values: SelectModelValue[];
|
|
54
|
+
}) => any) | undefined;
|
|
55
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
56
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
57
|
+
declare const _default: typeof __VLS_export;
|
|
58
|
+
export default _default;
|
|
59
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
60
|
+
new (): {
|
|
61
|
+
$slots: S;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, getCurrentInstance, nextTick, ref, useSlots } from "vue";
|
|
3
|
+
import Button from "../components/Button.vue";
|
|
4
|
+
import { useFormField } from "../composables/use-form-field";
|
|
5
|
+
import { useIcons } from "../composables/use-icons";
|
|
6
|
+
import { useMessages } from "../composables/use-messages";
|
|
7
|
+
import { transferListTheme } from "../theme/transfer-list";
|
|
8
|
+
import { resolveSlot, useComponentTheme, useRootProps } from "../utils/ui";
|
|
9
|
+
import { isOptionGroup, useComboboxSelect } from "./combobox-select";
|
|
10
|
+
import { assertUniqueSelection, useListSelection } from "./list-selection";
|
|
11
|
+
import ListboxBase from "./ListboxBase.vue";
|
|
12
|
+
import ListSelectionForm from "./ListSelectionForm.vue";
|
|
13
|
+
defineOptions({ inheritAttrs: false });
|
|
14
|
+
const props = defineProps({
|
|
15
|
+
items: { type: null, required: true },
|
|
16
|
+
valueKey: { type: String, required: false },
|
|
17
|
+
labelKey: { type: String, required: false },
|
|
18
|
+
descriptionKey: { type: String, required: false },
|
|
19
|
+
checked: { type: null, required: false },
|
|
20
|
+
sourceLabel: { type: String, required: false },
|
|
21
|
+
targetLabel: { type: String, required: false },
|
|
22
|
+
sourceSearchTerm: { type: String, required: false },
|
|
23
|
+
targetSearchTerm: { type: String, required: false },
|
|
24
|
+
showSelectAll: { type: Boolean, required: false },
|
|
25
|
+
layout: { type: String, required: false },
|
|
26
|
+
ui: { type: Object, required: false },
|
|
27
|
+
modelValue: { type: null, required: false },
|
|
28
|
+
defaultValue: { type: null, required: false },
|
|
29
|
+
searchable: { type: Boolean, required: false },
|
|
30
|
+
virtualize: { type: [Boolean, Object], required: false },
|
|
31
|
+
disabled: { type: Boolean, required: false },
|
|
32
|
+
loading: { type: Boolean, required: false },
|
|
33
|
+
invalid: { type: Boolean, required: false },
|
|
34
|
+
size: { type: null, required: false },
|
|
35
|
+
color: { type: null, required: false },
|
|
36
|
+
id: { type: String, required: false },
|
|
37
|
+
name: { type: String, required: false },
|
|
38
|
+
form: { type: String, required: false },
|
|
39
|
+
required: { type: Boolean, required: false }
|
|
40
|
+
});
|
|
41
|
+
const emit = defineEmits(["update:modelValue", "update:checked", "update:sourceSearchTerm", "update:targetSearchTerm", "transfer"]);
|
|
42
|
+
const slots = useSlots();
|
|
43
|
+
const field = useFormField();
|
|
44
|
+
const messages = useMessages();
|
|
45
|
+
const icons = useIcons();
|
|
46
|
+
const { values, update, reset } = useListSelection({
|
|
47
|
+
get modelValue() {
|
|
48
|
+
return props.modelValue;
|
|
49
|
+
},
|
|
50
|
+
get defaultValue() {
|
|
51
|
+
return props.defaultValue;
|
|
52
|
+
},
|
|
53
|
+
get disabled() {
|
|
54
|
+
return props.disabled;
|
|
55
|
+
},
|
|
56
|
+
multiple: true
|
|
57
|
+
}, (value) => emit("update:modelValue", value));
|
|
58
|
+
const { flatOptions, toOption } = useComboboxSelect(props, (_, value) => update(value));
|
|
59
|
+
const instance = getCurrentInstance();
|
|
60
|
+
const localChecked = ref([]);
|
|
61
|
+
const checked = computed(() => {
|
|
62
|
+
const value = Object.hasOwn(instance.vnode.props ?? {}, "checked") ? props.checked ?? [] : localChecked.value;
|
|
63
|
+
if (!Array.isArray(value))
|
|
64
|
+
throw new TypeError("[selaras] TransferList checked must be an array.");
|
|
65
|
+
assertUniqueSelection(value);
|
|
66
|
+
return value;
|
|
67
|
+
});
|
|
68
|
+
function setChecked(value) {
|
|
69
|
+
if (props.disabled)
|
|
70
|
+
return;
|
|
71
|
+
if (!Object.hasOwn(instance.vnode.props ?? {}, "checked"))
|
|
72
|
+
localChecked.value = value;
|
|
73
|
+
emit("update:checked", value);
|
|
74
|
+
}
|
|
75
|
+
const sides = ["source", "target"];
|
|
76
|
+
const panelRefs = {};
|
|
77
|
+
const localSearch = ref({ source: "", target: "" });
|
|
78
|
+
function query(side) {
|
|
79
|
+
return (side === "source" ? props.sourceSearchTerm : props.targetSearchTerm) ?? localSearch.value[side];
|
|
80
|
+
}
|
|
81
|
+
function setQuery(side, value) {
|
|
82
|
+
localSearch.value[side] = value;
|
|
83
|
+
if (side === "source")
|
|
84
|
+
emit("update:sourceSearchTerm", value);
|
|
85
|
+
else
|
|
86
|
+
emit("update:targetSearchTerm", value);
|
|
87
|
+
}
|
|
88
|
+
function onSide(value, side) {
|
|
89
|
+
return values.value.includes(value) === (side === "target");
|
|
90
|
+
}
|
|
91
|
+
function panelItems(side) {
|
|
92
|
+
return props.items.flatMap((entry) => {
|
|
93
|
+
if (isOptionGroup(entry)) {
|
|
94
|
+
const items = entry.items.filter((item) => onSide(toOption(item).value, side));
|
|
95
|
+
return items.length ? [{ ...entry, items }] : [];
|
|
96
|
+
}
|
|
97
|
+
return onSide(toOption(entry).value, side) ? [entry] : [];
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const panels = computed(() => Object.fromEntries(sides.map((side) => [side, {
|
|
101
|
+
items: panelItems(side),
|
|
102
|
+
options: flatOptions.value.filter((option) => onSide(option.value, side))
|
|
103
|
+
}])));
|
|
104
|
+
function panelChecked(side) {
|
|
105
|
+
return checked.value.filter((value) => onSide(value, side));
|
|
106
|
+
}
|
|
107
|
+
function updatePanelChecked(side, next) {
|
|
108
|
+
setChecked([...checked.value.filter((value) => !onSide(value, side)), ...Array.isArray(next) ? next : []]);
|
|
109
|
+
}
|
|
110
|
+
function movable(side) {
|
|
111
|
+
return panels.value[side].options.filter((option) => !option.disabled && checked.value.includes(option.value));
|
|
112
|
+
}
|
|
113
|
+
const announcement = ref("");
|
|
114
|
+
const sourceLabel = computed(() => props.sourceLabel ?? messages.value.transferSource);
|
|
115
|
+
const targetLabel = computed(() => props.targetLabel ?? messages.value.transferTarget);
|
|
116
|
+
function transfer(side) {
|
|
117
|
+
if (props.disabled)
|
|
118
|
+
return;
|
|
119
|
+
const options = movable(side);
|
|
120
|
+
if (!options.length)
|
|
121
|
+
return;
|
|
122
|
+
const moved = options.map((option) => option.value);
|
|
123
|
+
update(side === "source" ? [...values.value, ...moved] : values.value.filter((value) => !moved.includes(value)));
|
|
124
|
+
setChecked(checked.value.filter((value) => !moved.includes(value)));
|
|
125
|
+
emit("transfer", { direction: side === "source" ? "to-target" : "to-source", items: options.map((option) => option.raw), values: moved });
|
|
126
|
+
announcement.value = "";
|
|
127
|
+
void nextTick(() => {
|
|
128
|
+
const destination = side === "source" ? "target" : "source";
|
|
129
|
+
if (moved.every((value) => onSide(value, destination))) {
|
|
130
|
+
announcement.value = messages.value.transferItems(moved.length, destination === "target" ? targetLabel.value : sourceLabel.value);
|
|
131
|
+
panelRefs[destination]?.focus();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const theme = useComponentTheme("transferList", transferListTheme);
|
|
136
|
+
const ui = computed(() => theme.value({ layout: props.layout }));
|
|
137
|
+
const rootProps = useRootProps(() => ui.value.root, () => props.ui?.root);
|
|
138
|
+
const parts = computed(() => Object.fromEntries(Object.keys(transferListTheme.slots).map((key) => [key, resolveSlot(ui.value[key], props.ui?.[key])])));
|
|
139
|
+
function resetForm() {
|
|
140
|
+
reset();
|
|
141
|
+
localChecked.value = [];
|
|
142
|
+
emit("update:checked", []);
|
|
143
|
+
setQuery("source", "");
|
|
144
|
+
setQuery("target", "");
|
|
145
|
+
announcement.value = "";
|
|
146
|
+
}
|
|
147
|
+
</script>
|
|
148
|
+
|
|
149
|
+
<template>
|
|
150
|
+
<div :id="id ?? field?.id" :aria-invalid="invalid || field?.invalid.value || void 0" :aria-describedby="field?.describedBy.value" v-bind="rootProps">
|
|
151
|
+
<template v-for="side in sides" :key="side">
|
|
152
|
+
<div v-bind="parts.panel">
|
|
153
|
+
<div v-bind="parts.header">
|
|
154
|
+
<slot :name="`${side}-header`" :count="panels[side].options.length" :checked-count="panelChecked(side).length">
|
|
155
|
+
<span>{{ side === "source" ? sourceLabel : targetLabel }}</span>
|
|
156
|
+
<span v-bind="parts.count">{{ messages.listSelectionCount(panelChecked(side).length, panels[side].options.length) }}</span>
|
|
157
|
+
</slot>
|
|
158
|
+
</div>
|
|
159
|
+
<ListboxBase
|
|
160
|
+
:ref="(element) => {
|
|
161
|
+
panelRefs[side] = element;
|
|
162
|
+
}"
|
|
163
|
+
:items="panels[side].items"
|
|
164
|
+
:model-value="panelChecked(side)"
|
|
165
|
+
:value-key="valueKey"
|
|
166
|
+
:label-key="labelKey"
|
|
167
|
+
:description-key="descriptionKey"
|
|
168
|
+
:label="side === 'source' ? sourceLabel : targetLabel"
|
|
169
|
+
:search-term="query(side)"
|
|
170
|
+
:select-all="showSelectAll ?? true"
|
|
171
|
+
:searchable="searchable"
|
|
172
|
+
:virtualize="virtualize"
|
|
173
|
+
:disabled="disabled"
|
|
174
|
+
:loading="loading"
|
|
175
|
+
:invalid="invalid || field?.invalid.value"
|
|
176
|
+
:size="size ?? field?.size"
|
|
177
|
+
:color="color"
|
|
178
|
+
:form-field="false"
|
|
179
|
+
multiple checkbox
|
|
180
|
+
v-bind="parts.listbox"
|
|
181
|
+
@update:model-value="updatePanelChecked(side, $event)"
|
|
182
|
+
@update:search-term="setQuery(side, $event)"
|
|
183
|
+
>
|
|
184
|
+
<template v-if="slots.item" #item="data">
|
|
185
|
+
<slot name="item" v-bind="data" :side="side" />
|
|
186
|
+
</template>
|
|
187
|
+
<template v-if="slots.empty" #empty>
|
|
188
|
+
<slot name="empty" :side="side" />
|
|
189
|
+
</template>
|
|
190
|
+
<template v-if="slots['empty-filter']" #empty-filter>
|
|
191
|
+
<slot name="empty-filter" :side="side" />
|
|
192
|
+
</template>
|
|
193
|
+
<template v-if="slots[`${side}-footer`]" #footer>
|
|
194
|
+
<slot :name="`${side}-footer`" />
|
|
195
|
+
</template>
|
|
196
|
+
</ListboxBase>
|
|
197
|
+
</div>
|
|
198
|
+
<div v-if="side === 'source'" v-bind="parts.actions">
|
|
199
|
+
<Button :icon="icons.chevronRight" :aria-label="messages.moveToTarget" :title="messages.moveToTarget" :disabled="disabled || !movable('source').length" :size="size ?? field?.size" :color="color" variant="outline" square v-bind="parts.action" @click="transfer('source')" />
|
|
200
|
+
<Button :icon="icons.chevronLeft" :aria-label="messages.moveToSource" :title="messages.moveToSource" :disabled="disabled || !movable('target').length" :size="size ?? field?.size" :color="color" variant="outline" square v-bind="parts.action" @click="transfer('target')" />
|
|
201
|
+
</div>
|
|
202
|
+
</template>
|
|
203
|
+
<span role="status" aria-live="polite" aria-atomic="true" v-bind="parts.announcement">{{ announcement }}</span>
|
|
204
|
+
<ListSelectionForm :values="values" :name="name ?? field?.name" :form="form" :required="required" :disabled="disabled" @reset="resetForm" />
|
|
205
|
+
</div>
|
|
206
|
+
</template>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { SelectModelValue } from '../utils/select-contracts.js';
|
|
2
|
+
import type { TransferListProps, TransferSide } from '../utils/transfer-list-contracts.js';
|
|
3
|
+
import type { SelectItems } from './combobox-select.js';
|
|
4
|
+
export interface TransferListBaseProps extends Omit<TransferListProps, 'items' | 'valueKey' | 'labelKey' | 'descriptionKey'> {
|
|
5
|
+
items: SelectItems;
|
|
6
|
+
valueKey?: string;
|
|
7
|
+
labelKey?: string;
|
|
8
|
+
descriptionKey?: string;
|
|
9
|
+
}
|
|
10
|
+
declare var __VLS_2: "source-header" | "target-header", __VLS_3: {
|
|
11
|
+
count: number;
|
|
12
|
+
checkedCount: number;
|
|
13
|
+
}, __VLS_15: {
|
|
14
|
+
side: TransferSide;
|
|
15
|
+
item: SelectModelValue;
|
|
16
|
+
index: number;
|
|
17
|
+
selected: boolean;
|
|
18
|
+
disabled: boolean;
|
|
19
|
+
}, __VLS_18: {
|
|
20
|
+
side: TransferSide;
|
|
21
|
+
}, __VLS_21: {
|
|
22
|
+
side: TransferSide;
|
|
23
|
+
}, __VLS_25: "source-footer" | "target-footer", __VLS_26: {};
|
|
24
|
+
type __VLS_Slots = {} & {
|
|
25
|
+
[K in NonNullable<typeof __VLS_2>]?: (props: typeof __VLS_3) => any;
|
|
26
|
+
} & {
|
|
27
|
+
[K in NonNullable<typeof __VLS_25>]?: (props: typeof __VLS_26) => any;
|
|
28
|
+
} & {
|
|
29
|
+
item?: (props: typeof __VLS_15) => any;
|
|
30
|
+
} & {
|
|
31
|
+
empty?: (props: typeof __VLS_18) => any;
|
|
32
|
+
} & {
|
|
33
|
+
'empty-filter'?: (props: typeof __VLS_21) => any;
|
|
34
|
+
};
|
|
35
|
+
declare const __VLS_base: import("vue").DefineComponent<TransferListBaseProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
36
|
+
"update:modelValue": (value: SelectModelValue[]) => any;
|
|
37
|
+
"update:checked": (value: SelectModelValue[]) => any;
|
|
38
|
+
"update:sourceSearchTerm": (value: string) => any;
|
|
39
|
+
"update:targetSearchTerm": (value: string) => any;
|
|
40
|
+
transfer: (payload: {
|
|
41
|
+
direction: "to-target" | "to-source";
|
|
42
|
+
items: SelectModelValue[];
|
|
43
|
+
values: SelectModelValue[];
|
|
44
|
+
}) => any;
|
|
45
|
+
}, string, import("vue").PublicProps, Readonly<TransferListBaseProps> & Readonly<{
|
|
46
|
+
"onUpdate:modelValue"?: ((value: SelectModelValue[]) => any) | undefined;
|
|
47
|
+
"onUpdate:checked"?: ((value: SelectModelValue[]) => any) | undefined;
|
|
48
|
+
"onUpdate:sourceSearchTerm"?: ((value: string) => any) | undefined;
|
|
49
|
+
"onUpdate:targetSearchTerm"?: ((value: string) => any) | undefined;
|
|
50
|
+
onTransfer?: ((payload: {
|
|
51
|
+
direction: "to-target" | "to-source";
|
|
52
|
+
items: SelectModelValue[];
|
|
53
|
+
values: SelectModelValue[];
|
|
54
|
+
}) => any) | undefined;
|
|
55
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
56
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
57
|
+
declare const _default: typeof __VLS_export;
|
|
58
|
+
export default _default;
|
|
59
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
60
|
+
new (): {
|
|
61
|
+
$slots: S;
|
|
62
|
+
};
|
|
63
|
+
};
|