@nuxt/ui 3.0.0-alpha.9 → 3.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/.nuxt/ui/accordion.ts +2 -2
  2. package/.nuxt/ui/alert.ts +122 -95
  3. package/.nuxt/ui/avatar-group.ts +14 -2
  4. package/.nuxt/ui/avatar.ts +16 -4
  5. package/.nuxt/ui/badge.ts +139 -93
  6. package/.nuxt/ui/breadcrumb.ts +5 -5
  7. package/.nuxt/ui/button-group.ts +13 -0
  8. package/.nuxt/ui/button.ts +167 -136
  9. package/.nuxt/ui/calendar.ts +103 -0
  10. package/.nuxt/ui/card.ts +27 -1
  11. package/.nuxt/ui/carousel.ts +7 -5
  12. package/.nuxt/ui/checkbox.ts +45 -27
  13. package/.nuxt/ui/chip.ts +44 -15
  14. package/.nuxt/ui/color-picker.ts +47 -0
  15. package/.nuxt/ui/command-palette.ts +28 -18
  16. package/.nuxt/ui/container.ts +1 -1
  17. package/.nuxt/ui/context-menu.ts +64 -46
  18. package/.nuxt/ui/drawer.ts +83 -15
  19. package/.nuxt/ui/dropdown-menu.ts +65 -47
  20. package/.nuxt/ui/form-field.ts +15 -7
  21. package/.nuxt/ui/index.ts +5 -1
  22. package/.nuxt/ui/input-menu.ts +150 -116
  23. package/.nuxt/ui/input-number.ts +102 -71
  24. package/.nuxt/ui/input.ts +111 -76
  25. package/.nuxt/ui/kbd.ts +18 -6
  26. package/.nuxt/ui/link.ts +3 -3
  27. package/.nuxt/ui/modal.ts +8 -7
  28. package/.nuxt/ui/navigation-menu.ts +244 -131
  29. package/.nuxt/ui/pin-input.ts +77 -51
  30. package/.nuxt/ui/popover.ts +2 -2
  31. package/.nuxt/ui/progress.ts +111 -72
  32. package/.nuxt/ui/radio-group.ts +45 -22
  33. package/.nuxt/ui/select-menu.ts +122 -86
  34. package/.nuxt/ui/select.ts +120 -84
  35. package/.nuxt/ui/separator.ts +60 -31
  36. package/.nuxt/ui/skeleton.ts +1 -1
  37. package/.nuxt/ui/slideover.ts +17 -9
  38. package/.nuxt/ui/slider.ts +61 -38
  39. package/.nuxt/ui/stepper.ts +202 -0
  40. package/.nuxt/ui/switch.ts +40 -22
  41. package/.nuxt/ui/table.ts +48 -31
  42. package/.nuxt/ui/tabs.ts +100 -72
  43. package/.nuxt/ui/textarea.ts +111 -76
  44. package/.nuxt/ui/toast.ts +53 -33
  45. package/.nuxt/ui/toaster.ts +31 -15
  46. package/.nuxt/ui/tooltip.ts +2 -2
  47. package/.nuxt/ui/tree.ts +154 -0
  48. package/.nuxt/ui.css +92 -0
  49. package/README.md +19 -8
  50. package/cli/commands/make/component.mjs +12 -0
  51. package/cli/package.json +2 -2
  52. package/cli/templates.mjs +27 -22
  53. package/dist/module.cjs +9 -171
  54. package/dist/module.d.cts +5 -15
  55. package/dist/module.d.mts +5 -15
  56. package/dist/module.d.ts +5 -15
  57. package/dist/module.json +2 -2
  58. package/dist/module.mjs +12 -168
  59. package/dist/runtime/components/Accordion.vue +8 -39
  60. package/dist/runtime/components/Alert.vue +22 -21
  61. package/dist/runtime/components/App.vue +9 -15
  62. package/dist/runtime/components/Avatar.vue +33 -21
  63. package/dist/runtime/components/AvatarGroup.vue +5 -7
  64. package/dist/runtime/components/Badge.vue +10 -8
  65. package/dist/runtime/components/Breadcrumb.vue +5 -31
  66. package/dist/runtime/components/Button.vue +5 -8
  67. package/dist/runtime/components/ButtonGroup.vue +5 -7
  68. package/dist/runtime/components/Calendar.vue +194 -0
  69. package/dist/runtime/components/Card.vue +10 -9
  70. package/dist/runtime/components/Carousel.vue +15 -10
  71. package/dist/runtime/components/Checkbox.vue +32 -38
  72. package/dist/runtime/components/Chip.vue +6 -8
  73. package/dist/runtime/components/Collapsible.vue +10 -11
  74. package/dist/runtime/components/ColorPicker.vue +297 -0
  75. package/dist/runtime/components/CommandPalette.vue +111 -97
  76. package/dist/runtime/components/Container.vue +4 -7
  77. package/dist/runtime/components/ContextMenu.vue +16 -69
  78. package/dist/runtime/components/ContextMenuContent.vue +12 -10
  79. package/dist/runtime/components/Drawer.vue +23 -12
  80. package/dist/runtime/components/DropdownMenu.vue +18 -59
  81. package/dist/runtime/components/DropdownMenuContent.vue +12 -10
  82. package/dist/runtime/components/Form.vue +65 -34
  83. package/dist/runtime/components/FormField.vue +22 -13
  84. package/dist/runtime/components/Icon.vue +1 -1
  85. package/dist/runtime/components/Input.vue +15 -7
  86. package/dist/runtime/components/InputMenu.vue +95 -104
  87. package/dist/runtime/components/InputNumber.vue +14 -9
  88. package/dist/runtime/components/Kbd.vue +5 -6
  89. package/dist/runtime/components/Link.vue +5 -8
  90. package/dist/runtime/components/LinkBase.vue +1 -1
  91. package/dist/runtime/components/Modal.vue +51 -38
  92. package/dist/runtime/components/NavigationMenu.vue +129 -114
  93. package/dist/runtime/components/OverlayProvider.vue +29 -0
  94. package/dist/runtime/components/Pagination.vue +6 -9
  95. package/dist/runtime/components/PinInput.vue +10 -11
  96. package/dist/runtime/components/Popover.vue +16 -18
  97. package/dist/runtime/components/Progress.vue +28 -12
  98. package/dist/runtime/components/RadioGroup.vue +9 -12
  99. package/dist/runtime/components/Select.vue +60 -34
  100. package/dist/runtime/components/SelectMenu.vue +160 -165
  101. package/dist/runtime/components/Separator.vue +6 -5
  102. package/dist/runtime/components/Skeleton.vue +4 -7
  103. package/dist/runtime/components/Slideover.vue +51 -37
  104. package/dist/runtime/components/Slider.vue +8 -7
  105. package/dist/runtime/components/Stepper.vue +170 -0
  106. package/dist/runtime/components/Switch.vue +19 -23
  107. package/dist/runtime/components/Table.vue +146 -31
  108. package/dist/runtime/components/Tabs.vue +12 -31
  109. package/dist/runtime/components/Textarea.vue +17 -8
  110. package/dist/runtime/components/Toast.vue +36 -25
  111. package/dist/runtime/components/Toaster.vue +10 -11
  112. package/dist/runtime/components/Tooltip.vue +9 -12
  113. package/dist/runtime/components/Tree.vue +196 -0
  114. package/dist/runtime/composables/defineShortcuts.d.ts +1 -1
  115. package/dist/runtime/composables/defineShortcuts.js +2 -2
  116. package/dist/runtime/composables/useFormField.d.ts +7 -3
  117. package/dist/runtime/composables/useFormField.js +18 -8
  118. package/dist/runtime/composables/useLocale.js +1 -1
  119. package/dist/runtime/composables/useOverlay.d.ts +30 -0
  120. package/dist/runtime/composables/useOverlay.js +71 -0
  121. package/dist/runtime/composables/useToast.d.ts +4 -3
  122. package/dist/runtime/composables/useToast.js +20 -6
  123. package/dist/runtime/index.css +1 -1
  124. package/dist/runtime/keyframes.css +1 -1
  125. package/dist/runtime/locale/ar.js +9 -1
  126. package/dist/runtime/locale/az.js +54 -0
  127. package/dist/runtime/locale/bn.js +54 -0
  128. package/dist/runtime/locale/cs.js +9 -1
  129. package/dist/runtime/locale/da.d.ts +2 -0
  130. package/dist/runtime/locale/da.js +54 -0
  131. package/dist/runtime/locale/de.js +11 -3
  132. package/dist/runtime/locale/el.d.ts +2 -0
  133. package/dist/runtime/locale/el.js +54 -0
  134. package/dist/runtime/locale/en.js +9 -1
  135. package/dist/runtime/locale/es.js +9 -1
  136. package/dist/runtime/locale/et.d.ts +2 -0
  137. package/dist/runtime/locale/et.js +54 -0
  138. package/dist/runtime/locale/fa_ir.js +9 -1
  139. package/dist/runtime/locale/fi.d.ts +2 -0
  140. package/dist/runtime/locale/fi.js +54 -0
  141. package/dist/runtime/locale/fr.js +9 -1
  142. package/dist/runtime/locale/he.d.ts +2 -0
  143. package/dist/runtime/locale/he.js +53 -0
  144. package/dist/runtime/locale/hi.d.ts +2 -0
  145. package/dist/runtime/locale/hi.js +54 -0
  146. package/dist/runtime/locale/hu.d.ts +2 -0
  147. package/dist/runtime/locale/hu.js +54 -0
  148. package/dist/runtime/locale/id.d.ts +2 -0
  149. package/dist/runtime/locale/id.js +54 -0
  150. package/dist/runtime/locale/index.d.ts +25 -3
  151. package/dist/runtime/locale/index.js +25 -3
  152. package/dist/runtime/locale/it.js +9 -1
  153. package/dist/runtime/locale/ja.d.ts +2 -0
  154. package/dist/runtime/locale/ja.js +54 -0
  155. package/dist/runtime/locale/km.d.ts +2 -0
  156. package/dist/runtime/locale/km.js +54 -0
  157. package/dist/runtime/locale/ko.d.ts +2 -0
  158. package/dist/runtime/locale/ko.js +54 -0
  159. package/dist/runtime/locale/nb_no.d.ts +2 -0
  160. package/dist/runtime/locale/nb_no.js +54 -0
  161. package/dist/runtime/locale/nl.d.ts +2 -0
  162. package/dist/runtime/locale/nl.js +54 -0
  163. package/dist/runtime/locale/pl.js +9 -1
  164. package/dist/runtime/locale/pt.d.ts +2 -0
  165. package/dist/runtime/locale/pt.js +54 -0
  166. package/dist/runtime/locale/pt_br.d.ts +2 -0
  167. package/dist/runtime/locale/pt_br.js +54 -0
  168. package/dist/runtime/locale/ru.js +9 -1
  169. package/dist/runtime/locale/sk.d.ts +2 -0
  170. package/dist/runtime/locale/sk.js +54 -0
  171. package/dist/runtime/locale/sv.d.ts +2 -0
  172. package/dist/runtime/locale/sv.js +54 -0
  173. package/dist/runtime/locale/th.d.ts +2 -0
  174. package/dist/runtime/locale/th.js +54 -0
  175. package/dist/runtime/locale/tr.d.ts +2 -0
  176. package/dist/runtime/locale/tr.js +54 -0
  177. package/dist/runtime/locale/uk.d.ts +2 -0
  178. package/dist/runtime/locale/uk.js +54 -0
  179. package/dist/runtime/locale/vi.d.ts +2 -0
  180. package/dist/runtime/locale/vi.js +54 -0
  181. package/dist/runtime/locale/zh_cn.d.ts +2 -0
  182. package/dist/runtime/locale/{zh_hans.js → zh_cn.js} +10 -2
  183. package/dist/runtime/locale/zh_tw.d.ts +2 -0
  184. package/dist/runtime/locale/{zh_hant.js → zh_tw.js} +16 -8
  185. package/dist/runtime/plugins/colors.js +8 -5
  186. package/dist/runtime/types/form.d.ts +24 -15
  187. package/dist/runtime/types/form.js +2 -2
  188. package/dist/runtime/types/index.d.ts +5 -0
  189. package/dist/runtime/types/index.js +5 -0
  190. package/dist/runtime/types/locale.d.ts +8 -0
  191. package/dist/runtime/types/utils.d.ts +7 -3
  192. package/dist/runtime/utils/form.d.ts +4 -4
  193. package/dist/runtime/utils/form.js +6 -8
  194. package/dist/runtime/utils/index.d.ts +1 -1
  195. package/dist/runtime/utils/index.js +15 -2
  196. package/dist/runtime/utils/tv.d.ts +1 -0
  197. package/dist/runtime/utils/tv.js +4 -0
  198. package/dist/runtime/vue/components/Icon.vue +2 -2
  199. package/dist/runtime/vue/components/Link.vue +87 -23
  200. package/dist/runtime/vue/composables/useAppConfig.d.ts +1 -0
  201. package/dist/runtime/vue/composables/useAppConfig.js +2 -0
  202. package/dist/runtime/vue/plugins/head.d.ts +1 -1
  203. package/dist/runtime/vue/plugins/head.js +3 -3
  204. package/dist/runtime/vue/stubs.d.ts +19 -0
  205. package/dist/runtime/vue/stubs.js +37 -0
  206. package/dist/shared/{ui.d52SSCwA.mjs → ui.DSPu6Y8j.mjs} +905 -401
  207. package/dist/shared/{ui.BPnAe4QS.cjs → ui.DfK2RX6G.cjs} +908 -401
  208. package/dist/unplugin.cjs +62 -26
  209. package/dist/unplugin.d.cts +13 -1
  210. package/dist/unplugin.d.mts +13 -1
  211. package/dist/unplugin.d.ts +13 -1
  212. package/dist/unplugin.mjs +61 -25
  213. package/dist/vite.cjs +3 -2
  214. package/dist/vite.d.cts +2 -0
  215. package/dist/vite.d.mts +2 -0
  216. package/dist/vite.d.ts +2 -0
  217. package/dist/vite.mjs +3 -2
  218. package/package.json +103 -67
  219. package/dist/chunks/component-meta.cjs +0 -77249
  220. package/dist/chunks/component-meta.mjs +0 -77247
  221. package/dist/client/devtools/200.html +0 -1
  222. package/dist/client/devtools/404.html +0 -1
  223. package/dist/client/devtools/_fonts/rP2Wp2ywxg089UriCZaSExd86J3t9jz86MvyyKK58UfivUw4aw-SK92LTmKP0.woff2 +0 -0
  224. package/dist/client/devtools/_fonts/rP2Wp2ywxg089UriCZaSExd86J3t9jz86MvyyKy58UfivUw-9uYm8d48E4.woff2 +0 -0
  225. package/dist/client/devtools/_fonts/rP2Yp2ywxg089UriI5-g4vlH9VoD8Cmcqbu0-K6z9mXg-VNw7NHvqnp.woff2 +0 -0
  226. package/dist/client/devtools/_fonts/rP2Yp2ywxg089UriI5-g4vlH9VoD8Cmcqbu6-K6z9mXgjU0-EkYdMs3MAX.woff2 +0 -0
  227. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat-7DW3w-R7n06NbhGy.woff +0 -0
  228. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat-JDG3w-leGNfkTmsj.woff +0 -0
  229. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat-JDW3w-ciElMF03ku.woff +0 -0
  230. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat8JCm3w-sB722Uoa0f.woff +0 -0
  231. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat8JDW3w-PBDI1JDjwV.woff +0 -0
  232. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat8gCm3w-0q1hqVn09O.woff +0 -0
  233. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat9XCm3w-ByvzuLy2fa.woff +0 -0
  234. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat9uCm3w-5TKCIs6EEw.woff +0 -0
  235. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat_XDW3w-r2swRDGfn2.woff +0 -0
  236. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAC5thTQ-ldB60l5U8r.woff +0 -0
  237. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAIpthTQ-wbFKGFDrgo.woff +0 -0
  238. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAIpxhTQ-LLUBv5jskj.woff +0 -0
  239. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwARZthTQ-F71RdTRbNg.woff +0 -0
  240. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwA_JxhTQ-6nh65gZkc0.woff +0 -0
  241. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAfJthTQ-aMoWEJLmTt.woff +0 -0
  242. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAkJxhTQ-WMBvmLOgKm.woff +0 -0
  243. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAop1hTQ-utGC2hnWq2.woff +0 -0
  244. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAopxhTQ-YpyTieVa5B.woff +0 -0
  245. package/dist/client/devtools/_nuxt/BDC8ywlR.js +0 -1
  246. package/dist/client/devtools/_nuxt/BT004Uxp.js +0 -53
  247. package/dist/client/devtools/_nuxt/C1H2Soz3.js +0 -1
  248. package/dist/client/devtools/_nuxt/builds/latest.json +0 -1
  249. package/dist/client/devtools/_nuxt/builds/meta/f98c13ea-d751-4b37-8ce3-94e99708745f.json +0 -1
  250. package/dist/client/devtools/_nuxt/entry.Bx6WJjrx.css +0 -1
  251. package/dist/client/devtools/_nuxt/error-404.Cq4qshcf.css +0 -1
  252. package/dist/client/devtools/_nuxt/error-500.ax1aM34m.css +0 -1
  253. package/dist/client/devtools/favicon.svg +0 -1
  254. package/dist/client/devtools/index.html +0 -1
  255. package/dist/devtools/runtime/DevtoolsRenderer.vue +0 -53
  256. package/dist/devtools/runtime/examples/AvatarGroupExample.vue +0 -7
  257. package/dist/devtools/runtime/examples/ButtonGroupExample.vue +0 -8
  258. package/dist/devtools/runtime/examples/CardExample.vue +0 -13
  259. package/dist/devtools/runtime/examples/CarouselExample.vue +0 -13
  260. package/dist/devtools/runtime/examples/ChipExample.vue +0 -5
  261. package/dist/devtools/runtime/examples/CollapsibleExample.vue +0 -8
  262. package/dist/devtools/runtime/examples/CommandPaletteExample.vue +0 -29
  263. package/dist/devtools/runtime/examples/ContainerExample.vue +0 -5
  264. package/dist/devtools/runtime/examples/ContextMenuExample.vue +0 -5
  265. package/dist/devtools/runtime/examples/DrawerExample.vue +0 -8
  266. package/dist/devtools/runtime/examples/DropdownMenuExample.vue +0 -5
  267. package/dist/devtools/runtime/examples/FormExample.vue +0 -30
  268. package/dist/devtools/runtime/examples/FormFieldExample.vue +0 -5
  269. package/dist/devtools/runtime/examples/LinkExample.vue +0 -5
  270. package/dist/devtools/runtime/examples/ModalExample.vue +0 -8
  271. package/dist/devtools/runtime/examples/PopoverExample.vue +0 -8
  272. package/dist/devtools/runtime/examples/SkeletonExample.vue +0 -3
  273. package/dist/devtools/runtime/examples/SlideoverExample.vue +0 -8
  274. package/dist/devtools/runtime/examples/ToasterExample.vue +0 -11
  275. package/dist/devtools/runtime/examples/TooltipExample.vue +0 -5
  276. package/dist/runtime/components/ModalProvider.vue +0 -12
  277. package/dist/runtime/components/SlideoverProvider.vue +0 -12
  278. package/dist/runtime/composables/extendDevtoolsMeta.d.ts +0 -7
  279. package/dist/runtime/composables/extendDevtoolsMeta.js +0 -2
  280. package/dist/runtime/composables/useModal.d.ts +0 -17
  281. package/dist/runtime/composables/useModal.js +0 -46
  282. package/dist/runtime/composables/useSlideover.d.ts +0 -17
  283. package/dist/runtime/composables/useSlideover.js +0 -46
  284. package/dist/runtime/plugins/modal.d.ts +0 -2
  285. package/dist/runtime/plugins/modal.js +0 -10
  286. package/dist/runtime/plugins/slideover.d.ts +0 -2
  287. package/dist/runtime/plugins/slideover.js +0 -10
  288. /package/dist/runtime/locale/{zh_hans.d.ts → az.d.ts} +0 -0
  289. /package/dist/runtime/locale/{zh_hant.d.ts → bn.d.ts} +0 -0
