@nuxt/ui 3.0.0-alpha.11 → 3.0.0-alpha.12
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/alert.ts +1 -1
- package/.nuxt/ui/avatar.ts +1 -1
- package/.nuxt/ui/button.ts +0 -1
- package/.nuxt/ui/context-menu.ts +1 -1
- package/.nuxt/ui/dropdown-menu.ts +1 -1
- package/.nuxt/ui/navigation-menu.ts +79 -11
- package/cli/package.json +2 -2
- package/cli/templates.mjs +6 -6
- package/dist/chunks/component-meta.cjs +2590 -2511
- package/dist/chunks/component-meta.mjs +2590 -2511
- package/dist/client/devtools/200.html +1 -1
- package/dist/client/devtools/404.html +1 -1
- package/dist/client/devtools/_nuxt/{CQ-XZFu7.js → CgoQwOTV.js} +26 -23
- package/dist/client/devtools/_nuxt/{DNZFyo26.js → DLmPhbaq.js} +1 -1
- package/dist/client/devtools/_nuxt/{Dtz-o9GB.js → DYuJNiSW.js} +1 -1
- package/dist/client/devtools/_nuxt/builds/latest.json +1 -1
- package/dist/client/devtools/_nuxt/builds/meta/3184ffe2-7577-4441-af98-fe6e81c30e3a.json +1 -0
- package/dist/client/devtools/_nuxt/entry.ChJczkiN.css +1 -0
- package/dist/client/devtools/_nuxt/error-404.DyY3rYaL.css +1 -0
- package/dist/client/devtools/_nuxt/error-500.D_1PCaJh.css +1 -0
- package/dist/client/devtools/index.html +1 -1
- package/dist/module.cjs +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/components/Accordion.vue +2 -2
- package/dist/runtime/components/Alert.vue +2 -2
- package/dist/runtime/components/Avatar.vue +31 -10
- package/dist/runtime/components/AvatarGroup.vue +2 -2
- package/dist/runtime/components/Badge.vue +2 -2
- package/dist/runtime/components/Breadcrumb.vue +2 -2
- package/dist/runtime/components/Button.vue +2 -2
- package/dist/runtime/components/ButtonGroup.vue +2 -2
- package/dist/runtime/components/Calendar.vue +4 -2
- package/dist/runtime/components/Card.vue +2 -2
- package/dist/runtime/components/Carousel.vue +2 -2
- package/dist/runtime/components/Checkbox.vue +4 -4
- package/dist/runtime/components/Chip.vue +2 -2
- package/dist/runtime/components/Collapsible.vue +2 -2
- package/dist/runtime/components/ColorPicker.vue +46 -15
- package/dist/runtime/components/CommandPalette.vue +2 -2
- package/dist/runtime/components/Container.vue +2 -2
- package/dist/runtime/components/ContextMenu.vue +2 -2
- package/dist/runtime/components/ContextMenuContent.vue +1 -1
- package/dist/runtime/components/Drawer.vue +2 -2
- package/dist/runtime/components/DropdownMenu.vue +2 -2
- package/dist/runtime/components/DropdownMenuContent.vue +1 -1
- package/dist/runtime/components/Form.vue +46 -17
- package/dist/runtime/components/FormField.vue +12 -6
- package/dist/runtime/components/Input.vue +5 -4
- package/dist/runtime/components/InputMenu.vue +6 -5
- package/dist/runtime/components/InputNumber.vue +7 -4
- package/dist/runtime/components/Kbd.vue +2 -2
- package/dist/runtime/components/Link.vue +2 -2
- package/dist/runtime/components/Modal.vue +2 -2
- package/dist/runtime/components/NavigationMenu.vue +89 -66
- package/dist/runtime/components/Pagination.vue +2 -2
- package/dist/runtime/components/PinInput.vue +5 -4
- package/dist/runtime/components/Popover.vue +2 -2
- package/dist/runtime/components/Progress.vue +2 -2
- package/dist/runtime/components/RadioGroup.vue +4 -4
- package/dist/runtime/components/Select.vue +9 -7
- package/dist/runtime/components/SelectMenu.vue +5 -4
- package/dist/runtime/components/Separator.vue +2 -2
- package/dist/runtime/components/Skeleton.vue +2 -2
- package/dist/runtime/components/Slideover.vue +2 -2
- package/dist/runtime/components/Slider.vue +4 -4
- package/dist/runtime/components/Stepper.vue +2 -2
- package/dist/runtime/components/Switch.vue +4 -4
- package/dist/runtime/components/Table.vue +2 -2
- package/dist/runtime/components/Tabs.vue +2 -2
- package/dist/runtime/components/Textarea.vue +5 -4
- package/dist/runtime/components/Toast.vue +2 -2
- package/dist/runtime/components/Toaster.vue +2 -2
- package/dist/runtime/components/Tooltip.vue +2 -2
- package/dist/runtime/composables/useFormField.d.ts +6 -2
- package/dist/runtime/composables/useFormField.js +18 -11
- package/dist/runtime/composables/useToast.d.ts +1 -1
- package/dist/runtime/composables/useToast.js +21 -7
- package/dist/runtime/index.css +1 -1
- package/dist/runtime/locale/es.js +2 -2
- package/dist/runtime/locale/he.js +53 -0
- package/dist/runtime/locale/hi.js +54 -0
- package/dist/runtime/locale/hu.d.ts +2 -0
- package/dist/runtime/locale/hu.js +54 -0
- package/dist/runtime/locale/index.d.ts +5 -2
- package/dist/runtime/locale/index.js +5 -2
- package/dist/runtime/locale/km.d.ts +2 -0
- package/dist/runtime/locale/km.js +54 -0
- package/dist/runtime/locale/nb_no.d.ts +2 -0
- package/dist/runtime/locale/nb_no.js +54 -0
- package/dist/runtime/locale/zh_cn.d.ts +2 -0
- package/dist/runtime/locale/{zh_hans.js → zh_cn.js} +1 -1
- package/dist/runtime/locale/zh_tw.d.ts +2 -0
- package/dist/runtime/locale/{zh_hant.js → zh_tw.js} +1 -1
- package/dist/runtime/plugins/colors.js +7 -4
- package/dist/runtime/types/form.d.ts +21 -12
- package/dist/runtime/utils/form.js +3 -5
- package/dist/runtime/utils/tv.js +2 -2
- package/dist/runtime/vue/components/Link.vue +2 -2
- package/dist/shared/{ui.BKsd22-Q.mjs → ui.CENZ17mM.mjs} +72 -16
- package/dist/shared/{ui.DCjJwb6l.cjs → ui.ClMXjgLK.cjs} +72 -16
- package/dist/unplugin.cjs +1 -1
- package/dist/unplugin.mjs +1 -1
- package/dist/vite.cjs +1 -1
- package/dist/vite.mjs +1 -1
- package/package.json +22 -23
- package/dist/client/devtools/_nuxt/builds/meta/1f5fbd7b-9d97-480e-9ae4-e02b4ff7018b.json +0 -1
- package/dist/client/devtools/_nuxt/entry.qOdop8tq.css +0 -1
- package/dist/client/devtools/_nuxt/error-404.D-rS9BDk.css +0 -1
- package/dist/client/devtools/_nuxt/error-500.CfcCHs91.css +0 -1
- /package/dist/runtime/locale/{zh_hans.d.ts → he.d.ts} +0 -0
- /package/dist/runtime/locale/{zh_hant.d.ts → hi.d.ts} +0 -0
|
@@ -7,9 +7,9 @@ import theme from '#build/ui/checkbox'
|
|
|
7
7
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
8
8
|
import { tv } from '../utils/tv'
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const appConfigCheckbox = _appConfig as AppConfig & { ui: { checkbox: Partial<typeof theme> } }
|
|
11
11
|
|
|
12
|
-
const checkbox = tv({ extend: tv(theme), ...(
|
|
12
|
+
const checkbox = tv({ extend: tv(theme), ...(appConfigCheckbox.ui?.checkbox || {}) })
|
|
13
13
|
|
|
14
14
|
type CheckboxVariants = VariantProps<typeof checkbox>
|
|
15
15
|
|
|
@@ -66,7 +66,7 @@ const modelValue = defineModel<boolean | 'indeterminate'>({ default: undefined }
|
|
|
66
66
|
const rootProps = useForwardProps(reactivePick(props, 'required', 'value', 'defaultValue'))
|
|
67
67
|
|
|
68
68
|
const appConfig = useAppConfig()
|
|
69
|
-
const { id: _id, emitFormChange, emitFormInput, size, color, name, disabled } = useFormField<CheckboxProps>(props)
|
|
69
|
+
const { id: _id, emitFormChange, emitFormInput, size, color, name, disabled, ariaAttrs } = useFormField<CheckboxProps>(props)
|
|
70
70
|
const id = _id.value ?? useId()
|
|
71
71
|
|
|
72
72
|
const ui = computed(() => checkbox({
|
|
@@ -92,7 +92,7 @@ function onUpdate(value: any) {
|
|
|
92
92
|
<div :class="ui.container({ class: props.ui?.container })">
|
|
93
93
|
<CheckboxRoot
|
|
94
94
|
:id="id"
|
|
95
|
-
v-bind="rootProps"
|
|
95
|
+
v-bind="{ ...rootProps, ...ariaAttrs }"
|
|
96
96
|
v-model="modelValue"
|
|
97
97
|
:name="name"
|
|
98
98
|
:disabled="disabled"
|
|
@@ -6,9 +6,9 @@ import theme from '#build/ui/chip'
|
|
|
6
6
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
7
7
|
import { tv } from '../utils/tv'
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const appConfigChip = _appConfig as AppConfig & { ui: { chip: Partial<typeof theme> } }
|
|
10
10
|
|
|
11
|
-
const chip = tv({ extend: tv(theme), ...(
|
|
11
|
+
const chip = tv({ extend: tv(theme), ...(appConfigChip.ui?.chip || {}) })
|
|
12
12
|
|
|
13
13
|
type ChipVariants = VariantProps<typeof chip>
|
|
14
14
|
|
|
@@ -6,9 +6,9 @@ import theme from '#build/ui/collapsible'
|
|
|
6
6
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
7
7
|
import { tv } from '../utils/tv'
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const appConfigCollapsible = _appConfig as AppConfig & { ui: { collapsible: Partial<typeof theme> } }
|
|
10
10
|
|
|
11
|
-
const collapsible = tv({ extend: tv(theme), ...(
|
|
11
|
+
const collapsible = tv({ extend: tv(theme), ...(appConfigCollapsible.ui?.collapsible || {}) })
|
|
12
12
|
|
|
13
13
|
export interface CollapsibleProps extends Pick<CollapsibleRootProps, 'defaultOpen' | 'open' | 'disabled' | 'unmountOnHide'> {
|
|
14
14
|
/**
|
|
@@ -5,10 +5,11 @@ import type { AppConfig } from '@nuxt/schema'
|
|
|
5
5
|
import _appConfig from '#build/app.config'
|
|
6
6
|
import theme from '#build/ui/color-picker'
|
|
7
7
|
import { tv } from '../utils/tv'
|
|
8
|
+
import type { HSLObject } from 'colortranslator'
|
|
8
9
|
|
|
9
|
-
const
|
|
10
|
+
const appConfigColorPicker = _appConfig as AppConfig & { ui: { colorPicker: Partial<typeof theme> } }
|
|
10
11
|
|
|
11
|
-
const colorPicker = tv({ extend: tv(theme), ...(
|
|
12
|
+
const colorPicker = tv({ extend: tv(theme), ...(appConfigColorPicker.ui?.colorPicker || {}) })
|
|
12
13
|
|
|
13
14
|
type ColorPickerVariants = VariantProps<typeof colorPicker>
|
|
14
15
|
|
|
@@ -18,6 +19,27 @@ type HSVColor = {
|
|
|
18
19
|
v: number
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
function HSLtoHSV(hsl: HSLObject): HSVColor {
|
|
23
|
+
const x = hsl.S * (hsl.L < 50 ? hsl.L : 100 - hsl.L)
|
|
24
|
+
const v = hsl.L + (x / 100)
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
h: hsl.H,
|
|
28
|
+
s: hsl.L === 0 ? hsl.S : 2 * x / v,
|
|
29
|
+
v
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function HSVtoHSL(hsv: HSVColor): HSLObject {
|
|
34
|
+
const x = (200 - hsv.s) * hsv.v / 100
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
H: hsv.h,
|
|
38
|
+
S: x === 0 || x === 200 ? 0 : Math.round(hsv.s * hsv.v / (x <= 100 ? x : 200 - x)),
|
|
39
|
+
L: Math.round(x / 2)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
21
43
|
export type ColorPickerProps = {
|
|
22
44
|
/**
|
|
23
45
|
* The element or component this component should render as.
|
|
@@ -40,7 +62,7 @@ export type ColorPickerProps = {
|
|
|
40
62
|
* Format of the color
|
|
41
63
|
* @defaultValue 'hex'
|
|
42
64
|
*/
|
|
43
|
-
format?: 'hex' | 'rgb' | 'hsl' | '
|
|
65
|
+
format?: 'hex' | 'rgb' | 'hsl' | 'cmyk' | 'lab'
|
|
44
66
|
size?: ColorPickerVariants['size']
|
|
45
67
|
class?: any
|
|
46
68
|
ui?: Partial<typeof colorPicker.slots>
|
|
@@ -52,7 +74,7 @@ import { ref, nextTick, computed, toValue } from 'vue'
|
|
|
52
74
|
import { Primitive } from 'reka-ui'
|
|
53
75
|
import { useEventListener, useElementBounding, watchThrottled, watchPausable } from '@vueuse/core'
|
|
54
76
|
import { isClient } from '@vueuse/shared'
|
|
55
|
-
import
|
|
77
|
+
import { ColorTranslator } from 'colortranslator'
|
|
56
78
|
|
|
57
79
|
const props = withDefaults(defineProps<ColorPickerProps>(), {
|
|
58
80
|
format: 'hex',
|
|
@@ -64,28 +86,37 @@ const modelValue = defineModel<string>(undefined)
|
|
|
64
86
|
const pickedColor = computed<HSVColor>({
|
|
65
87
|
get() {
|
|
66
88
|
try {
|
|
67
|
-
const color =
|
|
68
|
-
|
|
89
|
+
const color = new ColorTranslator(modelValue.value || props.defaultValue)
|
|
90
|
+
|
|
91
|
+
return HSLtoHSV(color.HSLObject)
|
|
69
92
|
} catch (_) {
|
|
70
93
|
return { h: 0, s: 0, v: 100 }
|
|
71
94
|
}
|
|
72
95
|
},
|
|
73
96
|
set(value) {
|
|
74
|
-
const color =
|
|
97
|
+
const color = new ColorTranslator(HSVtoHSL(value), {
|
|
98
|
+
decimals: 2,
|
|
99
|
+
labUnit: 'percent',
|
|
100
|
+
cmykUnit: 'percent',
|
|
101
|
+
cmykFunction: 'cmyk'
|
|
102
|
+
})
|
|
75
103
|
|
|
76
104
|
switch (props.format) {
|
|
77
105
|
case 'rgb':
|
|
78
|
-
modelValue.value = color.
|
|
106
|
+
modelValue.value = color.RGB
|
|
79
107
|
break
|
|
80
108
|
case 'hsl':
|
|
81
|
-
modelValue.value = color.
|
|
109
|
+
modelValue.value = color.HSL
|
|
110
|
+
break
|
|
111
|
+
case 'cmyk':
|
|
112
|
+
modelValue.value = color.CMYK
|
|
82
113
|
break
|
|
83
|
-
case '
|
|
84
|
-
modelValue.value = color.
|
|
114
|
+
case 'lab':
|
|
115
|
+
modelValue.value = color.CIELab
|
|
85
116
|
break
|
|
86
117
|
case 'hex':
|
|
87
118
|
default:
|
|
88
|
-
modelValue.value = color.
|
|
119
|
+
modelValue.value = color.HEX
|
|
89
120
|
}
|
|
90
121
|
}
|
|
91
122
|
})
|
|
@@ -202,18 +233,18 @@ watchThrottled([selectorThumbPosition, trackThumbPosition], () => {
|
|
|
202
233
|
nextTick(resumeWatchColor)
|
|
203
234
|
}, { throttle: () => props.throttle })
|
|
204
235
|
|
|
205
|
-
const trackThumbColor = computed(() =>
|
|
236
|
+
const trackThumbColor = computed(() => new ColorTranslator(HSVtoHSL({
|
|
206
237
|
h: normalizeHue(trackThumbPosition.value.y),
|
|
207
238
|
s: 100,
|
|
208
239
|
v: 100
|
|
209
|
-
}).
|
|
240
|
+
})).HEX)
|
|
210
241
|
|
|
211
242
|
const selectorStyle = computed(() => ({
|
|
212
243
|
backgroundColor: trackThumbColor.value
|
|
213
244
|
}))
|
|
214
245
|
|
|
215
246
|
const selectorThumbStyle = computed(() => ({
|
|
216
|
-
backgroundColor:
|
|
247
|
+
backgroundColor: new ColorTranslator(modelValue.value || props.defaultValue).HEX,
|
|
217
248
|
left: `${selectorThumbPosition.value.x}%`,
|
|
218
249
|
top: `${selectorThumbPosition.value.y}%`
|
|
219
250
|
}))
|
|
@@ -11,9 +11,9 @@ import { tv } from '../utils/tv'
|
|
|
11
11
|
import type { AvatarProps, ButtonProps, ChipProps, KbdProps, InputProps } from '../types'
|
|
12
12
|
import type { DynamicSlots, PartialString } from '../types/utils'
|
|
13
13
|
|
|
14
|
-
const
|
|
14
|
+
const appConfigCommandPalette = _appConfig as AppConfig & { ui: { commandPalette: Partial<typeof theme> } }
|
|
15
15
|
|
|
16
|
-
const commandPalette = tv({ extend: tv(theme), ...(
|
|
16
|
+
const commandPalette = tv({ extend: tv(theme), ...(appConfigCommandPalette.ui?.commandPalette || {}) })
|
|
17
17
|
|
|
18
18
|
export interface CommandPaletteItem {
|
|
19
19
|
prefix?: string
|
|
@@ -5,9 +5,9 @@ import theme from '#build/ui/container'
|
|
|
5
5
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
6
6
|
import { tv } from '../utils/tv'
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const appConfigContainer = _appConfig as AppConfig & { ui: { container: Partial<typeof theme> } }
|
|
9
9
|
|
|
10
|
-
const container = tv({ extend: tv(theme), ...(
|
|
10
|
+
const container = tv({ extend: tv(theme), ...(appConfigContainer.ui?.container || {}) })
|
|
11
11
|
|
|
12
12
|
export interface ContainerProps {
|
|
13
13
|
/**
|
|
@@ -9,9 +9,9 @@ import { tv } from '../utils/tv'
|
|
|
9
9
|
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
|
10
10
|
import type { DynamicSlots, PartialString } from '../types/utils'
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const appConfigContextMenu = _appConfig as AppConfig & { ui: { contextMenu: Partial<typeof theme> } }
|
|
13
13
|
|
|
14
|
-
const contextMenu = tv({ extend: tv(theme), ...(
|
|
14
|
+
const contextMenu = tv({ extend: tv(theme), ...(appConfigContextMenu.ui?.contextMenu || {}) })
|
|
15
15
|
|
|
16
16
|
type ContextMenuVariants = VariantProps<typeof contextMenu>
|
|
17
17
|
|
|
@@ -42,7 +42,7 @@ const emits = defineEmits<ContextMenuContentEmits>()
|
|
|
42
42
|
const slots = defineSlots<ContextMenuSlots<T>>()
|
|
43
43
|
|
|
44
44
|
const appConfig = useAppConfig()
|
|
45
|
-
const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'class', 'ui'), emits)
|
|
45
|
+
const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'labelKey', 'checkedIcon', 'loadingIcon', 'class', 'ui', 'uiOverride'), emits)
|
|
46
46
|
const proxySlots = omit(slots, ['default']) as Record<string, ContextMenuSlots<T>[string]>
|
|
47
47
|
|
|
48
48
|
const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: ContextMenuItem, active?: boolean, index: number }>()
|
|
@@ -7,9 +7,9 @@ import theme from '#build/ui/drawer'
|
|
|
7
7
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
8
8
|
import { tv } from '../utils/tv'
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const appConfigDrawer = _appConfig as AppConfig & { ui: { drawer: Partial<typeof theme> } }
|
|
11
11
|
|
|
12
|
-
const drawer = tv({ extend: tv(theme), ...(
|
|
12
|
+
const drawer = tv({ extend: tv(theme), ...(appConfigDrawer.ui?.drawer || {}) })
|
|
13
13
|
|
|
14
14
|
export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | 'closeThreshold' | 'defaultOpen' | 'direction' | 'fadeFromIndex' | 'fixed' | 'modal' | 'nested' | 'direction' | 'open' | 'scrollLockTimeout' | 'shouldScaleBackground' | 'snapPoints'> {
|
|
15
15
|
/**
|
|
@@ -9,9 +9,9 @@ import { tv } from '../utils/tv'
|
|
|
9
9
|
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
|
10
10
|
import type { DynamicSlots, PartialString } from '../types/utils'
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const appConfigDropdownMenu = _appConfig as AppConfig & { ui: { dropdownMenu: Partial<typeof theme> } }
|
|
13
13
|
|
|
14
|
-
const dropdownMenu = tv({ extend: tv(theme), ...(
|
|
14
|
+
const dropdownMenu = tv({ extend: tv(theme), ...(appConfigDropdownMenu.ui?.dropdownMenu || {}) })
|
|
15
15
|
|
|
16
16
|
type DropdownMenuVariants = VariantProps<typeof dropdownMenu>
|
|
17
17
|
|
|
@@ -48,7 +48,7 @@ const emits = defineEmits<DropdownMenuContentEmits>()
|
|
|
48
48
|
const slots = defineSlots<DropdownMenuContentSlots<T>>()
|
|
49
49
|
|
|
50
50
|
const appConfig = useAppConfig()
|
|
51
|
-
const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'class', 'ui'), emits)
|
|
51
|
+
const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'labelKey', 'checkedIcon', 'loadingIcon', 'class', 'ui', 'uiOverride'), emits)
|
|
52
52
|
const proxySlots = omit(slots, ['default']) as Record<string, DropdownMenuContentSlots<T>[string]>
|
|
53
53
|
|
|
54
54
|
const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: DropdownMenuItem, active?: boolean, index: number }>()
|
|
@@ -5,10 +5,11 @@ import theme from '#build/ui/form'
|
|
|
5
5
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
6
6
|
import { tv } from '../utils/tv'
|
|
7
7
|
import type { FormSchema, FormError, FormInputEvents, FormErrorEvent, FormSubmitEvent, FormEvent, Form, FormErrorWithId } from '../types/form'
|
|
8
|
+
import type { DeepReadonly } from 'vue'
|
|
8
9
|
|
|
9
|
-
const
|
|
10
|
+
const appConfigForm = _appConfig as AppConfig & { ui: { form: Partial<typeof theme> } }
|
|
10
11
|
|
|
11
|
-
const form = tv({ extend: tv(theme), ...(
|
|
12
|
+
const form = tv({ extend: tv(theme), ...(appConfigForm.ui?.form || {}) })
|
|
12
13
|
|
|
13
14
|
export interface FormProps<T extends object> {
|
|
14
15
|
id?: string | number
|
|
@@ -52,7 +53,7 @@ defineSlots<FormSlots>()
|
|
|
52
53
|
|
|
53
54
|
const formId = props.id ?? useId() as string
|
|
54
55
|
|
|
55
|
-
const bus = useEventBus<FormEvent
|
|
56
|
+
const bus = useEventBus<FormEvent<T>>(`form-${formId}`)
|
|
56
57
|
const parentBus = inject(
|
|
57
58
|
formBusInjectionKey,
|
|
58
59
|
undefined
|
|
@@ -60,7 +61,7 @@ const parentBus = inject(
|
|
|
60
61
|
|
|
61
62
|
provide(formBusInjectionKey, bus)
|
|
62
63
|
|
|
63
|
-
const nestedForms = ref<Map<string | number, { validate:
|
|
64
|
+
const nestedForms = ref<Map<string | number, { validate: typeof _validate }>>(new Map())
|
|
64
65
|
|
|
65
66
|
onMounted(async () => {
|
|
66
67
|
bus.on(async (event) => {
|
|
@@ -68,8 +69,24 @@ onMounted(async () => {
|
|
|
68
69
|
nestedForms.value.set(event.formId, { validate: event.validate })
|
|
69
70
|
} else if (event.type === 'detach') {
|
|
70
71
|
nestedForms.value.delete(event.formId)
|
|
71
|
-
} else if (props.validateOn?.includes(event.type
|
|
72
|
-
|
|
72
|
+
} else if (props.validateOn?.includes(event.type)) {
|
|
73
|
+
if (event.type !== 'input') {
|
|
74
|
+
await _validate({ name: event.name, silent: true, nested: false })
|
|
75
|
+
} else if (event.eager || blurredFields.has(event.name)) {
|
|
76
|
+
await _validate({ name: event.name, silent: true, nested: false })
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (event.type === 'blur') {
|
|
81
|
+
blurredFields.add(event.name)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (event.type === 'change' || event.type === 'input' || event.type === 'blur' || event.type === 'focus') {
|
|
85
|
+
touchedFields.add(event.name)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (event.type === 'change' || event.type === 'input') {
|
|
89
|
+
dirtyFields.add(event.name)
|
|
73
90
|
}
|
|
74
91
|
})
|
|
75
92
|
})
|
|
@@ -94,8 +111,12 @@ onUnmounted(() => {
|
|
|
94
111
|
const errors = ref<FormErrorWithId[]>([])
|
|
95
112
|
provide('form-errors', errors)
|
|
96
113
|
|
|
97
|
-
const inputs = ref<
|
|
98
|
-
provide(formInputsInjectionKey, inputs)
|
|
114
|
+
const inputs = ref<{ [P in keyof T]?: { id?: string, pattern?: RegExp } }>({})
|
|
115
|
+
provide(formInputsInjectionKey, inputs as any)
|
|
116
|
+
|
|
117
|
+
const dirtyFields = new Set<keyof T>()
|
|
118
|
+
const touchedFields = new Set<keyof T>()
|
|
119
|
+
const blurredFields = new Set<keyof T>()
|
|
99
120
|
|
|
100
121
|
function resolveErrorIds(errs: FormError[]): FormErrorWithId[] {
|
|
101
122
|
return errs.map(err => ({
|
|
@@ -121,12 +142,12 @@ async function getErrors(): Promise<FormErrorWithId[]> {
|
|
|
121
142
|
return resolveErrorIds(errs)
|
|
122
143
|
}
|
|
123
144
|
|
|
124
|
-
async function _validate(opts: { name?:
|
|
125
|
-
const names = opts.name && !Array.isArray(opts.name) ? [opts.name] : opts.name as
|
|
145
|
+
async function _validate(opts: { name?: keyof T | (keyof T)[], silent?: boolean, nested?: boolean, transform?: boolean } = { silent: false, nested: true, transform: false }): Promise<T | false> {
|
|
146
|
+
const names = opts.name && !Array.isArray(opts.name) ? [opts.name] : opts.name as (keyof T)[]
|
|
126
147
|
|
|
127
148
|
const nestedValidatePromises = !names && opts.nested
|
|
128
149
|
? Array.from(nestedForms.value.values()).map(
|
|
129
|
-
({ validate }) => validate().then(() => undefined).catch((error: Error) => {
|
|
150
|
+
({ validate }) => validate(opts).then(() => undefined).catch((error: Error) => {
|
|
130
151
|
if (!(error instanceof FormValidationException)) {
|
|
131
152
|
throw error
|
|
132
153
|
}
|
|
@@ -151,13 +172,17 @@ async function _validate(opts: { name?: string | string[], silent?: boolean, nes
|
|
|
151
172
|
errors.value = await getErrors()
|
|
152
173
|
}
|
|
153
174
|
|
|
154
|
-
const childErrors = (await Promise.all(nestedValidatePromises)).filter(val => val)
|
|
175
|
+
const childErrors = (await Promise.all(nestedValidatePromises)).filter(val => val !== undefined)
|
|
155
176
|
|
|
156
177
|
if (errors.value.length + childErrors.length > 0) {
|
|
157
178
|
if (opts.silent) return false
|
|
158
179
|
throw new FormValidationException(formId, errors.value, childErrors)
|
|
159
180
|
}
|
|
160
181
|
|
|
182
|
+
if (opts.transform) {
|
|
183
|
+
Object.assign(props.state, transformedState.value)
|
|
184
|
+
}
|
|
185
|
+
|
|
161
186
|
return props.state as T
|
|
162
187
|
}
|
|
163
188
|
|
|
@@ -170,8 +195,7 @@ async function onSubmitWrapper(payload: Event) {
|
|
|
170
195
|
const event = payload as FormSubmitEvent<any>
|
|
171
196
|
|
|
172
197
|
try {
|
|
173
|
-
await _validate({ nested: true })
|
|
174
|
-
event.data = props.schema ? transformedState.value : props.state
|
|
198
|
+
event.data = await _validate({ nested: true, transform: true })
|
|
175
199
|
await props.onSubmit?.(event)
|
|
176
200
|
} catch (error) {
|
|
177
201
|
if (!(error instanceof FormValidationException)) {
|
|
@@ -200,7 +224,7 @@ defineExpose<Form<T>>({
|
|
|
200
224
|
validate: _validate,
|
|
201
225
|
errors,
|
|
202
226
|
|
|
203
|
-
setErrors(errs: FormError[], name?:
|
|
227
|
+
setErrors(errs: FormError[], name?: keyof T) {
|
|
204
228
|
if (name) {
|
|
205
229
|
errors.value = errors.value
|
|
206
230
|
.filter(error => error.name !== name)
|
|
@@ -214,7 +238,7 @@ defineExpose<Form<T>>({
|
|
|
214
238
|
await onSubmitWrapper(new Event('submit'))
|
|
215
239
|
},
|
|
216
240
|
|
|
217
|
-
getErrors(name?:
|
|
241
|
+
getErrors(name?: keyof T) {
|
|
218
242
|
if (name) {
|
|
219
243
|
return errors.value.filter(err => err.name === name)
|
|
220
244
|
}
|
|
@@ -229,7 +253,12 @@ defineExpose<Form<T>>({
|
|
|
229
253
|
}
|
|
230
254
|
},
|
|
231
255
|
|
|
232
|
-
disabled
|
|
256
|
+
disabled,
|
|
257
|
+
dirty: computed(() => !!dirtyFields.size),
|
|
258
|
+
|
|
259
|
+
dirtyFields: readonly(dirtyFields) as DeepReadonly<Set<keyof T>>,
|
|
260
|
+
blurredFields: readonly(blurredFields) as DeepReadonly<Set<keyof T>>,
|
|
261
|
+
touchedFields: readonly(touchedFields) as DeepReadonly<Set<keyof T>>
|
|
233
262
|
})
|
|
234
263
|
</script>
|
|
235
264
|
|
|
@@ -6,9 +6,9 @@ import theme from '#build/ui/form-field'
|
|
|
6
6
|
import { tv } from '../utils/tv'
|
|
7
7
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const appConfigFormField = _appConfig as AppConfig & { ui: { formField: Partial<typeof theme> } }
|
|
10
10
|
|
|
11
|
-
const formField = tv({ extend: tv(theme), ...(
|
|
11
|
+
const formField = tv({ extend: tv(theme), ...(appConfigFormField.ui?.formField || {}) })
|
|
12
12
|
|
|
13
13
|
type FormFieldVariants = VariantProps<typeof formField>
|
|
14
14
|
|
|
@@ -66,6 +66,9 @@ const formErrors = inject<Ref<FormError[]> | null>('form-errors', null)
|
|
|
66
66
|
const error = computed(() => props.error || formErrors?.value?.find(error => error.name === props.name || (props.errorPattern && error.name.match(props.errorPattern)))?.message)
|
|
67
67
|
|
|
68
68
|
const id = ref(useId())
|
|
69
|
+
// Copies id's initial value to bind aria-attributes such as aria-describedby.
|
|
70
|
+
// This is required for the RadioGroup component which unsets the id value.
|
|
71
|
+
const ariaId = id.value
|
|
69
72
|
|
|
70
73
|
provide(inputIdInjectionKey, id)
|
|
71
74
|
|
|
@@ -75,7 +78,10 @@ provide(formFieldInjectionKey, computed(() => ({
|
|
|
75
78
|
size: props.size,
|
|
76
79
|
eagerValidation: props.eagerValidation,
|
|
77
80
|
validateOnInputDelay: props.validateOnInputDelay,
|
|
78
|
-
errorPattern: props.errorPattern
|
|
81
|
+
errorPattern: props.errorPattern,
|
|
82
|
+
hint: props.hint,
|
|
83
|
+
description: props.description,
|
|
84
|
+
ariaId
|
|
79
85
|
}) as FormFieldInjectedOptions<FormFieldProps>))
|
|
80
86
|
</script>
|
|
81
87
|
|
|
@@ -88,14 +94,14 @@ provide(formFieldInjectionKey, computed(() => ({
|
|
|
88
94
|
{{ label }}
|
|
89
95
|
</slot>
|
|
90
96
|
</Label>
|
|
91
|
-
<span v-if="hint || !!slots.hint" :class="ui.hint({ class: props.ui?.hint })">
|
|
97
|
+
<span v-if="hint || !!slots.hint" :id="`${ariaId}-hint`" :class="ui.hint({ class: props.ui?.hint })">
|
|
92
98
|
<slot name="hint" :hint="hint">
|
|
93
99
|
{{ hint }}
|
|
94
100
|
</slot>
|
|
95
101
|
</span>
|
|
96
102
|
</div>
|
|
97
103
|
|
|
98
|
-
<p v-if="description || !!slots.description" :class="ui.description({ class: props.ui?.description })">
|
|
104
|
+
<p v-if="description || !!slots.description" :id="`${ariaId}-description`" :class="ui.description({ class: props.ui?.description })">
|
|
99
105
|
<slot name="description" :description="description">
|
|
100
106
|
{{ description }}
|
|
101
107
|
</slot>
|
|
@@ -105,7 +111,7 @@ provide(formFieldInjectionKey, computed(() => ({
|
|
|
105
111
|
<div :class="[(label || !!slots.label || description || !!slots.description) && ui.container({ class: props.ui?.container })]">
|
|
106
112
|
<slot :error="error" />
|
|
107
113
|
|
|
108
|
-
<p v-if="(typeof error === 'string' && error) || !!slots.error" :class="ui.error({ class: props.ui?.error })">
|
|
114
|
+
<p v-if="(typeof error === 'string' && error) || !!slots.error" :id="`${ariaId}-error`" :class="ui.error({ class: props.ui?.error })">
|
|
109
115
|
<slot name="error" :error="error">
|
|
110
116
|
{{ error }}
|
|
111
117
|
</slot>
|
|
@@ -9,9 +9,9 @@ import { tv } from '../utils/tv'
|
|
|
9
9
|
import type { AvatarProps } from '../types'
|
|
10
10
|
import type { PartialString } from '../types/utils'
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const appConfigInput = _appConfig as AppConfig & { ui: { input: Partial<typeof theme> } }
|
|
13
13
|
|
|
14
|
-
const input = tv({ extend: tv(theme), ...(
|
|
14
|
+
const input = tv({ extend: tv(theme), ...(appConfigInput.ui?.input || {}) })
|
|
15
15
|
|
|
16
16
|
type InputVariants = VariantProps<typeof input>
|
|
17
17
|
|
|
@@ -75,7 +75,7 @@ const slots = defineSlots<InputSlots>()
|
|
|
75
75
|
|
|
76
76
|
const [modelValue, modelModifiers] = defineModel<string | number>()
|
|
77
77
|
|
|
78
|
-
const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled } = useFormField<InputProps>(props, { deferInputValidation: true })
|
|
78
|
+
const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, emitFormFocus, ariaAttrs } = useFormField<InputProps>(props, { deferInputValidation: true })
|
|
79
79
|
const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
|
|
80
80
|
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props)
|
|
81
81
|
|
|
@@ -166,10 +166,11 @@ onMounted(() => {
|
|
|
166
166
|
:disabled="disabled"
|
|
167
167
|
:required="required"
|
|
168
168
|
:autocomplete="autocomplete"
|
|
169
|
-
v-bind="
|
|
169
|
+
v-bind="{ ...$attrs, ...ariaAttrs }"
|
|
170
170
|
@input="onInput"
|
|
171
171
|
@blur="onBlur"
|
|
172
172
|
@change="onChange"
|
|
173
|
+
@focus="emitFormFocus"
|
|
173
174
|
>
|
|
174
175
|
|
|
175
176
|
<slot />
|
|
@@ -11,9 +11,9 @@ import { tv } from '../utils/tv'
|
|
|
11
11
|
import type { AvatarProps, ChipProps, InputProps } from '../types'
|
|
12
12
|
import type { PartialString, MaybeArrayOfArray, MaybeArrayOfArrayItem, SelectModelValue, SelectModelValueEmits, SelectItemKey } from '../types/utils'
|
|
13
13
|
|
|
14
|
-
const
|
|
14
|
+
const appConfigInputMenu = _appConfig as AppConfig & { ui: { inputMenu: Partial<typeof theme> } }
|
|
15
15
|
|
|
16
|
-
const inputMenu = tv({ extend: tv(theme), ...(
|
|
16
|
+
const inputMenu = tv({ extend: tv(theme), ...(appConfigInputMenu.ui?.inputMenu || {}) })
|
|
17
17
|
|
|
18
18
|
export interface InputMenuItem {
|
|
19
19
|
label?: string
|
|
@@ -178,7 +178,7 @@ const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', '
|
|
|
178
178
|
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as ComboboxContentProps)
|
|
179
179
|
const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
|
|
180
180
|
|
|
181
|
-
const { emitFormBlur, emitFormChange, emitFormInput, size: formGroupSize, color, id, name, highlight, disabled } = useFormField<InputProps>(props)
|
|
181
|
+
const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField<InputProps>(props)
|
|
182
182
|
const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
|
|
183
183
|
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown })))
|
|
184
184
|
|
|
@@ -279,6 +279,7 @@ function onBlur(event: FocusEvent) {
|
|
|
279
279
|
|
|
280
280
|
function onFocus(event: FocusEvent) {
|
|
281
281
|
emits('focus', event)
|
|
282
|
+
emitFormFocus()
|
|
282
283
|
}
|
|
283
284
|
|
|
284
285
|
function onUpdateOpen(value: boolean) {
|
|
@@ -365,7 +366,7 @@ defineExpose({
|
|
|
365
366
|
<ComboboxInput v-model="searchTerm" :display-value="displayValue" as-child>
|
|
366
367
|
<TagsInputInput
|
|
367
368
|
ref="inputRef"
|
|
368
|
-
v-bind="
|
|
369
|
+
v-bind="{ ...$attrs, ...ariaAttrs }"
|
|
369
370
|
:placeholder="placeholder"
|
|
370
371
|
:required="required"
|
|
371
372
|
:class="ui.tagsInput({ class: props.ui?.tagsInput })"
|
|
@@ -379,7 +380,7 @@ defineExpose({
|
|
|
379
380
|
ref="inputRef"
|
|
380
381
|
v-model="searchTerm"
|
|
381
382
|
:display-value="displayValue"
|
|
382
|
-
v-bind="
|
|
383
|
+
v-bind="{ ...$attrs, ...ariaAttrs }"
|
|
383
384
|
:type="type"
|
|
384
385
|
:placeholder="placeholder"
|
|
385
386
|
:required="required"
|
|
@@ -7,9 +7,9 @@ import theme from '#build/ui/input-number'
|
|
|
7
7
|
import { tv } from '../utils/tv'
|
|
8
8
|
import type { ButtonProps } from '../types'
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const appConfigInputNumber = _appConfig as AppConfig & { ui: { inputNumber: Partial<typeof theme> } }
|
|
11
11
|
|
|
12
|
-
const inputNumber = tv({ extend: tv(theme), ...(
|
|
12
|
+
const inputNumber = tv({ extend: tv(theme), ...(appConfigInputNumber.ui?.inputNumber || {}) })
|
|
13
13
|
|
|
14
14
|
type InputNumberVariants = VariantProps<typeof inputNumber>
|
|
15
15
|
|
|
@@ -78,6 +78,7 @@ export interface InputNumberSlots {
|
|
|
78
78
|
import { onMounted, ref, computed } from 'vue'
|
|
79
79
|
import { NumberFieldRoot, NumberFieldInput, NumberFieldDecrement, NumberFieldIncrement, useForwardPropsEmits } from 'reka-ui'
|
|
80
80
|
import { reactivePick } from '@vueuse/core'
|
|
81
|
+
import { useAppConfig } from '#imports'
|
|
81
82
|
import { useFormField } from '../composables/useFormField'
|
|
82
83
|
import { useLocale } from '../composables/useLocale'
|
|
83
84
|
import UButton from './Button.vue'
|
|
@@ -92,7 +93,8 @@ defineSlots<InputNumberSlots>()
|
|
|
92
93
|
|
|
93
94
|
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'min', 'max', 'step', 'formatOptions'), emits)
|
|
94
95
|
|
|
95
|
-
const
|
|
96
|
+
const appConfig = useAppConfig()
|
|
97
|
+
const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, id, color, size, name, highlight, disabled, ariaAttrs } = useFormField<InputNumberProps>(props)
|
|
96
98
|
|
|
97
99
|
const { t, code: codeLocale } = useLocale()
|
|
98
100
|
const locale = computed(() => props.locale || codeLocale.value)
|
|
@@ -152,12 +154,13 @@ defineExpose({
|
|
|
152
154
|
@update:model-value="onUpdate"
|
|
153
155
|
>
|
|
154
156
|
<NumberFieldInput
|
|
155
|
-
v-bind="
|
|
157
|
+
v-bind="{ ...$attrs, ...ariaAttrs }"
|
|
156
158
|
ref="inputRef"
|
|
157
159
|
:placeholder="placeholder"
|
|
158
160
|
:required="required"
|
|
159
161
|
:class="ui.base({ class: props.ui?.base })"
|
|
160
162
|
@blur="onBlur"
|
|
163
|
+
@focus="emitFormFocus"
|
|
161
164
|
/>
|
|
162
165
|
|
|
163
166
|
<div :class="ui.increment({ class: props.ui?.increment })">
|
|
@@ -7,9 +7,9 @@ import type { KbdKey } from '../composables/useKbd'
|
|
|
7
7
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
8
8
|
import { tv } from '../utils/tv'
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const appConfigKbd = _appConfig as AppConfig & { ui: { kbd: Partial<typeof theme> } }
|
|
11
11
|
|
|
12
|
-
const kbd = tv({ extend: tv(theme), ...(
|
|
12
|
+
const kbd = tv({ extend: tv(theme), ...(appConfigKbd.ui?.kbd || {}) })
|
|
13
13
|
|
|
14
14
|
type KbdVariants = VariantProps<typeof kbd>
|
|
15
15
|
|
|
@@ -53,9 +53,9 @@ interface NuxtLinkProps extends Omit<RouterLinkProps, 'to'> {
|
|
|
53
53
|
noPrefetch?: boolean
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
const
|
|
56
|
+
const appConfigLink = _appConfig as AppConfig & { ui: { link: Partial<typeof theme> } }
|
|
57
57
|
|
|
58
|
-
const link = tv({ extend: tv(theme), ...(
|
|
58
|
+
const link = tv({ extend: tv(theme), ...(appConfigLink.ui?.link || {}) })
|
|
59
59
|
|
|
60
60
|
export interface LinkProps extends NuxtLinkProps {
|
|
61
61
|
/**
|
|
@@ -7,9 +7,9 @@ import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
|
7
7
|
import { tv } from '../utils/tv'
|
|
8
8
|
import type { ButtonProps } from '../types'
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const appConfigModal = _appConfig as AppConfig & { ui: { modal: Partial<typeof theme> } }
|
|
11
11
|
|
|
12
|
-
const modal = tv({ extend: tv(theme), ...(
|
|
12
|
+
const modal = tv({ extend: tv(theme), ...(appConfigModal.ui?.modal || {}) })
|
|
13
13
|
|
|
14
14
|
export interface ModalProps extends DialogRootProps {
|
|
15
15
|
title?: string
|