@nuxt/ui 3.2.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/.nuxt/ui/button.ts +42 -42
  2. package/.nuxt/ui/command-palette.ts +2 -1
  3. package/.nuxt/ui/drawer.ts +1 -1
  4. package/.nuxt/ui/file-upload.ts +290 -0
  5. package/.nuxt/ui/index.ts +1 -0
  6. package/.nuxt/ui/input.ts +1 -1
  7. package/.nuxt/ui/kbd.ts +167 -3
  8. package/.nuxt/ui/select.ts +7 -7
  9. package/.nuxt/ui/table.ts +8 -0
  10. package/.nuxt/ui/tabs.ts +4 -2
  11. package/.nuxt/ui/textarea.ts +1 -1
  12. package/.nuxt/ui/toast.ts +8 -15
  13. package/.nuxt/ui/tooltip.ts +1 -1
  14. package/cli/templates.mjs +8 -3
  15. package/dist/module.d.mts +15 -1
  16. package/dist/module.json +2 -2
  17. package/dist/module.mjs +3 -3
  18. package/dist/runtime/components/Accordion.vue.d.ts +1 -1
  19. package/dist/runtime/components/Alert.vue.d.ts +2 -2
  20. package/dist/runtime/components/App.vue.d.ts +1 -1
  21. package/dist/runtime/components/Avatar.vue.d.ts +1 -1
  22. package/dist/runtime/components/AvatarGroup.vue.d.ts +1 -1
  23. package/dist/runtime/components/Badge.vue.d.ts +1 -1
  24. package/dist/runtime/components/Breadcrumb.vue.d.ts +1 -1
  25. package/dist/runtime/components/Button.vue +6 -6
  26. package/dist/runtime/components/Button.vue.d.ts +1 -5
  27. package/dist/runtime/components/ButtonGroup.vue.d.ts +1 -1
  28. package/dist/runtime/components/Calendar.vue.d.ts +1 -1
  29. package/dist/runtime/components/Card.vue.d.ts +1 -1
  30. package/dist/runtime/components/Carousel.vue +18 -14
  31. package/dist/runtime/components/Carousel.vue.d.ts +1 -1
  32. package/dist/runtime/components/Checkbox.vue.d.ts +1 -1
  33. package/dist/runtime/components/CheckboxGroup.vue.d.ts +1 -1
  34. package/dist/runtime/components/Chip.vue.d.ts +2 -2
  35. package/dist/runtime/components/Collapsible.vue.d.ts +1 -1
  36. package/dist/runtime/components/ColorPicker.vue +1 -2
  37. package/dist/runtime/components/ColorPicker.vue.d.ts +1 -1
  38. package/dist/runtime/components/CommandPalette.vue +4 -0
  39. package/dist/runtime/components/CommandPalette.vue.d.ts +6 -1
  40. package/dist/runtime/components/Container.vue.d.ts +1 -1
  41. package/dist/runtime/components/ContextMenu.vue.d.ts +1 -1
  42. package/dist/runtime/components/ContextMenuContent.vue +5 -3
  43. package/dist/runtime/components/ContextMenuContent.vue.d.ts +1 -1
  44. package/dist/runtime/components/Drawer.vue +4 -4
  45. package/dist/runtime/components/Drawer.vue.d.ts +6 -1
  46. package/dist/runtime/components/DropdownMenu.vue.d.ts +1 -1
  47. package/dist/runtime/components/DropdownMenuContent.vue +5 -3
  48. package/dist/runtime/components/DropdownMenuContent.vue.d.ts +8 -5
  49. package/dist/runtime/components/FileUpload.vue +248 -0
  50. package/dist/runtime/components/FileUpload.vue.d.ts +167 -0
  51. package/dist/runtime/components/Form.vue +3 -3
  52. package/dist/runtime/components/Form.vue.d.ts +6 -5
  53. package/dist/runtime/components/FormField.vue +2 -2
  54. package/dist/runtime/components/FormField.vue.d.ts +4 -4
  55. package/dist/runtime/components/Icon.vue.d.ts +1 -1
  56. package/dist/runtime/components/Input.vue +10 -7
  57. package/dist/runtime/components/Input.vue.d.ts +9 -9
  58. package/dist/runtime/components/InputMenu.vue +25 -13
  59. package/dist/runtime/components/InputMenu.vue.d.ts +16 -221
  60. package/dist/runtime/components/InputNumber.vue +3 -2
  61. package/dist/runtime/components/InputNumber.vue.d.ts +7 -81
  62. package/dist/runtime/components/InputTags.vue +2 -0
  63. package/dist/runtime/components/InputTags.vue.d.ts +7 -5
  64. package/dist/runtime/components/Kbd.vue +2 -1
  65. package/dist/runtime/components/Kbd.vue.d.ts +5 -1
  66. package/dist/runtime/components/Link.vue +6 -5
  67. package/dist/runtime/components/Link.vue.d.ts +1 -3
  68. package/dist/runtime/components/LinkBase.vue.d.ts +1 -1
  69. package/dist/runtime/components/Modal.vue.d.ts +2 -2
  70. package/dist/runtime/components/NavigationMenu.vue +3 -2
  71. package/dist/runtime/components/NavigationMenu.vue.d.ts +1 -1
  72. package/dist/runtime/components/OverlayProvider.vue.d.ts +1 -1
  73. package/dist/runtime/components/Pagination.vue.d.ts +1 -1
  74. package/dist/runtime/components/PinInput.vue +3 -1
  75. package/dist/runtime/components/PinInput.vue.d.ts +5 -5
  76. package/dist/runtime/components/Popover.vue +2 -1
  77. package/dist/runtime/components/Popover.vue.d.ts +8 -2
  78. package/dist/runtime/components/Progress.vue.d.ts +1 -1
  79. package/dist/runtime/components/RadioGroup.vue +1 -1
  80. package/dist/runtime/components/RadioGroup.vue.d.ts +5 -4
  81. package/dist/runtime/components/Select.vue +16 -6
  82. package/dist/runtime/components/Select.vue.d.ts +3 -3
  83. package/dist/runtime/components/SelectMenu.vue +18 -10
  84. package/dist/runtime/components/SelectMenu.vue.d.ts +3 -3
  85. package/dist/runtime/components/Separator.vue.d.ts +1 -1
  86. package/dist/runtime/components/Skeleton.vue.d.ts +1 -1
  87. package/dist/runtime/components/Slideover.vue.d.ts +2 -2
  88. package/dist/runtime/components/Slider.vue.d.ts +5 -5
  89. package/dist/runtime/components/Stepper.vue.d.ts +1 -1
  90. package/dist/runtime/components/Switch.vue.d.ts +1 -1
  91. package/dist/runtime/components/Table.vue +83 -8
  92. package/dist/runtime/components/Table.vue.d.ts +20 -4
  93. package/dist/runtime/components/Tabs.vue +12 -2
  94. package/dist/runtime/components/Tabs.vue.d.ts +8 -3
  95. package/dist/runtime/components/Textarea.vue +10 -7
  96. package/dist/runtime/components/Textarea.vue.d.ts +10 -9
  97. package/dist/runtime/components/Timeline.vue.d.ts +1 -1
  98. package/dist/runtime/components/Toast.vue +12 -4
  99. package/dist/runtime/components/Toast.vue.d.ts +14 -13
  100. package/dist/runtime/components/Toaster.vue.d.ts +1 -1
  101. package/dist/runtime/components/Tooltip.vue +4 -3
  102. package/dist/runtime/components/Tooltip.vue.d.ts +8 -2
  103. package/dist/runtime/components/Tree.vue +31 -28
  104. package/dist/runtime/components/Tree.vue.d.ts +2 -1
  105. package/dist/runtime/composables/defineShortcuts.js +5 -3
  106. package/dist/runtime/composables/useAvatarGroup.d.ts +1 -1
  107. package/dist/runtime/composables/useComponentIcons.d.ts +1 -1
  108. package/dist/runtime/composables/useFileUpload.d.ts +19 -0
  109. package/dist/runtime/composables/useFileUpload.js +69 -0
  110. package/dist/runtime/composables/useFormField.d.ts +3 -6
  111. package/dist/runtime/composables/useFormField.js +5 -2
  112. package/dist/runtime/composables/useKbd.d.ts +1 -1
  113. package/dist/runtime/composables/useLocale.js +2 -2
  114. package/dist/runtime/composables/useOverlay.d.ts +29 -2
  115. package/dist/runtime/composables/useOverlay.js +8 -7
  116. package/dist/runtime/composables/usePortal.d.ts +1 -1
  117. package/dist/runtime/inertia/components/Link.vue.d.ts +1 -1
  118. package/dist/runtime/inertia/components/LinkBase.vue.d.ts +1 -1
  119. package/dist/runtime/inertia/stubs.d.ts +1 -0
  120. package/dist/runtime/inertia/stubs.js +4 -2
  121. package/dist/runtime/locale/ar.js +5 -1
  122. package/dist/runtime/locale/az.js +4 -0
  123. package/dist/runtime/locale/bg.js +4 -0
  124. package/dist/runtime/locale/bn.js +4 -0
  125. package/dist/runtime/locale/ca.js +4 -0
  126. package/dist/runtime/locale/ckb.js +5 -1
  127. package/dist/runtime/locale/cs.js +4 -0
  128. package/dist/runtime/locale/da.js +4 -0
  129. package/dist/runtime/locale/de.js +4 -0
  130. package/dist/runtime/locale/el.js +4 -0
  131. package/dist/runtime/locale/en.js +4 -0
  132. package/dist/runtime/locale/es.js +4 -0
  133. package/dist/runtime/locale/et.js +4 -0
  134. package/dist/runtime/locale/fa_ir.js +4 -0
  135. package/dist/runtime/locale/fi.js +4 -0
  136. package/dist/runtime/locale/fr.js +4 -0
  137. package/dist/runtime/locale/he.js +4 -0
  138. package/dist/runtime/locale/hi.js +5 -1
  139. package/dist/runtime/locale/hu.js +4 -0
  140. package/dist/runtime/locale/hy.js +4 -0
  141. package/dist/runtime/locale/id.js +4 -0
  142. package/dist/runtime/locale/it.js +4 -0
  143. package/dist/runtime/locale/ja.js +4 -0
  144. package/dist/runtime/locale/kk.js +4 -0
  145. package/dist/runtime/locale/km.js +5 -1
  146. package/dist/runtime/locale/ko.js +4 -0
  147. package/dist/runtime/locale/ky.js +4 -0
  148. package/dist/runtime/locale/lb.js +4 -0
  149. package/dist/runtime/locale/lt.js +4 -0
  150. package/dist/runtime/locale/mn.js +4 -0
  151. package/dist/runtime/locale/ms.js +4 -0
  152. package/dist/runtime/locale/nb_no.js +4 -0
  153. package/dist/runtime/locale/nl.js +4 -0
  154. package/dist/runtime/locale/pl.js +4 -0
  155. package/dist/runtime/locale/pt.js +4 -0
  156. package/dist/runtime/locale/pt_br.js +4 -0
  157. package/dist/runtime/locale/ro.js +4 -0
  158. package/dist/runtime/locale/ru.js +4 -0
  159. package/dist/runtime/locale/sk.js +4 -0
  160. package/dist/runtime/locale/sl.js +4 -0
  161. package/dist/runtime/locale/sv.js +4 -0
  162. package/dist/runtime/locale/th.js +4 -0
  163. package/dist/runtime/locale/tj.js +4 -0
  164. package/dist/runtime/locale/tr.js +4 -0
  165. package/dist/runtime/locale/ug_cn.js +4 -0
  166. package/dist/runtime/locale/uk.js +4 -0
  167. package/dist/runtime/locale/ur.js +4 -0
  168. package/dist/runtime/locale/uz.js +4 -0
  169. package/dist/runtime/locale/vi.js +4 -0
  170. package/dist/runtime/locale/zh_cn.js +4 -0
  171. package/dist/runtime/locale/zh_tw.js +4 -0
  172. package/dist/runtime/types/index.d.ts +1 -0
  173. package/dist/runtime/types/index.js +1 -0
  174. package/dist/runtime/types/locale.d.ts +4 -0
  175. package/dist/runtime/utils/index.d.ts +6 -0
  176. package/dist/runtime/utils/index.js +27 -0
  177. package/dist/runtime/vue/components/Icon.vue.d.ts +1 -1
  178. package/dist/runtime/vue/components/Link.vue.d.ts +1 -1
  179. package/dist/runtime/vue/stubs.d.ts +1 -0
  180. package/dist/runtime/vue/stubs.js +3 -1
  181. package/dist/shared/{ui.BPVxSPGY.mjs → ui.BTUFJHYJ.mjs} +299 -36
  182. package/dist/unplugin.d.mts +2 -0
  183. package/dist/unplugin.mjs +6 -3
  184. package/dist/vite.mjs +1 -1
  185. package/package.json +31 -27