@@ -1,17 +1,16 @@
1
1
  <script lang="ts">
2
- import { tv } from 'tailwind-variants'
3
2
  import type { DrawerRootProps, DrawerRootEmits } from 'vaul-vue'
4
- import type { DialogContentProps } from 'radix-vue'
3
+ import type { DialogContentProps } from 'reka-ui'
5
4
  import type { AppConfig } from '@nuxt/schema'
6
5
  import _appConfig from '#build/app.config'
7
6
  import theme from '#build/ui/drawer'
8
- import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
7
+ import { tv } from '../utils/tv'
9
8
 
10
- const appConfig = _appConfig as AppConfig & { ui: { drawer: Partial<typeof theme> } }
9
+ const appConfigDrawer = _appConfig as AppConfig & { ui: { drawer: Partial<typeof theme> } }
11
10
 
12
- const drawer = tv({ extend: tv(theme), ...(appConfig.ui?.drawer || {}) })
11
+ const drawer = tv({ extend: tv(theme), ...(appConfigDrawer.ui?.drawer || {}) })
13
12
 
14
- export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | 'closeThreshold' | 'defaultOpen' | 'direction' | 'dismissible' | 'fadeFromIndex' | 'fixed' | 'modal' | 'nested' | 'direction' | 'open' | 'scrollLockTimeout' | 'shouldScaleBackground' | 'snapPoints'> {
13
+ export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | 'closeThreshold' | 'defaultOpen' | 'direction' | 'fadeFromIndex' | 'fixed' | 'modal' | 'nested' | 'direction' | 'open' | 'scrollLockTimeout' | 'shouldScaleBackground' | 'snapPoints'> {
15
14
  /**
16
15
  * The element or component this component should render as.
17
16
  * @defaultValue 'div'
@@ -19,6 +18,11 @@ export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | '
19
18
  as?: any
20
19
  title?: string
21
20
  description?: string
21
+ /**
22
+ * Whether to inset the drawer from the edges.
23
+ * @defaultValue false
24
+ */
25
+ inset?: boolean
22
26
  /** The content of the drawer. */
23
27
  content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'>
24
28
  /**
@@ -36,6 +40,11 @@ export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | '
36
40
  * @defaultValue true
37
41
  */
38
42
  portal?: boolean
43
+ /**
44
+ * When `false`, the drawer will not close when clicking outside or pressing escape.
45
+ * @defaultValue true
46
+ */
47
+ dismissible?: boolean
39
48
  class?: any
40
49
  ui?: Partial<typeof drawer.slots>
41
50
  }
@@ -52,13 +61,11 @@ export interface DrawerSlots {
52
61
  body(props?: {}): any
53
62
  footer(props?: {}): any
54
63
  }
55
-
56
- extendDevtoolsMeta({ example: 'DrawerExample' })
57
64
  </script>
58
65
 
59
66
  <script setup lang="ts">
60
67
  import { computed, toRef } from 'vue'
61
- import { useForwardPropsEmits } from 'radix-vue'
68
+ import { useForwardPropsEmits } from 'reka-ui'
62
69
  import { DrawerRoot, DrawerTrigger, DrawerPortal, DrawerOverlay, DrawerContent, DrawerTitle, DrawerDescription } from 'vaul-vue'
63
70
  import { reactivePick } from '@vueuse/core'
64
71
 
@@ -73,22 +80,26 @@ const slots = defineSlots<DrawerSlots>()
73
80
 
74
81
  const rootProps = useForwardPropsEmits(reactivePick(props, 'activeSnapPoint', 'closeThreshold', 'defaultOpen', 'dismissible', 'fadeFromIndex', 'fixed', 'modal', 'nested', 'direction', 'open', 'scrollLockTimeout', 'shouldScaleBackground', 'snapPoints'), emits)
75
82
  const contentProps = toRef(() => props.content)
83
+ const contentEvents = {
84
+ closeAutoFocus: (e: Event) => e.preventDefault()
85
+ }
76
86
 
77
87
  const ui = computed(() => drawer({
78
- direction: props.direction
88
+ direction: props.direction,
89
+ inset: props.inset
79
90
  }))
80
91
  </script>
81
92
 
82
93
  <template>
83
94
  <DrawerRoot v-bind="rootProps">
84
- <DrawerTrigger v-if="!!slots.default" as-child>
95
+ <DrawerTrigger v-if="!!slots.default" as-child :class="props.class">
85
96
  <slot />
86
97
  </DrawerTrigger>
87
98
 
88
99
  <DrawerPortal :disabled="!portal">
89
100
  <DrawerOverlay v-if="overlay" :class="ui.overlay({ class: props.ui?.overlay })" />
90
101
 
91
- <DrawerContent :class="ui.content({ class: [props.class, props.ui?.content] })" v-bind="contentProps">
102
+ <DrawerContent :class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })" v-bind="contentProps" v-on="contentEvents">
92
103
  <slot name="handle">
