@byyuurin/ui 0.0.10 → 0.1.0

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 (289) hide show
  1. package/README.md +170 -58
  2. package/dist/module.d.mts +4 -13
  3. package/dist/module.json +4 -4
  4. package/dist/module.mjs +54 -20
  5. package/dist/runtime/components/Accordion.vue +48 -72
  6. package/dist/runtime/components/Accordion.vue.d.ts +74 -0
  7. package/dist/runtime/components/Alert.vue +65 -83
  8. package/dist/runtime/components/Alert.vue.d.ts +71 -0
  9. package/dist/runtime/components/App.vue +28 -42
  10. package/dist/runtime/components/App.vue.d.ts +26 -0
  11. package/dist/runtime/components/Avatar.vue +82 -58
  12. package/dist/runtime/components/Avatar.vue.d.ts +39 -0
  13. package/dist/runtime/components/AvatarGroup.vue +43 -70
  14. package/dist/runtime/components/AvatarGroup.vue.d.ts +30 -0
  15. package/dist/runtime/components/Badge.vue +70 -64
  16. package/dist/runtime/components/Badge.vue.d.ts +45 -0
  17. package/dist/runtime/components/Breadcrumb.vue +56 -76
  18. package/dist/runtime/components/Breadcrumb.vue.d.ts +79 -0
  19. package/dist/runtime/components/Button.vue +150 -78
  20. package/dist/runtime/components/Button.vue.d.ts +45 -0
  21. package/dist/runtime/components/Calendar.vue +99 -125
  22. package/dist/runtime/components/Calendar.vue.d.ts +102 -0
  23. package/dist/runtime/components/Card.vue +33 -46
  24. package/dist/runtime/components/Card.vue.d.ts +34 -0
  25. package/dist/runtime/components/Carousel.vue +164 -237
  26. package/dist/runtime/components/Carousel.vue.d.ts +125 -0
  27. package/dist/runtime/components/Checkbox.vue +86 -88
  28. package/dist/runtime/components/Checkbox.vue.d.ts +63 -0
  29. package/dist/runtime/components/Chip.vue +44 -58
  30. package/dist/runtime/components/Chip.vue.d.ts +48 -0
  31. package/dist/runtime/components/Collapsible.vue +29 -33
  32. package/dist/runtime/components/Collapsible.vue.d.ts +34 -0
  33. package/dist/runtime/components/Drawer.vue +110 -150
  34. package/dist/runtime/components/Drawer.vue.d.ts +103 -0
  35. package/dist/runtime/components/DropdownMenu.vue +49 -120
  36. package/dist/runtime/components/DropdownMenu.vue.d.ts +140 -0
  37. package/dist/runtime/components/DropdownMenuContent.vue +177 -143
  38. package/dist/runtime/components/DropdownMenuContent.vue.d.ts +51 -0
  39. package/dist/runtime/components/FieldGroup.vue +33 -0
  40. package/dist/runtime/components/FieldGroup.vue.d.ts +33 -0
  41. package/dist/runtime/components/Form.vue +245 -275
  42. package/dist/runtime/components/Form.vue.d.ts +103 -0
  43. package/dist/runtime/components/FormField.vue +108 -0
  44. package/dist/runtime/components/FormField.vue.d.ts +63 -0
  45. package/dist/runtime/components/Icon.vue +20 -0
  46. package/dist/runtime/components/Icon.vue.d.ts +9 -0
  47. package/dist/runtime/components/Input.vue +125 -156
  48. package/dist/runtime/components/Input.vue.d.ts +76 -0
  49. package/dist/runtime/components/InputNumber.vue +103 -130
  50. package/dist/runtime/components/InputNumber.vue.d.ts +191 -0
  51. package/dist/runtime/components/Kbd.vue +24 -39
  52. package/dist/runtime/components/Kbd.vue.d.ts +34 -0
  53. package/dist/runtime/components/Link.vue +105 -337
  54. package/dist/runtime/components/Link.vue.d.ts +95 -0
  55. package/dist/runtime/components/LinkBase.vue +31 -71
  56. package/dist/runtime/components/LinkBase.vue.d.ts +33 -0
  57. package/dist/runtime/components/Modal.vue +82 -103
  58. package/dist/runtime/components/Modal.vue.d.ts +96 -0
  59. package/dist/runtime/components/NavigationMenu.vue +336 -0
  60. package/dist/runtime/components/NavigationMenu.vue.d.ts +181 -0
  61. package/dist/runtime/components/OverlayProvider.vue +12 -16
  62. package/dist/runtime/components/OverlayProvider.vue.d.ts +3 -0
  63. package/dist/runtime/components/Pagination.vue +65 -139
  64. package/dist/runtime/components/Pagination.vue.d.ts +116 -0
  65. package/dist/runtime/components/PinInput.vue +78 -73
  66. package/dist/runtime/components/PinInput.vue.d.ts +54 -0
  67. package/dist/runtime/components/Popover.vue +60 -75
  68. package/dist/runtime/components/Popover.vue.d.ts +70 -0
  69. package/dist/runtime/components/Progress.vue +79 -126
  70. package/dist/runtime/components/Progress.vue.d.ts +63 -0
  71. package/dist/runtime/components/RadioGroup.vue +109 -140
  72. package/dist/runtime/components/RadioGroup.vue.d.ts +96 -0
  73. package/dist/runtime/components/ScrollArea.vue +50 -48
  74. package/dist/runtime/components/ScrollArea.vue.d.ts +21 -0
  75. package/dist/runtime/components/Select.vue +221 -221
  76. package/dist/runtime/components/Select.vue.d.ts +260 -0
  77. package/dist/runtime/components/Separator.vue +55 -47
  78. package/dist/runtime/components/Separator.vue.d.ts +48 -0
  79. package/dist/runtime/components/Skeleton.vue +25 -22
  80. package/dist/runtime/components/Skeleton.vue.d.ts +19 -0
  81. package/dist/runtime/components/Slider.vue +73 -77
  82. package/dist/runtime/components/Slider.vue.d.ts +52 -0
  83. package/dist/runtime/components/Switch.vue +66 -77
  84. package/dist/runtime/components/Switch.vue.d.ts +58 -0
  85. package/dist/runtime/components/Table.vue +357 -215
  86. package/dist/runtime/components/Table.vue.d.ts +234 -0
  87. package/dist/runtime/components/Tabs.vue +90 -88
  88. package/dist/runtime/components/Tabs.vue.d.ts +97 -0
  89. package/dist/runtime/components/Textarea.vue +147 -146
  90. package/dist/runtime/components/Textarea.vue.d.ts +76 -0
  91. package/dist/runtime/components/Toast.vue +108 -90
  92. package/dist/runtime/components/Toast.vue.d.ts +152 -0
  93. package/dist/runtime/components/ToastProvider.vue +82 -109
  94. package/dist/runtime/components/ToastProvider.vue.d.ts +51 -0
  95. package/dist/runtime/components/Tooltip.vue +54 -53
  96. package/dist/runtime/components/Tooltip.vue.d.ts +53 -0
  97. package/dist/runtime/composables/defineShortcuts.d.ts +16 -0
  98. package/dist/runtime/composables/defineShortcuts.js +129 -0
  99. package/dist/runtime/composables/useAvatarGroup.d.ts +8 -3
  100. package/dist/runtime/composables/useAvatarGroup.js +10 -3
  101. package/dist/runtime/composables/useComponentIcons.d.ts +9 -6
  102. package/dist/runtime/composables/useComponentIcons.js +4 -4
  103. package/dist/runtime/composables/useFieldGroup.d.ts +8 -0
  104. package/dist/runtime/composables/useFieldGroup.js +14 -0
  105. package/dist/runtime/composables/useFormField.d.ts +62 -0
  106. package/dist/runtime/composables/useFormField.js +99 -0
  107. package/dist/runtime/composables/useKbd.d.ts +3 -2
  108. package/dist/runtime/composables/useKbd.js +3 -2
  109. package/dist/runtime/composables/useLocale.d.ts +68 -5
  110. package/dist/runtime/composables/useLocale.js +11 -11
  111. package/dist/runtime/composables/useOverlay.d.ts +51 -15
  112. package/dist/runtime/composables/useOverlay.js +44 -30
  113. package/dist/runtime/composables/usePortal.d.ts +6 -0
  114. package/dist/runtime/composables/usePortal.js +17 -0
  115. package/dist/runtime/composables/useToast.d.ts +12 -5
  116. package/dist/runtime/composables/useToast.js +12 -7
  117. package/dist/runtime/locale/en.d.ts +30 -1
  118. package/dist/runtime/locale/en.js +2 -1
  119. package/dist/runtime/locale/index.d.ts +2 -2
  120. package/dist/runtime/locale/index.js +1 -1
  121. package/dist/runtime/locale/zh_tw.d.ts +31 -0
  122. package/dist/runtime/locale/{zh-tw.js → zh_tw.js} +2 -1
  123. package/dist/runtime/plugins/colors.d.ts +2 -0
  124. package/dist/runtime/plugins/colors.js +50 -0
  125. package/dist/runtime/types/app.config.d.ts +6 -0
  126. package/dist/runtime/types/form.d.ts +58 -17
  127. package/dist/runtime/types/form.js +11 -0
  128. package/dist/runtime/types/index.d.ts +51 -8
  129. package/dist/runtime/types/index.js +45 -2
  130. package/dist/runtime/types/input.d.ts +8 -0
  131. package/dist/runtime/types/locale.d.ts +5 -0
  132. package/dist/runtime/types/style.d.ts +33 -0
  133. package/dist/runtime/types/style.js +0 -0
  134. package/dist/runtime/types/unocss.d.ts +4 -0
  135. package/dist/runtime/types/utils.d.ts +38 -37
  136. package/dist/runtime/utils/form.d.ts +5 -1
  137. package/dist/runtime/utils/form.js +49 -0
  138. package/dist/runtime/utils/index.d.ts +10 -13
  139. package/dist/runtime/utils/index.js +41 -48
  140. package/dist/runtime/utils/link.d.ts +5 -6
  141. package/dist/runtime/utils/link.js +16 -2
  142. package/dist/runtime/utils/locale.d.ts +5 -0
  143. package/dist/runtime/utils/locale.js +10 -0
  144. package/dist/runtime/utils/style.d.ts +94 -0
  145. package/dist/runtime/utils/style.js +37 -0
  146. package/dist/runtime/vue/components/Icon.vue +15 -0
  147. package/dist/runtime/vue/components/Icon.vue.d.ts +7 -0
  148. package/dist/runtime/vue/components/Link.vue +163 -0
  149. package/dist/runtime/vue/components/Link.vue.d.ts +95 -0
  150. package/dist/runtime/vue/composables/useAppConfig.d.ts +1 -0
  151. package/dist/runtime/vue/composables/useAppConfig.js +4 -0
  152. package/dist/runtime/vue/plugins/color-mode.d.ts +4 -0
  153. package/dist/runtime/vue/plugins/color-mode.js +6 -0
  154. package/dist/runtime/vue/plugins/head.d.ts +4 -0
  155. package/dist/runtime/vue/plugins/head.js +9 -0
  156. package/dist/runtime/vue/stubs.d.ts +16 -1
  157. package/dist/runtime/vue/stubs.js +32 -1
  158. package/dist/setup.d.mts +13 -0
  159. package/dist/setup.mjs +12 -0
  160. package/dist/shared/ui.CzIlLITK.mjs +51 -0
  161. package/dist/shared/ui.DSyJHSTk.mjs +3787 -0
  162. package/dist/shared/ui.DpbffTXs.d.mts +84 -0
  163. package/dist/shared/ui.IulR-OYx.d.mts +64 -0
  164. package/dist/types.d.mts +3 -1
  165. package/dist/unocss.d.mts +12 -52
  166. package/dist/unocss.mjs +144 -253
  167. package/dist/unplugin.d.mts +13 -26
  168. package/dist/unplugin.mjs +193 -18
  169. package/dist/vite.d.mts +10 -1
  170. package/dist/vite.mjs +12 -3
  171. package/package.json +156 -87
  172. package/vue-plugin.d.ts +5 -0
  173. package/dist/module.cjs +0 -5
  174. package/dist/module.d.ts +0 -13
  175. package/dist/module.mjs.map +0 -1
  176. package/dist/runtime/app/injections.d.ts +0 -9307
  177. package/dist/runtime/app/injections.js +0 -61
  178. package/dist/runtime/components/ButtonGroup.vue +0 -46
  179. package/dist/runtime/components/FormItem.vue +0 -129
  180. package/dist/runtime/composables/useButtonGroup.d.ts +0 -5
  181. package/dist/runtime/composables/useButtonGroup.js +0 -9
  182. package/dist/runtime/composables/useFormItem.d.ts +0 -27
  183. package/dist/runtime/composables/useFormItem.js +0 -64
  184. package/dist/runtime/composables/useTheme.d.ts +0 -9
  185. package/dist/runtime/composables/useTheme.js +0 -23
  186. package/dist/runtime/index.d.ts +0 -44
  187. package/dist/runtime/index.js +0 -44
  188. package/dist/runtime/locale/zh-tw.d.ts +0 -2
  189. package/dist/runtime/theme/accordion.d.ts +0 -56
  190. package/dist/runtime/theme/accordion.js +0 -28
  191. package/dist/runtime/theme/alert.d.ts +0 -125
  192. package/dist/runtime/theme/alert.js +0 -47
  193. package/dist/runtime/theme/app.d.ts +0 -19
  194. package/dist/runtime/theme/app.js +0 -19
  195. package/dist/runtime/theme/avatar-group.d.ts +0 -52
  196. package/dist/runtime/theme/avatar-group.js +0 -32
  197. package/dist/runtime/theme/avatar.d.ts +0 -56
  198. package/dist/runtime/theme/avatar.js +0 -34
  199. package/dist/runtime/theme/badge.d.ts +0 -82
  200. package/dist/runtime/theme/badge.js +0 -92
  201. package/dist/runtime/theme/breadcrumb.d.ts +0 -67
  202. package/dist/runtime/theme/breadcrumb.js +0 -44
  203. package/dist/runtime/theme/button-group.d.ts +0 -66
  204. package/dist/runtime/theme/button-group.js +0 -42
  205. package/dist/runtime/theme/button.d.ts +0 -190
  206. package/dist/runtime/theme/button.js +0 -164
  207. package/dist/runtime/theme/calendar.d.ts +0 -56
  208. package/dist/runtime/theme/calendar.js +0 -69
  209. package/dist/runtime/theme/card.d.ts +0 -62
  210. package/dist/runtime/theme/card.js +0 -37
  211. package/dist/runtime/theme/carousel.d.ts +0 -113
  212. package/dist/runtime/theme/carousel.js +0 -43
  213. package/dist/runtime/theme/checkbox.d.ts +0 -88
  214. package/dist/runtime/theme/checkbox.js +0 -54
  215. package/dist/runtime/theme/chip.d.ts +0 -67
  216. package/dist/runtime/theme/chip.js +0 -66
  217. package/dist/runtime/theme/collapsible.d.ts +0 -38
  218. package/dist/runtime/theme/collapsible.js +0 -10
  219. package/dist/runtime/theme/drawer.d.ts +0 -148
  220. package/dist/runtime/theme/drawer.js +0 -113
  221. package/dist/runtime/theme/dropdown-menu.d.ts +0 -71
  222. package/dist/runtime/theme/dropdown-menu.js +0 -83
  223. package/dist/runtime/theme/form-item.d.ts +0 -76
  224. package/dist/runtime/theme/form-item.js +0 -34
  225. package/dist/runtime/theme/form.d.ts +0 -8
  226. package/dist/runtime/theme/form.js +0 -7
  227. package/dist/runtime/theme/index.d.ts +0 -41
  228. package/dist/runtime/theme/index.js +0 -41
  229. package/dist/runtime/theme/input-number.d.ts +0 -121
  230. package/dist/runtime/theme/input-number.js +0 -95
  231. package/dist/runtime/theme/input.d.ts +0 -178
  232. package/dist/runtime/theme/input.js +0 -151
  233. package/dist/runtime/theme/kbd.d.ts +0 -39
  234. package/dist/runtime/theme/kbd.js +0 -26
  235. package/dist/runtime/theme/link.d.ts +0 -44
  236. package/dist/runtime/theme/link.js +0 -26
  237. package/dist/runtime/theme/modal.d.ts +0 -48
  238. package/dist/runtime/theme/modal.js +0 -55
  239. package/dist/runtime/theme/pagination.d.ts +0 -80
  240. package/dist/runtime/theme/pagination.js +0 -17
  241. package/dist/runtime/theme/pinInput.d.ts +0 -100
  242. package/dist/runtime/theme/pinInput.js +0 -111
  243. package/dist/runtime/theme/popover.d.ts +0 -38
  244. package/dist/runtime/theme/popover.js +0 -13
  245. package/dist/runtime/theme/progress.d.ts +0 -186
  246. package/dist/runtime/theme/progress.js +0 -95
  247. package/dist/runtime/theme/radio-group.d.ts +0 -110
  248. package/dist/runtime/theme/radio-group.js +0 -61
  249. package/dist/runtime/theme/scroll-area.d.ts +0 -73
  250. package/dist/runtime/theme/scroll-area.js +0 -33
  251. package/dist/runtime/theme/select.d.ts +0 -192
  252. package/dist/runtime/theme/select.js +0 -173
  253. package/dist/runtime/theme/separator.d.ts +0 -80
  254. package/dist/runtime/theme/separator.js +0 -53
  255. package/dist/runtime/theme/skeleton.d.ts +0 -8
  256. package/dist/runtime/theme/skeleton.js +0 -7
  257. package/dist/runtime/theme/slider.d.ts +0 -76
  258. package/dist/runtime/theme/slider.js +0 -52
  259. package/dist/runtime/theme/switch.d.ts +0 -122
  260. package/dist/runtime/theme/switch.js +0 -78
  261. package/dist/runtime/theme/table.d.ts +0 -92
  262. package/dist/runtime/theme/table.js +0 -36
  263. package/dist/runtime/theme/tabs.d.ts +0 -135
  264. package/dist/runtime/theme/tabs.js +0 -146
  265. package/dist/runtime/theme/textarea.d.ts +0 -96
  266. package/dist/runtime/theme/textarea.js +0 -116
  267. package/dist/runtime/theme/toast-provider.d.ts +0 -122
  268. package/dist/runtime/theme/toast-provider.js +0 -97
  269. package/dist/runtime/theme/toast.d.ts +0 -89
  270. package/dist/runtime/theme/toast.js +0 -35
  271. package/dist/runtime/theme/tooltip.d.ts +0 -44
  272. package/dist/runtime/theme/tooltip.js +0 -11
  273. package/dist/runtime/types/components.d.ts +0 -42
  274. package/dist/runtime/utils/extend-theme.d.ts +0 -9
  275. package/dist/runtime/utils/extend-theme.js +0 -27
  276. package/dist/runtime/utils/styler.d.ts +0 -4
  277. package/dist/runtime/utils/styler.js +0 -10
  278. package/dist/runtime/utils/translator.d.ts +0 -18
  279. package/dist/runtime/utils/translator.js +0 -8
  280. package/dist/shared/ui.3e7fad19.mjs +0 -5
  281. package/dist/shared/ui.3e7fad19.mjs.map +0 -1
  282. package/dist/types.d.ts +0 -1
  283. package/dist/unocss.d.ts +0 -52
  284. package/dist/unocss.mjs.map +0 -1
  285. package/dist/unplugin.d.ts +0 -26
  286. package/dist/unplugin.mjs.map +0 -1
  287. package/dist/vite.d.ts +0 -9
  288. package/dist/vite.mjs.map +0 -1
  289. /package/dist/runtime/types/{components.js → input.js} +0 -0
