@nuxt/ui 3.0.0 → 3.0.2
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/.nuxt/ui/button.ts +36 -36
- package/.nuxt/ui/calendar.ts +1 -1
- package/.nuxt/ui/container.ts +1 -1
- package/.nuxt/ui/drawer.ts +8 -5
- package/.nuxt/ui/navigation-menu.ts +2 -2
- package/.nuxt/ui/switch.ts +1 -1
- package/.nuxt/ui/tabs.ts +1 -1
- package/.nuxt/ui.css +1 -1
- package/README.md +11 -11
- package/cli/package.json +1 -1
- package/dist/module.cjs +2 -2
- package/dist/module.d.cts +8 -7
- package/dist/module.d.mts +8 -7
- package/dist/module.d.ts +8 -7
- package/dist/module.json +2 -2
- package/dist/module.mjs +2 -2
- package/dist/runtime/components/Accordion.vue +11 -8
- package/dist/runtime/components/Alert.vue +1 -1
- package/dist/runtime/components/App.vue +1 -1
- package/dist/runtime/components/Avatar.vue +7 -5
- package/dist/runtime/components/Breadcrumb.vue +13 -10
- package/dist/runtime/components/Button.vue +1 -1
- package/dist/runtime/components/Calendar.vue +40 -11
- package/dist/runtime/components/Carousel.vue +7 -3
- package/dist/runtime/components/ColorPicker.vue +2 -0
- package/dist/runtime/components/CommandPalette.vue +14 -11
- package/dist/runtime/components/ContextMenu.vue +31 -17
- package/dist/runtime/components/ContextMenuContent.vue +22 -15
- package/dist/runtime/components/Drawer.vue +10 -15
- package/dist/runtime/components/DropdownMenu.vue +29 -16
- package/dist/runtime/components/DropdownMenuContent.vue +22 -15
- package/dist/runtime/components/Form.vue +22 -1
- package/dist/runtime/components/FormField.vue +6 -0
- package/dist/runtime/components/InputMenu.vue +93 -55
- package/dist/runtime/components/Link.vue +3 -0
- package/dist/runtime/components/LinkBase.vue +1 -0
- package/dist/runtime/components/Modal.vue +11 -8
- package/dist/runtime/components/NavigationMenu.vue +53 -27
- package/dist/runtime/components/PinInput.vue +2 -0
- package/dist/runtime/components/Popover.vue +3 -2
- package/dist/runtime/components/RadioGroup.vue +25 -14
- package/dist/runtime/components/Select.vue +70 -43
- package/dist/runtime/components/SelectMenu.vue +84 -49
- package/dist/runtime/components/Slideover.vue +11 -8
- package/dist/runtime/components/Stepper.vue +13 -10
- package/dist/runtime/components/Table.vue +8 -3
- package/dist/runtime/components/Tabs.vue +8 -5
- package/dist/runtime/components/Toast.vue +2 -2
- package/dist/runtime/components/Tooltip.vue +3 -2
- package/dist/runtime/components/Tree.vue +48 -32
- package/dist/runtime/composables/defineLocale.js +1 -0
- package/dist/runtime/composables/useFormField.js +1 -1
- package/dist/runtime/composables/useKbd.js +1 -1
- package/dist/runtime/composables/useLocale.js +2 -2
- package/dist/runtime/composables/useOverlay.d.ts +3 -2
- package/dist/runtime/composables/useOverlay.js +1 -1
- package/dist/runtime/locale/ca.d.ts +3 -0
- package/dist/runtime/locale/ca.js +54 -0
- package/dist/runtime/locale/ckb.d.ts +3 -0
- package/dist/runtime/locale/ckb.js +55 -0
- package/dist/runtime/locale/hy.d.ts +3 -0
- package/dist/runtime/locale/hy.js +54 -0
- package/dist/runtime/locale/index.d.ts +6 -0
- package/dist/runtime/locale/index.js +6 -0
- package/dist/runtime/locale/ro.d.ts +3 -0
- package/dist/runtime/locale/ro.js +54 -0
- package/dist/runtime/locale/ur.d.ts +3 -0
- package/dist/runtime/locale/ur.js +55 -0
- package/dist/runtime/locale/uz.d.ts +3 -0
- package/dist/runtime/locale/uz.js +54 -0
- package/dist/runtime/plugins/colors.js +19 -15
- package/dist/runtime/types/form.d.ts +2 -3
- package/dist/runtime/types/index.d.ts +2 -0
- package/dist/runtime/types/index.js +2 -0
- package/dist/runtime/types/utils.d.ts +28 -8
- package/dist/runtime/utils/form.d.ts +0 -4
- package/dist/runtime/utils/form.js +2 -47
- package/dist/runtime/utils/index.d.ts +1 -0
- package/dist/runtime/utils/index.js +3 -0
- package/dist/runtime/utils/link.d.ts +2 -25
- package/dist/runtime/utils/link.js +31 -1
- package/dist/runtime/utils/tv.js +1 -1
- package/dist/runtime/vue/components/Link.vue +3 -0
- package/dist/runtime/vue/plugins/head.js +3 -0
- package/dist/runtime/vue/stubs.d.ts +7 -3
- package/dist/runtime/vue/stubs.js +12 -4
- package/dist/shared/{ui.COBs1u5H.cjs → ui.BL-XB3u1.cjs} +26 -26
- package/dist/shared/{ui.yjbGE7Dx.mjs → ui.BkNUP964.mjs} +26 -26
- package/dist/types.d.mts +7 -1
- package/dist/types.d.ts +7 -1
- package/dist/unplugin.cjs +6 -2
- package/dist/unplugin.d.cts +2 -1
- package/dist/unplugin.d.mts +2 -1
- package/dist/unplugin.d.ts +2 -1
- package/dist/unplugin.mjs +6 -2
- package/dist/vite.cjs +1 -1
- package/dist/vite.mjs +1 -1
- package/package.json +46 -29
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { InputHTMLAttributes } from 'vue'
|
|
3
3
|
import type { VariantProps } from 'tailwind-variants'
|
|
4
|
-
import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps,
|
|
4
|
+
import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxContentEmits, ComboboxArrowProps } from 'reka-ui'
|
|
5
5
|
import type { AppConfig } from '@nuxt/schema'
|
|
6
6
|
import _appConfig from '#build/app.config'
|
|
7
7
|
import theme from '#build/ui/input-menu'
|
|
8
8
|
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
|
9
9
|
import { tv } from '../utils/tv'
|
|
10
10
|
import type { AvatarProps, ChipProps, InputProps } from '../types'
|
|
11
|
-
import type {
|
|
11
|
+
import type {
|
|
12
|
+
AcceptableValue,
|
|
13
|
+
ArrayOrNested,
|
|
14
|
+
GetItemKeys,
|
|
15
|
+
GetModelValue,
|
|
16
|
+
GetModelValueEmits,
|
|
17
|
+
NestedItem,
|
|
18
|
+
PartialString,
|
|
19
|
+
EmitsToProps
|
|
20
|
+
} from '../types/utils'
|
|
12
21
|
|
|
13
22
|
const appConfigInputMenu = _appConfig as AppConfig & { ui: { inputMenu: Partial<typeof theme> } }
|
|
14
23
|
|
|
15
24
|
const inputMenu = tv({ extend: tv(theme), ...(appConfigInputMenu.ui?.inputMenu || {}) })
|
|
16
25
|
|
|
17
|
-
|
|
26
|
+
interface _InputMenuItem {
|
|
18
27
|
label?: string
|
|
19
28
|
/**
|
|
20
29
|
* @IconifyIcon
|
|
@@ -27,13 +36,16 @@ export interface InputMenuItem {
|
|
|
27
36
|
* @defaultValue 'item'
|
|
28
37
|
*/
|
|
29
38
|
type?: 'label' | 'separator' | 'item'
|
|
39
|
+
value?: string | number
|
|
30
40
|
disabled?: boolean
|
|
31
41
|
onSelect?(e?: Event): void
|
|
42
|
+
[key: string]: any
|
|
32
43
|
}
|
|
44
|
+
export type InputMenuItem = _InputMenuItem | AcceptableValue | boolean
|
|
33
45
|
|
|
34
46
|
type InputMenuVariants = VariantProps<typeof inputMenu>
|
|
35
47
|
|
|
36
|
-
export interface InputMenuProps<T extends
|
|
48
|
+
export interface InputMenuProps<T extends ArrayOrNested<InputMenuItem> = ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false> extends Pick<ComboboxRootProps<T>, 'open' | 'defaultOpen' | 'disabled' | 'name' | 'resetSearchTermOnBlur' | 'highlightOnHover'>, UseComponentIconsProps {
|
|
37
49
|
/**
|
|
38
50
|
* The element or component this component should render as.
|
|
39
51
|
* @defaultValue 'div'
|
|
@@ -81,7 +93,7 @@ export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends Ma
|
|
|
81
93
|
* The content of the menu.
|
|
82
94
|
* @defaultValue { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }
|
|
83
95
|
*/
|
|
84
|
-
content?: Omit<ComboboxContentProps, 'as' | 'asChild' | 'forceMount'>
|
|
96
|
+
content?: Omit<ComboboxContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<ComboboxContentEmits>>
|
|
85
97
|
/**
|
|
86
98
|
* Display an arrow alongside the menu.
|
|
87
99
|
* @defaultValue false
|
|
@@ -96,17 +108,17 @@ export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends Ma
|
|
|
96
108
|
* When `items` is an array of objects, select the field to use as the value instead of the object itself.
|
|
97
109
|
* @defaultValue undefined
|
|
98
110
|
*/
|
|
99
|
-
valueKey?:
|
|
111
|
+
valueKey?: VK
|
|
100
112
|
/**
|
|
101
113
|
* When `items` is an array of objects, select the field to use as the label.
|
|
102
114
|
* @defaultValue 'label'
|
|
103
115
|
*/
|
|
104
|
-
labelKey?:
|
|
105
|
-
items?:
|
|
116
|
+
labelKey?: keyof NestedItem<T>
|
|
117
|
+
items?: T
|
|
106
118
|
/** The value of the InputMenu when initially rendered. Use when you do not need to control the state of the InputMenu. */
|
|
107
|
-
defaultValue?:
|
|
119
|
+
defaultValue?: GetModelValue<T, VK, M>
|
|
108
120
|
/** The controlled value of the InputMenu. Can be binded-with with `v-model`. */
|
|
109
|
-
modelValue?:
|
|
121
|
+
modelValue?: GetModelValue<T, VK, M>
|
|
110
122
|
/** Whether multiple options can be selected or not. */
|
|
111
123
|
multiple?: M & boolean
|
|
112
124
|
/** Highlight the ring color like a focus state. */
|
|
@@ -130,18 +142,28 @@ export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends Ma
|
|
|
130
142
|
ui?: PartialString<typeof inputMenu.slots>
|
|
131
143
|
}
|
|
132
144
|
|
|
133
|
-
export type InputMenuEmits<
|
|
145
|
+
export type InputMenuEmits<A extends ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<A> | undefined, M extends boolean> = Pick<ComboboxRootEmits, 'update:open'> & {
|
|
134
146
|
change: [payload: Event]
|
|
135
147
|
blur: [payload: FocusEvent]
|
|
136
148
|
focus: [payload: FocusEvent]
|
|
137
149
|
create: [item: string]
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
150
|
+
/** Event handler when highlighted element changes. */
|
|
151
|
+
highlight: [payload: {
|
|
152
|
+
ref: HTMLElement
|
|
153
|
+
value: GetModelValue<A, VK, M>
|
|
154
|
+
} | undefined]
|
|
155
|
+
} & GetModelValueEmits<A, VK, M>
|
|
156
|
+
|
|
157
|
+
type SlotProps<T extends InputMenuItem> = (props: { item: T, index: number }) => any
|
|
158
|
+
|
|
159
|
+
export interface InputMenuSlots<
|
|
160
|
+
A extends ArrayOrNested<InputMenuItem> = ArrayOrNested<InputMenuItem>,
|
|
161
|
+
VK extends GetItemKeys<A> | undefined = undefined,
|
|
162
|
+
M extends boolean = false,
|
|
163
|
+
T extends NestedItem<A> = NestedItem<A>
|
|
164
|
+
> {
|
|
165
|
+
'leading'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean, ui: ReturnType<typeof inputMenu> }): any
|
|
166
|
+
'trailing'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean, ui: ReturnType<typeof inputMenu> }): any
|
|
145
167
|
'empty'(props: { searchTerm?: string }): any
|
|
146
168
|
'item': SlotProps<T>
|
|
147
169
|
'item-leading': SlotProps<T>
|
|
@@ -153,8 +175,8 @@ export interface InputMenuSlots<T, M extends boolean> {
|
|
|
153
175
|
}
|
|
154
176
|
</script>
|
|
155
177
|
|
|
156
|
-
<script setup lang="ts" generic="T extends
|
|
157
|
-
import { computed, ref, toRef, onMounted, toRaw } from 'vue'
|
|
178
|
+
<script setup lang="ts" generic="T extends ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false">
|
|
179
|
+
import { computed, ref, toRef, onMounted, toRaw, nextTick } from 'vue'
|
|
158
180
|
import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxViewport, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, TagsInputRoot, TagsInputItem, TagsInputItemText, TagsInputItemDelete, TagsInputInput, useForwardPropsEmits, useFilter } from 'reka-ui'
|
|
159
181
|
import { defu } from 'defu'
|
|
160
182
|
import { isEqual } from 'ohash/utils'
|
|
@@ -164,21 +186,21 @@ import { useButtonGroup } from '../composables/useButtonGroup'
|
|
|
164
186
|
import { useComponentIcons } from '../composables/useComponentIcons'
|
|
165
187
|
import { useFormField } from '../composables/useFormField'
|
|
166
188
|
import { useLocale } from '../composables/useLocale'
|
|
167
|
-
import { get,
|
|
189
|
+
import { compare, get, isArrayOfArray } from '../utils'
|
|
168
190
|
import UIcon from './Icon.vue'
|
|
169
191
|
import UAvatar from './Avatar.vue'
|
|
170
192
|
import UChip from './Chip.vue'
|
|
171
193
|
|
|
172
194
|
defineOptions({ inheritAttrs: false })
|
|
173
195
|
|
|
174
|
-
const props = withDefaults(defineProps<InputMenuProps<T,
|
|
196
|
+
const props = withDefaults(defineProps<InputMenuProps<T, VK, M>>(), {
|
|
175
197
|
type: 'text',
|
|
176
198
|
autofocusDelay: 0,
|
|
177
199
|
portal: true,
|
|
178
200
|
labelKey: 'label' as never
|
|
179
201
|
})
|
|
180
|
-
const emits = defineEmits<InputMenuEmits<T,
|
|
181
|
-
const slots = defineSlots<InputMenuSlots<T, M>>()
|
|
202
|
+
const emits = defineEmits<InputMenuEmits<T, VK, M>>()
|
|
203
|
+
const slots = defineSlots<InputMenuSlots<T, VK, M>>()
|
|
182
204
|
|
|
183
205
|
const searchTerm = defineModel<string>('searchTerm', { default: '' })
|
|
184
206
|
|
|
@@ -219,9 +241,15 @@ function displayValue(value: T): string {
|
|
|
219
241
|
return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
|
|
220
242
|
}
|
|
221
243
|
|
|
222
|
-
const groups = computed
|
|
244
|
+
const groups = computed<InputMenuItem[][]>(() =>
|
|
245
|
+
props.items?.length
|
|
246
|
+
? isArrayOfArray(props.items)
|
|
247
|
+
? props.items
|
|
248
|
+
: [props.items]
|
|
249
|
+
: []
|
|
250
|
+
)
|
|
223
251
|
// eslint-disable-next-line vue/no-dupe-keys
|
|
224
|
-
const items = computed(() => groups.value.flatMap(group => group)
|
|
252
|
+
const items = computed(() => groups.value.flatMap(group => group))
|
|
225
253
|
|
|
226
254
|
const filteredGroups = computed(() => {
|
|
227
255
|
if (props.ignoreFilter || !searchTerm.value) {
|
|
@@ -230,9 +258,9 @@ const filteredGroups = computed(() => {
|
|
|
230
258
|
|
|
231
259
|
const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey] as string[]
|
|
232
260
|
|
|
233
|
-
return groups.value.map(
|
|
234
|
-
if (typeof item !== 'object') {
|
|
235
|
-
return contains(item, searchTerm.value)
|
|
261
|
+
return groups.value.map(group => group.filter((item) => {
|
|
262
|
+
if (typeof item !== 'object' || item === null) {
|
|
263
|
+
return contains(String(item), searchTerm.value)
|
|
236
264
|
}
|
|
237
265
|
|
|
238
266
|
if (item.type && ['label', 'separator'].includes(item.type)) {
|
|
@@ -240,19 +268,21 @@ const filteredGroups = computed(() => {
|
|
|
240
268
|
}
|
|
241
269
|
|
|
242
270
|
return fields.some(field => contains(get(item, field), searchTerm.value))
|
|
243
|
-
})).filter(group => group.filter(item =>
|
|
271
|
+
})).filter(group => group.filter(item =>
|
|
272
|
+
!isInputItem(item) || (!item.type || !['label', 'separator'].includes(item.type))
|
|
273
|
+
).length > 0)
|
|
244
274
|
})
|
|
245
|
-
const filteredItems = computed(() => filteredGroups.value.flatMap(group => group)
|
|
275
|
+
const filteredItems = computed(() => filteredGroups.value.flatMap(group => group))
|
|
246
276
|
|
|
247
277
|
const createItem = computed(() => {
|
|
248
278
|
if (!props.createItem || !searchTerm.value) {
|
|
249
279
|
return false
|
|
250
280
|
}
|
|
251
281
|
|
|
252
|
-
const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as T : searchTerm.value
|
|
282
|
+
const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as NestedItem<T> : searchTerm.value
|
|
253
283
|
|
|
254
284
|
if ((typeof props.createItem === 'object' && props.createItem.when === 'always') || props.createItem === 'always') {
|
|
255
|
-
return !filteredItems.value.find(item => compare(item, newItem, props.valueKey))
|
|
285
|
+
return !filteredItems.value.find(item => compare(item, newItem, String(props.valueKey)))
|
|
256
286
|
}
|
|
257
287
|
|
|
258
288
|
return !filteredItems.value.length
|
|
@@ -303,16 +333,24 @@ function onUpdateOpen(value: boolean) {
|
|
|
303
333
|
const event = new FocusEvent('focus')
|
|
304
334
|
emits('focus', event)
|
|
305
335
|
}
|
|
336
|
+
|
|
337
|
+
nextTick(() => {
|
|
338
|
+
searchTerm.value = ''
|
|
339
|
+
})
|
|
306
340
|
}
|
|
307
341
|
|
|
308
342
|
function onRemoveTag(event: any) {
|
|
309
343
|
if (props.multiple) {
|
|
310
|
-
const modelValue = props.modelValue as
|
|
344
|
+
const modelValue = props.modelValue as GetModelValue<T, VK, true>
|
|
311
345
|
const filteredValue = modelValue.filter(value => !isEqual(value, event))
|
|
312
|
-
emits('update:modelValue', filteredValue as
|
|
346
|
+
emits('update:modelValue', filteredValue as GetModelValue<T, VK, M>)
|
|
313
347
|
}
|
|
314
348
|
}
|
|
315
349
|
|
|
350
|
+
function isInputItem(item: InputMenuItem): item is _InputMenuItem {
|
|
351
|
+
return typeof item === 'object' && item !== null
|
|
352
|
+
}
|
|
353
|
+
|
|
316
354
|
defineExpose({
|
|
317
355
|
inputRef
|
|
318
356
|
})
|
|
@@ -362,21 +400,21 @@ defineExpose({
|
|
|
362
400
|
@focus="onFocus"
|
|
363
401
|
@remove-tag="onRemoveTag"
|
|
364
402
|
>
|
|
365
|
-
<TagsInputItem v-for="(item, index) in tags" :key="index" :value="
|
|
403
|
+
<TagsInputItem v-for="(item, index) in tags" :key="index" :value="item" :class="ui.tagsItem({ class: props.ui?.tagsItem })">
|
|
366
404
|
<TagsInputItemText :class="ui.tagsItemText({ class: props.ui?.tagsItemText })">
|
|
367
|
-
<slot name="tags-item-text" :item="(item as T)" :index="index">
|
|
405
|
+
<slot name="tags-item-text" :item="(item as NestedItem<T>)" :index="index">
|
|
368
406
|
{{ displayValue(item as T) }}
|
|
369
407
|
</slot>
|
|
370
408
|
</TagsInputItemText>
|
|
371
409
|
|
|
372
410
|
<TagsInputItemDelete :class="ui.tagsItemDelete({ class: props.ui?.tagsItemDelete })" :disabled="disabled">
|
|
373
|
-
<slot name="tags-item-delete" :item="(item as T)" :index="index">
|
|
411
|
+
<slot name="tags-item-delete" :item="(item as NestedItem<T>)" :index="index">
|
|
374
412
|
<UIcon :name="deleteIcon || appConfig.ui.icons.close" :class="ui.tagsItemDeleteIcon({ class: props.ui?.tagsItemDeleteIcon })" />
|
|
375
413
|
</slot>
|
|
376
414
|
</TagsInputItemDelete>
|
|
377
415
|
</TagsInputItem>
|
|
378
416
|
|
|
379
|
-
<ComboboxInput
|
|
417
|
+
<ComboboxInput as-child @update:model-value="searchTerm = $event">
|
|
380
418
|
<TagsInputInput
|
|
381
419
|
ref="inputRef"
|
|
382
420
|
v-bind="{ ...$attrs, ...ariaAttrs }"
|
|
@@ -390,7 +428,6 @@ defineExpose({
|
|
|
390
428
|
<ComboboxInput
|
|
391
429
|
v-else
|
|
392
430
|
ref="inputRef"
|
|
393
|
-
v-model="searchTerm"
|
|
394
431
|
:display-value="displayValue"
|
|
395
432
|
v-bind="{ ...$attrs, ...ariaAttrs }"
|
|
396
433
|
:type="type"
|
|
@@ -398,17 +435,18 @@ defineExpose({
|
|
|
398
435
|
:required="required"
|
|
399
436
|
@blur="onBlur"
|
|
400
437
|
@focus="onFocus"
|
|
438
|
+
@update:model-value="searchTerm = $event"
|
|
401
439
|
/>
|
|
402
440
|
|
|
403
441
|
<span v-if="isLeading || !!avatar || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
|
|
404
|
-
<slot name="leading" :model-value="(modelValue as
|
|
442
|
+
<slot name="leading" :model-value="(modelValue as GetModelValue<T, VK, M>)" :open="open" :ui="ui">
|
|
405
443
|
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
|
|
406
444
|
<UAvatar v-else-if="!!avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
|
|
407
445
|
</slot>
|
|
408
446
|
</span>
|
|
409
447
|
|
|
410
448
|
<ComboboxTrigger v-if="isTrailing || !!slots.trailing" :class="ui.trailing({ class: props.ui?.trailing })">
|
|
411
|
-
<slot name="trailing" :model-value="(modelValue as
|
|
449
|
+
<slot name="trailing" :model-value="(modelValue as GetModelValue<T, VK, M>)" :open="open" :ui="ui">
|
|
412
450
|
<UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
|
|
413
451
|
</slot>
|
|
414
452
|
</ComboboxTrigger>
|
|
@@ -427,25 +465,25 @@ defineExpose({
|
|
|
427
465
|
|
|
428
466
|
<ComboboxGroup v-for="(group, groupIndex) in filteredGroups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
|
|
429
467
|
<template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
|
|
430
|
-
<ComboboxLabel v-if="item
|
|
468
|
+
<ComboboxLabel v-if="isInputItem(item) && item.type === 'label'" :class="ui.label({ class: props.ui?.label })">
|
|
431
469
|
{{ get(item, props.labelKey as string) }}
|
|
432
470
|
</ComboboxLabel>
|
|
433
471
|
|
|
434
|
-
<ComboboxSeparator v-else-if="item
|
|
472
|
+
<ComboboxSeparator v-else-if="isInputItem(item) && item.type === 'separator'" :class="ui.separator({ class: props.ui?.separator })" />
|
|
435
473
|
|
|
436
474
|
<ComboboxItem
|
|
437
475
|
v-else
|
|
438
476
|
:class="ui.item({ class: props.ui?.item })"
|
|
439
|
-
:disabled="item.disabled"
|
|
440
|
-
:value="valueKey &&
|
|
441
|
-
@select="item.onSelect"
|
|
477
|
+
:disabled="isInputItem(item) && item.disabled"
|
|
478
|
+
:value="props.valueKey && isInputItem(item) ? get(item, String(props.valueKey)) : item"
|
|
479
|
+
@select="isInputItem(item) && item.onSelect"
|
|
442
480
|
>
|
|
443
|
-
<slot name="item" :item="(item as T)" :index="index">
|
|
444
|
-
<slot name="item-leading" :item="(item as T)" :index="index">
|
|
445
|
-
<UIcon v-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
|
|
446
|
-
<UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
|
|
481
|
+
<slot name="item" :item="(item as NestedItem<T>)" :index="index">
|
|
482
|
+
<slot name="item-leading" :item="(item as NestedItem<T>)" :index="index">
|
|
483
|
+
<UIcon v-if="isInputItem(item) && item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
|
|
484
|
+
<UAvatar v-else-if="isInputItem(item) && item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
|
|
447
485
|
<UChip
|
|
448
|
-
v-else-if="item.chip"
|
|
486
|
+
v-else-if="isInputItem(item) && item.chip"
|
|
449
487
|
:size="((props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()) as ChipProps['size'])"
|
|
450
488
|
inset
|
|
451
489
|
standalone
|
|
@@ -455,13 +493,13 @@ defineExpose({
|
|
|
455
493
|
</slot>
|
|
456
494
|
|
|
457
495
|
<span :class="ui.itemLabel({ class: props.ui?.itemLabel })">
|
|
458
|
-
<slot name="item-label" :item="(item as T)" :index="index">
|
|
459
|
-
{{
|
|
496
|
+
<slot name="item-label" :item="(item as NestedItem<T>)" :index="index">
|
|
497
|
+
{{ isInputItem(item) ? get(item, props.labelKey as string) : item }}
|
|
460
498
|
</slot>
|
|
461
499
|
</span>
|
|
462
500
|
|
|
463
501
|
<span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
|
|
464
|
-
<slot name="item-trailing" :item="(item as T)" :index="index" />
|
|
502
|
+
<slot name="item-trailing" :item="(item as NestedItem<T>)" :index="index" />
|
|
465
503
|
|
|
466
504
|
<ComboboxItemIndicator as-child>
|
|
467
505
|
<UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
|
|
@@ -102,6 +102,7 @@ defineOptions({ inheritAttrs: false })
|
|
|
102
102
|
const props = withDefaults(defineProps<LinkProps>(), {
|
|
103
103
|
as: 'button',
|
|
104
104
|
type: 'button',
|
|
105
|
+
ariaCurrentValue: 'page',
|
|
105
106
|
active: undefined,
|
|
106
107
|
activeClass: '',
|
|
107
108
|
inactiveClass: ''
|
|
@@ -178,6 +179,7 @@ function resolveLinkClass({ route, isActive, isExactActive }: any) {
|
|
|
178
179
|
<slot
|
|
179
180
|
v-bind="{
|
|
180
181
|
...$attrs,
|
|
182
|
+
...(exact && isExactActive ? { 'aria-current': props.ariaCurrentValue } : {}),
|
|
181
183
|
as,
|
|
182
184
|
type,
|
|
183
185
|
disabled,
|
|
@@ -194,6 +196,7 @@ function resolveLinkClass({ route, isActive, isExactActive }: any) {
|
|
|
194
196
|
v-else
|
|
195
197
|
v-bind="{
|
|
196
198
|
...$attrs,
|
|
199
|
+
...(exact && isExactActive ? { 'aria-current': props.ariaCurrentValue } : {}),
|
|
197
200
|
as,
|
|
198
201
|
type,
|
|
199
202
|
disabled,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { DialogRootProps, DialogRootEmits, DialogContentProps } from 'reka-ui'
|
|
2
|
+
import type { DialogRootProps, DialogRootEmits, DialogContentProps, DialogContentEmits } from 'reka-ui'
|
|
3
3
|
import type { AppConfig } from '@nuxt/schema'
|
|
4
4
|
import _appConfig from '#build/app.config'
|
|
5
5
|
import theme from '#build/ui/modal'
|
|
6
6
|
import { tv } from '../utils/tv'
|
|
7
7
|
import type { ButtonProps } from '../types'
|
|
8
|
+
import type { EmitsToProps } from '../types/utils'
|
|
8
9
|
|
|
9
10
|
const appConfigModal = _appConfig as AppConfig & { ui: { modal: Partial<typeof theme> } }
|
|
10
11
|
|
|
@@ -14,7 +15,7 @@ export interface ModalProps extends DialogRootProps {
|
|
|
14
15
|
title?: string
|
|
15
16
|
description?: string
|
|
16
17
|
/** The content of the modal. */
|
|
17
|
-
content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'>
|
|
18
|
+
content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<DialogContentEmits>>
|
|
18
19
|
/**
|
|
19
20
|
* Render an overlay behind the modal.
|
|
20
21
|
* @defaultValue true
|
|
@@ -66,7 +67,7 @@ export interface ModalSlots {
|
|
|
66
67
|
header(props?: {}): any
|
|
67
68
|
title(props?: {}): any
|
|
68
69
|
description(props?: {}): any
|
|
69
|
-
close(props: { ui:
|
|
70
|
+
close(props: { ui: ReturnType<typeof modal> }): any
|
|
70
71
|
body(props?: {}): any
|
|
71
72
|
footer(props?: {}): any
|
|
72
73
|
}
|
|
@@ -97,18 +98,20 @@ const appConfig = useAppConfig()
|
|
|
97
98
|
const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'modal'), emits)
|
|
98
99
|
const contentProps = toRef(() => props.content)
|
|
99
100
|
const contentEvents = computed(() => {
|
|
101
|
+
const events = {
|
|
102
|
+
closeAutoFocus: (e: Event) => e.preventDefault()
|
|
103
|
+
}
|
|
104
|
+
|
|
100
105
|
if (!props.dismissible) {
|
|
101
106
|
return {
|
|
102
107
|
pointerDownOutside: (e: Event) => e.preventDefault(),
|
|
103
108
|
interactOutside: (e: Event) => e.preventDefault(),
|
|
104
109
|
escapeKeyDown: (e: Event) => e.preventDefault(),
|
|
105
|
-
|
|
110
|
+
...events
|
|
106
111
|
}
|
|
107
112
|
}
|
|
108
113
|
|
|
109
|
-
return
|
|
110
|
-
closeAutoFocus: (e: Event) => e.preventDefault()
|
|
111
|
-
}
|
|
114
|
+
return events
|
|
112
115
|
})
|
|
113
116
|
|
|
114
117
|
const ui = computed(() => modal({
|
|
@@ -158,7 +161,7 @@ const ui = computed(() => modal({
|
|
|
158
161
|
</DialogDescription>
|
|
159
162
|
</div>
|
|
160
163
|
|
|
161
|
-
<DialogClose as-child>
|
|
164
|
+
<DialogClose v-if="close || !!slots.close" as-child>
|
|
162
165
|
<slot name="close" :ui="ui">
|
|
163
166
|
<UButton
|
|
164
167
|
v-if="close"
|