93
104
  <div v-if="handle" :class="ui.handle({ class: props.ui?.handle })" />
94
105
  </slot>
@@ -1,16 +1,16 @@
1
1
  <script lang="ts">
2
- import { tv, type VariantProps } from 'tailwind-variants'
3
- import type { DropdownMenuRootProps, DropdownMenuRootEmits, DropdownMenuContentProps, DropdownMenuArrowProps } from 'radix-vue'
2
+ import type { VariantProps } from 'tailwind-variants'
3
+ import type { DropdownMenuRootProps, DropdownMenuRootEmits, DropdownMenuContentProps, DropdownMenuArrowProps } 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/dropdown-menu'
7
- import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
7
+ import { tv } from '../utils/tv'
8
8
  import type { AvatarProps, KbdProps, LinkProps } from '../types'
9
9
  import type { DynamicSlots, PartialString } from '../types/utils'
10
10
 
11
- const appConfig = _appConfig as AppConfig & { ui: { dropdownMenu: Partial<typeof theme> } }
11
+ const appConfigDropdownMenu = _appConfig as AppConfig & { ui: { dropdownMenu: Partial<typeof theme> } }
12
12
 
13
- const dropdownMenu = tv({ extend: tv(theme), ...(appConfig.ui?.dropdownMenu || {}) })
13
+ const dropdownMenu = tv({ extend: tv(theme), ...(appConfigDropdownMenu.ui?.dropdownMenu || {}) })
14
14
 