@@ -1,311 +1,281 @@
1
- <script lang="ts">
2
- import type { ComputedRef, DeepReadonly, Ref } from 'vue'
3
- import type { form } from '../theme'
4
- import type { ComponentAttrs, FormError, FormErrorEvent, FormErrorWithId, FormEvent, FormInputEvents, FormSchema, FormSubmitEvent, FormValidateOptions } from '../types'
5
-
6
- export interface FormEmits<T extends object> {
7
- submit: [payload: FormSubmitEvent<T>]
8
- error: [payload: FormErrorEvent]
9
- }
10
-
11
- export interface FormSlots {
12
- default?: (props?: { errors: FormError[] }) => any
13
- }
14
-
15
- export interface FormExpose<T extends object> {
16
- validate: (options?: FormValidateOptions<T>) => Promise<T | false>
17
- clear: (path?: string) => void
18
- errors: Ref<FormError[]>
19
- setErrors: (errors: FormError[], name?: keyof T) => void
20
- getErrors: (name?: keyof T) => FormError[]
21
- submit: () => Promise<void>
22
- disabled: ComputedRef<boolean>
23
- dirty: ComputedRef<boolean>
24
- dirtyFields: DeepReadonly<Set<keyof T>>
25
- touchedFields: DeepReadonly<Set<keyof T>>
26
- blurredFields: DeepReadonly<Set<keyof T>>
27
- }
28
-
29
- export interface FormProps<T extends object> extends Omit<ComponentAttrs<typeof form>, 'ui'> {
30
- id?: string | number
31
- /** Schema to validate the form state. */
32
- schema?: FormSchema<T>
33
- /** An object representing the current state of the form. */
34
- state: Partial<T>
35
- /**
36
- * Custom validation function to validate the form state.
37
- * @param state - The current state of the form.
38
- * @returns A promise that resolves to an array of FormError objects, or an array of FormError objects directly.
39
- */
40
- validate?: (state: Partial<T>) => Promise<FormError[]> | FormError[]
41
- /**
42
- * The list of input events that trigger the form validation.
43
- * @default ["blur", "change", "input"]
44
- */
45
- validateOn?: FormInputEvents[]
46
- /** Disable all inputs inside the form. */
47
- disabled?: boolean
48
- /**
49
- * Delay in milliseconds before validating the form on input events.
50
- * @default 300
51
- */
52
- validateOnInputDelay?: number
53
- /**
54
- * If true, schema transformations will be applied to the state on submit.
55
- * @default true
56
- */
57
- transform?: boolean
58
- onSubmit?: ((event: FormSubmitEvent<T>) => void | Promise<void>) | (() => void | Promise<void>)
59
- }
60
-
61
- export class FormValidationExceptionError extends Error {
62
- formId: string | number
63
- errors: FormErrorWithId[]
64
- children?: FormValidationExceptionError[]
65
-
66
- constructor(formId: string | number, errors: FormErrorWithId[], childErrors?: FormValidationExceptionError[]) {
67
- super('Form validation exception')
68
- this.name = 'FormValidationExceptionError'
69
- this.formId = formId
70
- this.errors = errors
71
- this.children = childErrors
72
- Object.setPrototypeOf(this, FormValidationExceptionError.prototype)
73
- }
74
- }
1
+ <script>
2
+ import theme from "#build/ui/form";
75
3
  </script>
