@nuxt/ui 4.6.1 → 4.7.1
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 +7 -4
- package/cli/package.json +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +3 -2
- package/dist/runtime/components/Accordion.d.vue.ts +6 -1
- package/dist/runtime/components/Accordion.vue +1 -1
- package/dist/runtime/components/Accordion.vue.d.ts +6 -1
- package/dist/runtime/components/AuthForm.d.vue.ts +22 -29
- package/dist/runtime/components/AuthForm.vue +8 -6
- package/dist/runtime/components/AuthForm.vue.d.ts +22 -29
- package/dist/runtime/components/Button.vue +1 -0
- package/dist/runtime/components/Calendar.vue +4 -4
- package/dist/runtime/components/Card.d.vue.ts +4 -0
- package/dist/runtime/components/Card.vue +16 -2
- package/dist/runtime/components/Card.vue.d.ts +4 -0
- package/dist/runtime/components/ChatMessage.d.vue.ts +27 -17
- package/dist/runtime/components/ChatMessage.vue +10 -16
- package/dist/runtime/components/ChatMessage.vue.d.ts +27 -17
- package/dist/runtime/components/ChatMessages.d.vue.ts +27 -21
- package/dist/runtime/components/ChatMessages.vue +22 -23
- package/dist/runtime/components/ChatMessages.vue.d.ts +27 -21
- package/dist/runtime/components/ChatPromptSubmit.vue +3 -1
- package/dist/runtime/components/Checkbox.vue +5 -3
- package/dist/runtime/components/CommandPalette.d.vue.ts +18 -3
- package/dist/runtime/components/CommandPalette.vue +11 -7
- package/dist/runtime/components/CommandPalette.vue.d.ts +18 -3
- package/dist/runtime/components/ContextMenuContent.vue +68 -65
- package/dist/runtime/components/DashboardSearch.d.vue.ts +8 -37
- package/dist/runtime/components/DashboardSearch.vue +11 -10
- package/dist/runtime/components/DashboardSearch.vue.d.ts +8 -37
- package/dist/runtime/components/DashboardSidebar.vue +1 -5
- package/dist/runtime/components/Drawer.vue +44 -41
- package/dist/runtime/components/DropdownMenuContent.vue +90 -87
- package/dist/runtime/components/EditorEmojiMenu.d.vue.ts +1 -1
- package/dist/runtime/components/EditorEmojiMenu.vue +2 -0
- package/dist/runtime/components/EditorEmojiMenu.vue.d.ts +1 -1
- package/dist/runtime/components/EditorMentionMenu.d.vue.ts +1 -1
- package/dist/runtime/components/EditorMentionMenu.vue +2 -0
- package/dist/runtime/components/EditorMentionMenu.vue.d.ts +1 -1
- package/dist/runtime/components/EditorSuggestionMenu.d.vue.ts +1 -1
- package/dist/runtime/components/EditorSuggestionMenu.vue +2 -0
- package/dist/runtime/components/EditorSuggestionMenu.vue.d.ts +1 -1
- package/dist/runtime/components/FileUpload.vue +7 -5
- package/dist/runtime/components/FormField.d.vue.ts +1 -1
- package/dist/runtime/components/FormField.vue.d.ts +1 -1
- package/dist/runtime/components/Header.vue +1 -5
- package/dist/runtime/components/Input.vue +2 -2
- package/dist/runtime/components/InputDate.vue +6 -6
- package/dist/runtime/components/InputMenu.d.vue.ts +3 -3
- package/dist/runtime/components/InputMenu.vue +40 -38
- package/dist/runtime/components/InputMenu.vue.d.ts +3 -3
- package/dist/runtime/components/InputNumber.vue +2 -2
- package/dist/runtime/components/InputTags.vue +2 -2
- package/dist/runtime/components/InputTime.vue +2 -2
- package/dist/runtime/components/Link.d.vue.ts +10 -1
- package/dist/runtime/components/Link.vue +74 -9
- package/dist/runtime/components/Link.vue.d.ts +10 -1
- package/dist/runtime/components/Listbox.d.vue.ts +228 -0
- package/dist/runtime/components/Listbox.vue +242 -0
- package/dist/runtime/components/Listbox.vue.d.ts +228 -0
- package/dist/runtime/components/Modal.vue +13 -10
- package/dist/runtime/components/Popover.vue +7 -4
- package/dist/runtime/components/PricingTable.vue +60 -58
- package/dist/runtime/components/RadioGroup.vue +5 -3
- package/dist/runtime/components/Select.d.vue.ts +1 -1
- package/dist/runtime/components/Select.vue +84 -73
- package/dist/runtime/components/Select.vue.d.ts +1 -1
- package/dist/runtime/components/SelectMenu.d.vue.ts +3 -3
- package/dist/runtime/components/SelectMenu.vue +54 -52
- package/dist/runtime/components/SelectMenu.vue.d.ts +3 -3
- package/dist/runtime/components/Sidebar.vue +1 -4
- package/dist/runtime/components/Slideover.vue +67 -64
- package/dist/runtime/components/Table.d.vue.ts +1 -2
- package/dist/runtime/components/Table.vue +17 -29
- package/dist/runtime/components/Table.vue.d.ts +1 -2
- package/dist/runtime/components/Tabs.d.vue.ts +5 -1
- package/dist/runtime/components/Tabs.vue +2 -2
- package/dist/runtime/components/Tabs.vue.d.ts +5 -1
- package/dist/runtime/components/Textarea.d.vue.ts +1 -0
- package/dist/runtime/components/Textarea.vue +2 -1
- package/dist/runtime/components/Textarea.vue.d.ts +1 -0
- package/dist/runtime/components/Tooltip.vue +12 -9
- package/dist/runtime/components/content/ContentSearch.d.vue.ts +19 -35
- package/dist/runtime/components/content/ContentSearch.vue +10 -9
- package/dist/runtime/components/content/ContentSearch.vue.d.ts +19 -35
- package/dist/runtime/components/content/ContentToc.vue +19 -9
- package/dist/runtime/components/prose/Img.vue +7 -1
- package/dist/runtime/components/prose/Pre.vue +8 -3
- package/dist/runtime/components/prose/Prompt.d.vue.ts +32 -0
- package/dist/runtime/components/prose/Prompt.vue +88 -0
- package/dist/runtime/components/prose/Prompt.vue.d.ts +32 -0
- package/dist/runtime/composables/index.d.ts +1 -0
- package/dist/runtime/composables/index.js +1 -0
- package/dist/runtime/composables/useContentSearch.js +19 -5
- package/dist/runtime/composables/useEditorMenu.d.ts +5 -0
- package/dist/runtime/composables/useEditorMenu.js +3 -1
- package/dist/runtime/composables/useFieldGroup.d.ts +3 -0
- package/dist/runtime/composables/useFieldGroup.js +11 -1
- package/dist/runtime/composables/useResolvedVariants.d.ts +21 -0
- package/dist/runtime/composables/useResolvedVariants.js +14 -0
- package/dist/runtime/locale/ar.js +9 -0
- package/dist/runtime/locale/az.js +9 -0
- package/dist/runtime/locale/be.js +9 -0
- package/dist/runtime/locale/bg.js +9 -0
- package/dist/runtime/locale/bn.js +9 -0
- package/dist/runtime/locale/ca.js +9 -0
- package/dist/runtime/locale/ckb.js +10 -1
- package/dist/runtime/locale/cs.js +9 -0
- package/dist/runtime/locale/da.js +9 -0
- package/dist/runtime/locale/de.js +9 -0
- package/dist/runtime/locale/de_ch.js +9 -0
- package/dist/runtime/locale/el.js +9 -0
- package/dist/runtime/locale/en.js +10 -1
- package/dist/runtime/locale/es.js +9 -0
- package/dist/runtime/locale/et.js +9 -0
- package/dist/runtime/locale/eu.js +11 -2
- package/dist/runtime/locale/fa_ir.js +9 -0
- package/dist/runtime/locale/fi.js +9 -0
- package/dist/runtime/locale/fr.js +9 -0
- package/dist/runtime/locale/gl.js +9 -0
- package/dist/runtime/locale/he.js +9 -0
- package/dist/runtime/locale/hi.js +9 -0
- package/dist/runtime/locale/hr.js +9 -0
- package/dist/runtime/locale/hu.js +9 -0
- package/dist/runtime/locale/hy.js +9 -0
- package/dist/runtime/locale/id.js +9 -0
- package/dist/runtime/locale/is.js +9 -0
- package/dist/runtime/locale/it.js +9 -0
- package/dist/runtime/locale/ja.js +9 -0
- package/dist/runtime/locale/ka.js +9 -0
- package/dist/runtime/locale/kk.js +9 -0
- package/dist/runtime/locale/km.js +9 -0
- package/dist/runtime/locale/ko.js +9 -0
- package/dist/runtime/locale/ky.js +9 -0
- package/dist/runtime/locale/lb.js +11 -2
- package/dist/runtime/locale/lo.js +15 -6
- package/dist/runtime/locale/lt.js +9 -0
- package/dist/runtime/locale/mn.js +9 -0
- package/dist/runtime/locale/ms.js +9 -0
- package/dist/runtime/locale/nb_no.js +9 -0
- package/dist/runtime/locale/nl.js +9 -0
- package/dist/runtime/locale/pl.js +9 -0
- package/dist/runtime/locale/pt.js +9 -0
- package/dist/runtime/locale/pt_br.js +9 -0
- package/dist/runtime/locale/ro.js +9 -0
- package/dist/runtime/locale/ru.js +9 -0
- package/dist/runtime/locale/sk.js +9 -0
- package/dist/runtime/locale/sl.js +9 -0
- package/dist/runtime/locale/sq.js +9 -0
- package/dist/runtime/locale/sv.js +9 -0
- package/dist/runtime/locale/th.js +9 -0
- package/dist/runtime/locale/tj.js +9 -0
- package/dist/runtime/locale/tr.js +9 -0
- package/dist/runtime/locale/ug_cn.js +9 -0
- package/dist/runtime/locale/uk.js +9 -0
- package/dist/runtime/locale/ur.js +9 -0
- package/dist/runtime/locale/uz.js +9 -0
- package/dist/runtime/locale/vi.js +9 -0
- package/dist/runtime/locale/zh_cn.js +9 -0
- package/dist/runtime/locale/zh_tw.js +9 -0
- package/dist/runtime/plugins/colors.js +1 -1
- package/dist/runtime/types/index.d.ts +1 -0
- package/dist/runtime/types/index.js +1 -0
- package/dist/runtime/types/locale.d.ts +9 -0
- package/dist/runtime/types/prose.d.ts +1 -0
- package/dist/runtime/types/prose.js +1 -0
- package/dist/runtime/utils/link.d.ts +1 -1
- package/dist/runtime/utils/link.js +1 -0
- package/dist/runtime/vue/overrides/inertia/Link.vue +3 -3
- package/dist/runtime/vue/overrides/none/Link.vue +3 -2
- package/dist/runtime/vue/overrides/vue-router/Link.vue +27 -31
- package/dist/shared/{ui.DJUbBpE2.mjs → ui.C6mn7iap.mjs} +189 -37
- package/dist/unplugin.mjs +1 -1
- package/dist/vite.mjs +1 -1
- package/package.json +49 -40
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { computed, toRaw, toRef } from "vue";
|
|
3
|
+
import theme from "#build/ui/listbox";
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<script setup>
|
|
7
|
+
import { ListboxRoot, ListboxContent, ListboxGroup, ListboxGroupLabel, ListboxVirtualizer, ListboxItem as RekaListboxItem, ListboxItemIndicator, ListboxFilter, useForwardPropsEmits } from "reka-ui";
|
|
8
|
+
import { createReusableTemplate, reactivePick } from "@vueuse/core";
|
|
9
|
+
import { defu } from "defu";
|
|
10
|
+
import { useAppConfig } from "#imports";
|
|
11
|
+
import { useComponentUI } from "../composables/useComponentUI";
|
|
12
|
+
import { useFilter } from "../composables/useFilter";
|
|
13
|
+
import { useFormField } from "../composables/useFormField";
|
|
14
|
+
import { useLocale } from "../composables/useLocale";
|
|
15
|
+
import { get, isArrayOfArray, looseToNumber } from "../utils";
|
|
16
|
+
import { getEstimateSize } from "../utils/virtualizer";
|
|
17
|
+
import { tv } from "../utils/tv";
|
|
18
|
+
import UIcon from "./Icon.vue";
|
|
19
|
+
import UAvatar from "./Avatar.vue";
|
|
20
|
+
import UChip from "./Chip.vue";
|
|
21
|
+
import UInput from "./Input.vue";
|
|
22
|
+
defineOptions({ inheritAttrs: false });
|
|
23
|
+
const props = defineProps({
|
|
24
|
+
id: { type: String, required: false },
|
|
25
|
+
as: { type: null, required: false },
|
|
26
|
+
size: { type: null, required: false },
|
|
27
|
+
items: { type: null, required: false },
|
|
28
|
+
modelValue: { type: null, required: false },
|
|
29
|
+
modelModifiers: { type: null, required: false },
|
|
30
|
+
defaultValue: { type: null, required: false },
|
|
31
|
+
multiple: { type: Boolean, required: false },
|
|
32
|
+
valueKey: { type: null, required: false },
|
|
33
|
+
labelKey: { type: null, required: false, default: "label" },
|
|
34
|
+
descriptionKey: { type: null, required: false, default: "description" },
|
|
35
|
+
loading: { type: Boolean, required: false },
|
|
36
|
+
loadingIcon: { type: null, required: false },
|
|
37
|
+
filter: { type: [Boolean, Object], required: false, default: false },
|
|
38
|
+
filterFields: { type: Array, required: false },
|
|
39
|
+
ignoreFilter: { type: Boolean, required: false },
|
|
40
|
+
selectedIcon: { type: null, required: false },
|
|
41
|
+
virtualize: { type: [Boolean, Object], required: false, default: false },
|
|
42
|
+
highlight: { type: Boolean, required: false },
|
|
43
|
+
autofocus: { type: Boolean, required: false },
|
|
44
|
+
autofocusDelay: { type: Number, required: false, default: 0 },
|
|
45
|
+
class: { type: null, required: false },
|
|
46
|
+
ui: { type: Object, required: false },
|
|
47
|
+
by: { type: [String, Function], required: false },
|
|
48
|
+
disabled: { type: Boolean, required: false },
|
|
49
|
+
highlightOnHover: { type: Boolean, required: false, default: true },
|
|
50
|
+
name: { type: String, required: false },
|
|
51
|
+
orientation: { type: String, required: false },
|
|
52
|
+
required: { type: Boolean, required: false },
|
|
53
|
+
selectionBehavior: { type: String, required: false }
|
|
54
|
+
});
|
|
55
|
+
const emits = defineEmits(["entryFocus", "highlight", "leave", "change", "update:modelValue"]);
|
|
56
|
+
const slots = defineSlots();
|
|
57
|
+
const searchTerm = defineModel("searchTerm", { type: String, ...{ default: "" } });
|
|
58
|
+
const { t } = useLocale();
|
|
59
|
+
const appConfig = useAppConfig();
|
|
60
|
+
const uiProp = useComponentUI("listbox", props);
|
|
61
|
+
const { filterGroups } = useFilter();
|
|
62
|
+
const rootProps = useForwardPropsEmits(reactivePick(props, "as", "modelValue", "defaultValue", "multiple", "selectionBehavior", "highlightOnHover", "by", "orientation", "required"), emits);
|
|
63
|
+
const virtualizerProps = toRef(() => {
|
|
64
|
+
if (!props.virtualize) return false;
|
|
65
|
+
return defu(typeof props.virtualize === "boolean" ? {} : props.virtualize, {
|
|
66
|
+
estimateSize: getEstimateSize(filteredItems.value, size.value || "md", props.descriptionKey, !!slots["item-description"])
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
const inputProps = toRef(() => defu(typeof props.filter === "object" ? props.filter : {}, { placeholder: t("listbox.search"), variant: "none" }));
|
|
70
|
+
const { emitFormChange, emitFormInput, name, size, color, id, highlight, disabled, ariaAttrs } = useFormField(props, { bind: false });
|
|
71
|
+
const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate({
|
|
72
|
+
props: {
|
|
73
|
+
item: {
|
|
74
|
+
type: Object,
|
|
75
|
+
required: true
|
|
76
|
+
},
|
|
77
|
+
index: {
|
|
78
|
+
type: Number,
|
|
79
|
+
required: false
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.ui?.listbox || {} })({
|
|
84
|
+
color: color.value,
|
|
85
|
+
size: size.value,
|
|
86
|
+
highlight: highlight.value,
|
|
87
|
+
disabled: disabled.value,
|
|
88
|
+
virtualize: !!props.virtualize
|
|
89
|
+
}));
|
|
90
|
+
function onUpdate(value) {
|
|
91
|
+
if (toRaw(props.modelValue) === value) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (props.modelModifiers?.trim && (typeof value === "string" || value === null || value === void 0)) {
|
|
95
|
+
value = value?.trim() ?? null;
|
|
96
|
+
}
|
|
97
|
+
if (props.modelModifiers?.number) {
|
|
98
|
+
value = looseToNumber(value);
|
|
99
|
+
}
|
|
100
|
+
if (props.modelModifiers?.nullable) {
|
|
101
|
+
value ??= null;
|
|
102
|
+
}
|
|
103
|
+
if (props.modelModifiers?.optional && !props.modelModifiers?.nullable && value !== null) {
|
|
104
|
+
value ??= void 0;
|
|
105
|
+
}
|
|
106
|
+
const event = new Event("change", { target: { value } });
|
|
107
|
+
emits("change", event);
|
|
108
|
+
emitFormChange();
|
|
109
|
+
emitFormInput();
|
|
110
|
+
}
|
|
111
|
+
function onSelect(e, item) {
|
|
112
|
+
if (item.disabled) {
|
|
113
|
+
e.preventDefault();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
item.onSelect?.(e);
|
|
117
|
+
}
|
|
118
|
+
const groups = computed(
|
|
119
|
+
() => props.items?.length ? isArrayOfArray(props.items) ? props.items : [props.items] : []
|
|
120
|
+
);
|
|
121
|
+
function isStructuralItem(item) {
|
|
122
|
+
return !!item.type && ["label", "separator"].includes(item.type);
|
|
123
|
+
}
|
|
124
|
+
const filteredGroups = computed(() => {
|
|
125
|
+
if (props.ignoreFilter || !searchTerm.value) {
|
|
126
|
+
return groups.value;
|
|
127
|
+
}
|
|
128
|
+
const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey];
|
|
129
|
+
return filterGroups(groups.value, searchTerm.value, {
|
|
130
|
+
fields,
|
|
131
|
+
isStructural: isStructuralItem
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
const filteredItems = computed(() => filteredGroups.value.flatMap((group) => group));
|
|
135
|
+
</script>
|
|
136
|
+
|
|
137
|
+
<template>
|
|
138
|
+
<DefineItemTemplate v-slot="{ item, index }">
|
|
139
|
+
<ListboxGroupLabel v-if="item.type === 'label'" data-slot="label" :class="ui.label({ class: [uiProp?.label, item.ui?.label, item.class] })">
|
|
140
|
+
{{ get(item, props.labelKey) }}
|
|
141
|
+
</ListboxGroupLabel>
|
|
142
|
+
|
|
143
|
+
<div v-else-if="item.type === 'separator'" role="separator" data-slot="separator" :class="ui.separator({ class: [uiProp?.separator, item.ui?.separator, item.class] })" />
|
|
144
|
+
|
|
145
|
+
<RekaListboxItem
|
|
146
|
+
v-else
|
|
147
|
+
:value="props.valueKey ? get(item, props.valueKey) : item"
|
|
148
|
+
:disabled="item.disabled"
|
|
149
|
+
data-slot="item"
|
|
150
|
+
:class="ui.item({ class: [uiProp?.item, item.ui?.item, item.class] })"
|
|
151
|
+
@select="onSelect($event, item)"
|
|
152
|
+
>
|
|
153
|
+
<slot name="item" :item="item" :index="index" :ui="ui">
|
|
154
|
+
<slot name="item-leading" :item="item" :index="index" :ui="ui">
|
|
155
|
+
<UIcon v-if="item.icon" :name="item.icon" data-slot="itemLeadingIcon" :class="ui.itemLeadingIcon({ class: [uiProp?.itemLeadingIcon, item.ui?.itemLeadingIcon] })" />
|
|
156
|
+
<UAvatar v-else-if="item.avatar" :size="item.ui?.itemLeadingAvatarSize || uiProp?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()" v-bind="item.avatar" data-slot="itemLeadingAvatar" :class="ui.itemLeadingAvatar({ class: [uiProp?.itemLeadingAvatar, item.ui?.itemLeadingAvatar] })" />
|
|
157
|
+
<UChip
|
|
158
|
+
v-else-if="item.chip"
|
|
159
|
+
:size="item.ui?.itemLeadingChipSize || uiProp?.itemLeadingChipSize || ui.itemLeadingChipSize()"
|
|
160
|
+
inset
|
|
161
|
+
standalone
|
|
162
|
+
v-bind="item.chip"
|
|
163
|
+
data-slot="itemLeadingChip"
|
|
164
|
+
:class="ui.itemLeadingChip({ class: [uiProp?.itemLeadingChip, item.ui?.itemLeadingChip] })"
|
|
165
|
+
/>
|
|
166
|
+
</slot>
|
|
167
|
+
|
|
168
|
+
<span v-if="get(item, props.labelKey) || get(item, props.descriptionKey) || !!slots['item-label'] || !!slots['item-description']" data-slot="itemWrapper" :class="ui.itemWrapper({ class: [uiProp?.itemWrapper, item.ui?.itemWrapper] })">
|
|
169
|
+
<span v-if="get(item, props.labelKey) || !!slots['item-label']" data-slot="itemLabel" :class="ui.itemLabel({ class: [uiProp?.itemLabel, item.ui?.itemLabel] })">
|
|
170
|
+
<slot name="item-label" :item="item" :index="index">
|
|
171
|
+
{{ get(item, props.labelKey) }}
|
|
172
|
+
</slot>
|
|
173
|
+
</span>
|
|
174
|
+
|
|
175
|
+
<span v-if="get(item, props.descriptionKey) || !!slots['item-description']" data-slot="itemDescription" :class="ui.itemDescription({ class: [uiProp?.itemDescription, item.ui?.itemDescription] })">
|
|
176
|
+
<slot name="item-description" :item="item" :index="index">
|
|
177
|
+
{{ get(item, props.descriptionKey) }}
|
|
178
|
+
</slot>
|
|
179
|
+
</span>
|
|
180
|
+
</span>
|
|
181
|
+
|
|
182
|
+
<span data-slot="itemTrailing" :class="ui.itemTrailing({ class: [uiProp?.itemTrailing, item.ui?.itemTrailing] })">
|
|
183
|
+
<slot name="item-trailing" :item="item" :index="index" :ui="ui" />
|
|
184
|
+
|
|
185
|
+
<ListboxItemIndicator as-child>
|
|
186
|
+
<UIcon :name="selectedIcon || appConfig.ui.icons.check" data-slot="itemTrailingIcon" :class="ui.itemTrailingIcon({ class: [uiProp?.itemTrailingIcon, item.ui?.itemTrailingIcon] })" />
|
|
187
|
+
</ListboxItemIndicator>
|
|
188
|
+
</span>
|
|
189
|
+
</slot>
|
|
190
|
+
</RekaListboxItem>
|
|
191
|
+
</DefineItemTemplate>
|
|
192
|
+
|
|
193
|
+
<ListboxRoot
|
|
194
|
+
:id="id"
|
|
195
|
+
v-bind="{ ...rootProps, ...$attrs, ...ariaAttrs }"
|
|
196
|
+
:disabled="disabled"
|
|
197
|
+
:name="name"
|
|
198
|
+
data-slot="root"
|
|
199
|
+
:class="ui.root({ class: [uiProp?.root, props.class] })"
|
|
200
|
+
@update:model-value="onUpdate"
|
|
201
|
+
>
|
|
202
|
+
<ListboxFilter v-if="filter" v-model="searchTerm" as-child>
|
|
203
|
+
<UInput
|
|
204
|
+
:autofocus="autofocus"
|
|
205
|
+
:autofocus-delay="autofocusDelay"
|
|
206
|
+
:size="size"
|
|
207
|
+
v-bind="inputProps"
|
|
208
|
+
data-slot="input"
|
|
209
|
+
:class="ui.input({ class: uiProp?.input })"
|
|
210
|
+
/>
|
|
211
|
+
</ListboxFilter>
|
|
212
|
+
|
|
213
|
+
<ListboxContent data-slot="content" :class="ui.content({ class: uiProp?.content })">
|
|
214
|
+
<div v-if="loading" data-slot="loading" :class="ui.loading({ class: uiProp?.loading })">
|
|
215
|
+
<slot name="loading">
|
|
216
|
+
<UIcon :name="loadingIcon || appConfig.ui.icons.loading" data-slot="loadingIcon" :class="ui.loadingIcon({ class: uiProp?.loadingIcon })" />
|
|
217
|
+
</slot>
|
|
218
|
+
</div>
|
|
219
|
+
<div v-else-if="!filteredItems.length" data-slot="empty" :class="ui.empty({ class: uiProp?.empty })">
|
|
220
|
+
<slot name="empty" :search-term="searchTerm">
|
|
221
|
+
{{ searchTerm ? t("listbox.noMatch", { searchTerm }) : t("listbox.noData") }}
|
|
222
|
+
</slot>
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
<ListboxVirtualizer
|
|
226
|
+
v-else-if="!!virtualize"
|
|
227
|
+
v-slot="{ option: item, virtualItem }"
|
|
228
|
+
:options="filteredItems"
|
|
229
|
+
:text-content="(item2) => get(item2, props.labelKey)"
|
|
230
|
+
v-bind="virtualizerProps"
|
|
231
|
+
>
|
|
232
|
+
<ReuseItemTemplate :item="item" :index="virtualItem.index" />
|
|
233
|
+
</ListboxVirtualizer>
|
|
234
|
+
|
|
235
|
+
<template v-else>
|
|
236
|
+
<ListboxGroup v-for="(group, groupIndex) in filteredGroups" :key="`group-${groupIndex}`" data-slot="group" :class="ui.group({ class: uiProp?.group })">
|
|
237
|
+
<ReuseItemTemplate v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`" :item="item" :index="index" />
|
|
238
|
+
</ListboxGroup>
|
|
239
|
+
</template>
|
|
240
|
+
</ListboxContent>
|
|
241
|
+
</ListboxRoot>
|
|
242
|
+
</template>
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import type { VNode } from 'vue';
|
|
2
|
+
import type { ListboxRootEmits, ListboxRootProps } from 'reka-ui';
|
|
3
|
+
import type { AppConfig } from '@nuxt/schema';
|
|
4
|
+
import theme from '#build/ui/listbox';
|
|
5
|
+
import type { AvatarProps, ChipProps, IconProps, InputProps } from '../types';
|
|
6
|
+
import type { ModelModifiers, ApplyModifiers } from '../types/input';
|
|
7
|
+
import type { ArrayOrNested, GetItemKeys, GetModelValue, NestedItem } from '../types/utils';
|
|
8
|
+
import type { ComponentConfig } from '../types/tv';
|
|
9
|
+
type Listbox = ComponentConfig<typeof theme, AppConfig, 'listbox'>;
|
|
10
|
+
export interface ListboxItem {
|
|
11
|
+
label?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
/**
|
|
14
|
+
* @IconifyIcon
|
|
15
|
+
*/
|
|
16
|
+
icon?: IconProps['name'];
|
|
17
|
+
avatar?: AvatarProps;
|
|
18
|
+
chip?: ChipProps;
|
|
19
|
+
/**
|
|
20
|
+
* The item type.
|
|
21
|
+
* @defaultValue 'item'
|
|
22
|
+
*/
|
|
23
|
+
type?: 'label' | 'separator' | 'item';
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
onSelect?: (e: Event) => void;
|
|
26
|
+
class?: any;
|
|
27
|
+
ui?: Pick<Listbox['slots'], 'label' | 'separator' | 'item' | 'itemLeadingIcon' | 'itemLeadingAvatar' | 'itemLeadingAvatarSize' | 'itemLeadingChip' | 'itemLeadingChipSize' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemTrailing' | 'itemTrailingIcon'>;
|
|
28
|
+
[key: string]: any;
|
|
29
|
+
}
|
|
30
|
+
export interface ListboxProps<T extends ArrayOrNested<ListboxItem> = ArrayOrNested<ListboxItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false, Mod extends Omit<ModelModifiers, 'lazy'> = Omit<ModelModifiers, 'lazy'>> extends Pick<ListboxRootProps, 'by' | 'disabled' | 'highlightOnHover' | 'name' | 'orientation' | 'required' | 'selectionBehavior'> {
|
|
31
|
+
id?: string;
|
|
32
|
+
/**
|
|
33
|
+
* The element or component this component should render as.
|
|
34
|
+
* @defaultValue 'div'
|
|
35
|
+
*/
|
|
36
|
+
as?: any;
|
|
37
|
+
/**
|
|
38
|
+
* @defaultValue 'md'
|
|
39
|
+
*/
|
|
40
|
+
size?: Listbox['variants']['size'];
|
|
41
|
+
/**
|
|
42
|
+
* The items to display in the list.
|
|
43
|
+
*/
|
|
44
|
+
items?: T;
|
|
45
|
+
/**
|
|
46
|
+
* The controlled value of the Listbox. Can be bound with `v-model`.
|
|
47
|
+
*/
|
|
48
|
+
modelValue?: ApplyModifiers<GetModelValue<T, VK, M>, Mod>;
|
|
49
|
+
modelModifiers?: Mod;
|
|
50
|
+
/**
|
|
51
|
+
* The default value when not controlled.
|
|
52
|
+
*/
|
|
53
|
+
defaultValue?: ApplyModifiers<GetModelValue<T, VK, M>, Mod>;
|
|
54
|
+
/**
|
|
55
|
+
* Whether multiple items can be selected.
|
|
56
|
+
* @defaultValue false
|
|
57
|
+
*/
|
|
58
|
+
multiple?: M & boolean;
|
|
59
|
+
/**
|
|
60
|
+
* When `items` is an array of objects, select the field to use as the value instead of the object itself.
|
|
61
|
+
* @defaultValue undefined
|
|
62
|
+
*/
|
|
63
|
+
valueKey?: VK;
|
|
64
|
+
/**
|
|
65
|
+
* The key used to get the label from the item.
|
|
66
|
+
* @defaultValue 'label'
|
|
67
|
+
*/
|
|
68
|
+
labelKey?: GetItemKeys<T>;
|
|
69
|
+
/**
|
|
70
|
+
* The key used to get the description from the item.
|
|
71
|
+
* @defaultValue 'description'
|
|
72
|
+
*/
|
|
73
|
+
descriptionKey?: GetItemKeys<T>;
|
|
74
|
+
/**
|
|
75
|
+
* Whether the list is in a loading state.
|
|
76
|
+
*/
|
|
77
|
+
loading?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* The icon displayed when loading.
|
|
80
|
+
* @defaultValue appConfig.ui.icons.loading
|
|
81
|
+
* @IconifyIcon
|
|
82
|
+
*/
|
|
83
|
+
loadingIcon?: IconProps['name'];
|
|
84
|
+
/**
|
|
85
|
+
* Whether to display a filter input or not.
|
|
86
|
+
* Can be an object to pass additional props to the input.
|
|
87
|
+
* `{ placeholder: 'Search...', variant: 'none' }`{lang="ts-type"}
|
|
88
|
+
* @defaultValue false
|
|
89
|
+
*/
|
|
90
|
+
filter?: boolean | Omit<InputProps, 'modelValue' | 'defaultValue'>;
|
|
91
|
+
/**
|
|
92
|
+
* The fields to filter by.
|
|
93
|
+
* @defaultValue [labelKey]
|
|
94
|
+
*/
|
|
95
|
+
filterFields?: string[];
|
|
96
|
+
/**
|
|
97
|
+
* When `true`, disable the default filters, useful for custom filtering (useAsyncData, useFetch, etc.).
|
|
98
|
+
* @defaultValue false
|
|
99
|
+
*/
|
|
100
|
+
ignoreFilter?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* The icon displayed when an item is selected.
|
|
103
|
+
* @defaultValue appConfig.ui.icons.check
|
|
104
|
+
* @IconifyIcon
|
|
105
|
+
*/
|
|
106
|
+
selectedIcon?: IconProps['name'];
|
|
107
|
+
/**
|
|
108
|
+
* Enable virtualization for large lists.
|
|
109
|
+
* @defaultValue false
|
|
110
|
+
*/
|
|
111
|
+
virtualize?: boolean | {
|
|
112
|
+
/**
|
|
113
|
+
* Number of items rendered outside the visible area
|
|
114
|
+
* @defaultValue 12
|
|
115
|
+
*/
|
|
116
|
+
overscan?: number;
|
|
117
|
+
/**
|
|
118
|
+
* Estimated size (in px) of each item, or a function that returns the size for a given index
|
|
119
|
+
* @defaultValue 32
|
|
120
|
+
*/
|
|
121
|
+
estimateSize?: number | ((index: number) => number);
|
|
122
|
+
};
|
|
123
|
+
/** Highlight the ring color like a focus state. */
|
|
124
|
+
highlight?: boolean;
|
|
125
|
+
autofocus?: boolean;
|
|
126
|
+
autofocusDelay?: number;
|
|
127
|
+
class?: any;
|
|
128
|
+
ui?: Listbox['slots'];
|
|
129
|
+
}
|
|
130
|
+
export type ListboxEmits<T extends ArrayOrNested<ListboxItem> = ArrayOrNested<ListboxItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false, Mod extends Omit<ModelModifiers, 'lazy'> = Omit<ModelModifiers, 'lazy'>> = Pick<ListboxRootEmits, 'entryFocus' | 'highlight' | 'leave'> & {
|
|
131
|
+
'change': [event: Event];
|
|
132
|
+
'update:modelValue': [value: ApplyModifiers<GetModelValue<T, VK, M>, Mod>];
|
|
133
|
+
};
|
|
134
|
+
type SlotProps<T> = (props: {
|
|
135
|
+
item: T;
|
|
136
|
+
index: number;
|
|
137
|
+
ui: Listbox['ui'];
|
|
138
|
+
}) => VNode[];
|
|
139
|
+
export type ListboxSlots<T extends ArrayOrNested<ListboxItem> = ArrayOrNested<ListboxItem>> = {
|
|
140
|
+
'loading'?(props?: {}): VNode[];
|
|
141
|
+
'empty'?(props: {
|
|
142
|
+
searchTerm: string;
|
|
143
|
+
}): VNode[];
|
|
144
|
+
'item'?: SlotProps<NestedItem<T>>;
|
|
145
|
+
'item-leading'?: SlotProps<NestedItem<T>>;
|
|
146
|
+
'item-label'?(props: {
|
|
147
|
+
item: NestedItem<T>;
|
|
148
|
+
index: number;
|
|
149
|
+
}): VNode[];
|
|
150
|
+
'item-description'?(props: {
|
|
151
|
+
item: NestedItem<T>;
|
|
152
|
+
index: number;
|
|
153
|
+
}): VNode[];
|
|
154
|
+
'item-trailing'?: SlotProps<NestedItem<T>>;
|
|
155
|
+
};
|
|
156
|
+
declare const _default: typeof __VLS_export;
|
|
157
|
+
export default _default;
|
|
158
|
+
declare const __VLS_export: <T extends ArrayOrNested<ListboxItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false, Mod extends Omit<ModelModifiers, "lazy"> = Omit<ModelModifiers, "lazy">>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
159
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<(ListboxProps<T, VK, M, Mod> & {
|
|
160
|
+
searchTerm?: string;
|
|
161
|
+
}) & {
|
|
162
|
+
onChange?: ((event: Event) => any) | undefined;
|
|
163
|
+
onHighlight?: ((payload: {
|
|
164
|
+
ref: HTMLElement;
|
|
165
|
+
value: import("reka-ui").AcceptableValue;
|
|
166
|
+
} | undefined) => any) | undefined;
|
|
167
|
+
"onUpdate:modelValue"?: ((value: Mod extends {
|
|
168
|
+
number: true;
|
|
169
|
+
} ? (Mod extends {
|
|
170
|
+
optional: true;
|
|
171
|
+
} ? (Mod extends {
|
|
172
|
+
nullable: true;
|
|
173
|
+
} ? GetModelValue<T, VK, M, undefined> | null : GetModelValue<T, VK, M, undefined>) | undefined : Mod extends {
|
|
174
|
+
nullable: true;
|
|
175
|
+
} ? GetModelValue<T, VK, M, undefined> | null : GetModelValue<T, VK, M, undefined>) extends infer T_1 ? T_1 extends (Mod extends {
|
|
176
|
+
optional: true;
|
|
177
|
+
} ? (Mod extends {
|
|
178
|
+
nullable: true;
|
|
179
|
+
} ? GetModelValue<T, VK, M, undefined> | null : GetModelValue<T, VK, M, undefined>) | undefined : Mod extends {
|
|
180
|
+
nullable: true;
|
|
181
|
+
} ? GetModelValue<T, VK, M, undefined> | null : GetModelValue<T, VK, M, undefined>) ? T_1 extends string ? number | T_1 : T_1 : never : never : Mod extends {
|
|
182
|
+
optional: true;
|
|
183
|
+
} ? (Mod extends {
|
|
184
|
+
nullable: true;
|
|
185
|
+
} ? GetModelValue<T, VK, M, undefined> | null : GetModelValue<T, VK, M, undefined>) | undefined : Mod extends {
|
|
186
|
+
nullable: true;
|
|
187
|
+
} ? GetModelValue<T, VK, M, undefined> | null : GetModelValue<T, VK, M, undefined>) => any) | undefined;
|
|
188
|
+
"onUpdate:searchTerm"?: ((value: string) => any) | undefined;
|
|
189
|
+
onEntryFocus?: ((event: CustomEvent<any>) => any) | undefined;
|
|
190
|
+
onLeave?: ((event: Event) => any) | undefined;
|
|
191
|
+
}> & (typeof globalThis extends {
|
|
192
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
193
|
+
} ? P : {});
|
|
194
|
+
expose: (exposed: {}) => void;
|
|
195
|
+
attrs: any;
|
|
196
|
+
slots: ListboxSlots<T>;
|
|
197
|
+
emit: (((evt: "change", event: Event) => void) & ((evt: "highlight", payload: {
|
|
198
|
+
ref: HTMLElement;
|
|
199
|
+
value: import("reka-ui").AcceptableValue;
|
|
200
|
+
} | undefined) => void) & ((evt: "update:modelValue", value: Mod extends {
|
|
201
|
+
number: true;
|
|
202
|
+
} ? (Mod extends {
|
|
203
|
+
optional: true;
|
|
204
|
+
} ? (Mod extends {
|
|
205
|
+
nullable: true;
|
|
206
|
+
} ? GetModelValue<T, VK, M, undefined> | null : GetModelValue<T, VK, M, undefined>) | undefined : Mod extends {
|
|
207
|
+
nullable: true;
|
|
208
|
+
} ? GetModelValue<T, VK, M, undefined> | null : GetModelValue<T, VK, M, undefined>) extends infer T_1 ? T_1 extends (Mod extends {
|
|
209
|
+
optional: true;
|
|
210
|
+
} ? (Mod extends {
|
|
211
|
+
nullable: true;
|
|
212
|
+
} ? GetModelValue<T, VK, M, undefined> | null : GetModelValue<T, VK, M, undefined>) | undefined : Mod extends {
|
|
213
|
+
nullable: true;
|
|
214
|
+
} ? GetModelValue<T, VK, M, undefined> | null : GetModelValue<T, VK, M, undefined>) ? T_1 extends string ? number | T_1 : T_1 : never : never : Mod extends {
|
|
215
|
+
optional: true;
|
|
216
|
+
} ? (Mod extends {
|
|
217
|
+
nullable: true;
|
|
218
|
+
} ? GetModelValue<T, VK, M, undefined> | null : GetModelValue<T, VK, M, undefined>) | undefined : Mod extends {
|
|
219
|
+
nullable: true;
|
|
220
|
+
} ? GetModelValue<T, VK, M, undefined> | null : GetModelValue<T, VK, M, undefined>) => void) & ((evt: "entryFocus", event: CustomEvent<any>) => void) & ((evt: "leave", event: Event) => void)) & ((event: "update:searchTerm", value: string) => void);
|
|
221
|
+
}>) => import("vue").VNode & {
|
|
222
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
223
|
+
};
|
|
224
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
225
|
+
[K in keyof T]: T[K];
|
|
226
|
+
} : {
|
|
227
|
+
[K in keyof T as K]: T[K];
|
|
228
|
+
}) & {};
|
|
@@ -8,6 +8,7 @@ import { DialogRoot, DialogTrigger, DialogPortal, DialogOverlay, DialogContent,
|
|
|
8
8
|
import { reactivePick, createReusableTemplate } from "@vueuse/core";
|
|
9
9
|
import { useAppConfig } from "#imports";
|
|
10
10
|
import { useComponentUI } from "../composables/useComponentUI";
|
|
11
|
+
import { FieldGroupReset } from "../composables/useFieldGroup";
|
|
11
12
|
import { useLocale } from "../composables/useLocale";
|
|
12
13
|
import { usePortal } from "../composables/usePortal";
|
|
13
14
|
import { pointerDownOutside } from "../utils/overlay";
|
|
@@ -93,7 +94,7 @@ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.ui?.modal || {} }
|
|
|
93
94
|
<slot name="content" :close="close">
|
|
94
95
|
<div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description) || (props.close || !!slots.close)" data-slot="header" :class="ui.header({ class: uiProp?.header })">
|
|
95
96
|
<slot name="header" :close="close">
|
|
96
|
-
<div data-slot="wrapper" :class="ui.wrapper({ class: uiProp?.wrapper })">
|
|
97
|
+
<div v-if="title || !!slots.title || description || !!slots.description" data-slot="wrapper" :class="ui.wrapper({ class: uiProp?.wrapper })">
|
|
97
98
|
<DialogTitle v-if="title || !!slots.title" data-slot="title" :class="ui.title({ class: uiProp?.title })">
|
|
98
99
|
<slot name="title">
|
|
99
100
|
{{ title }}
|
|
@@ -142,17 +143,19 @@ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.ui?.modal || {} }
|
|
|
142
143
|
</DialogTrigger>
|
|
143
144
|
|
|
144
145
|
<DialogPortal v-bind="portalProps">
|
|
145
|
-
<
|
|
146
|
-
<
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
<FieldGroupReset>
|
|
147
|
+
<template v-if="scrollable">
|
|
148
|
+
<DialogOverlay data-slot="overlay" :class="ui.overlay({ class: uiProp?.overlay })">
|
|
149
|
+
<ReuseContentTemplate />
|
|
150
|
+
</DialogOverlay>
|
|
151
|
+
</template>
|
|
150
152
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
+
<template v-else>
|
|
154
|
+
<DialogOverlay v-if="overlay" data-slot="overlay" :class="ui.overlay({ class: uiProp?.overlay })" />
|
|
153
155
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
+
<ReuseContentTemplate />
|
|
157
|
+
</template>
|
|
158
|
+
</FieldGroupReset>
|
|
156
159
|
</DialogPortal>
|
|
157
160
|
</DialogRoot>
|
|
158
161
|
</template>
|
|
@@ -10,6 +10,7 @@ import { Popover, HoverCard } from "reka-ui/namespaced";
|
|
|
10
10
|
import { reactivePick } from "@vueuse/core";
|
|
11
11
|
import { useAppConfig } from "#imports";
|
|
12
12
|
import { useComponentUI } from "../composables/useComponentUI";
|
|
13
|
+
import { FieldGroupReset } from "../composables/useFieldGroup";
|
|
13
14
|
import { usePortal } from "../composables/usePortal";
|
|
14
15
|
import { pointerDownOutside } from "../utils/overlay";
|
|
15
16
|
import { tv } from "../utils/tv";
|
|
@@ -69,11 +70,13 @@ const Component = computed(() => props.mode === "hover" ? HoverCard : Popover);
|
|
|
69
70
|
</Component.Anchor>
|
|
70
71
|
|
|
71
72
|
<Component.Portal v-bind="portalProps">
|
|
72
|
-
<
|
|
73
|
-
<
|
|
73
|
+
<FieldGroupReset>
|
|
74
|
+
<Component.Content v-bind="contentProps" data-slot="content" :class="ui.content({ class: [!slots.default && props.class, uiProp?.content] })" v-on="contentEvents">
|
|
75
|
+
<slot name="content" v-bind="close ? { close } : {}" />
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
<Component.Arrow v-if="!!arrow" v-bind="arrowProps" data-slot="arrow" :class="ui.arrow({ class: uiProp?.arrow })" />
|
|
78
|
+
</Component.Content>
|
|
79
|
+
</FieldGroupReset>
|
|
77
80
|
</Component.Portal>
|
|
78
81
|
</Component.Root>
|
|
79
82
|
</template>
|