15
15
  type DropdownMenuVariants = VariantProps<typeof dropdownMenu>
16
16
 
@@ -50,9 +50,15 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
50
50
  * @defaultValue appConfig.ui.icons.loading
51
51
  */
52
52
  loadingIcon?: string
53
+ /**
54
+ * The icon displayed when the item is an external link.
55
+ * Set to `false` to hide the external icon.
56
+ * @defaultValue appConfig.ui.icons.external
57
+ */
58
+ externalIcon?: boolean | string
53
59
  /**
54
60
  * The content of the menu.
55
- * @defaultValue { side: 'bottom', sideOffset: 8 }
61
+ * @defaultValue { side: 'bottom', sideOffset: 8, collisionPadding: 8 }
56
62
  */
57
63
  content?: Omit<DropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'>
58
64
  /**
@@ -86,61 +92,12 @@ export type DropdownMenuSlots<T extends { slot?: string }> = {
86
92
  'item-label': SlotProps<T>
87
93
  'item-trailing': SlotProps<T>
88
94
  } & DynamicSlots<T, SlotProps<T>>
89
-
90
- extendDevtoolsMeta({
91
- example: 'DropdownMenuExample',
92
- ignoreProps: ['items'],
93
- defaultProps: {
94
- items: [
95
- [{
96
- label: 'My account',
97
- avatar: {
98
- src: 'https://avatars.githubusercontent.com/u/739984?v=4'
99
- },
100
- type: 'label'
101
- }], [{
102
- label: 'Profile',
103
- icon: 'i-lucide-user',
104
- slot: 'custom'
105
- }, {
106
- label: 'Billing',
107
- icon: 'i-lucide-credit-card',
108
- kbds: ['meta', 'b']
109
- }, {
110
- label: 'Settings',
111
- icon: 'i-lucide-cog',
112
- kbds: ['?']
113
- }], [{
114
- label: 'Invite users',
115
- icon: 'i-lucide-user-plus',
116
- children: [[{
117
- label: 'Invite by email',
118
- icon: 'i-lucide-send-horizontal'
119
- }, {
120
- label: 'Invite by link',
121
- icon: 'i-lucide-link',
122
- kbds: ['meta', 'i']
123
- }]]
124
- }],
125
- [{
126
- label: 'GitHub',
127
- icon: 'i-simple-icons-github',
128
- to: 'https://github.com/nuxt/ui',
129
- target: '_blank'
130
- }, {
131
- label: 'Support',
132
- icon: 'i-lucide-life-buoy',
133
- to: '/components/dropdown-menu'
134
- }]
135
- ]
136
- }
137
- })
138
95
  </script>
139
96
 
140
97
  <script setup lang="ts" generic="T extends DropdownMenuItem">
141
98
  import { computed, toRef } from 'vue'
142
99
  import { defu } from 'defu'
143
- import { DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuArrow, useForwardPropsEmits } from 'radix-vue'
100
+ import { DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuArrow, useForwardPropsEmits } from 'reka-ui'
144
101
  import { reactivePick } from '@vueuse/core'
145
102
  import { omit } from '../utils'
146
103
  import UDropdownMenuContent from './DropdownMenuContent.vue'
@@ -148,13 +105,14 @@ import UDropdownMenuContent from './DropdownMenuContent.vue'
148
105
  const props = withDefaults(defineProps<DropdownMenuProps<T>>(), {
149
106
  portal: true,
150
107
  modal: true,
108
+ externalIcon: true,
151
109
  labelKey: 'label'
152
110
  })
153
111
  const emits = defineEmits<DropdownMenuEmits>()
154
112
  const slots = defineSlots<DropdownMenuSlots<T>>()
155
113
 
156
114
  const rootProps = useForwardPropsEmits(reactivePick(props, 'defaultOpen', 'open', 'modal'), emits)
157
- const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8 }) as DropdownMenuContentProps)
115
+ const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8 }) as DropdownMenuContentProps)
158
116
  const arrowProps = toRef(() => props.arrow as DropdownMenuArrowProps)
159
117
  const proxySlots = omit(slots, ['default']) as Record<string, DropdownMenuSlots<T>[string]>
160
118
 
@@ -165,12 +123,12 @@ const ui = computed(() => dropdownMenu({
165
123
 
166
124
  <template>
167
125
  <DropdownMenuRoot v-slot="{ open }" v-bind="rootProps">
168
- <DropdownMenuTrigger v-if="!!slots.default" as-child :disabled="disabled">
126
+ <DropdownMenuTrigger v-if="!!slots.default" as-child :class="props.class" :disabled="disabled">
169
127
  <slot :open="open" />
170
128
  </DropdownMenuTrigger>
171
129
 
172
130
  <UDropdownMenuContent
173
- :class="ui.content({ class: [props.class, props.ui?.content] })"
131
+ :class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })"
174
132
  :ui="ui"
175
133
  :ui-override="props.ui"
176
134
  v-bind="contentProps"
@@ -179,6 +137,7 @@ const ui = computed(() => dropdownMenu({
179
137
  :label-key="labelKey"
180
138
  :checked-icon="checkedIcon"
181
139
  :loading-icon="loadingIcon"
140
+ :external-icon="externalIcon"
182
141
  >
183
142
  <template v-for="(_, name) in proxySlots" #[name]="slotData: any">
184
143
  <slot :name="name" v-bind="slotData" />
@@ -1,25 +1,26 @@
1
1
  <!-- eslint-disable vue/block-tag-newline -->
2
2
  <script lang="ts">
3
- import { tv } from 'tailwind-variants'
4
- import type { DropdownMenuContentProps as RadixDropdownMenuContentProps, DropdownMenuContentEmits as RadixDropdownMenuContentEmits } from 'radix-vue'
3
+ import type { DropdownMenuContentProps as RekaDropdownMenuContentProps, DropdownMenuContentEmits as RekaDropdownMenuContentEmits } from 'reka-ui'
5
4
  import theme from '#build/ui/dropdown-menu'
5
+ import { tv } from '../utils/tv'
6
6
  import type { KbdProps, AvatarProps, DropdownMenuItem, DropdownMenuSlots } from '../types'
7
7
 
8
8
  const _dropdownMenu = tv(theme)()
9
9
 
10
- interface DropdownMenuContentProps<T> extends Omit<RadixDropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
10
+ interface DropdownMenuContentProps<T> extends Omit<RekaDropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
11
11
  items?: T[] | T[][]
12
12
  portal?: boolean
13
13
  sub?: boolean
14
14
  labelKey: string
15
15
  checkedIcon?: string
16
16
  loadingIcon?: string
17
+ externalIcon?: boolean | string
17
18
  class?: any
18
19
  ui: typeof _dropdownMenu
19
20
  uiOverride?: any
20
21
  }
21
22
 
22
- interface DropdownMenuContentEmits extends RadixDropdownMenuContentEmits {}
23
+ interface DropdownMenuContentEmits extends RekaDropdownMenuContentEmits {}
23
24
 
24
25
  type DropdownMenuContentSlots<T extends { slot?: string }> = Omit<DropdownMenuSlots<T>, 'default'> & {
25
26
  default(props?: {}): any
@@ -29,8 +30,8 @@ type DropdownMenuContentSlots<T extends { slot?: string }> = Omit<DropdownMenuSl
29
30
 
30
31
  <script setup lang="ts" generic="T extends DropdownMenuItem">
31
32
  import { computed } from 'vue'
32
- import { DropdownMenu } from 'radix-vue/namespaced'
33
- import { useForwardPropsEmits } from 'radix-vue'
33
+ import { DropdownMenu } from 'reka-ui/namespaced'
34
+ import { useForwardPropsEmits } from 'reka-ui'
34
35
  import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
35
36
  import { useAppConfig } from '#imports'
36
37
  import { omit, get } from '../utils'
@@ -48,7 +49,7 @@ const emits = defineEmits<DropdownMenuContentEmits>()
48
49
  const slots = defineSlots<DropdownMenuContentSlots<T>>()
49
50
 
50
51
  const appConfig = useAppConfig()
51
- const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'class', 'ui'), emits)
52
+ const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'labelKey', 'checkedIcon', 'loadingIcon', 'externalIcon', 'class', 'ui', 'uiOverride'), emits)
52
53
  const proxySlots = omit(slots, ['default']) as Record<string, DropdownMenuContentSlots<T>[string]>
53
54
 
54
55
  const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: DropdownMenuItem, active?: boolean, index: number }>()
@@ -70,7 +71,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
70
71
  {{ get(item, props.labelKey as string) }}
71
72
  </slot>
72
73
 
73
- <UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.itemLabelExternalIcon({ class: uiOverride?.itemLabelExternalIcon, color: item?.color, active })" />
74
+ <UIcon v-if="item.target === '_blank' && externalIcon !== false" :name="typeof externalIcon === 'string' ? externalIcon : appConfig.ui.icons.external" :class="ui.itemLabelExternalIcon({ class: uiOverride?.itemLabelExternalIcon, color: item?.color, active })" />
74
75
  </span>
75
76
 
76
77
  <span :class="ui.itemTrailing({ class: uiOverride?.itemTrailing })">
@@ -121,6 +122,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
121
122
  :label-key="labelKey"
122
123
  :checked-icon="checkedIcon"
123
124
  :loading-icon="loadingIcon"
125
+ :external-icon="externalIcon"
124
126
  v-bind="item.content"
125
127
  >
126
128
  <template v-for="(_, name) in proxySlots" #[name]="slotData: any">
@@ -130,11 +132,11 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
130
132
  </DropdownMenu.Sub>
131
133
  <DropdownMenu.CheckboxItem
132
134
  v-else-if="item.type === 'checkbox'"
133
- :checked="item.checked"
135
+ :model-value="item.checked"
134
136
  :disabled="item.disabled"
135
137
  :text-value="get(item, props.labelKey as string)"
136
138
  :class="ui.item({ class: [uiOverride?.item, item.class], color: item?.color })"
137
- @update:checked="item.onUpdateChecked"
139
+ @update:model-value="item.onUpdateChecked"
138
140
  @select="item.onSelect"
139
141
  >
140
142
  <ReuseItemTemplate :item="item" :index="index" />
@@ -1,14 +1,14 @@
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
- import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
5
+ import { tv } from '../utils/tv'
7
6
  import type { FormSchema, FormError, FormInputEvents, FormErrorEvent, FormSubmitEvent, FormEvent, Form, FormErrorWithId } from '../types/form'
7
+ import type { DeepReadonly } from 'vue'
8
8
 
9
- const appConfig = _appConfig as AppConfig & { ui: { form: Partial<typeof theme> } }
9
+ const appConfigForm = _appConfig as AppConfig & { ui: { form: Partial<typeof theme> } }
10
10
 
11
- const form = tv({ extend: tv(theme), ...(appConfig.ui?.form || {}) })
11
+ const form = tv({ extend: tv(theme), ...(appConfigForm.ui?.form || {}) })
12
12
 
13
13
  export interface FormProps<T extends object> {
14
14
  id?: string | number
@@ -19,6 +19,7 @@ export interface FormProps<T extends object> {
19
19
  disabled?: boolean
20
20
  validateOnInputDelay?: number
21
21
  class?: any
22
+ transform?: boolean
22
23
  onSubmit?: ((event: FormSubmitEvent<T>) => void | Promise<void>) | (() => void | Promise<void>)
23
24
  }
24
25
 
@@ -30,29 +31,29 @@ export interface FormEmits<T extends object> {
30
31
  export interface FormSlots {
31
32
  default(props?: {}): any
32
33
  }
33
-
34
- extendDevtoolsMeta({ example: 'FormExample' })
35
34
  </script>
36
35
 
37
36
  <script lang="ts" setup generic="T extends object">
38
37
  import { provide, inject, nextTick, ref, onUnmounted, onMounted, computed, useId, readonly } from 'vue'
39
38
  import { useEventBus } from '@vueuse/core'
40
39
  import { formOptionsInjectionKey, formInputsInjectionKey, formBusInjectionKey, formLoadingInjectionKey } from '../composables/useFormField'
41
- import { parseSchema } from '../utils/form'
40
+ import { validateSchema } from '../utils/form'
42
41
  import { FormValidationException } from '../types/form'
43
42
 
44
43
  const props = withDefaults(defineProps<FormProps<T>>(), {
45
44
  validateOn() {
46
45
  return ['input', 'blur', 'change'] as FormInputEvents[]
47
46
  },
48
- validateOnInputDelay: 300
47
+ validateOnInputDelay: 300,
48
+ transform: true
49
49
  })
50
+
50
51
  const emits = defineEmits<FormEmits<T>>()
51
52
  defineSlots<FormSlots>()
52
53
 
53
54
  const formId = props.id ?? useId() as string
54
55
 
55
- const bus = useEventBus<FormEvent>(`form-${formId}`)
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: () => any }>>(new Map())
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 as FormInputEvents)) {
72
- await _validate({ name: event.name, silent: true, nested: false })
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<Record<string, { id?: string, pattern?: RegExp }>>({})
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,33 +125,35 @@ function resolveErrorIds(errs: FormError[]): FormErrorWithId[] {
104
125
  }))
105
126
  }
106
127
 
128
+ const transformedState = ref<T | null>(null)
129
+
107
130
  async function getErrors(): Promise<FormErrorWithId[]> {
108
131
  let errs = props.validate ? (await props.validate(props.state)) ?? [] : []
109
132
 
110
133
  if (props.schema) {
111
- const { errors, result } = await parseSchema(props.state, props.schema as FormSchema<typeof props.state>)
134
+ const { errors, result } = await validateSchema(props.state, props.schema as FormSchema<typeof props.state>)
112
135
  if (errors) {
113
136
  errs = errs.concat(errors)
114
137
  } else {
115
- Object.assign(props.state, result)
138
+ transformedState.value = result
116
139
  }
117
140
  }
118
141
 
119
142
  return resolveErrorIds(errs)
120
143
  }
121
144
 
122
- async function _validate(opts: { name?: string | string[], silent?: boolean, nested?: boolean } = { silent: false, nested: true }): Promise<T | false> {
123
- const names = opts.name && !Array.isArray(opts.name) ? [opts.name] : opts.name as string[]
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)[]
124
147
 
125
148
  const nestedValidatePromises = !names && opts.nested
126
149
  ? Array.from(nestedForms.value.values()).map(
127
- ({ validate }) => validate().then(() => undefined).catch((error: Error) => {
128
- if (!(error instanceof FormValidationException)) {
129
- throw error
130
- }
131
- return error
132
- })
133
- )
150
+ ({ validate }) => validate(opts).then(() => undefined).catch((error: Error) => {
151
+ if (!(error instanceof FormValidationException)) {
152
+ throw error
153
+ }
154
+ return error
155
+ })
156
+ )
134
157
  : []
135
158
 
136
159
  if (names) {
@@ -149,13 +172,17 @@ async function _validate(opts: { name?: string | string[], silent?: boolean, nes
149
172
  errors.value = await getErrors()
150
173
  }
151
174
 
152
- const childErrors = (await Promise.all(nestedValidatePromises)).filter(val => val)
175
+ const childErrors = (await Promise.all(nestedValidatePromises)).filter(val => val !== undefined)
153
176
 
154
177
  if (errors.value.length + childErrors.length > 0) {
155
178
  if (opts.silent) return false
156
179
  throw new FormValidationException(formId, errors.value, childErrors)
157
180
  }
158
181
 
182
+ if (opts.transform) {
183
+ Object.assign(props.state, transformedState.value)
184
+ }
185
+
159
186
  return props.state as T
160
187
  }
161
188
 
@@ -168,8 +195,7 @@ async function onSubmitWrapper(payload: Event) {
168
195
  const event = payload as FormSubmitEvent<any>
169
196
 
170
197
  try {
171
- await _validate({ nested: true })
172
- event.data = props.state
198
+ event.data = await _validate({ nested: true, transform: props.transform })
173
199
  await props.onSubmit?.(event)
174
200
  } catch (error) {
175
201
  if (!(error instanceof FormValidationException)) {
@@ -179,12 +205,12 @@ async function onSubmitWrapper(payload: Event) {
179
205
  const errorEvent: FormErrorEvent = {
180
206
  ...event,
181
207
  errors: error.errors,
182
- childrens: error.childrens
208
+ children: error.children
183
209
  }
184
210
  emits('error', errorEvent)
211
+ } finally {
212
+ loading.value = false
185
213
  }
186
-
187
- loading.value = false
188
214
  }
189
215
 
190
216
  const disabled = computed(() => props.disabled || loading.value)
@@ -198,7 +224,7 @@ defineExpose<Form<T>>({
198
224
  validate: _validate,
199
225
  errors,
200
226
 
201
- setErrors(errs: FormError[], name?: string) {
227
+ setErrors(errs: FormError[], name?: keyof T) {
202
228
  if (name) {
203
229
  errors.value = errors.value
204
230
  .filter(error => error.name !== name)
@@ -212,7 +238,7 @@ defineExpose<Form<T>>({
212
238
  await onSubmitWrapper(new Event('submit'))
213
239
  },
214
240
 
215
- getErrors(name?: string) {
241
+ getErrors(name?: keyof T) {
216
242
  if (name) {
217
243
  return errors.value.filter(err => err.name === name)
218
244
  }
@@ -227,7 +253,12 @@ defineExpose<Form<T>>({
227
253
  }
228
254
  },
229
255
 
230
- 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>>
231
262
  })
232
263
  </script>
233
264
 
@@ -1,17 +1,22 @@
1
1
  <script lang="ts">
2
- import { tv, type VariantProps } from 'tailwind-variants'
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 { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
6
+ import { tv } from '../utils/tv'
7
7
 
8
- const appConfig = _appConfig as AppConfig & { ui: { formField: Partial<typeof theme> } }
8
+ const appConfigFormField = _appConfig as AppConfig & { ui: { formField: Partial<typeof theme> } }
9
9
 
10
- const formField = tv({ extend: tv(theme), ...(appConfig.ui?.formField || {}) })
10
+ const formField = tv({ extend: tv(theme), ...(appConfigFormField.ui?.formField || {}) })
11
11
 
12
12
  type FormFieldVariants = VariantProps<typeof formField>
13
13
 
14
14
  export interface FormFieldProps {
15
+ /**
16
+ * The element or component this component should render as.
17
+ * @defaultValue 'div'
18
+ */
19
+ as?: any
15
20
  /** The name of the FormField. Also used to match form errors. */