76
4
 
77
- <script setup lang="ts" generic="T extends object">
78
- import { useEventBus } from '@vueuse/core'
79
- import { computed, nextTick, onMounted, onUnmounted, readonly, ref, useId } from 'vue'
80
- import { injectFormBus, provideFormBus, provideFormErrors, provideFormInputs, provideFormLoading, provideFormOptions } from '../app/injections'
81
- import { useTheme } from '../composables/useTheme'
82
- import { validateSchema } from '../utils'
83
-
84
- const props = withDefaults(defineProps<FormProps<T>>(), {
85
- validateOn: () => ['input', 'blur', 'change'],
86
- validateOnInputDelay: 300,
87
- transform: true,
88
- })
89
-
90
- const emit = defineEmits<FormEmits<T>>()
91
- defineSlots<FormSlots>()
92
-
93
- const formId = props.id ?? useId()
94
-
95
- const bus = useEventBus<FormEvent<T>>(`form-${formId}`)
96
- const parentBus = injectFormBus()
97
-
98
- provideFormBus(bus)
99
-
100
- const nestedForms = ref<Map<string | number, { validate: typeof validateFn }>>(new Map())
101
-
102
- onMounted(() => {
103
- bus.on(async (event) => {
104
- if (event.type === 'attach') {
105
- nestedForms.value.set(event.formId, { validate: event.validate })
106
- }
107
- else if (event.type === 'detach') {
108
- nestedForms.value.delete(event.formId)
109
- }
110
- else if (props.validateOn?.includes(event.type) && !loading.value) {
111
- if (event.type !== 'input')
112
- await validateFn({ name: event.name, silent: true, nested: false })
113
- else if (event.eager || blurredFields.has(event.name))
114
- await validateFn({ name: event.name, silent: true, nested: false })
115
- }
116
-
117
- if (event.type === 'blur')
118
- blurredFields.add(event.name)
119
-
120
- if (event.type === 'change' || event.type === 'input' || event.type === 'blur' || event.type === 'focus')
121
- touchedFields.add(event.name)
122
-
123
- if (event.type === 'change' || event.type === 'input')
124
- dirtyFields.add(event.name)
125
- })
126
- })
127
-
128
- onUnmounted(() => {
129
- bus.reset()
130
- })
131
-
5
+ <script setup>
6
+ import { useEventBus } from "@vueuse/core";
7
+ import { computed, nextTick, onMounted, onUnmounted, reactive, readonly, ref, shallowRef, useId } from "vue";
8
+ import { useAppConfig } from "#imports";
9
+ import { injectFormBus, injectFormState, provideFormBus, provideFormErrors, provideFormInputs, provideFormLoading, provideFormOptions, provideFormState } from "../composables/useFormField";
10
+ import { FormValidationException } from "../types/form";
11
+ import { getAtPath, setAtPath, validateSchema } from "../utils/form";
12
+ import { cv, merge } from "../utils/style";
13
+ const props = defineProps({
14
+ id: { type: [String, Number], required: false },
15
+ schema: { type: null, required: false },
16
+ state: { type: null, required: true },
17
+ validate: { type: Function, required: false },
18
+ validateOn: { type: Array, required: false, default: () => ["input", "blur", "change"] },
19
+ disabled: { type: Boolean, required: false },
20
+ name: { type: null, required: false },
21
+ validateOnInputDelay: { type: Number, required: false, default: 300 },
22
+ transform: { type: Boolean, required: false, default: true },
23
+ nested: { type: Boolean, required: false },
24
+ loadingAuto: { type: Boolean, required: false, default: true },
25
+ onSubmit: { type: Function, required: false },
26
+ class: { type: [Object, String, Number, Boolean, null, Array], required: false, skipCheck: true }
27
+ });
28
+ const emit = defineEmits(["submit", "error"]);
29
+ defineSlots();
30
+ const formId = props.id ?? useId();
31
+ const bus = useEventBus(`form-${formId}`);
32
+ const parentBus = props.nested && injectFormBus();
33
+ const parentState = props.nested ? injectFormState() : void 0;
34
+ const state = computed(() => {
35
+ if (parentState?.value)
36
+ return props.name ? getAtPath(parentState.value, props.name) : parentState.value;
37
+ return props.state;
38
+ });
39
+ provideFormBus(bus);
40
+ provideFormState(state);
41
+ const nestedForms = ref(/* @__PURE__ */ new Map());
132
42
  onMounted(async () => {
133
43
  if (parentBus) {
134
- await nextTick()
135
- parentBus.emit({ type: 'attach', validate: validateFn as FormExpose<T>['validate'], formId })
44
+ await nextTick();
45
+ parentBus.emit({ type: "attach", validate: validateFn, formId, name: props.name, api });
136
46
  }
137
- })
138
-
47
+ });
139
48
  onUnmounted(() => {
49
+ bus.reset();
140
50
  if (parentBus)
141
- parentBus.emit({ type: 'detach', formId })
142
- })
143
-
144
- const errors = ref<FormErrorWithId[]>([])
145
- provideFormErrors(errors)
146
-
147
- const inputs = ref<{ [P in keyof T]?: { id?: string, pattern?: RegExp } }>({})
148
- provideFormInputs(inputs as any)
149
-
150
- const dirtyFields = new Set<keyof T>()
151
- const touchedFields = new Set<keyof T>()
152
- const blurredFields = new Set<keyof T>()
153
-
154
- function resolveErrorIds(errs: FormError[]): FormErrorWithId[] {
51
+ parentBus.emit({ type: "detach", formId });
52
+ });
53
+ onMounted(() => {
54
+ bus.on(async (event) => {
55
+ if (event.type === "attach") {
56
+ nestedForms.value.set(event.formId, { validate: event.validate, name: event.name, api: event.api });
57
+ } else if (event.type === "detach") {
58
+ nestedForms.value.delete(event.formId);
59
+ } else if (props.validateOn?.includes(event.type) && !loading.value) {
60
+ if (event.type !== "input")
61
+ await validateFn({ name: event.name, silent: true, nested: false });
62
+ else if (event.eager || blurredFields.has(event.name))
63
+ await validateFn({ name: event.name, silent: true, nested: false });
64
+ }
65
+ if (event.type === "blur")
66
+ blurredFields.add(event.name);
67
+ if (event.type === "change" || event.type === "input" || event.type === "blur" || event.type === "focus")
68
+ touchedFields.add(event.name);
69
+ if (event.type === "change" || event.type === "input")
70
+ dirtyFields.add(event.name);
71
+ });
72
+ });
73
+ const errors = ref([]);
74
+ provideFormErrors(errors);
75
+ const inputs = ref({});
76
+ provideFormInputs(inputs);
77
+ const dirtyFields = reactive(/* @__PURE__ */ new Set());
78
+ const touchedFields = reactive(/* @__PURE__ */ new Set());
79
+ const blurredFields = reactive(/* @__PURE__ */ new Set());
80
+ function resolveErrorIds(errs) {
155
81
  return errs.map((err) => ({
156
82
  ...err,
157
- id: err?.name ? inputs.value[err.name]?.id : undefined,
158
- }))
83
+ id: err?.name ? inputs.value[err.name]?.id : void 0
84
+ }));
159
85
  }