@@ -0,0 +1,248 @@
1
+ <script>
2
+ import theme from "#build/ui/file-upload";
3
+ </script>
4
+
5
+ <script setup>
6
+ import { computed, watch } from "vue";
7
+ import { Primitive } from "reka-ui";
8
+ import { createReusableTemplate } from "@vueuse/core";
9
+ import { useAppConfig, useLocale } from "#imports";
10
+ import { useFormField } from "../composables/useFormField";
11
+ import { useFileUpload } from "../composables/useFileUpload";
12
+ import { tv } from "../utils/tv";
13
+ import UAvatar from "./Avatar.vue";
14
+ import UButton from "./Button.vue";
15
+ import UIcon from "./Icon.vue";
16
+ defineOptions({ inheritAttrs: false });
17
+ const props = defineProps({
18
+ as: { type: null, required: false },
19
+ id: { type: String, required: false },
20
+ name: { type: String, required: false },
21
+ icon: { type: String, required: false },
22
+ label: { type: String, required: false },
23
+ description: { type: String, required: false },
24
+ color: { type: null, required: false },
25
+ variant: { type: null, required: false },
26
+ size: { type: null, required: false },
27
+ layout: { type: null, required: false, default: "grid" },
28
+ position: { type: null, required: false, default: "outside" },
29
+ highlight: { type: Boolean, required: false },
30
+ accept: { type: String, required: false, default: "*" },
31
+ multiple: { type: Boolean, required: false, default: false },
32
+ reset: { type: Boolean, required: false, default: false },
33
+ dropzone: { type: Boolean, required: false, default: true },
34
+ interactive: { type: Boolean, required: false, default: true },
35
+ required: { type: Boolean, required: false },
36
+ disabled: { type: Boolean, required: false },
37
+ fileIcon: { type: String, required: false },
38
+ fileDelete: { type: [Boolean, Object], required: false },
39
+ fileDeleteIcon: { type: String, required: false },
40
+ class: { type: null, required: false },
41
+ ui: { type: null, required: false }
42
+ });
43
+ const emits = defineEmits(["update:modelValue", "change"]);
44
+ const slots = defineSlots();
45
+ const modelValue = defineModel({ type: null });
46
+ const appConfig = useAppConfig();
47
+ const { t } = useLocale();
48
+ const [DefineFilesTemplate, ReuseFilesTemplate] = createReusableTemplate();
49
+ const { isDragging, open, inputRef, dropzoneRef } = useFileUpload({
50
+ accept: props.accept,
51
+ reset: props.reset,
52
+ multiple: props.multiple,
53
+ dropzone: props.dropzone,
54
+ onUpdate
55
+ });
56
+ const { emitFormInput, emitFormChange, id, name, disabled, ariaAttrs } = useFormField(props);
57
+ const variant = computed(() => props.multiple ? "area" : props.variant);
58
+ const layout = computed(() => props.variant === "button" && !props.multiple ? "grid" : props.layout);
59
+ const position = computed(() => {
60
+ if (layout.value === "grid" && props.multiple) {
61
+ return "inside";
62
+ }
63
+ if (variant.value === "button") {
64
+ return "outside";
65
+ }
66
+ return props.position;
67
+ });
68
+ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.ui?.fileUpload || {} })({
69
+ dropzone: props.dropzone,
70
+ interactive: props.interactive,
71
+ color: props.color,
72
+ size: props.size,
73
+ variant: variant.value,
74
+ layout: layout.value,
75
+ position: position.value,
76
+ multiple: props.multiple,
77
+ highlight: props.highlight,
78
+ disabled: props.disabled
79
+ }));
80
+ function createObjectUrl(file) {
81
+ return URL.createObjectURL(file);
82
+ }
83
+ function formatFileSize(bytes) {
84
+ if (bytes === 0) {
85
+ return "0B";
86
+ }
87
+ const k = 1024;
88
+ const sizes = ["B", "KB", "MB", "GB"];
89
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
90
+ const size = bytes / Math.pow(k, i);
91
+ const formattedSize = i === 0 ? size.toString() : size.toFixed(0);
92
+ return `${formattedSize}${sizes[i]}`;
93
+ }
94
+ function onUpdate(files, reset = false) {
95
+ if (props.multiple) {
96
+ if (reset) {
97
+ modelValue.value = files;
98
+ } else {
99
+ const existingFiles = modelValue.value || [];
100
+ modelValue.value = [...existingFiles, ...files || []];
101
+ }
102
+ } else {
103
+ modelValue.value = files?.[0];
104
+ }
105
+ const event = new Event("change", { target: { value: modelValue.value } });
106
+ emits("change", event);
107
+ emitFormChange();
108
+ emitFormInput();
109
+ }
110
+ function removeFile(index) {
111
+ if (!modelValue.value) {
112
+ return;
113
+ }
114
+ if (!props.multiple || index === void 0) {
115
+ onUpdate([], true);
116
+ dropzoneRef.value?.focus();
117
+ return;
118
+ }
119
+ const files = [...modelValue.value];
120
+ files.splice(index, 1);
121
+ onUpdate(files, true);
122
+ dropzoneRef.value?.focus();
123
+ }
124
+ watch(modelValue, (newValue) => {
125
+ const hasModelReset = !Array.isArray(newValue) || !newValue.length;
126
+ if (hasModelReset && inputRef.value) {
127
+ inputRef.value.value = "";
128
+ }
129
+ });
130
+ defineExpose({
131
+ inputRef,
132
+ dropzoneRef
133
+ });
134
+ </script>
135
+
136
+ <template>
137
+ <DefineFilesTemplate>
138
+ <template v-if="modelValue && (Array.isArray(modelValue) ? modelValue.length : true)">
139
+ <slot name="files-top" :files="modelValue" :open="open" :remove-file="removeFile" />
140
+
141
+ <div :class="ui.files({ class: props.ui?.files })">
142
+ <slot name="files" :files="modelValue">
143
+ <div v-for="(file, index) in Array.isArray(modelValue) ? modelValue : [modelValue]" :key="file.name" :class="ui.file({ class: props.ui?.file })">
144
+ <slot name="file" :file="file" :index="index">
145
+ <slot name="file-leading" :file="file" :index="index">
146
+ <UAvatar :src="createObjectUrl(file)" :icon="fileIcon || appConfig.ui.icons.file" :size="props.size" :class="ui.fileLeadingAvatar({ class: props.ui?.fileLeadingAvatar })" />
147
+ </slot>
148
+
149
+ <div :class="ui.fileWrapper({ class: props.ui?.fileWrapper })">
150
+ <span :class="ui.fileName({ class: props.ui?.fileName })">
151
+ <slot name="file-name" :file="file" :index="index">
152
+ {{ file.name }}
153
+ </slot>
154
+ </span>
155
+
156
+ <span :class="ui.fileSize({ class: props.ui?.fileSize })">
157
+ <slot name="file-size" :file="file" :index="index">
158
+ {{ formatFileSize(file.size) }}
159
+ </slot>
160
+ </span>
161
+ </div>
162
+
163
+ <slot name="file-trailing" :file="file" :index="index">
164
+ <UButton
165
+ color="neutral"
166
+ v-bind="{
167
+ ...layout === 'grid' ? {
168
+ variant: 'solid',
169
+ size: 'xs'
170
+ } : {
171
+ variant: 'link',
172
+ size
173
+ },
174
+ ...typeof fileDelete === 'object' ? fileDelete : void 0
175
+ }"
176
+ :aria-label="t('fileUpload.removeFile', { filename: file.name })"
177
+ :trailing-icon="fileDeleteIcon || appConfig.ui.icons.close"
178
+ :class="ui.fileTrailingButton({ class: props.ui?.fileTrailingButton })"
179
+ @click.stop.prevent="removeFile(index)"
180
+ />
181
+ </slot>
182
+ </slot>
183
+ </div>
184
+ </slot>
185
+ </div>
186
+
187
+ <slot name="files-bottom" :files="modelValue" :open="open" :remove-file="removeFile" />
188
+ </template>
189
+ </DefineFilesTemplate>
190
+
191
+ <Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })">
192
+ <slot :open="open" :remove-file="removeFile">
193
+ <component
194
+ :is="variant === 'button' ? 'button' : 'div'"
195
+ ref="dropzoneRef"
196
+ :role="variant === 'button' ? void 0 : 'button'"
197
+ :data-dragging="isDragging"
198
+ :class="ui.base({ class: props.ui?.base })"
199
+ :tabindex="interactive && !disabled ? 0 : -1"
200
+ @click="interactive && !disabled && open()"
201
+ @keydown.prevent
202
+ @keyup.enter.space="interactive && !disabled && open()"
203
+ >
204
+ <ReuseFilesTemplate v-if="position === 'inside'" />
205
+
206
+ <div v-if="position === 'inside' ? multiple ? !modelValue?.length : !modelValue : true" :class="ui.wrapper({ class: props.ui?.wrapper })">
207
+ <slot name="leading">
208
+ <UIcon v-if="variant === 'button'" :name="icon || appConfig.ui.icons.upload" :class="ui.icon({ class: props.ui?.icon })" />
209
+ <UAvatar v-else :icon="icon || appConfig.ui.icons.upload" :size="props.size" :class="ui.avatar({ class: props.ui?.avatar })" />
210
+ </slot>
211
+
212
+ <template v-if="variant !== 'button'">
213
+ <div v-if="label || !!slots.label" :class="ui.label({ class: props.ui?.label })">
214
+ <slot name="label">
215
+ {{ label }}
216
+ </slot>
217
+ </div>
218
+ <div v-if="description || !!slots.description" :class="ui.description({ class: props.ui?.description })">
219
+ <slot name="description">
220
+ {{ description }}
221
+ </slot>
222
+ </div>
223
+
224
+ <div v-if="!!slots.actions" :class="ui.actions({ class: props.ui?.actions })">
225
+ <slot name="actions" :files="modelValue" :open="open" :remove-file="removeFile" />
226
+ </div>
227
+ </template>
228
+ </div>
229
+ </component>
230
+
231
+ <ReuseFilesTemplate v-if="position === 'outside'" />
232
+ </slot>
233
+
234
+ <input
235
+ :id="id"
236
+ ref="inputRef"
237
+ type="file"
238
+ :name="name"
239
+ :accept="accept"
240
+ :multiple="multiple"
241
+ :required="required"
242
+ :disabled="disabled"
243
+ v-bind="{ ...$attrs, ...ariaAttrs }"
244
+ class="sr-only"
245
+ tabindex="-1"
246
+ >
247
+ </Primitive>
248
+ </template>
@@ -0,0 +1,167 @@
1
+ import type { AppConfig } from '@nuxt/schema';
2
+ import type { UseFileDialogReturn } from '@vueuse/core';
3
+ import theme from '#build/ui/file-upload';
4
+ import type { ButtonProps } from '../types';
5
+ import type { ComponentConfig } from '../types/utils';
6
+ type FileUpload = ComponentConfig<typeof theme, AppConfig, 'fileUpload'>;
7
+ export interface FileUploadProps<M extends boolean = false> {
8
+ /**
9
+ * The element or component this component should render as.
10
+ * @defaultValue 'div'
11
+ */
12
+ as?: any;
13
+ id?: string;
14
+ name?: string;
15
+ /**
16
+ * The icon to display.
17
+ * @defaultValue appConfig.ui.icons.upload
18
+ * @IconifyIcon
19
+ */
20
+ icon?: string;
21
+ label?: string;
22
+ description?: string;
23
+ /**
24
+ * @defaultValue 'primary'
25
+ */
26
+ color?: FileUpload['variants']['color'];
27
+ /**
28
+ * The `button` variant is only available when `multiple` is `false`.
29
+ * @defaultValue 'area'
30
+ */
31
+ variant?: FileUpload['variants']['variant'];
32
+ /**
33
+ * @defaultValue 'md'
34
+ */
35
+ size?: FileUpload['variants']['size'];
36
+ /**
37
+ * The layout of how files are displayed.
38
+ * Only works when `variant` is `area`.
39
+ * @defaultValue 'list'
40
+ */
41
+ layout?: FileUpload['variants']['layout'];
42
+ /**
43
+ * The position of the files.
44
+ * Only works when `variant` is `area` and when `layout` is `list`.
45
+ * @defaultValue 'outside'
46
+ */
47
+ position?: FileUpload['variants']['position'];
48
+ /** Highlight the ring color like a focus state. */
49
+ highlight?: boolean;
50
+ /**
51
+ * Specifies the allowed file types for the input. Provide a comma-separated list of MIME types or file extensions (e.g., "image/png,application/pdf,.jpg").
52
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/accept
53
+ * @defaultValue '*'
54
+ */
55
+ accept?: string;
56
+ multiple?: M & boolean;
57
+ /**
58
+ * Reset the file input when the dialog is opened.
59
+ * @defaultValue false
60
+ */
61
+ reset?: boolean;
62
+ /**
63
+ * Create a zone that allows the user to drop files onto it.
64
+ * @defaultValue true
65
+ */
66
+ dropzone?: boolean;
67
+ /**
68
+ * Make the dropzone interactive when the user is clicking on it.
69
+ * @defaultValue true
70
+ */
71
+ interactive?: boolean;
72
+ required?: boolean;
73
+ disabled?: boolean;
74
+ /**
75
+ * The icon to display for the file.
76
+ * @defaultValue appConfig.ui.icons.file
77
+ * @IconifyIcon
78
+ */
79
+ fileIcon?: string;
80
+ /**
81
+ * Configure the delete button for the file.
82
+ * When `layout` is `grid`, the default is `{ color: 'neutral', variant: 'solid', size: 'xs' }`{lang="ts-type"}
83
+ * When `layout` is `list`, the default is `{ color: 'neutral', variant: 'link' }`{lang="ts-type"}
84
+ */
85
+ fileDelete?: boolean | Partial<ButtonProps>;
86
+ /**
87
+ * The icon displayed to delete a file.
88
+ * @defaultValue appConfig.ui.icons.close
89
+ * @IconifyIcon
90
+ */
91
+ fileDeleteIcon?: string;
92
+ class?: any;
93
+ ui?: FileUpload['slots'];
94
+ }
95
+ export interface FileUploadEmits<M extends boolean = false> {
96
+ 'update:modelValue': [payload: M extends true ? File[] : File | null];
97
+ 'change': [event: Event];
98
+ }
99
+ type FileUploadFiles<M> = (M extends true ? File[] : File) | null;
100
+ export interface FileUploadSlots<M extends boolean = false> {
101
+ 'default'(props: {
102
+ open: UseFileDialogReturn['open'];
103
+ removeFile: (index?: number) => void;
104
+ }): any;
105
+ 'leading'(props?: {}): any;
106
+ 'label'(props?: {}): any;
107
+ 'description'(props?: {}): any;
108
+ 'actions'(props: {
109
+ files?: FileUploadFiles<M>;
110
+ open: UseFileDialogReturn['open'];
111
+ removeFile: (index?: number) => void;
112
+ }): any;
113
+ 'files'(props: {
114
+ files?: FileUploadFiles<M>;
115
+ }): any;
116
+ 'files-top'(props: {
117
+ files?: FileUploadFiles<M>;
118
+ open: UseFileDialogReturn['open'];
119
+ removeFile: (index?: number) => void;
120
+ }): any;
121
+ 'files-bottom'(props: {
122
+ files?: FileUploadFiles<M>;
123
+ open: UseFileDialogReturn['open'];
124
+ removeFile: (index?: number) => void;
125
+ }): any;
126
+ 'file'(props: {
127
+ file: File;
128
+ index: number;
129
+ }): any;
130
+ 'file-leading'(props: {
131
+ file: File;
132
+ index: number;
133
+ }): any;
134
+ 'file-name'(props: {
135
+ file: File;
136
+ index: number;
137
+ }): any;
138
+ 'file-size'(props: {
139
+ file: File;
140
+ index: number;
141
+ }): any;
142
+ 'file-trailing'(props: {
143
+ file: File;
144
+ index: number;
145
+ }): any;
146
+ }
147
+ declare const _default: <M extends boolean = false>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
148
+ props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
149
+ readonly onChange?: ((event: Event) => any) | undefined;
150
+ readonly "onUpdate:modelValue"?: ((...args: unknown[]) => any) | undefined;
151
+ } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onChange" | "onUpdate:modelValue"> & (FileUploadProps<M> & {
152
+ modelValue?: (M extends true ? File[] : File) | null;
153
+ }) & Partial<{}>> & import("vue").PublicProps;
154
+ expose(exposed: import("vue").ShallowUnwrapRef<{
155
+ inputRef: import("vue").Ref<HTMLInputElement | undefined, HTMLInputElement | undefined>;
156
+ dropzoneRef: import("vue").Ref<HTMLDivElement | undefined, HTMLDivElement | undefined>;
157
+ }>): void;
158
+ attrs: any;
159
+ slots: FileUploadSlots<M>;
160
+ emit: (((evt: "change", event: Event) => void) & ((evt: "update:modelValue", payload: M extends true ? File[] : File | null) => void)) & ((evt: "update:modelValue", value: (M extends true ? File[] : File) | null | undefined) => void);
161
+ }>) => import("vue").VNode & {
162
+ __ctx?: Awaited<typeof __VLS_setup>;
163
+ };
164
+ export default _default;
165
+ type __VLS_PrettifyLocal<T> = {
166
+ [K in keyof T as K]: T[K];
167
+ } & {};
@@ -168,7 +168,7 @@ defineExpose({
168
168
  errors,
169
169
  setErrors(errs, name) {
170
170
  if (name) {
171
- errors.value = errors.value.filter((error) => error.name !== name).concat(resolveErrorIds(errs));
171
+ errors.value = errors.value.filter((err) => name instanceof RegExp ? !(err.name && name.test(err.name)) : err.name !== name).concat(resolveErrorIds(errs));
172
172
  } else {
173
173
  errors.value = resolveErrorIds(errs);
174
174
  }
@@ -178,13 +178,13 @@ defineExpose({
178
178
  },
179
179
  getErrors(name) {
180
180
  if (name) {
181
- return errors.value.filter((err) => err.name === name);
181
+ return errors.value.filter((err) => name instanceof RegExp ? err.name && name.test(err.name) : err.name === name);
182
182
  }
183
183
  return errors.value;
184
184
  },
185
185
  clear(name) {
186
186
  if (name) {
187
- errors.value = errors.value.filter((err) => err.name !== name);
187
+ errors.value = errors.value.filter((err) => name instanceof RegExp ? !(err.name && name.test(err.name)) : err.name !== name);
188
188
  } else {
189
189
  errors.value = [];
190
190
  }
@@ -13,6 +13,7 @@ export interface FormProps<S extends FormSchema, T extends boolean = true> {
13
13
  validate?: (state: Partial<InferInput<S>>) => Promise<FormError[]> | FormError[];
14
14
  /**
15
15
  * The list of input events that trigger the form validation.
16
+ * @remarks The form always validates on submit.
16
17
  * @defaultValue `['blur', 'change', 'input']`
17
18
  */
18
19
  validateOn?: FormInputEvents[];
@@ -43,8 +44,8 @@ export interface FormProps<S extends FormSchema, T extends boolean = true> {
43
44
  onSubmit?: ((event: FormSubmitEvent<FormData<S, T>>) => void | Promise<void>) | (() => void | Promise<void>);
44
45
  }
45
46
  export interface FormEmits<S extends FormSchema, T extends boolean = true> {
46
- (e: 'submit', payload: FormSubmitEvent<FormData<S, T>>): void;
47
- (e: 'error', payload: FormErrorEvent): void;
47
+ submit: [payload: FormSubmitEvent<FormData<S, T>>];
48
+ error: [payload: FormErrorEvent];
48
49
  }
49
50
  export interface FormSlots {
50
51
  default(props?: {
@@ -56,15 +57,15 @@ declare const _default: <S extends FormSchema, T extends boolean = true>(__VLS_p
56
57
  props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
57
58
  readonly onError?: ((payload: FormErrorEvent) => any) | undefined;
58
59
  readonly onSubmit?: ((payload: FormSubmitEvent<FormData<S, T>>) => any) | undefined;
59
- } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onError" | "onSubmit"> & FormProps<S, T> & Partial<{}>> & import("vue").PublicProps;
60
+ } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onSubmit" | "onError"> & FormProps<S, T> & Partial<{}>> & import("vue").PublicProps;
60
61
  expose(exposed: import("vue").ShallowUnwrapRef<Form<S>>): void;
61
62
  attrs: any;
62
63
  slots: FormSlots;
63
- emit: FormEmits<S, T>;
64
+ emit: ((evt: "error", payload: FormErrorEvent) => void) & ((evt: "submit", payload: FormSubmitEvent<FormData<S, T>>) => void);
64
65
  }>) => import("vue").VNode & {
65
66
  __ctx?: Awaited<typeof __VLS_setup>;
66
67
  };
67
68
  export default _default;
68
69
  type __VLS_PrettifyLocal<T> = {
69
- [K in keyof T]: T[K];
70
+ [K in keyof T as K]: T[K];
70
71
  } & {};
@@ -15,7 +15,7 @@ const props = defineProps({
15
15
  label: { type: String, required: false },
16
16
  description: { type: String, required: false },
17
17
  help: { type: String, required: false },
18
- error: { type: [String, Boolean], required: false },
18
+ error: { type: [Boolean, String], required: false },
19
19
  hint: { type: String, required: false },
20
20
  size: { type: null, required: false },
21
21
  required: { type: Boolean, required: false },
@@ -80,7 +80,7 @@ provide(formFieldInjectionKey, computed(() => ({
80
80
  {{ error }}
81
81
  </slot>
82
82
  </div>
83
- <div v-else-if="help || !!slots.help" :class="ui.help({ class: props.ui?.help })">
83
+ <div v-else-if="help || !!slots.help" :id="`${ariaId}-help`" :class="ui.help({ class: props.ui?.help })">
84
84
  <slot name="help" :help="help">
85
85
  {{ help }}
86
86
  </slot>
@@ -15,7 +15,7 @@ export interface FormFieldProps {
15
15
  label?: string;
16
16
  description?: string;
17
17
  help?: string;
18
- error?: string | boolean;
18
+ error?: boolean | string;
19
19
  hint?: string;
20
20
  /**
21
21
  * @defaultValue 'md'
@@ -46,13 +46,13 @@ export interface FormFieldSlots {
46
46
  help?: string;
47
47
  }): any;
48
48
  error(props: {
49
- error?: string | boolean;
49
+ error?: boolean | string;
50
50
  }): any;
51
51
  default(props: {
52
- error?: string | boolean;
52
+ error?: boolean | string;
53
53
  }): any;
54
54
  }
55
- declare const _default: __VLS_WithSlots<import("vue").DefineComponent<FormFieldProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<FormFieldProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, FormFieldSlots>;
55
+ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<FormFieldProps, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<FormFieldProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, FormFieldSlots>;
56
56
  export default _default;
57
57
  type __VLS_WithSlots<T, S> = T & {
58
58
  new (): {
@@ -4,5 +4,5 @@ export interface IconProps {
4
4
  size?: string | number;
5
5
  customize?: (content: string, name?: string, prefix?: string, provider?: string) => string;
6
6
  }
7
- declare const _default: import("vue").DefineComponent<IconProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<IconProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: import("vue").DefineComponent<IconProps, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<IconProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
8
  export default _default;
@@ -5,6 +5,7 @@ import theme from "#build/ui/input";
5
5
  <script setup>
6
6
  import { ref, computed, onMounted } from "vue";
7
7
  import { Primitive } from "reka-ui";
8
+ import { useVModel } from "@vueuse/core";
8
9
  import { useAppConfig } from "#imports";
9
10
  import { useButtonGroup } from "../composables/useButtonGroup";
10
11
  import { useComponentIcons } from "../composables/useComponentIcons";
@@ -29,6 +30,8 @@ const props = defineProps({
29
30
  autofocusDelay: { type: Number, required: false, default: 0 },
30
31
  disabled: { type: Boolean, required: false },
31
32
  highlight: { type: Boolean, required: false },
33
+ modelValue: { type: null, required: false },
34
+ defaultValue: { type: null, required: false },
32
35
  modelModifiers: { type: Object, required: false },
33
36
  class: { type: null, required: false },
34
37
  ui: { type: null, required: false },
@@ -43,7 +46,7 @@ const props = defineProps({
43
46
  });
44
47
  const emits = defineEmits(["update:modelValue", "blur", "change"]);
45
48
  const slots = defineSlots();
46
- const [modelValue, modelModifiers] = defineModel({ type: null });
49
+ const modelValue = useVModel(props, "modelValue", emits, { defaultValue: props.defaultValue });
47
50
  const appConfig = useAppConfig();
48
51
  const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, emitFormFocus, ariaAttrs } = useFormField(props, { deferInputValidation: true });
49
52
  const { orientation, size: buttonGroupSize } = useButtonGroup(props);
@@ -62,29 +65,29 @@ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.ui?.input || {} }
62
65
  }));
63
66
  const inputRef = ref(null);
64
67
  function updateInput(value) {
65
- if (modelModifiers.trim) {
68
+ if (props.modelModifiers?.trim) {
66
69
  value = value?.trim() ?? null;
67
70
  }
68
- if (modelModifiers.number || props.type === "number") {
71
+ if (props.modelModifiers?.number || props.type === "number") {
69
72
  value = looseToNumber(value);
70
73
  }
71
- if (modelModifiers.nullify) {
74
+ if (props.modelModifiers?.nullify) {
72
75
  value ||= null;
73
76
  }
74
77
  modelValue.value = value;
75
78
  emitFormInput();
76
79
  }
77
80
  function onInput(event) {
78
- if (!modelModifiers.lazy) {
81
+ if (!props.modelModifiers?.lazy) {
79
82
  updateInput(event.target.value);
80
83
  }
81
84
  }
82
85
  function onChange(event) {
83
86
  const value = event.target.value;
84
- if (modelModifiers.lazy) {
87
+ if (props.modelModifiers?.lazy) {
85
88
  updateInput(value);
86
89
  }
87
- if (modelModifiers.trim) {
90
+ if (props.modelModifiers?.trim) {
88
91
  event.target.value = value.trim();
89
92
  }
90
93
  emitFormChange();
@@ -4,7 +4,7 @@ import theme from '#build/ui/input';
4
4
  import type { UseComponentIconsProps } from '../composables/useComponentIcons';
5
5
  import type { AcceptableValue, ComponentConfig } from '../types/utils';
6
6
  type Input = ComponentConfig<typeof theme, AppConfig, 'input'>;
7
- export interface InputProps extends UseComponentIconsProps {
7
+ export interface InputProps<T extends AcceptableValue = AcceptableValue> extends UseComponentIconsProps {
8
8
  /**
9
9
  * The element or component this component should render as.
10
10
  * @defaultValue 'div'
@@ -34,6 +34,8 @@ export interface InputProps extends UseComponentIconsProps {
34
34
  disabled?: boolean;
35
35
  /** Highlight the ring color like a focus state. */
36
36
  highlight?: boolean;
37
+ modelValue?: T;
38
+ defaultValue?: T;
37
39
  modelModifiers?: {
38
40
  string?: boolean;
39
41
  number?: boolean;
@@ -45,9 +47,9 @@ export interface InputProps extends UseComponentIconsProps {
45
47
  ui?: Input['slots'];
46
48
  }
47
49
  export interface InputEmits<T extends AcceptableValue = AcceptableValue> {
48
- (e: 'update:modelValue', payload: T): void;
49
- (e: 'blur', event: FocusEvent): void;
50
- (e: 'change', event: Event): void;
50
+ 'update:modelValue': [payload: T];
51
+ 'blur': [event: FocusEvent];
52
+ 'change': [event: Event];
51
53
  }
52
54
  export interface InputSlots {
53
55
  leading(props?: {}): any;
@@ -59,19 +61,17 @@ declare const _default: <T extends AcceptableValue>(__VLS_props: NonNullable<Awa
59
61
  readonly onBlur?: ((event: FocusEvent) => any) | undefined;
60
62
  readonly onChange?: ((event: Event) => any) | undefined;
61
63
  readonly "onUpdate:modelValue"?: ((payload: T) => any) | undefined;
62
- } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onBlur" | "onChange" | "onUpdate:modelValue"> & (InputProps & {
63
- modelValue?: T;
64
- }) & Partial<{}>> & import("vue").PublicProps;
64
+ } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onBlur" | "onChange" | "onUpdate:modelValue"> & InputProps<T> & Partial<{}>> & import("vue").PublicProps;
65
65
  expose(exposed: import("vue").ShallowUnwrapRef<{
66
66
  inputRef: import("vue").Ref<HTMLInputElement | null, HTMLInputElement | null>;
67
67
  }>): void;
68
68
  attrs: any;
69
69
  slots: InputSlots;
70
- emit: InputEmits<T> & ((evt: "update:modelValue", value: T | undefined) => void);
70
+ emit: ((evt: "blur", event: FocusEvent) => void) & ((evt: "change", event: Event) => void) & ((evt: "update:modelValue", payload: T) => void);
71
71
  }>) => import("vue").VNode & {
72
72
  __ctx?: Awaited<typeof __VLS_setup>;
73
73
  };
74
74
  export default _default;
75
75
  type __VLS_PrettifyLocal<T> = {
76
- [K in keyof T]: T[K];
76
+ [K in keyof T as K]: T[K];
77
77
  } & {};