16
21
  name?: string
17
22
  /** A regular expression to match form error names. */
@@ -37,13 +42,11 @@ export interface FormFieldSlots {
37
42
  error(props: { error?: string | boolean }): any
38
43
  default(props: { error?: string | boolean }): any
39
44
  }
40
-
41
- extendDevtoolsMeta({ example: 'FormFieldExample', defaultProps: { label: 'Label' } })
42
45
  </script>
43
46
 
44
47
  <script setup lang="ts">
45
48
  import { computed, ref, inject, provide, type Ref, useId } from 'vue'
46
- import { Label } from 'radix-vue'
49
+ import { Primitive, Label } from 'reka-ui'
47
50
  import { formFieldInjectionKey, inputIdInjectionKey } from '../composables/useFormField'
48
51
  import type { FormError, FormFieldInjectedOptions } from '../types/form'
49
52
 
@@ -60,6 +63,9 @@ const formErrors = inject<Ref<FormError[]> | null>('form-errors', null)
60
63
  const error = computed(() => props.error || formErrors?.value?.find(error => error.name === props.name || (props.errorPattern && error.name.match(props.errorPattern)))?.message)
61
64
 
62
65
  const id = ref(useId())
66
+ // Copies id's initial value to bind aria-attributes such as aria-describedby.
67
+ // This is required for the RadioGroup component which unsets the id value.
68
+ const ariaId = id.value
63
69
 
