@nuxt/ui 3.0.0-alpha.10 → 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 +76 -59
- package/.nuxt/ui/avatar-group.ts +13 -1
- package/.nuxt/ui/avatar.ts +14 -2
- package/.nuxt/ui/badge.ts +102 -65
- package/.nuxt/ui/button-group.ts +13 -0
- package/.nuxt/ui/button.ts +124 -93
- package/.nuxt/ui/calendar.ts +20 -2
- package/.nuxt/ui/carousel.ts +5 -3
- package/.nuxt/ui/checkbox.ts +27 -9
- package/.nuxt/ui/chip.ts +36 -7
- package/.nuxt/ui/color-picker.ts +9 -1
- package/.nuxt/ui/context-menu.ts +31 -13
- package/.nuxt/ui/drawer.ts +11 -4
- package/.nuxt/ui/dropdown-menu.ts +31 -13
- package/.nuxt/ui/form-field.ts +9 -1
- package/.nuxt/ui/input-menu.ts +110 -75
- package/.nuxt/ui/input-number.ts +82 -51
- package/.nuxt/ui/input.ts +88 -53
- package/.nuxt/ui/kbd.ts +14 -2
- package/.nuxt/ui/navigation-menu.ts +193 -79
- package/.nuxt/ui/pin-input.ts +57 -31
- package/.nuxt/ui/popover.ts +1 -1
- package/.nuxt/ui/progress.ts +93 -54
- package/.nuxt/ui/radio-group.ts +25 -2
- package/.nuxt/ui/select-menu.ts +89 -54
- package/.nuxt/ui/select.ts +89 -54
- package/.nuxt/ui/separator.ts +52 -23
- package/.nuxt/ui/slideover.ts +11 -4
- package/.nuxt/ui/slider.ts +45 -22
- package/.nuxt/ui/stepper.ts +45 -23
- package/.nuxt/ui/switch.ts +20 -2
- package/.nuxt/ui/table.ts +30 -13
- package/.nuxt/ui/tabs.ts +67 -39
- package/.nuxt/ui/textarea.ts +88 -53
- package/.nuxt/ui/toast.ts +11 -1
- package/.nuxt/ui/toaster.ts +27 -11
- package/.nuxt/ui/tooltip.ts +1 -1
- package/README.md +8 -1
- package/cli/commands/make/component.mjs +12 -0
- package/cli/package.json +2 -2
- package/cli/templates.mjs +21 -16
- 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/CgoQwOTV.js +57 -0
- package/dist/client/devtools/_nuxt/DLmPhbaq.js +1 -0
- package/dist/client/devtools/_nuxt/DYuJNiSW.js +1 -0
- 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 +4 -3
- package/dist/runtime/components/Alert.vue +5 -4
- package/dist/runtime/components/Avatar.vue +35 -13
- package/dist/runtime/components/AvatarGroup.vue +4 -3
- package/dist/runtime/components/Badge.vue +4 -3
- package/dist/runtime/components/Breadcrumb.vue +3 -3
- package/dist/runtime/components/Button.vue +4 -3
- package/dist/runtime/components/ButtonGroup.vue +4 -3
- package/dist/runtime/components/Calendar.vue +6 -3
- package/dist/runtime/components/Card.vue +3 -3
- package/dist/runtime/components/Carousel.vue +4 -3
- package/dist/runtime/components/Checkbox.vue +6 -5
- package/dist/runtime/components/Chip.vue +5 -4
- package/dist/runtime/components/Collapsible.vue +6 -4
- package/dist/runtime/components/ColorPicker.vue +48 -16
- package/dist/runtime/components/CommandPalette.vue +14 -7
- package/dist/runtime/components/Container.vue +3 -3
- package/dist/runtime/components/ContextMenu.vue +4 -3
- package/dist/runtime/components/ContextMenuContent.vue +4 -4
- package/dist/runtime/components/Drawer.vue +9 -4
- package/dist/runtime/components/DropdownMenu.vue +4 -3
- package/dist/runtime/components/DropdownMenuContent.vue +4 -4
- package/dist/runtime/components/Form.vue +59 -29
- package/dist/runtime/components/FormField.vue +14 -7
- package/dist/runtime/components/Input.vue +7 -5
- package/dist/runtime/components/InputMenu.vue +19 -7
- package/dist/runtime/components/InputNumber.vue +9 -5
- package/dist/runtime/components/Kbd.vue +4 -3
- package/dist/runtime/components/Link.vue +3 -3
- package/dist/runtime/components/Modal.vue +10 -15
- package/dist/runtime/components/NavigationMenu.vue +97 -69
- package/dist/runtime/components/Pagination.vue +3 -3
- package/dist/runtime/components/PinInput.vue +9 -7
- package/dist/runtime/components/Popover.vue +9 -8
- package/dist/runtime/components/Progress.vue +6 -5
- package/dist/runtime/components/RadioGroup.vue +6 -5
- package/dist/runtime/components/Select.vue +12 -9
- package/dist/runtime/components/SelectMenu.vue +25 -8
- package/dist/runtime/components/Separator.vue +4 -3
- package/dist/runtime/components/Skeleton.vue +3 -3
- package/dist/runtime/components/Slideover.vue +10 -14
- package/dist/runtime/components/Slider.vue +6 -5
- package/dist/runtime/components/Stepper.vue +8 -6
- package/dist/runtime/components/Switch.vue +6 -5
- package/dist/runtime/components/Table.vue +4 -3
- package/dist/runtime/components/Tabs.vue +5 -3
- package/dist/runtime/components/Textarea.vue +7 -5
- package/dist/runtime/components/Toast.vue +4 -3
- package/dist/runtime/components/Toaster.vue +4 -3
- package/dist/runtime/components/Tooltip.vue +3 -3
- package/dist/runtime/composables/defineShortcuts.js +1 -1
- package/dist/runtime/composables/useFormField.d.ts +7 -3
- package/dist/runtime/composables/useFormField.js +18 -8
- 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/ar.js +3 -1
- package/dist/runtime/locale/cs.js +3 -1
- package/dist/runtime/locale/da.js +54 -0
- package/dist/runtime/locale/de.js +3 -1
- package/dist/runtime/locale/el.js +54 -0
- package/dist/runtime/locale/en.js +3 -1
- package/dist/runtime/locale/es.js +5 -3
- package/dist/runtime/locale/et.d.ts +2 -0
- package/dist/runtime/locale/et.js +54 -0
- package/dist/runtime/locale/fa_ir.js +3 -1
- package/dist/runtime/locale/fi.d.ts +2 -0
- package/dist/runtime/locale/fi.js +54 -0
- package/dist/runtime/locale/fr.js +3 -1
- package/dist/runtime/locale/he.d.ts +2 -0
- package/dist/runtime/locale/he.js +53 -0
- package/dist/runtime/locale/hi.d.ts +2 -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/id.d.ts +2 -0
- package/dist/runtime/locale/id.js +54 -0
- package/dist/runtime/locale/index.d.ts +14 -2
- package/dist/runtime/locale/index.js +14 -2
- package/dist/runtime/locale/it.js +3 -1
- package/dist/runtime/locale/ja.js +3 -1
- package/dist/runtime/locale/km.d.ts +2 -0
- package/dist/runtime/locale/km.js +54 -0
- package/dist/runtime/locale/ko.js +3 -1
- package/dist/runtime/locale/nb_no.d.ts +2 -0
- package/dist/runtime/locale/nb_no.js +54 -0
- package/dist/runtime/locale/nl.js +3 -1
- package/dist/runtime/locale/pl.js +3 -1
- package/dist/runtime/locale/pt.js +3 -1
- package/dist/runtime/locale/pt_br.js +3 -1
- package/dist/runtime/locale/ru.js +3 -1
- package/dist/runtime/locale/sk.js +3 -1
- package/dist/runtime/locale/sv.d.ts +2 -0
- package/dist/runtime/locale/sv.js +54 -0
- package/dist/runtime/locale/th.d.ts +2 -0
- package/dist/runtime/locale/th.js +54 -0
- package/dist/runtime/locale/tr.js +3 -1
- package/dist/runtime/locale/uk.d.ts +2 -0
- package/dist/runtime/locale/uk.js +54 -0
- package/dist/runtime/locale/vi.d.ts +2 -0
- package/dist/runtime/locale/vi.js +54 -0
- package/dist/runtime/locale/zh_cn.d.ts +2 -0
- package/dist/runtime/locale/{zh_hans.js → zh_cn.js} +4 -2
- package/dist/runtime/locale/zh_tw.d.ts +2 -0
- package/dist/runtime/locale/{zh_hant.js → zh_tw.js} +12 -10
- package/dist/runtime/plugins/colors.js +7 -4
- package/dist/runtime/types/form.d.ts +23 -14
- package/dist/runtime/types/form.js +2 -2
- package/dist/runtime/types/locale.d.ts +2 -0
- package/dist/runtime/utils/form.d.ts +2 -2
- package/dist/runtime/utils/form.js +5 -7
- package/dist/runtime/utils/tv.d.ts +1 -0
- package/dist/runtime/utils/tv.js +4 -0
- package/dist/runtime/vue/components/Icon.vue +2 -2
- package/dist/runtime/vue/components/Link.vue +2 -2
- package/dist/runtime/vue/plugins/head.d.ts +1 -1
- package/dist/runtime/vue/plugins/head.js +3 -3
- package/dist/shared/{ui.D_93iuwH.mjs → ui.CENZ17mM.mjs} +131 -48
- package/dist/shared/{ui.CwmIkD_Y.cjs → ui.ClMXjgLK.cjs} +131 -48
- package/dist/unplugin.cjs +5 -5
- package/dist/unplugin.mjs +5 -5
- package/dist/vite.cjs +1 -1
- package/dist/vite.mjs +1 -1
- package/package.json +55 -51
- package/dist/client/devtools/_nuxt/B1fTd_Rf.js +0 -1
- package/dist/client/devtools/_nuxt/CSUxkedg.js +0 -1
- package/dist/client/devtools/_nuxt/CbRTGpb7.js +0 -1
- package/dist/client/devtools/_nuxt/YQTlxbcT.js +0 -53
- package/dist/client/devtools/_nuxt/builds/meta/b3465abc-0a92-4ee7-ba6b-e5174891fcc0.json +0 -1
- package/dist/client/devtools/_nuxt/entry.DXXceQev.css +0 -1
- package/dist/client/devtools/_nuxt/error-404.BJpkZFyY.css +0 -1
- package/dist/client/devtools/_nuxt/error-500.D3i9VDlt.css +0 -1
- /package/dist/runtime/locale/{zh_hans.d.ts → da.d.ts} +0 -0
- /package/dist/runtime/locale/{zh_hant.d.ts → el.d.ts} +0 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { tv } from 'tailwind-variants'
|
|
3
2
|
import type { AppConfig } from '@nuxt/schema'
|
|
4
3
|
import _appConfig from '#build/app.config'
|
|
5
4
|
import theme from '#build/ui/form'
|
|
6
5
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
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
|
|
@@ -38,7 +39,7 @@ extendDevtoolsMeta({ example: 'FormExample' })
|
|
|
38
39
|
import { provide, inject, nextTick, ref, onUnmounted, onMounted, computed, useId, readonly } from 'vue'
|
|
39
40
|
import { useEventBus } from '@vueuse/core'
|
|
40
41
|
import { formOptionsInjectionKey, formInputsInjectionKey, formBusInjectionKey, formLoadingInjectionKey } from '../composables/useFormField'
|
|
41
|
-
import {
|
|
42
|
+
import { validateSchema } from '../utils/form'
|
|
42
43
|
import { FormValidationException } from '../types/form'
|
|
43
44
|
|
|
44
45
|
const props = withDefaults(defineProps<FormProps<T>>(), {
|
|
@@ -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 => ({
|
|
@@ -104,34 +125,35 @@ function resolveErrorIds(errs: FormError[]): FormErrorWithId[] {
|
|
|
104
125
|
}))
|
|
105
126
|
}
|
|
106
127
|
|
|
107
|
-
const
|
|
128
|
+
const transformedState = ref<T | null>(null)
|
|
129
|
+
|
|
108
130
|
async function getErrors(): Promise<FormErrorWithId[]> {
|
|
109
131
|
let errs = props.validate ? (await props.validate(props.state)) ?? [] : []
|
|
110
132
|
|
|
111
133
|
if (props.schema) {
|
|
112
|
-
const { errors, result } = await
|
|
134
|
+
const { errors, result } = await validateSchema(props.state, props.schema as FormSchema<typeof props.state>)
|
|
113
135
|
if (errors) {
|
|
114
136
|
errs = errs.concat(errors)
|
|
115
137
|
} else {
|
|
116
|
-
|
|
138
|
+
transformedState.value = result
|
|
117
139
|
}
|
|
118
140
|
}
|
|
119
141
|
|
|
120
142
|
return resolveErrorIds(errs)
|
|
121
143
|
}
|
|
122
144
|
|
|
123
|
-
async function _validate(opts: { name?:
|
|
124
|
-
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)[]
|
|
125
147
|
|
|
126
148
|
const nestedValidatePromises = !names && opts.nested
|
|
127
149
|
? Array.from(nestedForms.value.values()).map(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
150
|
+
({ validate }) => validate(opts).then(() => undefined).catch((error: Error) => {
|
|
151
|
+
if (!(error instanceof FormValidationException)) {
|
|
152
|
+
throw error
|
|
153
|
+
}
|
|
154
|
+
return error
|
|
155
|
+
})
|
|
156
|
+
)
|
|
135
157
|
: []
|
|
136
158
|
|
|
137
159
|
if (names) {
|
|
@@ -150,13 +172,17 @@ async function _validate(opts: { name?: string | string[], silent?: boolean, nes
|
|
|
150
172
|
errors.value = await getErrors()
|
|
151
173
|
}
|
|
152
174
|
|
|
153
|
-
const childErrors = (await Promise.all(nestedValidatePromises)).filter(val => val)
|
|
175
|
+
const childErrors = (await Promise.all(nestedValidatePromises)).filter(val => val !== undefined)
|
|
154
176
|
|
|
155
177
|
if (errors.value.length + childErrors.length > 0) {
|
|
156
178
|
if (opts.silent) return false
|
|
157
179
|
throw new FormValidationException(formId, errors.value, childErrors)
|
|
158
180
|
}
|
|
159
181
|
|
|
182
|
+
if (opts.transform) {
|
|
183
|
+
Object.assign(props.state, transformedState.value)
|
|
184
|
+
}
|
|
185
|
+
|
|
160
186
|
return props.state as T
|
|
161
187
|
}
|
|
162
188
|
|
|
@@ -169,8 +195,7 @@ async function onSubmitWrapper(payload: Event) {
|
|
|
169
195
|
const event = payload as FormSubmitEvent<any>
|
|
170
196
|
|
|
171
197
|
try {
|
|
172
|
-
await _validate({ nested: true })
|
|
173
|
-
event.data = props.schema ? parsedValue.value : props.state
|
|
198
|
+
event.data = await _validate({ nested: true, transform: true })
|
|
174
199
|
await props.onSubmit?.(event)
|
|
175
200
|
} catch (error) {
|
|
176
201
|
if (!(error instanceof FormValidationException)) {
|
|
@@ -180,7 +205,7 @@ async function onSubmitWrapper(payload: Event) {
|
|
|
180
205
|
const errorEvent: FormErrorEvent = {
|
|
181
206
|
...event,
|
|
182
207
|
errors: error.errors,
|
|
183
|
-
|
|
208
|
+
children: error.children
|
|
184
209
|
}
|
|
185
210
|
emits('error', errorEvent)
|
|
186
211
|
}
|
|
@@ -199,7 +224,7 @@ defineExpose<Form<T>>({
|
|
|
199
224
|
validate: _validate,
|
|
200
225
|
errors,
|
|
201
226
|
|
|
202
|
-
setErrors(errs: FormError[], name?:
|
|
227
|
+
setErrors(errs: FormError[], name?: keyof T) {
|
|
203
228
|
if (name) {
|
|
204
229
|
errors.value = errors.value
|
|
205
230
|
.filter(error => error.name !== name)
|
|
@@ -213,7 +238,7 @@ defineExpose<Form<T>>({
|
|
|
213
238
|
await onSubmitWrapper(new Event('submit'))
|
|
214
239
|
},
|
|
215
240
|
|
|
216
|
-
getErrors(name?:
|
|
241
|
+
getErrors(name?: keyof T) {
|
|
217
242
|
if (name) {
|
|
218
243
|
return errors.value.filter(err => err.name === name)
|
|
219
244
|
}
|
|
@@ -228,7 +253,12 @@ defineExpose<Form<T>>({
|
|
|
228
253
|
}
|
|
229
254
|
},
|
|
230
255
|
|
|
231
|
-
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>>
|
|
232
262
|
})
|
|
233
263
|
</script>
|
|
234
264
|
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import type { VariantProps } from 'tailwind-variants'
|
|
3
3
|
import type { AppConfig } from '@nuxt/schema'
|
|
4
4
|
import _appConfig from '#build/app.config'
|
|
5
5
|
import theme from '#build/ui/form-field'
|
|
6
|
+
import { tv } from '../utils/tv'
|
|
6
7
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
+
const appConfigFormField = _appConfig as AppConfig & { ui: { formField: Partial<typeof theme> } }
|
|
9
10
|
|
|
10
|
-
const formField = tv({ extend: tv(theme), ...(
|
|
11
|
+
const formField = tv({ extend: tv(theme), ...(appConfigFormField.ui?.formField || {}) })
|
|
11
12
|
|
|
12
13
|
type FormFieldVariants = VariantProps<typeof formField>
|
|
13
14
|
|
|
@@ -65,6 +66,9 @@ const formErrors = inject<Ref<FormError[]> | null>('form-errors', null)
|
|
|
65
66
|
const error = computed(() => props.error || formErrors?.value?.find(error => error.name === props.name || (props.errorPattern && error.name.match(props.errorPattern)))?.message)
|
|
66
67
|
|
|
67
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
|
|
68
72
|
|
|
69
73
|
provide(inputIdInjectionKey, id)
|
|
70
74
|
|
|
@@ -74,7 +78,10 @@ provide(formFieldInjectionKey, computed(() => ({
|
|
|
74
78
|
size: props.size,
|
|
75
79
|
eagerValidation: props.eagerValidation,
|
|
76
80
|
validateOnInputDelay: props.validateOnInputDelay,
|
|
77
|
-
errorPattern: props.errorPattern
|
|
81
|
+
errorPattern: props.errorPattern,
|
|
82
|
+
hint: props.hint,
|
|
83
|
+
description: props.description,
|
|
84
|
+
ariaId
|
|
78
85
|
}) as FormFieldInjectedOptions<FormFieldProps>))
|
|
79
86
|
</script>
|
|
80
87
|
|
|
@@ -87,14 +94,14 @@ provide(formFieldInjectionKey, computed(() => ({
|
|
|
87
94
|
{{ label }}
|
|
88
95
|
</slot>
|
|
89
96
|
</Label>
|
|
90
|
-
<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 })">
|
|
91
98
|
<slot name="hint" :hint="hint">
|
|
92
99
|
{{ hint }}
|
|
93
100
|
</slot>
|
|
94
101
|
</span>
|
|
95
102
|
</div>
|
|
96
103
|
|
|
97
|
-
<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 })">
|
|
98
105
|
<slot name="description" :description="description">
|
|
99
106
|
{{ description }}
|
|
100
107
|
</slot>
|
|
@@ -104,7 +111,7 @@ provide(formFieldInjectionKey, computed(() => ({
|
|
|
104
111
|
<div :class="[(label || !!slots.label || description || !!slots.description) && ui.container({ class: props.ui?.container })]">
|
|
105
112
|
<slot :error="error" />
|
|
106
113
|
|
|
107
|
-
<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 })">
|
|
108
115
|
<slot name="error" :error="error">
|
|
109
116
|
{{ error }}
|
|
110
117
|
</slot>
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { InputHTMLAttributes } from 'vue'
|
|
3
|
-
import {
|
|
3
|
+
import type { VariantProps } from 'tailwind-variants'
|
|
4
4
|
import type { AppConfig } from '@nuxt/schema'
|
|
5
5
|
import _appConfig from '#build/app.config'
|
|
6
6
|
import theme from '#build/ui/input'
|
|
7
7
|
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
|
8
|
+
import { tv } from '../utils/tv'
|
|
8
9
|
import type { AvatarProps } from '../types'
|
|
9
10
|
import type { PartialString } from '../types/utils'
|
|
10
11
|
|
|
11
|
-
const
|
|
12
|
+
const appConfigInput = _appConfig as AppConfig & { ui: { input: Partial<typeof theme> } }
|
|
12
13
|
|
|
13
|
-
const input = tv({ extend: tv(theme), ...(
|
|
14
|
+
const input = tv({ extend: tv(theme), ...(appConfigInput.ui?.input || {}) })
|
|
14
15
|
|
|
15
16
|
type InputVariants = VariantProps<typeof input>
|
|
16
17
|
|
|
@@ -74,7 +75,7 @@ const slots = defineSlots<InputSlots>()
|
|
|
74
75
|
|
|
75
76
|
const [modelValue, modelModifiers] = defineModel<string | number>()
|
|
76
77
|
|
|
77
|
-
const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled } = useFormField<InputProps>(props)
|
|
78
|
+
const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, emitFormFocus, ariaAttrs } = useFormField<InputProps>(props, { deferInputValidation: true })
|
|
78
79
|
const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
|
|
79
80
|
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props)
|
|
80
81
|
|
|
@@ -165,10 +166,11 @@ onMounted(() => {
|
|
|
165
166
|
:disabled="disabled"
|
|
166
167
|
:required="required"
|
|
167
168
|
:autocomplete="autocomplete"
|
|
168
|
-
v-bind="
|
|
169
|
+
v-bind="{ ...$attrs, ...ariaAttrs }"
|
|
169
170
|
@input="onInput"
|
|
170
171
|
@blur="onBlur"
|
|
171
172
|
@change="onChange"
|
|
173
|
+
@focus="emitFormFocus"
|
|
172
174
|
>
|
|
173
175
|
|
|
174
176
|
<slot />
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { InputHTMLAttributes } from 'vue'
|
|
3
|
-
import {
|
|
3
|
+
import type { VariantProps } from 'tailwind-variants'
|
|
4
4
|
import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxArrowProps, AcceptableValue } 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 { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
10
|
+
import { tv } from '../utils/tv'
|
|
10
11
|
import type { AvatarProps, ChipProps, InputProps } from '../types'
|
|
11
12
|
import type { PartialString, MaybeArrayOfArray, MaybeArrayOfArrayItem, SelectModelValue, SelectModelValueEmits, SelectItemKey } from '../types/utils'
|
|
12
13
|
|
|
13
|
-
const
|
|
14
|
+
const appConfigInputMenu = _appConfig as AppConfig & { ui: { inputMenu: Partial<typeof theme> } }
|
|
14
15
|
|
|
15
|
-
const inputMenu = tv({ extend: tv(theme), ...(
|
|
16
|
+
const inputMenu = tv({ extend: tv(theme), ...(appConfigInputMenu.ui?.inputMenu || {}) })
|
|
16
17
|
|
|
17
18
|
export interface InputMenuItem {
|
|
18
19
|
label?: string
|
|
@@ -144,6 +145,7 @@ extendDevtoolsMeta({ defaultProps: { items: ['Option 1', 'Option 2', 'Option 3']
|
|
|
144
145
|
import { computed, ref, toRef, onMounted, toRaw } from 'vue'
|
|
145
146
|
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'
|
|
146
147
|
import { defu } from 'defu'
|
|
148
|
+
import { isEqual } from 'ohash'
|
|
147
149
|
import { reactivePick, createReusableTemplate } from '@vueuse/core'
|
|
148
150
|
import { useAppConfig } from '#imports'
|
|
149
151
|
import { useButtonGroup } from '../composables/useButtonGroup'
|
|
@@ -176,7 +178,7 @@ const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', '
|
|
|
176
178
|
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as ComboboxContentProps)
|
|
177
179
|
const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
|
|
178
180
|
|
|
179
|
-
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)
|
|
180
182
|
const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
|
|
181
183
|
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown })))
|
|
182
184
|
|
|
@@ -201,7 +203,7 @@ function displayValue(value: T): string {
|
|
|
201
203
|
return value && (typeof value === 'object' ? get(value, props.labelKey as string) : value)
|
|
202
204
|
}
|
|
203
205
|
|
|
204
|
-
const item = items.value.find(item => compare(typeof item === 'object' ? get(item, props.valueKey as string) : item, value))
|
|
206
|
+
const item = items.value.find(item => compare(typeof item === 'object' ? get(item as Record<string, any>, props.valueKey as string) : item, value))
|
|
205
207
|
return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
|
|
206
208
|
}
|
|
207
209
|
|
|
@@ -277,6 +279,7 @@ function onBlur(event: FocusEvent) {
|
|
|
277
279
|
|
|
278
280
|
function onFocus(event: FocusEvent) {
|
|
279
281
|
emits('focus', event)
|
|
282
|
+
emitFormFocus()
|
|
280
283
|
}
|
|
281
284
|
|
|
282
285
|
function onUpdateOpen(value: boolean) {
|
|
@@ -290,6 +293,14 @@ function onUpdateOpen(value: boolean) {
|
|
|
290
293
|
}
|
|
291
294
|
}
|
|
292
295
|
|
|
296
|
+
function onRemoveTag(event: any) {
|
|
297
|
+
if (props.multiple) {
|
|
298
|
+
const modelValue = props.modelValue as SelectModelValue<T, V, true>
|
|
299
|
+
const filteredValue = modelValue.filter(value => !isEqual(value, event))
|
|
300
|
+
emits('update:modelValue', filteredValue as SelectModelValue<T, V, M>)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
293
304
|
defineExpose({
|
|
294
305
|
inputRef
|
|
295
306
|
})
|
|
@@ -336,6 +347,7 @@ defineExpose({
|
|
|
336
347
|
as-child
|
|
337
348
|
@blur="onBlur"
|
|
338
349
|
@focus="onFocus"
|
|
350
|
+
@remove-tag="onRemoveTag"
|
|
339
351
|
>
|
|
340
352
|
<TagsInputItem v-for="(item, index) in tags" :key="index" :value="(item as string)" :class="ui.tagsItem({ class: props.ui?.tagsItem })">
|
|
341
353
|
<TagsInputItemText :class="ui.tagsItemText({ class: props.ui?.tagsItemText })">
|
|
@@ -354,7 +366,7 @@ defineExpose({
|
|
|
354
366
|
<ComboboxInput v-model="searchTerm" :display-value="displayValue" as-child>
|
|
355
367
|
<TagsInputInput
|
|
356
368
|
ref="inputRef"
|
|
357
|
-
v-bind="
|
|
369
|
+
v-bind="{ ...$attrs, ...ariaAttrs }"
|
|
358
370
|
:placeholder="placeholder"
|
|
359
371
|
:required="required"
|
|
360
372
|
:class="ui.tagsInput({ class: props.ui?.tagsInput })"
|
|
@@ -368,7 +380,7 @@ defineExpose({
|
|
|
368
380
|
ref="inputRef"
|
|
369
381
|
v-model="searchTerm"
|
|
370
382
|
:display-value="displayValue"
|
|
371
|
-
v-bind="
|
|
383
|
+
v-bind="{ ...$attrs, ...ariaAttrs }"
|
|
372
384
|
:type="type"
|
|
373
385
|
:placeholder="placeholder"
|
|
374
386
|
:required="required"
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import type { VariantProps } from 'tailwind-variants'
|
|
3
3
|
import type { NumberFieldRootProps } 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/input-number'
|
|
7
|
+
import { tv } from '../utils/tv'
|
|
7
8
|
import type { ButtonProps } from '../types'
|
|
8
9
|
|
|
9
|
-
const
|
|
10
|
+
const appConfigInputNumber = _appConfig as AppConfig & { ui: { inputNumber: Partial<typeof theme> } }
|
|
10
11
|
|
|
11
|
-
const inputNumber = tv({ extend: tv(theme), ...(
|
|
12
|
+
const inputNumber = tv({ extend: tv(theme), ...(appConfigInputNumber.ui?.inputNumber || {}) })
|
|
12
13
|
|
|
13
14
|
type InputNumberVariants = VariantProps<typeof inputNumber>
|
|
14
15
|
|
|
@@ -77,6 +78,7 @@ export interface InputNumberSlots {
|
|
|
77
78
|
import { onMounted, ref, computed } from 'vue'
|
|
78
79
|
import { NumberFieldRoot, NumberFieldInput, NumberFieldDecrement, NumberFieldIncrement, useForwardPropsEmits } from 'reka-ui'
|
|
79
80
|
import { reactivePick } from '@vueuse/core'
|
|
81
|
+
import { useAppConfig } from '#imports'
|
|
80
82
|
import { useFormField } from '../composables/useFormField'
|
|
81
83
|
import { useLocale } from '../composables/useLocale'
|
|
82
84
|
import UButton from './Button.vue'
|
|
@@ -91,7 +93,8 @@ defineSlots<InputNumberSlots>()
|
|
|
91
93
|
|
|
92
94
|
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'min', 'max', 'step', 'formatOptions'), emits)
|
|
93
95
|
|
|
94
|
-
const
|
|
96
|
+
const appConfig = useAppConfig()
|
|
97
|
+
const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, id, color, size, name, highlight, disabled, ariaAttrs } = useFormField<InputNumberProps>(props)
|
|
95
98
|
|
|
96
99
|
const { t, code: codeLocale } = useLocale()
|
|
97
100
|
const locale = computed(() => props.locale || codeLocale.value)
|
|
@@ -151,12 +154,13 @@ defineExpose({
|
|
|
151
154
|
@update:model-value="onUpdate"
|
|
152
155
|
>
|
|
153
156
|
<NumberFieldInput
|
|
154
|
-
v-bind="
|
|
157
|
+
v-bind="{ ...$attrs, ...ariaAttrs }"
|
|
155
158
|
ref="inputRef"
|
|
156
159
|
:placeholder="placeholder"
|
|
157
160
|
:required="required"
|
|
158
161
|
:class="ui.base({ class: props.ui?.base })"
|
|
159
162
|
@blur="onBlur"
|
|
163
|
+
@focus="emitFormFocus"
|
|
160
164
|
/>
|
|
161
165
|
|
|
162
166
|
<div :class="ui.increment({ class: props.ui?.increment })">
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import type { VariantProps } from 'tailwind-variants'
|
|
3
3
|
import type { AppConfig } from '@nuxt/schema'
|
|
4
4
|
import _appConfig from '#build/app.config'
|
|
5
5
|
import theme from '#build/ui/kbd'
|
|
6
6
|
import type { KbdKey } from '../composables/useKbd'
|
|
7
7
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
8
|
+
import { tv } from '../utils/tv'
|
|
8
9
|
|
|
9
|
-
const
|
|
10
|
+
const appConfigKbd = _appConfig as AppConfig & { ui: { kbd: Partial<typeof theme> } }
|
|
10
11
|
|
|
11
|
-
const kbd = tv({ extend: tv(theme), ...(
|
|
12
|
+
const kbd = tv({ extend: tv(theme), ...(appConfigKbd.ui?.kbd || {}) })
|
|
12
13
|
|
|
13
14
|
type KbdVariants = VariantProps<typeof kbd>
|
|
14
15
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { ButtonHTMLAttributes } from 'vue'
|
|
3
|
-
import { tv } from 'tailwind-variants'
|
|
4
3
|
import type { AppConfig } from '@nuxt/schema'
|
|
5
4
|
import _appConfig from '#build/app.config'
|
|
6
5
|
import type { RouterLinkProps, RouteLocationRaw } from 'vue-router'
|
|
7
6
|
import theme from '#build/ui/link'
|
|
7
|
+
import { tv } from '../utils/tv'
|
|
8
8
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
9
9
|
|
|
10
10
|
interface NuxtLinkProps extends Omit<RouterLinkProps, 'to'> {
|
|
@@ -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
|
/**
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { tv } from 'tailwind-variants'
|
|
3
2
|
import type { DialogRootProps, DialogRootEmits, DialogContentProps } from 'reka-ui'
|
|
4
3
|
import type { AppConfig } from '@nuxt/schema'
|
|
5
4
|
import _appConfig from '#build/app.config'
|
|
6
5
|
import theme from '#build/ui/modal'
|
|
7
6
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
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
|
|
@@ -48,10 +48,10 @@ export interface ModalProps extends DialogRootProps {
|
|
|
48
48
|
*/
|
|
49
49
|
closeIcon?: string
|
|
50
50
|
/**
|
|
51
|
-
* When `
|
|
52
|
-
* @defaultValue
|
|
51
|
+
* When `false`, the modal will not close when clicking outside or pressing escape.
|
|
52
|
+
* @defaultValue true
|
|
53
53
|
*/
|
|
54
|
-
|
|
54
|
+
dismissible?: boolean
|
|
55
55
|
class?: any
|
|
56
56
|
ui?: Partial<typeof modal.slots>
|
|
57
57
|
}
|
|
@@ -85,7 +85,8 @@ const props = withDefaults(defineProps<ModalProps>(), {
|
|
|
85
85
|
portal: true,
|
|
86
86
|
overlay: true,
|
|
87
87
|
transition: true,
|
|
88
|
-
modal: true
|
|
88
|
+
modal: true,
|
|
89
|
+
dismissible: true
|
|
89
90
|
})
|
|
90
91
|
const emits = defineEmits<ModalEmits>()
|
|
91
92
|
const slots = defineSlots<ModalSlots>()
|
|
@@ -96,7 +97,7 @@ const appConfig = useAppConfig()
|
|
|
96
97
|
const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'modal'), emits)
|
|
97
98
|
const contentProps = toRef(() => props.content)
|
|
98
99
|
const contentEvents = computed(() => {
|
|
99
|
-
if (props.
|
|
100
|
+
if (!props.dismissible) {
|
|
100
101
|
return {
|
|
101
102
|
pointerDownOutside: (e: Event) => e.preventDefault(),
|
|
102
103
|
interactOutside: (e: Event) => e.preventDefault(),
|
|
@@ -104,13 +105,7 @@ const contentEvents = computed(() => {
|
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
return {
|
|
108
|
-
interactOutside: (e: Event) => {
|
|
109
|
-
if (e.target instanceof Element && e.target.closest('[data-sonner-toaster]')) {
|
|
110
|
-
return e.preventDefault()
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
108
|
+
return {}
|
|
114
109
|
})
|
|
115
110
|
|
|
116
111
|
const ui = computed(() => modal({
|