@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,19 +1,29 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { VariantProps } from 'tailwind-variants'
|
|
3
|
-
import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps,
|
|
3
|
+
import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxContentEmits, ComboboxArrowProps } from 'reka-ui'
|
|
4
4
|
import type { AppConfig } from '@nuxt/schema'
|
|
5
5
|
import _appConfig from '#build/app.config'
|
|
6
6
|
import theme from '#build/ui/select-menu'
|
|
7
7
|
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
|
8
8
|
import { tv } from '../utils/tv'
|
|
9
9
|
import type { AvatarProps, ChipProps, InputProps } from '../types'
|
|
10
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
AcceptableValue,
|
|
12
|
+
ArrayOrNested,
|
|
13
|
+
GetItemKeys,
|
|
14
|
+
GetItemValue,
|
|
15
|
+
GetModelValue,
|
|
16
|
+
GetModelValueEmits,
|
|
17
|
+
NestedItem,
|
|
18
|
+
PartialString,
|
|
19
|
+
EmitsToProps
|
|
20
|
+
} from '../types/utils'
|
|
11
21
|
|
|
12
22
|
const appConfigSelectMenu = _appConfig as AppConfig & { ui: { selectMenu: Partial<typeof theme> } }
|
|
13
23
|
|
|
14
24
|
const selectMenu = tv({ extend: tv(theme), ...(appConfigSelectMenu.ui?.selectMenu || {}) })
|
|
15
25
|
|
|
16
|
-
|
|
26
|
+
interface _SelectMenuItem {
|
|
17
27
|
label?: string
|
|
18
28
|
/**
|
|
19
29
|
* @IconifyIcon
|
|
@@ -26,13 +36,16 @@ export interface SelectMenuItem {
|
|
|
26
36
|
* @defaultValue 'item'
|
|
27
37
|
*/
|
|
28
38
|
type?: 'label' | 'separator' | 'item'
|
|
39
|
+
value?: string | number
|
|
29
40
|
disabled?: boolean
|
|
30
41
|
onSelect?(e?: Event): void
|
|
42
|
+
[key: string]: any
|
|
31
43
|
}
|
|
44
|
+
export type SelectMenuItem = _SelectMenuItem | AcceptableValue | boolean
|
|
32
45
|
|
|
33
46
|
type SelectMenuVariants = VariantProps<typeof selectMenu>
|
|
34
47
|
|
|
35
|
-
export interface SelectMenuProps<T extends
|
|
48
|
+
export interface SelectMenuProps<T extends ArrayOrNested<SelectMenuItem> = ArrayOrNested<SelectMenuItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false> extends Pick<ComboboxRootProps<T>, 'open' | 'defaultOpen' | 'disabled' | 'name' | 'resetSearchTermOnBlur' | 'highlightOnHover'>, UseComponentIconsProps {
|
|
36
49
|
id?: string
|
|
37
50
|
/** The placeholder text when the select is empty. */
|
|
38
51
|
placeholder?: string
|
|
@@ -72,7 +85,7 @@ export interface SelectMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends M
|
|
|
72
85
|
* The content of the menu.
|
|
73
86
|
* @defaultValue { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }
|
|
74
87
|
*/
|
|
75
|
-
content?: Omit<ComboboxContentProps, 'as' | 'asChild' | 'forceMount'>
|
|
88
|
+
content?: Omit<ComboboxContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<ComboboxContentEmits>>
|
|
76
89
|
/**
|
|
77
90
|
* Display an arrow alongside the menu.
|
|
78
91
|
* @defaultValue false
|
|
@@ -88,17 +101,17 @@ export interface SelectMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends M
|
|
|
88
101
|
* When `items` is an array of objects, select the field to use as the value instead of the object itself.
|
|
89
102
|
* @defaultValue undefined
|
|
90
103
|
*/
|
|
91
|
-
valueKey?:
|
|
104
|
+
valueKey?: VK
|
|
92
105
|
/**
|
|
93
106
|
* When `items` is an array of objects, select the field to use as the label.
|
|
94
107
|
* @defaultValue 'label'
|
|
95
108
|
*/
|
|
96
|
-
labelKey?:
|
|
97
|
-
items?:
|
|
109
|
+
labelKey?: keyof NestedItem<T>
|
|
110
|
+
items?: T
|
|
98
111
|
/** The value of the SelectMenu when initially rendered. Use when you do not need to control the state of the SelectMenu. */
|
|
99
|
-
defaultValue?:
|
|
112
|
+
defaultValue?: GetModelValue<T, VK, M>
|
|
100
113
|
/** The controlled value of the SelectMenu. Can be binded-with with `v-model`. */
|
|
101
|
-
modelValue?:
|
|
114
|
+
modelValue?: GetModelValue<T, VK, M>
|
|
102
115
|
/** Whether multiple options can be selected or not. */
|
|
103
116
|
multiple?: M & boolean
|
|
104
117
|
/** Highlight the ring color like a focus state. */
|
|
@@ -122,19 +135,29 @@ export interface SelectMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends M
|
|
|
122
135
|
ui?: PartialString<typeof selectMenu.slots>
|
|
123
136
|
}
|
|
124
137
|
|
|
125
|
-
export type SelectMenuEmits<
|
|
138
|
+
export type SelectMenuEmits<A extends ArrayOrNested<SelectMenuItem>, VK extends GetItemKeys<A> | undefined, M extends boolean> = Pick<ComboboxRootEmits, 'update:open'> & {
|
|
126
139
|
change: [payload: Event]
|
|
127
140
|
blur: [payload: FocusEvent]
|
|
128
141
|
focus: [payload: FocusEvent]
|
|
129
142
|
create: [item: string]
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
143
|
+
/** Event handler when highlighted element changes. */
|
|
144
|
+
highlight: [payload: {
|
|
145
|
+
ref: HTMLElement
|
|
146
|
+
value: GetModelValue<A, VK, M>
|
|
147
|
+
} | undefined]
|
|
148
|
+
} & GetModelValueEmits<A, VK, M>
|
|
149
|
+
|
|
150
|
+
type SlotProps<T extends SelectMenuItem> = (props: { item: T, index: number }) => any
|
|
151
|
+
|
|
152
|
+
export interface SelectMenuSlots<
|
|
153
|
+
A extends ArrayOrNested<SelectMenuItem> = ArrayOrNested<SelectMenuItem>,
|
|
154
|
+
VK extends GetItemKeys<A> | undefined = undefined,
|
|
155
|
+
M extends boolean = false,
|
|
156
|
+
T extends NestedItem<A> = NestedItem<A>
|
|
157
|
+
> {
|
|
158
|
+
'leading'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean, ui: ReturnType<typeof selectMenu> }): any
|
|
159
|
+
'default'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean }): any
|
|
160
|
+
'trailing'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean, ui: ReturnType<typeof selectMenu> }): any
|
|
138
161
|
'empty'(props: { searchTerm?: string }): any
|
|
139
162
|
'item': SlotProps<T>
|
|
140
163
|
'item-leading': SlotProps<T>
|
|
@@ -144,7 +167,7 @@ export interface SelectMenuSlots<T, M extends boolean> {
|
|
|
144
167
|
}
|
|
145
168
|
</script>
|
|
146
169
|
|
|
147
|
-
<script setup lang="ts" generic="T extends
|
|
170
|
+
<script setup lang="ts" generic="T extends ArrayOrNested<SelectMenuItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false">
|
|
148
171
|
import { computed, toRef, toRaw } from 'vue'
|
|
149
172
|
import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxViewport, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, FocusScope, useForwardPropsEmits, useFilter } from 'reka-ui'
|
|
150
173
|
import { defu } from 'defu'
|
|
@@ -154,7 +177,7 @@ import { useButtonGroup } from '../composables/useButtonGroup'
|
|
|
154
177
|
import { useComponentIcons } from '../composables/useComponentIcons'
|
|
155
178
|
import { useFormField } from '../composables/useFormField'
|
|
156
179
|
import { useLocale } from '../composables/useLocale'
|
|
157
|
-
import { get,
|
|
180
|
+
import { compare, get, isArrayOfArray } from '../utils'
|
|
158
181
|
import UIcon from './Icon.vue'
|
|
159
182
|
import UAvatar from './Avatar.vue'
|
|
160
183
|
import UChip from './Chip.vue'
|
|
@@ -162,14 +185,14 @@ import UInput from './Input.vue'
|
|
|
162
185
|
|
|
163
186
|
defineOptions({ inheritAttrs: false })
|
|
164
187
|
|
|
165
|
-
const props = withDefaults(defineProps<SelectMenuProps<T,
|
|
188
|
+
const props = withDefaults(defineProps<SelectMenuProps<T, VK, M>>(), {
|
|
166
189
|
portal: true,
|
|
167
190
|
searchInput: true,
|
|
168
191
|
labelKey: 'label' as never,
|
|
169
192
|
resetSearchTermOnBlur: true
|
|
170
193
|
})
|
|
171
|
-
const emits = defineEmits<SelectMenuEmits<T,
|
|
172
|
-
const slots = defineSlots<SelectMenuSlots<T, M>>()
|
|
194
|
+
const emits = defineEmits<SelectMenuEmits<T, VK, M>>()
|
|
195
|
+
const slots = defineSlots<SelectMenuSlots<T, VK, M>>()
|
|
173
196
|
|
|
174
197
|
const searchTerm = defineModel<string>('searchTerm', { default: '' })
|
|
175
198
|
|
|
@@ -201,7 +224,7 @@ const ui = computed(() => selectMenu({
|
|
|
201
224
|
buttonGroup: orientation.value
|
|
202
225
|
}))
|
|
203
226
|
|
|
204
|
-
function displayValue(value: T | T[]): string {
|
|
227
|
+
function displayValue(value: GetItemValue<T, VK> | GetItemValue<T, VK>[]): string {
|
|
205
228
|
if (props.multiple && Array.isArray(value)) {
|
|
206
229
|
return value.map(v => displayValue(v)).filter(Boolean).join(', ')
|
|
207
230
|
}
|
|
@@ -214,7 +237,13 @@ function displayValue(value: T | T[]): string {
|
|
|
214
237
|
return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
|
|
215
238
|
}
|
|
216
239
|
|
|
217
|
-
const groups = computed
|
|
240
|
+
const groups = computed<SelectMenuItem[][]>(() =>
|
|
241
|
+
props.items?.length
|
|
242
|
+
? isArrayOfArray(props.items)
|
|
243
|
+
? props.items
|
|
244
|
+
: [props.items]
|
|
245
|
+
: []
|
|
246
|
+
)
|
|
218
247
|
// eslint-disable-next-line vue/no-dupe-keys
|
|
219
248
|
const items = computed(() => groups.value.flatMap(group => group) as T[])
|
|
220
249
|
|
|
@@ -226,8 +255,8 @@ const filteredGroups = computed(() => {
|
|
|
226
255
|
const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey] as string[]
|
|
227
256
|
|
|
228
257
|
return groups.value.map(items => items.filter((item) => {
|
|
229
|
-
if (typeof item !== 'object') {
|
|
230
|
-
return contains(item, searchTerm.value)
|
|
258
|
+
if (typeof item !== 'object' || item === null) {
|
|
259
|
+
return contains(String(item), searchTerm.value)
|
|
231
260
|
}
|
|
232
261
|
|
|
233
262
|
if (item.type && ['label', 'separator'].includes(item.type)) {
|
|
@@ -235,19 +264,21 @@ const filteredGroups = computed(() => {
|
|
|
235
264
|
}
|
|
236
265
|
|
|
237
266
|
return fields.some(field => contains(get(item, field), searchTerm.value))
|
|
238
|
-
})).filter(group => group.filter(item =>
|
|
267
|
+
})).filter(group => group.filter(item =>
|
|
268
|
+
!isSelectItem(item) || (!item.type || !['label', 'separator'].includes(item.type))
|
|
269
|
+
).length > 0)
|
|
239
270
|
})
|
|
240
|
-
const filteredItems = computed(() => filteredGroups.value.flatMap(group => group)
|
|
271
|
+
const filteredItems = computed(() => filteredGroups.value.flatMap(group => group))
|
|
241
272
|
|
|
242
273
|
const createItem = computed(() => {
|
|
243
274
|
if (!props.createItem || !searchTerm.value) {
|
|
244
275
|
return false
|
|
245
276
|
}
|
|
246
277
|
|
|
247
|
-
const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as T : searchTerm.value
|
|
278
|
+
const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as NestedItem<T> : searchTerm.value
|
|
248
279
|
|
|
249
280
|
if ((typeof props.createItem === 'object' && props.createItem.when === 'always') || props.createItem === 'always') {
|
|
250
|
-
return !filteredItems.value.find(item => compare(item, newItem, props.valueKey))
|
|
281
|
+
return !filteredItems.value.find(item => compare(item, newItem, String(props.valueKey)))
|
|
251
282
|
}
|
|
252
283
|
|
|
253
284
|
return !filteredItems.value.length
|
|
@@ -290,6 +321,10 @@ function onUpdateOpen(value: boolean) {
|
|
|
290
321
|
clearTimeout(timeoutId)
|
|
291
322
|
}
|
|
292
323
|
}
|
|
324
|
+
|
|
325
|
+
function isSelectItem(item: SelectMenuItem): item is _SelectMenuItem {
|
|
326
|
+
return typeof item === 'object' && item !== null
|
|
327
|
+
}
|
|
293
328
|
</script>
|
|
294
329
|
|
|
295
330
|
<!-- eslint-disable vue/no-template-shadow -->
|
|
@@ -324,14 +359,14 @@ function onUpdateOpen(value: boolean) {
|
|
|
324
359
|
<ComboboxAnchor as-child>
|
|
325
360
|
<ComboboxTrigger :class="ui.base({ class: [props.class, props.ui?.base] })" tabindex="0">
|
|
326
361
|
<span v-if="isLeading || !!avatar || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
|
|
327
|
-
<slot name="leading" :model-value="(modelValue as
|
|
362
|
+
<slot name="leading" :model-value="(modelValue as GetModelValue<T, VK, M>)" :open="open" :ui="ui">
|
|
328
363
|
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
|
|
329
364
|
<UAvatar v-else-if="!!avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
|
|
330
365
|
</slot>
|
|
331
366
|
</span>
|
|
332
367
|
|
|
333
|
-
<slot :model-value="(modelValue as
|
|
334
|
-
<template v-for="displayedModelValue in [displayValue(modelValue as
|
|
368
|
+
<slot :model-value="(modelValue as GetModelValue<T, VK, M>)" :open="open">
|
|
369
|
+
<template v-for="displayedModelValue in [displayValue(modelValue as GetModelValue<T, VK, M>)]" :key="displayedModelValue">
|
|
335
370
|
<span v-if="displayedModelValue" :class="ui.value({ class: props.ui?.value })">
|
|
336
371
|
{{ displayedModelValue }}
|
|
337
372
|
</span>
|
|
@@ -342,7 +377,7 @@ function onUpdateOpen(value: boolean) {
|
|
|
342
377
|
</slot>
|
|
343
378
|
|
|
344
379
|
<span v-if="isTrailing || !!slots.trailing" :class="ui.trailing({ class: props.ui?.trailing })">
|
|
345
|
-
<slot name="trailing" :model-value="(modelValue as
|
|
380
|
+
<slot name="trailing" :model-value="(modelValue as GetModelValue<T, VK, M>)" :open="open" :ui="ui">
|
|
346
381
|
<UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
|
|
347
382
|
</slot>
|
|
348
383
|
</span>
|
|
@@ -367,25 +402,25 @@ function onUpdateOpen(value: boolean) {
|
|
|
367
402
|
|
|
368
403
|
<ComboboxGroup v-for="(group, groupIndex) in filteredGroups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
|
|
369
404
|
<template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
|
|
370
|
-
<ComboboxLabel v-if="item
|
|
405
|
+
<ComboboxLabel v-if="isSelectItem(item) && item.type === 'label'" :class="ui.label({ class: props.ui?.label })">
|
|
371
406
|
{{ get(item, props.labelKey as string) }}
|
|
372
407
|
</ComboboxLabel>
|
|
373
408
|
|
|
374
|
-
<ComboboxSeparator v-else-if="item
|
|
409
|
+
<ComboboxSeparator v-else-if="isSelectItem(item) && item.type === 'separator'" :class="ui.separator({ class: props.ui?.separator })" />
|
|
375
410
|
|
|
376
411
|
<ComboboxItem
|
|
377
412
|
v-else
|
|
378
413
|
:class="ui.item({ class: props.ui?.item })"
|
|
379
|
-
:disabled="item.disabled"
|
|
380
|
-
:value="valueKey &&
|
|
381
|
-
@select="item.onSelect"
|
|
414
|
+
:disabled="isSelectItem(item) && item.disabled"
|
|
415
|
+
:value="props.valueKey && isSelectItem(item) ? get(item, props.valueKey as string) : item"
|
|
416
|
+
@select="isSelectItem(item) && item.onSelect"
|
|
382
417
|
>
|
|
383
|
-
<slot name="item" :item="(item as T)" :index="index">
|
|
384
|
-
<slot name="item-leading" :item="(item as T)" :index="index">
|
|
385
|
-
<UIcon v-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
|
|
386
|
-
<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 })" />
|
|
418
|
+
<slot name="item" :item="(item as NestedItem<T>)" :index="index">
|
|
419
|
+
<slot name="item-leading" :item="(item as NestedItem<T>)" :index="index">
|
|
420
|
+
<UIcon v-if="isSelectItem(item) && item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
|
|
421
|
+
<UAvatar v-else-if="isSelectItem(item) && item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
|
|
387
422
|
<UChip
|
|
388
|
-
v-else-if="item.chip"
|
|
423
|
+
v-else-if="isSelectItem(item) && item.chip"
|
|
389
424
|
:size="((props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()) as ChipProps['size'])"
|
|
390
425
|
inset
|
|
391
426
|
standalone
|
|
@@ -395,13 +430,13 @@ function onUpdateOpen(value: boolean) {
|
|
|
395
430
|
</slot>
|
|
396
431
|
|
|
397
432
|
<span :class="ui.itemLabel({ class: props.ui?.itemLabel })">
|
|
398
|
-
<slot name="item-label" :item="(item as T)" :index="index">
|
|
399
|
-
{{
|
|
433
|
+
<slot name="item-label" :item="(item as NestedItem<T>)" :index="index">
|
|
434
|
+
{{ isSelectItem(item) ? get(item, props.labelKey as string) : item }}
|
|
400
435
|
</slot>
|
|
401
436
|
</span>
|
|
402
437
|
|
|
403
438
|
<span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
|
|
404
|
-
<slot name="item-trailing" :item="(item as T)" :index="index" />
|
|
439
|
+
<slot name="item-trailing" :item="(item as NestedItem<T>)" :index="index" />
|
|
405
440
|
|
|
406
441
|
<ComboboxItemIndicator as-child>
|
|
407
442
|
<UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { VariantProps } from 'tailwind-variants'
|
|
3
|
-
import type { DialogRootProps, DialogRootEmits, DialogContentProps } from 'reka-ui'
|
|
3
|
+
import type { DialogRootProps, DialogRootEmits, DialogContentProps, DialogContentEmits } from 'reka-ui'
|
|
4
4
|
import type { AppConfig } from '@nuxt/schema'
|
|
5
5
|
import _appConfig from '#build/app.config'
|
|
6
6
|
import theme from '#build/ui/slideover'
|
|
7
7
|
import { tv } from '../utils/tv'
|
|
8
8
|
import type { ButtonProps } from '../types'
|
|
9
|
+
import type { EmitsToProps } from '../types/utils'
|
|
9
10
|
|
|
10
11
|
const appConfigSlideover = _appConfig as AppConfig & { ui: { slideover: Partial<typeof theme> } }
|
|
11
12
|
|
|
@@ -17,7 +18,7 @@ export interface SlideoverProps extends DialogRootProps {
|
|
|
17
18
|
title?: string
|
|
18
19
|
description?: string
|
|
19
20
|
/** The content of the slideover. */
|
|
20
|
-
content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'>
|
|
21
|
+
content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<DialogContentEmits>>
|
|
21
22
|
/**
|
|
22
23
|
* Render an overlay behind the slideover.
|
|
23
24
|
* @defaultValue true
|
|
@@ -69,7 +70,7 @@ export interface SlideoverSlots {
|
|
|
69
70
|
header(props?: {}): any
|
|
70
71
|
title(props?: {}): any
|
|
71
72
|
description(props?: {}): any
|
|
72
|
-
close(props: { ui:
|
|
73
|
+
close(props: { ui: ReturnType<typeof slideover> }): any
|
|
73
74
|
body(props?: {}): any
|
|
74
75
|
footer(props?: {}): any
|
|
75
76
|
}
|
|
@@ -101,18 +102,20 @@ const appConfig = useAppConfig()
|
|
|
101
102
|
const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'modal'), emits)
|
|
102
103
|
const contentProps = toRef(() => props.content)
|
|
103
104
|
const contentEvents = computed(() => {
|
|
105
|
+
const events = {
|
|
106
|
+
closeAutoFocus: (e: Event) => e.preventDefault()
|
|
107
|
+
}
|
|
108
|
+
|
|
104
109
|
if (!props.dismissible) {
|
|
105
110
|
return {
|
|
106
111
|
pointerDownOutside: (e: Event) => e.preventDefault(),
|
|
107
112
|
interactOutside: (e: Event) => e.preventDefault(),
|
|
108
113
|
escapeKeyDown: (e: Event) => e.preventDefault(),
|
|
109
|
-
|
|
114
|
+
...events
|
|
110
115
|
}
|
|
111
116
|
}
|
|
112
117
|
|
|
113
|
-
return
|
|
114
|
-
closeAutoFocus: (e: Event) => e.preventDefault()
|
|
115
|
-
}
|
|
118
|
+
return events
|
|
116
119
|
})
|
|
117
120
|
|
|
118
121
|
const ui = computed(() => slideover({
|
|
@@ -162,7 +165,7 @@ const ui = computed(() => slideover({
|
|
|
162
165
|
</DialogDescription>
|
|
163
166
|
</div>
|
|
164
167
|
|
|
165
|
-
<DialogClose as-child>
|
|
168
|
+
<DialogClose v-if="close || !!slots.close" as-child>
|
|
166
169
|
<slot name="close" :ui="ui">
|
|
167
170
|
<UButton
|
|
168
171
|
v-if="close"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<!-- eslint-disable vue/block-tag-newline -->
|
|
1
2
|
<script lang="ts">
|
|
2
3
|
import type { VariantProps } from 'tailwind-variants'
|
|
3
4
|
import type { StepperRootProps, StepperRootEmits } from 'reka-ui'
|
|
@@ -24,9 +25,10 @@ export interface StepperItem {
|
|
|
24
25
|
icon?: string
|
|
25
26
|
content?: string
|
|
26
27
|
disabled?: boolean
|
|
28
|
+
[key: string]: any
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
export interface StepperProps<T extends StepperItem> extends Pick<StepperRootProps, 'linear'> {
|
|
31
|
+
export interface StepperProps<T extends StepperItem = StepperItem> extends Pick<StepperRootProps, 'linear'> {
|
|
30
32
|
/**
|
|
31
33
|
* The element or component this component should render as.
|
|
32
34
|
* @defaultValue 'div'
|
|
@@ -55,19 +57,20 @@ export interface StepperProps<T extends StepperItem> extends Pick<StepperRootPro
|
|
|
55
57
|
class?: any
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
export type StepperEmits<T> = Omit<StepperRootEmits, 'update:modelValue'> & {
|
|
60
|
+
export type StepperEmits<T extends StepperItem = StepperItem> = Omit<StepperRootEmits, 'update:modelValue'> & {
|
|
59
61
|
next: [payload: T]
|
|
60
62
|
prev: [payload: T]
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
type SlotProps<T extends StepperItem> = (props: { item: T }) => any
|
|
64
66
|
|
|
65
|
-
export type StepperSlots<T extends StepperItem> = {
|
|
67
|
+
export type StepperSlots<T extends StepperItem = StepperItem> = {
|
|
66
68
|
indicator: SlotProps<T>
|
|
67
69
|
title: SlotProps<T>
|
|
68
70
|
description: SlotProps<T>
|
|
69
71
|
content: SlotProps<T>
|
|
70
|
-
} & DynamicSlots<T
|
|
72
|
+
} & DynamicSlots<T>
|
|
73
|
+
|
|
71
74
|
</script>
|
|
72
75
|
|
|
73
76
|
<script setup lang="ts" generic="T extends StepperItem">
|
|
@@ -106,7 +109,7 @@ const currentStepIndex = computed({
|
|
|
106
109
|
}
|
|
107
110
|
})
|
|
108
111
|
|
|
109
|
-
const currentStep = computed(() => props.items?.[currentStepIndex.value]
|
|
112
|
+
const currentStep = computed(() => props.items?.[currentStepIndex.value])
|
|
110
113
|
const hasNext = computed(() => currentStepIndex.value < props.items?.length - 1)
|
|
111
114
|
const hasPrev = computed(() => currentStepIndex.value > 0)
|
|
112
115
|
|
|
@@ -114,13 +117,13 @@ defineExpose({
|
|
|
114
117
|
next() {
|
|
115
118
|
if (hasNext.value) {
|
|
116
119
|
currentStepIndex.value += 1
|
|
117
|
-
emits('next', currentStep.value)
|
|
120
|
+
emits('next', currentStep.value as T)
|
|
118
121
|
}
|
|
119
122
|
},
|
|
120
123
|
prev() {
|
|
121
124
|
if (hasPrev.value) {
|
|
122
125
|
currentStepIndex.value -= 1
|
|
123
|
-
emits('prev', currentStep.value)
|
|
126
|
+
emits('prev', currentStep.value as T)
|
|
124
127
|
}
|
|
125
128
|
},
|
|
126
129
|
hasNext,
|
|
@@ -171,10 +174,10 @@ defineExpose({
|
|
|
171
174
|
</StepperItem>
|
|
172
175
|
</div>
|
|
173
176
|
|
|
174
|
-
<div v-if="currentStep?.content || !!slots.content ||
|
|
177
|
+
<div v-if="currentStep?.content || !!slots.content || currentStep?.slot" :class="ui.content({ class: props.ui?.description })">
|
|
175
178
|
<slot
|
|
176
|
-
:name="
|
|
177
|
-
:item="currentStep"
|
|
179
|
+
:name="((currentStep?.slot || 'content') as keyof StepperSlots<T>)"
|
|
180
|
+
:item="(currentStep as Extract<T, { slot: string }>)"
|
|
178
181
|
>
|
|
179
182
|
{{ currentStep?.content }}
|
|
180
183
|
</slot>
|
|
@@ -75,6 +75,11 @@ export interface TableProps<T extends TableData> extends TableOptions<T> {
|
|
|
75
75
|
data?: T[]
|
|
76
76
|
columns?: TableColumn<T>[]
|
|
77
77
|
caption?: string
|
|
78
|
+
/**
|
|
79
|
+
* The text to display when the table is empty.
|
|
80
|
+
* @defaultValue t('table.noData')
|
|
81
|
+
*/
|
|
82
|
+
empty?: string
|
|
78
83
|
/**
|
|
79
84
|
* Whether the table should have a sticky header.
|
|
80
85
|
* @defaultValue false
|
|
@@ -289,7 +294,7 @@ function handleRowSelect(row: TableRow<T>, e: Event) {
|
|
|
289
294
|
return
|
|
290
295
|
}
|
|
291
296
|
const target = e.target as HTMLElement
|
|
292
|
-
const isInteractive = target.closest('button')
|
|
297
|
+
const isInteractive = target.closest('button') || target.closest('a')
|
|
293
298
|
if (isInteractive) {
|
|
294
299
|
return
|
|
295
300
|
}
|
|
@@ -308,7 +313,7 @@ defineExpose({
|
|
|
308
313
|
<template>
|
|
309
314
|
<Primitive :as="as" :class="ui.root({ class: [props.class, props.ui?.root] })">
|
|
310
315
|
<table :class="ui.base({ class: [props.ui?.base] })">
|
|
311
|
-
<caption v-if="caption" :class="ui.caption({ class: [props.ui?.caption] })">
|
|
316
|
+
<caption v-if="caption || !!slots.caption" :class="ui.caption({ class: [props.ui?.caption] })">
|
|
312
317
|
<slot name="caption">
|
|
313
318
|
{{ caption }}
|
|
314
319
|
</slot>
|
|
@@ -369,7 +374,7 @@ defineExpose({
|
|
|
369
374
|
<tr v-else>
|
|
370
375
|
<td :colspan="columns?.length" :class="ui.empty({ class: props.ui?.empty })">
|
|
371
376
|
<slot name="empty">
|
|
372
|
-
{{ t('table.noData') }}
|
|
377
|
+
{{ empty || t('table.noData') }}
|
|
373
378
|
</slot>
|
|
374
379
|
</td>
|
|
375
380
|
</tr>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<!-- eslint-disable vue/block-tag-newline -->
|
|
1
2
|
<script lang="ts">
|
|
2
3
|
import type { VariantProps } from 'tailwind-variants'
|
|
3
4
|
import type { TabsRootProps, TabsRootEmits } from 'reka-ui'
|
|
@@ -24,11 +25,12 @@ export interface TabsItem {
|
|
|
24
25
|
/** A unique value for the tab item. Defaults to the index. */
|
|
25
26
|
value?: string | number
|
|
26
27
|
disabled?: boolean
|
|
28
|
+
[key: string]: any
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
type TabsVariants = VariantProps<typeof tabs>
|
|
30
32
|
|
|
31
|
-
export interface TabsProps<T> extends Pick<TabsRootProps<string | number>, 'defaultValue' | 'modelValue' | 'activationMode' | 'unmountOnHide'> {
|
|
33
|
+
export interface TabsProps<T extends TabsItem = TabsItem> extends Pick<TabsRootProps<string | number>, 'defaultValue' | 'modelValue' | 'activationMode' | 'unmountOnHide'> {
|
|
32
34
|
/**
|
|
33
35
|
* The element or component this component should render as.
|
|
34
36
|
* @defaultValue 'div'
|
|
@@ -68,14 +70,15 @@ export interface TabsProps<T> extends Pick<TabsRootProps<string | number>, 'defa
|
|
|
68
70
|
|
|
69
71
|
export interface TabsEmits extends TabsRootEmits<string | number> {}
|
|
70
72
|
|
|
71
|
-
type SlotProps<T> = (props: { item: T, index: number }) => any
|
|
73
|
+
type SlotProps<T extends TabsItem> = (props: { item: T, index: number }) => any
|
|
72
74
|
|
|
73
|
-
export type TabsSlots<T extends
|
|
75
|
+
export type TabsSlots<T extends TabsItem = TabsItem> = {
|
|
74
76
|
leading: SlotProps<T>
|
|
75
77
|
default: SlotProps<T>
|
|
76
78
|
trailing: SlotProps<T>
|
|
77
79
|
content: SlotProps<T>
|
|
78
|
-
} & DynamicSlots<T,
|
|
80
|
+
} & DynamicSlots<T, undefined, { index: number }>
|
|
81
|
+
|
|
79
82
|
</script>
|
|
80
83
|
|
|
81
84
|
<script setup lang="ts" generic="T extends TabsItem">
|
|
@@ -127,7 +130,7 @@ const ui = computed(() => tabs({
|
|
|
127
130
|
|
|
128
131
|
<template v-if="!!content">
|
|
129
132
|
<TabsContent v-for="(item, index) of items" :key="index" :value="item.value || String(index)" :class="ui.content({ class: props.ui?.content })">
|
|
130
|
-
<slot :name="item.slot || 'content'" :item="item" :index="index">
|
|
133
|
+
<slot :name="((item.slot || 'content') as keyof TabsSlots<T>)" :item="(item as Extract<T, { slot: string; }>)" :index="index">
|
|
131
134
|
{{ item.content }}
|
|
132
135
|
</slot>
|
|
133
136
|
</TabsContent>
|
|
@@ -66,7 +66,7 @@ export interface ToastSlots {
|
|
|
66
66
|
title(props?: {}): any
|
|
67
67
|
description(props?: {}): any
|
|
68
68
|
actions(props?: {}): any
|
|
69
|
-
close(props: { ui:
|
|
69
|
+
close(props: { ui: ReturnType<typeof toast> }): any
|
|
70
70
|
}
|
|
71
71
|
</script>
|
|
72
72
|
|
|
@@ -168,7 +168,7 @@ defineExpose({
|
|
|
168
168
|
</slot>
|
|
169
169
|
</template>
|
|
170
170
|
|
|
171
|
-
<ToastClose as-child>
|
|
171
|
+
<ToastClose v-if="close || !!slots.close" as-child>
|
|
172
172
|
<slot name="close" :ui="ui">
|
|
173
173
|
<UButton
|
|
174
174
|
v-if="close"
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { TooltipRootProps, TooltipRootEmits, TooltipContentProps, TooltipArrowProps } from 'reka-ui'
|
|
2
|
+
import type { TooltipRootProps, TooltipRootEmits, TooltipContentProps, TooltipContentEmits, TooltipArrowProps } 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/tooltip'
|
|
6
6
|
import { tv } from '../utils/tv'
|
|
7
7
|
import type { KbdProps } from '../types'
|
|
8
|
+
import type { EmitsToProps } from '../types/utils'
|
|
8
9
|
|
|
9
10
|
const appConfigTooltip = _appConfig as AppConfig & { ui: { tooltip: Partial<typeof theme> } }
|
|
10
11
|
|
|
@@ -19,7 +20,7 @@ export interface TooltipProps extends TooltipRootProps {
|
|
|
19
20
|
* The content of the tooltip.
|
|
20
21
|
* @defaultValue { side: 'bottom', sideOffset: 8, collisionPadding: 8 }
|
|
21
22
|
*/
|
|
22
|
-
content?: Omit<TooltipContentProps, 'as' | 'asChild'>
|
|
23
|
+
content?: Omit<TooltipContentProps, 'as' | 'asChild'> & Partial<EmitsToProps<TooltipContentEmits>>
|
|
23
24
|
/**
|
|
24
25
|
* Display an arrow alongside the tooltip.
|
|
25
26
|
* @defaultValue false
|