64
70
  provide(inputIdInjectionKey, id)
65
71
 
@@ -69,12 +75,15 @@ provide(formFieldInjectionKey, computed(() => ({
69
75
  size: props.size,
70
76
  eagerValidation: props.eagerValidation,
71
77
  validateOnInputDelay: props.validateOnInputDelay,
72
- errorPattern: props.errorPattern
78
+ errorPattern: props.errorPattern,
79
+ hint: props.hint,
80
+ description: props.description,
81
+ ariaId
73
82
  }) as FormFieldInjectedOptions<FormFieldProps>))
74
83
  </script>
75
84
 
76
85
  <template>
77
- <div :class="ui.root({ class: [props.class, props.ui?.root] })">
86
+ <Primitive :as="as" :class="ui.root({ class: [props.class, props.ui?.root] })">
78
87
  <div :class="ui.wrapper({ class: props.ui?.wrapper })">
79
88
  <div v-if="label || !!slots.label" :class="ui.labelWrapper({ class: props.ui?.labelWrapper })">
80
89
  <Label :for="id" :class="ui.label({ class: props.ui?.label })">
@@ -82,14 +91,14 @@ provide(formFieldInjectionKey, computed(() => ({
82
91
  {{ label }}
83
92
  </slot>
84
93
  </Label>
85
- <span v-if="hint || !!slots.hint" :class="ui.hint({ class: props.ui?.hint })">
94
+ <span v-if="hint || !!slots.hint" :id="`${ariaId}-hint`" :class="ui.hint({ class: props.ui?.hint })">
86
95
  <slot name="hint" :hint="hint">
87
96
  {{ hint }}
88
97
  </slot>
89
98
  </span>
90
99
  </div>
91
100
 
92
- <p v-if="description || !!slots.description" :class="ui.description({ class: props.ui?.description })">
101
+ <p v-if="description || !!slots.description" :id="`${ariaId}-description`" :class="ui.description({ class: props.ui?.description })">
93
102
  <slot name="description" :description="description">
94
103
  {{ description }}
95
104
  </slot>
@@ -99,7 +108,7 @@ provide(formFieldInjectionKey, computed(() => ({
99
108
  <div :class="[(label || !!slots.label || description || !!slots.description) && ui.container({ class: props.ui?.container })]">
100
109
  <slot :error="error" />
101
110
 
102
- <p v-if="(typeof error === 'string' && error) || !!slots.error" :class="ui.error({ class: props.ui?.error })">
111
+ <p v-if="(typeof error === 'string' && error) || !!slots.error" :id="`${ariaId}-error`" :class="ui.error({ class: props.ui?.error })">
103
112
  <slot name="error" :error="error">
104
113
  {{ error }}
105
114
  </slot>
@@ -110,5 +119,5 @@ provide(formFieldInjectionKey, computed(() => ({
110
119
  </slot>
111
120
  </p>
112
121
  </div>
113
- </div>
122
+ </Primitive>
114
123
  </template>
@@ -13,7 +13,7 @@ export interface IconProps {
13
13
  </script>
14
14
 
15
15
  <script setup lang="ts">
16
- import { useForwardProps } from 'radix-vue'
16
+ import { useForwardProps } from 'reka-ui'
17
17
  import { reactivePick } from '@vueuse/core'
18
18
 
19
19
  const props = defineProps<IconProps>()