160
-
161
- const transformedState = ref<T | null>(null)
162
-
163
- async function getErrors(): Promise<FormErrorWithId[]> {
164
- let errs = props.validate ? (await props.validate(props.state)) ?? [] : []
165
-
86
+ const transformedState = ref(null);
87
+ async function getErrors() {
88
+ let errs = props.validate ? await props.validate(props.state) ?? [] : [];
166
89
  if (props.schema) {
167
- const { errors, result } = await validateSchema(props.state, props.schema as FormSchema<typeof props.state>)
168
-
169
- if (errors)
170
- errs = errs.concat(errors)
90
+ const { errors: errors2, result } = await validateSchema(props.state, props.schema);
91
+ if (errors2)
92
+ errs = errs.concat(errors2);
171
93
  else
172
- transformedState.value = result
94
+ transformedState.value = result;
173
95
  }
174
-
175
- return resolveErrorIds(errs)
96
+ return resolveErrorIds(errs);
176
97
  }
177
-
178
- async function validateFn(options: FormValidateOptions<T>): Promise<T | false> {
179
- const _options: FormValidateOptions<T> = {
180
- silent: false,
181
- nested: true,
182
- transform: false,
183
- ...options,
98
+ async function validateFn(opts = { silent: false, nested: false, transform: false }) {
99
+ const names = opts.name && !Array.isArray(opts.name) ? [opts.name] : opts.name;
100
+ let nestedResults = [];
101
+ let nestedErrors = [];
102
+ if (!names && opts.nested) {
103
+ const validations = Array.from(nestedForms.value.values()).map((form) => validateNestedForm(form, opts));
104
+ const results = await Promise.all(validations);
105
+ nestedErrors = results.filter((r) => r.error).flatMap((r) => r.error.errors.map((e) => addFormPath(e, r.name)));
106
+ nestedResults = results.filter((r) => r.output !== void 0);
184
107
  }
185
-
186
- const names = _options.name && !Array.isArray(_options.name) ? [_options.name] : _options.name as (keyof T)[]
187
-
188
- const nestedValidatePromises = !names && _options.nested
189
- ? Array.from(nestedForms.value.values()).map(
190
- ({ validate }) => validate(_options).then(() => {}).catch((error: Error) => {
191
- if (!(error instanceof FormValidationExceptionError))
192
- throw error
193
-
194
- return error
195
- }),
196
- )
197
- : []
198
-
199
- if (names) {
200
- const otherErrors = errors.value.filter((error) => !names.some((name) => {
201
- const pattern = inputs.value?.[name]?.pattern
202
- return name === error.name || (pattern && error.name?.match(pattern))
203
- }))
204
-
205
- // eslint-disable-next-line unicorn/no-await-expression-member
206
- const pathErrors = (await getErrors()).filter((error) => names.some((name) => {
207
- const pattern = inputs.value?.[name]?.pattern
208
- return name === error.name || (pattern && error.name?.match(pattern))
209
- }))
210
-
211
- errors.value = otherErrors.concat(pathErrors)
108
+ const currentErrors = await getErrors();
109
+ const allErrors = [...currentErrors, ...nestedErrors];
110
+ errors.value = names ? filterErrorsByNames(allErrors, names) : allErrors;
111
+ if (errors.value?.length) {
112
+ if (opts.silent)
113
+ return false;
114
+ throw new FormValidationException(formId, errors.value);
212
115
  }
213
- else {
214
- errors.value = await getErrors()
116
+ if (opts.transform) {
117
+ nestedResults.forEach((result) => {
118
+ if (result.name)
119
+ setAtPath(transformedState.value, result.name, result.output);
120
+ else
121
+ Object.assign(transformedState.value, result.output);
122
+ });
123
+ return transformedState.value ?? state.value;
215
124
  }
216
-
217
- // eslint-disable-next-line unicorn/no-await-expression-member
218
- const childErrors = (await Promise.all(nestedValidatePromises)).filter((val) => val !== undefined)
219
-
220
- if (errors.value.length + childErrors.length > 0) {
221
- if (_options.silent)
222
- return false
223
-
224
- throw new FormValidationExceptionError(formId, errors.value, childErrors)
225
- }
226
-
227
- if (_options.transform)
228
- Object.assign(props.state, transformedState.value)
229
-
230
- return props.state as T
125
+ return state.value;
231
126
  }
232
-
233
- const loading = ref(false)
234
- provideFormLoading(readonly(loading))
235
-
236
- async function onSubmitWrapper(payload: Event) {
237
- loading.value = true
238
-
239
- const event = payload as FormSubmitEvent<any>
240
-
127
+ const loading = shallowRef(false);
128
+ provideFormLoading(readonly(loading));
129
+ async function onSubmitWrapper(payload) {
130
+ loading.value = props.loadingAuto && true;
131
+ const event = payload;
241
132
  try {
242
- event.data = await validateFn({ nested: true, transform: props.transform })
243
- await props.onSubmit?.(event)
244
- dirtyFields.clear()
245
- }
246
- catch (error) {
247
- if (!(error instanceof FormValidationExceptionError))
248
- throw error
249
-
250
- const errorEvent: FormErrorEvent = {
133
+ event.data = await validateFn({ nested: true, transform: props.transform });
134
+ await props.onSubmit?.(event);
135
+ dirtyFields.clear();
136
+ } catch (error) {
137
+ if (!(error instanceof FormValidationException))
138
+ throw error;
139
+ const errorEvent = {
251
140
  ...event,
252
- errors: error.errors,
253
- children: error.children,
254
- }
255
- emit('error', errorEvent)
256
- }
257
- finally {
258
- loading.value = false
141
+ errors: error.errors
142
+ };
143
+ emit("error", errorEvent);
144
+ } finally {
145
+ loading.value = false;
259
146
  }
260
147
  }
261
-
262
- const disabled = computed(() => props.disabled || loading.value)
263
-
148
+ const disabled = computed(() => props.disabled || loading.value);
264
149
  provideFormOptions(computed(() => ({
265
150
  disabled: disabled.value,
266
- validateOnInputDelay: props.validateOnInputDelay,
267
- })))
268
-
269
- defineExpose<FormExpose<T>>({
270
- validate: validateFn as FormExpose<T>['validate'],
151
+ validateOnInputDelay: props.validateOnInputDelay
152
+ })));
153
+ async function validateNestedForm(form, opts) {
154
+ try {
155
+ const result = await form.validate({ ...opts, silent: false });
156
+ return { name: form.name, output: result };
157
+ } catch (error) {
158
+ if (!(error instanceof FormValidationException))
159
+ throw error;
160
+ return { name: form.name, error };
161
+ }
162
+ }
163
+ function addFormPath(error, formPath) {
164
+ if (!formPath || !error.name)
165
+ return error;
166
+ return { ...error, name: `${formPath}.${error.name}` };
167
+ }
168
+ function stripFormPath(error, formPath) {
169
+ const prefix = `${formPath}.`;
170
+ const name = error?.name?.startsWith(prefix) ? error.name.slice(prefix.length) : error.name;
171
+ return { ...error, name };
172
+ }
173
+ function filterFormErrors(errors2, formPath) {
174
+ if (!formPath)
175
+ return errors2;
176
+ return errors2.filter((e) => e?.name?.startsWith(`${formPath}.`)).map((e) => stripFormPath(e, formPath));
177
+ }
178
+ function getFormErrors(form) {
179
+ return form.api.getErrors().map((e) => form.name ? { ...e, name: `${form.name}.${e.name}` } : e);
180
+ }
181
+ function matchesTarget(target, path) {
182
+ if (!target || !path)
183
+ return true;
184
+ if (target instanceof RegExp)
185
+ return target.test(path);
186
+ return path === target || typeof target === "string" && target.startsWith(`${path}.`);
187
+ }
188
+ function getNestedTarget(target, formPath) {
189
+ if (!target || target instanceof RegExp)
190
+ return target;
191
+ if (formPath === target)
192
+ return void 0;
193
+ if (typeof target === "string" && target.startsWith(`${formPath}.`))
194
+ return target.slice(Math.max(0, formPath.length + 1));
195
+ return target;
196
+ }
197
+ function filterErrorsByNames(allErrors, names) {
198
+ const nameSet = new Set(names);
199
+ const patterns = names.map((name) => inputs.value?.[name]?.pattern).filter(Boolean);
200
+ const matchesNames = (error) => {
201
+ if (!error.name)
202
+ return false;
203
+ if (nameSet.has(error.name))
204
+ return true;
205
+ return patterns.some((pattern) => pattern.test(error.name));
206
+ };
207
+ const keepErrors = errors.value.filter((error) => !matchesNames(error));
208
+ const newErrors = allErrors.filter(matchesNames);
209
+ return [...keepErrors, ...newErrors];
210
+ }
211
+ function filterErrorsByTarget(currentErrors, target) {
212
+ return currentErrors.filter((err) => target instanceof RegExp ? !(err.name && target.test(err.name)) : !err.name || err.name !== target);
213
+ }
214
+ function isLocalError(error) {
215
+ return !error.name || !!inputs.value[error.name];
216
+ }
217
+ const api = {
218
+ validate: validateFn,
219
+ // FIXME: type issue
271
220
  errors,
272
-
273
- setErrors(errs: FormError[], name?: keyof T) {
274
- errors.value = name
275
- ? errors.value
276
- .filter((error) => error.name !== name)
277
- .concat(resolveErrorIds(errs))
278
- : resolveErrorIds(errs)
221
+ setErrors(errs, name) {
222
+ const localErrors = resolveErrorIds(errs.filter(isLocalError));
223
+ const nestedErrors = [];
224
+ for (const form of nestedForms.value.values()) {
225
+ if (matchesTarget(name, form.name)) {
226
+ const formErrors = filterFormErrors(errs, form.name);
227
+ form.api.setErrors(formErrors, getNestedTarget(name, form.name || ""));
228
+ nestedErrors.push(...getFormErrors(form));
229
+ }
230
+ }
231
+ if (name) {
232
+ const keepErrors = filterErrorsByTarget(errors.value, name);
233
+ errors.value = [...keepErrors, ...localErrors, ...nestedErrors];
234
+ } else {
235
+ errors.value = [...localErrors, ...nestedErrors];
236
+ }
279
237
  },
280
-
281
238
  async submit() {
282
- await onSubmitWrapper(new Event('submit'))
239
+ await onSubmitWrapper(new Event("submit"));
283
240
  },
284
-
285
- getErrors(name?: keyof T) {
286
- if (name)
287
- return errors.value.filter((err) => err.name === name)
288
-
289
- return errors.value
241
+ getErrors(name) {
242
+ if (!name)
243
+ return errors.value;
244
+ return errors.value.filter((err) => name instanceof RegExp ? err.name && name.test(err.name) : err.name === name);
290
245
  },
291
-
292
- clear(name?: string) {
293
- errors.value = name ? errors.value.filter((err) => err.name !== name) : []
246
+ clear(name) {
247
+ const localErrors = name ? errors.value.filter((err) => isLocalError(err) && (name instanceof RegExp ? !(err.name && name.test(err.name)) : err.name !== name)) : [];
248
+ const nestedErrors = [];
249
+ for (const form of nestedForms.value.values()) {
250
+ if (matchesTarget(name, form.name))
251
+ form.api.clear();
252
+ nestedErrors.push(...getFormErrors(form));
253
+ }
254
+ errors.value = [...localErrors, ...nestedErrors];
294
255
  },
295
-
296
256
  disabled,
297
- dirty: computed(() => dirtyFields.size > 0),
298
- dirtyFields: readonly(dirtyFields) as DeepReadonly<Set<keyof T>>,
299
- blurredFields: readonly(blurredFields) as DeepReadonly<Set<keyof T>>,
300
- touchedFields: readonly(touchedFields) as DeepReadonly<Set<keyof T>>,
301
- })
302
-
303
- const { generateStyle } = useTheme()
304
- const style = computed(() => generateStyle('form', props))
257
+ loading,
258
+ dirty: computed(() => !!dirtyFields.size),
259
+ dirtyFields: readonly(dirtyFields),
260
+ blurredFields: readonly(blurredFields),
261
+ touchedFields: readonly(touchedFields)
262
+ };
263
+ defineExpose(api);
264
+ const appConfig = useAppConfig();
265
+ const ui = computed(() => {
266
+ const styler = cv(merge(theme, appConfig.ui.form));
267
+ return styler(props);
268
+ });
305
269
  </script>
306
270
 
307
271
  <template>
308
- <component :is="parentBus ? 'div' : 'form'" :id="formId" :class="style" @submit.prevent="onSubmitWrapper">
309
- <slot :errors="errors"></slot>
272
+ <component
273
+ :is="parentBus ? 'div' : 'form'"
274
+ :id="formId"
275
+ :class="ui.base({ class: props.class })"
276
+ data-part="base"
277
+ @submit.prevent="onSubmitWrapper"
278
+ >
279
+ <slot :errors="errors" :loading="loading"></slot>
310
280
  </component>
311
281
  </template>
@@ -0,0 +1,103 @@
1
+ import type { ComponentBaseProps, FormData, FormError, FormErrorEvent, FormErrorWithId, FormInputEvents, FormSchema, FormSubmitEvent, InferInput } from '../types';
2
+ import type { StaticSlot } from '../types/utils';
3
+ export interface FormProps<S extends FormSchema, T extends boolean = true, N extends boolean = false> extends ComponentBaseProps {
4
+ id?: string | number;
5
+ /** Schema to validate the form state. Supports Standard Schema objects, Yup, Joi, and Superstructs. */
6
+ schema?: S;
7
+ /** An object representing the current state of the form. */
8
+ state: N extends false ? Partial<InferInput<S>> : never;
9
+ /**
10
+ * Custom validation function to validate the form state.
11
+ * @param state - The current state of the form.
12
+ * @returns A promise that resolves to an array of FormError objects, or an array of FormError objects directly.
13
+ */
14
+ validate?: (state: Partial<InferInput<S>>) => Promise<FormError[]> | FormError[];
15
+ /**
16
+ * The list of input events that trigger the form validation.
17
+ * @remarks The form always validates on submit.
18
+ * @default ["blur", "change", "input"]
19
+ */
20
+ validateOn?: FormInputEvents[];
21
+ /** Disable all inputs inside the form. */
22
+ disabled?: boolean;
23
+ /**
24
+ * Path of the form's state within it's parent form.
25
+ * Used for nesting forms. Only available if `nested` is true.
26
+ */
27
+ name?: N extends true ? string : never;
28
+ /**
29
+ * Delay in milliseconds before validating the form on input events.
30
+ * @default 300
31
+ */
32
+ validateOnInputDelay?: number;
33
+ /**
34
+ * If true, schema transformations will be applied to the state on submit.
35
+ * @default true
36
+ */
37
+ transform?: T & boolean;
38
+ /**
39
+ * If true, this form will attach to its parent Form and validate at the same time.
40
+ * @default false
41
+ */
42
+ nested?: N & boolean;
43
+ /**
44
+ * When `true`, all form elements will be disabled on `@submit` event.
45
+ *
46
+ * This will cause any focused input elements to lose their focus state.
47
+ * @default true
48
+ */
49
+ loadingAuto?: boolean;
50
+ onSubmit?: ((event: FormSubmitEvent<T>) => void | Promise<void>) | (() => void | Promise<void>);
51
+ }
52
+ export interface FormEmits<S extends FormSchema, T extends boolean = true> {
53
+ submit: [event: FormSubmitEvent<FormData<S, T>>];
54
+ error: [event: FormErrorEvent];
55
+ }
56
+ export interface FormSlots {
57
+ default: StaticSlot<{
58
+ errors: FormError[];
59
+ loading: boolean;
60
+ }>;
61
+ }
62
+ declare const __VLS_export: <S extends FormSchema, T extends boolean = true, N 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<{
63
+ props: __VLS_PrettifyLocal<FormProps<S, T, N> & {
64
+ onError?: ((event: FormErrorEvent) => any) | undefined;
65
+ onSubmit?: ((event: FormSubmitEvent<FormData<S, T>>) => any) | undefined;
66
+ }> & import("vue").PublicProps;
67
+ expose: (exposed: import("vue").ShallowUnwrapRef<{
68
+ validate: any;
69
+ errors: import("vue").Ref<{
70
+ id?: string | undefined;
71
+ name?: string | undefined;
72
+ message: string;
73
+ }[], FormErrorWithId[] | {
74
+ id?: string | undefined;
75
+ name?: string | undefined;
76
+ message: string;
77
+ }[]>;
78
+ setErrors(errs: FormError[], name?: keyof InferInput<S> | string | RegExp): void;
79
+ submit(): Promise<void>;
80
+ getErrors(name?: keyof InferInput<S> | string | RegExp): {
81
+ id?: string | undefined;
82
+ name?: string | undefined;
83
+ message: string;
84
+ }[];
85
+ clear(name?: keyof InferInput<S> | string | RegExp): void;
86
+ disabled: import("vue").ComputedRef<boolean>;
87
+ loading: import("vue").ShallowRef<boolean, boolean>;
88
+ dirty: import("vue").ComputedRef<boolean>;
89
+ dirtyFields: ReadonlySet<import("vue").DeepReadonly<import("@vue/reactivity").UnwrapRefSimple<keyof InferInput<S>>>>;
90
+ blurredFields: ReadonlySet<import("vue").DeepReadonly<import("@vue/reactivity").UnwrapRefSimple<keyof InferInput<S>>>>;
91
+ touchedFields: ReadonlySet<import("vue").DeepReadonly<import("@vue/reactivity").UnwrapRefSimple<keyof InferInput<S>>>>;
92
+ }>) => void;
93
+ attrs: any;
94
+ slots: FormSlots;
95
+ emit: ((evt: "error", event: FormErrorEvent) => void) & ((evt: "submit", event: FormSubmitEvent<FormData<S, T>>) => void);
96
+ }>) => import("vue").VNode & {
97
+ __ctx?: Awaited<typeof __VLS_setup>;
98
+ };
99
+ declare const _default: typeof __VLS_export;
100
+ export default _default;
101
+ type __VLS_PrettifyLocal<T> = {
102
+ [K in keyof T as K]: T[K];
103
+ } & {};