@nuxt/ui 3.0.0-alpha.7 → 3.0.0-alpha.9

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 (296) hide show
  1. package/.nuxt/ui/accordion.ts +20 -0
  2. package/.nuxt/ui/alert.ts +242 -0
  3. package/.nuxt/ui/avatar-group.ts +40 -0
  4. package/.nuxt/ui/avatar.ts +42 -0
  5. package/.nuxt/ui/badge.ts +194 -0
  6. package/.nuxt/ui/breadcrumb.ts +45 -0
  7. package/.nuxt/ui/button-group.ts +16 -0
  8. package/.nuxt/ui/button.ts +344 -0
  9. package/.nuxt/ui/card.ts +8 -0
  10. package/.nuxt/ui/carousel.ts +41 -0
  11. package/.nuxt/ui/checkbox.ts +105 -0
  12. package/.nuxt/ui/chip.ts +67 -0
  13. package/.nuxt/ui/collapsible.ts +6 -0
  14. package/.nuxt/ui/command-palette.ts +40 -0
  15. package/.nuxt/ui/container.ts +3 -0
  16. package/.nuxt/ui/context-menu.ts +198 -0
  17. package/.nuxt/ui/drawer.ts +55 -0
  18. package/.nuxt/ui/dropdown-menu.ts +199 -0
  19. package/.nuxt/ui/form-field.ts +40 -0
  20. package/.nuxt/ui/form.ts +3 -0
  21. package/.nuxt/ui/index.ts +45 -0
  22. package/.nuxt/ui/input-menu.ts +411 -0
  23. package/.nuxt/ui/input-number.ts +204 -0
  24. package/.nuxt/ui/input.ts +263 -0
  25. package/.nuxt/ui/kbd.ts +19 -0
  26. package/.nuxt/ui/link.ts +15 -0
  27. package/.nuxt/ui/modal.ts +28 -0
  28. package/.nuxt/ui/navigation-menu.ts +390 -0
  29. package/.nuxt/ui/pagination.ts +8 -0
  30. package/.nuxt/ui/pin-input.ts +145 -0
  31. package/.nuxt/ui/popover.ts +6 -0
  32. package/.nuxt/ui/progress.ts +258 -0
  33. package/.nuxt/ui/radio-group.ts +112 -0
  34. package/.nuxt/ui/select-menu.ts +316 -0
  35. package/.nuxt/ui/select.ts +314 -0
  36. package/.nuxt/ui/separator.ts +143 -0
  37. package/.nuxt/ui/skeleton.ts +3 -0
  38. package/.nuxt/ui/slideover.ts +63 -0
  39. package/.nuxt/ui/slider.ts +148 -0
  40. package/.nuxt/ui/switch.ts +113 -0
  41. package/.nuxt/ui/table.ts +129 -0
  42. package/.nuxt/ui/tabs.ts +227 -0
  43. package/.nuxt/ui/textarea.ts +263 -0
  44. package/.nuxt/ui/toast.ts +66 -0
  45. package/.nuxt/ui/toaster.ts +75 -0
  46. package/.nuxt/ui/tooltip.ts +9 -0
  47. package/README.md +52 -12
  48. package/cli/commands/{init.mjs → make/component.mjs} +4 -4
  49. package/cli/commands/make/index.mjs +14 -0
  50. package/cli/commands/make/locale.mjs +64 -0
  51. package/cli/index.mjs +2 -2
  52. package/cli/templates.mjs +47 -2
  53. package/cli/utils.mjs +14 -0
  54. package/dist/chunks/component-meta.cjs +77249 -0
  55. package/dist/chunks/component-meta.mjs +77247 -0
  56. package/dist/client/devtools/200.html +1 -0
  57. package/dist/client/devtools/404.html +1 -0
  58. package/dist/client/devtools/_fonts/rP2Wp2ywxg089UriCZaSExd86J3t9jz86MvyyKK58UfivUw4aw-SK92LTmKP0.woff2 +0 -0
  59. package/dist/client/devtools/_fonts/rP2Wp2ywxg089UriCZaSExd86J3t9jz86MvyyKy58UfivUw-9uYm8d48E4.woff2 +0 -0
  60. package/dist/client/devtools/_fonts/rP2Yp2ywxg089UriI5-g4vlH9VoD8Cmcqbu0-K6z9mXg-VNw7NHvqnp.woff2 +0 -0
  61. package/dist/client/devtools/_fonts/rP2Yp2ywxg089UriI5-g4vlH9VoD8Cmcqbu6-K6z9mXgjU0-EkYdMs3MAX.woff2 +0 -0
  62. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat-7DW3w-R7n06NbhGy.woff +0 -0
  63. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat-JDG3w-leGNfkTmsj.woff +0 -0
  64. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat-JDW3w-ciElMF03ku.woff +0 -0
  65. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat8JCm3w-sB722Uoa0f.woff +0 -0
  66. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat8JDW3w-PBDI1JDjwV.woff +0 -0
  67. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat8gCm3w-0q1hqVn09O.woff +0 -0
  68. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat9XCm3w-ByvzuLy2fa.woff +0 -0
  69. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat9uCm3w-5TKCIs6EEw.woff +0 -0
  70. package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat_XDW3w-r2swRDGfn2.woff +0 -0
  71. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAC5thTQ-ldB60l5U8r.woff +0 -0
  72. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAIpthTQ-wbFKGFDrgo.woff +0 -0
  73. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAIpxhTQ-LLUBv5jskj.woff +0 -0
  74. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwARZthTQ-F71RdTRbNg.woff +0 -0
  75. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwA_JxhTQ-6nh65gZkc0.woff +0 -0
  76. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAfJthTQ-aMoWEJLmTt.woff +0 -0
  77. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAkJxhTQ-WMBvmLOgKm.woff +0 -0
  78. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAop1hTQ-utGC2hnWq2.woff +0 -0
  79. package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAopxhTQ-YpyTieVa5B.woff +0 -0
  80. package/dist/client/devtools/_nuxt/BDC8ywlR.js +1 -0
  81. package/dist/client/devtools/_nuxt/BT004Uxp.js +53 -0
  82. package/dist/client/devtools/_nuxt/C1H2Soz3.js +1 -0
  83. package/dist/client/devtools/_nuxt/builds/latest.json +1 -0
  84. package/dist/client/devtools/_nuxt/builds/meta/f98c13ea-d751-4b37-8ce3-94e99708745f.json +1 -0
  85. package/dist/client/devtools/_nuxt/entry.Bx6WJjrx.css +1 -0
  86. package/dist/client/devtools/_nuxt/error-404.Cq4qshcf.css +1 -0
  87. package/dist/client/devtools/_nuxt/error-500.ax1aM34m.css +1 -0
  88. package/dist/client/devtools/favicon.svg +1 -0
  89. package/dist/client/devtools/index.html +1 -0
  90. package/dist/devtools/runtime/DevtoolsRenderer.vue +53 -0
  91. package/dist/devtools/runtime/examples/AvatarGroupExample.vue +7 -0
  92. package/dist/devtools/runtime/examples/ButtonGroupExample.vue +8 -0
  93. package/dist/devtools/runtime/examples/CardExample.vue +13 -0
  94. package/dist/devtools/runtime/examples/CarouselExample.vue +13 -0
  95. package/dist/devtools/runtime/examples/ChipExample.vue +5 -0
  96. package/dist/devtools/runtime/examples/CollapsibleExample.vue +8 -0
  97. package/dist/devtools/runtime/examples/CommandPaletteExample.vue +29 -0
  98. package/dist/devtools/runtime/examples/ContainerExample.vue +5 -0
  99. package/dist/devtools/runtime/examples/ContextMenuExample.vue +5 -0
  100. package/dist/devtools/runtime/examples/DrawerExample.vue +8 -0
  101. package/dist/devtools/runtime/examples/DropdownMenuExample.vue +5 -0
  102. package/dist/devtools/runtime/examples/FormExample.vue +30 -0
  103. package/dist/devtools/runtime/examples/FormFieldExample.vue +5 -0
  104. package/dist/devtools/runtime/examples/LinkExample.vue +5 -0
  105. package/dist/devtools/runtime/examples/ModalExample.vue +8 -0
  106. package/dist/devtools/runtime/examples/PopoverExample.vue +8 -0
  107. package/dist/devtools/runtime/examples/SkeletonExample.vue +3 -0
  108. package/dist/devtools/runtime/examples/SlideoverExample.vue +8 -0
  109. package/dist/devtools/runtime/examples/ToasterExample.vue +11 -0
  110. package/dist/devtools/runtime/examples/TooltipExample.vue +5 -0
  111. package/dist/module.cjs +223 -4
  112. package/dist/module.d.cts +54 -0
  113. package/dist/module.d.mts +10 -0
  114. package/dist/module.d.ts +10 -0
  115. package/dist/module.json +1 -1
  116. package/dist/module.mjs +158 -2763
  117. package/dist/runtime/components/Accordion.vue +32 -1
  118. package/dist/runtime/components/Alert.vue +8 -1
  119. package/dist/runtime/components/App.vue +19 -5
  120. package/dist/runtime/components/Avatar.vue +23 -5
  121. package/dist/runtime/components/AvatarGroup.vue +3 -0
  122. package/dist/runtime/components/Badge.vue +33 -3
  123. package/dist/runtime/components/Breadcrumb.vue +33 -4
  124. package/dist/runtime/components/Button.vue +6 -1
  125. package/dist/runtime/components/ButtonGroup.vue +4 -1
  126. package/dist/runtime/components/Card.vue +3 -0
  127. package/dist/runtime/components/Carousel.vue +17 -8
  128. package/dist/runtime/components/Checkbox.vue +4 -0
  129. package/dist/runtime/components/Chip.vue +9 -2
  130. package/dist/runtime/components/Collapsible.vue +3 -0
  131. package/dist/runtime/components/CommandPalette.vue +8 -4
  132. package/dist/runtime/components/Container.vue +3 -0
  133. package/dist/runtime/components/ContextMenu.vue +64 -3
  134. package/dist/runtime/components/ContextMenuContent.vue +10 -8
  135. package/dist/runtime/components/Drawer.vue +3 -0
  136. package/dist/runtime/components/DropdownMenu.vue +52 -3
  137. package/dist/runtime/components/DropdownMenuContent.vue +10 -8
  138. package/dist/runtime/components/Form.vue +21 -20
  139. package/dist/runtime/components/FormField.vue +10 -3
  140. package/dist/runtime/components/Input.vue +1 -0
  141. package/dist/runtime/components/InputMenu.vue +118 -31
  142. package/dist/runtime/components/InputNumber.vue +192 -0
  143. package/dist/runtime/components/Kbd.vue +2 -0
  144. package/dist/runtime/components/Link.vue +69 -6
  145. package/dist/runtime/components/Modal.vue +15 -3
  146. package/dist/runtime/components/NavigationMenu.vue +95 -46
  147. package/dist/runtime/components/Pagination.vue +17 -6
  148. package/dist/runtime/components/PinInput.vue +96 -0
  149. package/dist/runtime/components/Popover.vue +20 -1
  150. package/dist/runtime/components/Progress.vue +1 -1
  151. package/dist/runtime/components/RadioGroup.vue +4 -1
  152. package/dist/runtime/components/Select.vue +24 -13
  153. package/dist/runtime/components/SelectMenu.vue +130 -42
  154. package/dist/runtime/components/Separator.vue +1 -1
  155. package/dist/runtime/components/Skeleton.vue +3 -0
  156. package/dist/runtime/components/Slideover.vue +15 -3
  157. package/dist/runtime/components/Slider.vue +1 -1
  158. package/dist/runtime/components/Switch.vue +5 -1
  159. package/dist/runtime/components/Table.vue +27 -8
  160. package/dist/runtime/components/Tabs.vue +20 -1
  161. package/dist/runtime/components/Textarea.vue +1 -1
  162. package/dist/runtime/components/Toast.vue +8 -1
  163. package/dist/runtime/components/Toaster.vue +29 -14
  164. package/dist/runtime/components/Tooltip.vue +3 -0
  165. package/dist/runtime/composables/defineLocale.d.ts +9 -0
  166. package/dist/runtime/composables/defineLocale.js +4 -0
  167. package/dist/runtime/composables/defineShortcuts.js +0 -6
  168. package/dist/runtime/composables/extendDevtoolsMeta.d.ts +7 -0
  169. package/dist/runtime/composables/extendDevtoolsMeta.js +2 -0
  170. package/dist/runtime/composables/useFormField.d.ts +4 -1
  171. package/dist/runtime/composables/useFormField.js +1 -1
  172. package/dist/runtime/composables/useKbd.d.ts +10 -3
  173. package/dist/runtime/composables/useKbd.js +14 -8
  174. package/dist/runtime/composables/useLocale.d.ts +4 -0
  175. package/dist/runtime/composables/useLocale.js +10 -0
  176. package/dist/runtime/index.css +1 -1
  177. package/dist/runtime/locale/ar.d.ts +2 -0
  178. package/dist/runtime/locale/ar.js +47 -0
  179. package/dist/runtime/locale/cs.d.ts +2 -0
  180. package/dist/runtime/locale/cs.js +46 -0
  181. package/dist/runtime/locale/de.d.ts +2 -0
  182. package/dist/runtime/locale/de.js +46 -0
  183. package/dist/runtime/locale/en.d.ts +2 -0
  184. package/dist/runtime/locale/en.js +46 -0
  185. package/dist/runtime/locale/es.d.ts +2 -0
  186. package/dist/runtime/locale/es.js +46 -0
  187. package/dist/runtime/locale/fa_ir.d.ts +2 -0
  188. package/dist/runtime/locale/fa_ir.js +47 -0
  189. package/dist/runtime/locale/fr.d.ts +2 -0
  190. package/dist/runtime/locale/fr.js +46 -0
  191. package/dist/runtime/locale/index.d.ts +12 -0
  192. package/dist/runtime/locale/index.js +12 -0
  193. package/dist/runtime/locale/it.d.ts +2 -0
  194. package/dist/runtime/locale/it.js +46 -0
  195. package/dist/runtime/locale/pl.d.ts +2 -0
  196. package/dist/runtime/locale/pl.js +46 -0
  197. package/dist/runtime/locale/ru.d.ts +2 -0
  198. package/dist/runtime/locale/ru.js +46 -0
  199. package/dist/runtime/locale/zh_hans.d.ts +2 -0
  200. package/dist/runtime/locale/zh_hans.js +46 -0
  201. package/dist/runtime/locale/zh_hant.d.ts +2 -0
  202. package/dist/runtime/locale/zh_hant.js +46 -0
  203. package/dist/runtime/types/form.d.ts +7 -2
  204. package/dist/runtime/types/index.d.ts +3 -0
  205. package/dist/runtime/types/index.js +3 -0
  206. package/dist/runtime/types/locale.d.ts +48 -0
  207. package/dist/runtime/types/locale.js +0 -0
  208. package/dist/runtime/types/utils.d.ts +7 -0
  209. package/dist/runtime/utils/form.d.ts +5 -9
  210. package/dist/runtime/utils/form.js +115 -50
  211. package/dist/runtime/utils/index.js +1 -1
  212. package/dist/runtime/utils/link.d.ts +2 -2
  213. package/dist/runtime/utils/locale.d.ts +15 -0
  214. package/dist/runtime/utils/locale.js +25 -0
  215. package/dist/runtime/vue/components/Icon.vue +15 -0
  216. package/dist/runtime/vue/components/Link.vue +195 -0
  217. package/dist/runtime/vue/plugins/color-mode.d.ts +4 -0
  218. package/dist/runtime/vue/plugins/color-mode.js +6 -0
  219. package/dist/runtime/vue/plugins/head.d.ts +4 -0
  220. package/dist/runtime/vue/plugins/head.js +6 -0
  221. package/dist/runtime/vue/stubs.d.ts +15 -0
  222. package/dist/runtime/vue/stubs.js +27 -0
  223. package/dist/shared/ui.BPnAe4QS.cjs +3030 -0
  224. package/dist/shared/ui.d52SSCwA.mjs +3023 -0
  225. package/dist/unplugin.cjs +192 -0
  226. package/dist/unplugin.d.cts +49 -0
  227. package/dist/unplugin.d.mts +49 -0
  228. package/dist/unplugin.d.ts +49 -0
  229. package/dist/unplugin.mjs +181 -0
  230. package/dist/vite.cjs +22 -0
  231. package/dist/vite.d.cts +13 -0
  232. package/dist/vite.d.mts +13 -0
  233. package/dist/vite.d.ts +13 -0
  234. package/dist/vite.mjs +20 -0
  235. package/package.json +78 -33
  236. package/vue-plugin.d.ts +5 -0
  237. package/cli/node_modules/.ignored/citty/LICENSE +0 -36
  238. package/cli/node_modules/.ignored/citty/README.md +0 -134
  239. package/cli/node_modules/.ignored/citty/dist/index.cjs +0 -475
  240. package/cli/node_modules/.ignored/citty/dist/index.d.cts +0 -80
  241. package/cli/node_modules/.ignored/citty/dist/index.d.mts +0 -80
  242. package/cli/node_modules/.ignored/citty/dist/index.d.ts +0 -80
  243. package/cli/node_modules/.ignored/citty/dist/index.mjs +0 -463
  244. package/cli/node_modules/.ignored/citty/package.json +0 -49
  245. package/cli/node_modules/.ignored/consola/LICENSE +0 -47
  246. package/cli/node_modules/.ignored/consola/README.md +0 -318
  247. package/cli/node_modules/.ignored/consola/core.d.ts +0 -1
  248. package/cli/node_modules/.ignored/consola/dist/basic.cjs +0 -32
  249. package/cli/node_modules/.ignored/consola/dist/basic.d.ts +0 -9
  250. package/cli/node_modules/.ignored/consola/dist/basic.mjs +0 -24
  251. package/cli/node_modules/.ignored/consola/dist/browser.cjs +0 -80
  252. package/cli/node_modules/.ignored/consola/dist/browser.d.ts +0 -7
  253. package/cli/node_modules/.ignored/consola/dist/browser.mjs +0 -72
  254. package/cli/node_modules/.ignored/consola/dist/chunks/prompt.cjs +0 -436
  255. package/cli/node_modules/.ignored/consola/dist/chunks/prompt.mjs +0 -429
  256. package/cli/node_modules/.ignored/consola/dist/core.cjs +0 -415
  257. package/cli/node_modules/.ignored/consola/dist/core.d.ts +0 -124
  258. package/cli/node_modules/.ignored/consola/dist/core.mjs +0 -410
  259. package/cli/node_modules/.ignored/consola/dist/index.cjs +0 -21
  260. package/cli/node_modules/.ignored/consola/dist/index.d.ts +0 -9
  261. package/cli/node_modules/.ignored/consola/dist/index.mjs +0 -8
  262. package/cli/node_modules/.ignored/consola/dist/shared/consola.06ad8a64.mjs +0 -62
  263. package/cli/node_modules/.ignored/consola/dist/shared/consola.36c0034f.mjs +0 -636
  264. package/cli/node_modules/.ignored/consola/dist/shared/consola.4bbae468.cjs +0 -645
  265. package/cli/node_modules/.ignored/consola/dist/shared/consola.deac7d5a.cjs +0 -65
  266. package/cli/node_modules/.ignored/consola/dist/utils.cjs +0 -308
  267. package/cli/node_modules/.ignored/consola/dist/utils.d.ts +0 -169
  268. package/cli/node_modules/.ignored/consola/dist/utils.mjs +0 -284
  269. package/cli/node_modules/.ignored/consola/lib/index.cjs +0 -9
  270. package/cli/node_modules/.ignored/consola/package.json +0 -101
  271. package/cli/node_modules/.ignored/consola/utils.d.ts +0 -1
  272. package/cli/node_modules/.ignored/pathe/LICENSE +0 -44
  273. package/cli/node_modules/.ignored/pathe/README.md +0 -69
  274. package/cli/node_modules/.ignored/pathe/dist/index.cjs +0 -23
  275. package/cli/node_modules/.ignored/pathe/dist/index.d.cts +0 -36
  276. package/cli/node_modules/.ignored/pathe/dist/index.d.mts +0 -36
  277. package/cli/node_modules/.ignored/pathe/dist/index.d.ts +0 -36
  278. package/cli/node_modules/.ignored/pathe/dist/index.mjs +0 -1
  279. package/cli/node_modules/.ignored/pathe/dist/shared/pathe.1f0a373c.cjs +0 -238
  280. package/cli/node_modules/.ignored/pathe/dist/shared/pathe.ff20891b.mjs +0 -221
  281. package/cli/node_modules/.ignored/pathe/dist/utils.cjs +0 -58
  282. package/cli/node_modules/.ignored/pathe/dist/utils.d.cts +0 -5
  283. package/cli/node_modules/.ignored/pathe/dist/utils.d.mts +0 -5
  284. package/cli/node_modules/.ignored/pathe/dist/utils.d.ts +0 -5
  285. package/cli/node_modules/.ignored/pathe/dist/utils.mjs +0 -54
  286. package/cli/node_modules/.ignored/pathe/package.json +0 -48
  287. package/cli/node_modules/.ignored/pathe/utils.d.ts +0 -1
  288. package/cli/node_modules/.ignored/scule/LICENSE +0 -21
  289. package/cli/node_modules/.ignored/scule/README.md +0 -149
  290. package/cli/node_modules/.ignored/scule/dist/index.cjs +0 -92
  291. package/cli/node_modules/.ignored/scule/dist/index.d.cts +0 -57
  292. package/cli/node_modules/.ignored/scule/dist/index.d.mts +0 -57
  293. package/cli/node_modules/.ignored/scule/dist/index.d.ts +0 -57
  294. package/cli/node_modules/.ignored/scule/dist/index.mjs +0 -80
  295. package/cli/node_modules/.ignored/scule/package.json +0 -43
  296. package/cli/node_modules/.package-lock.json +0 -33
@@ -6,8 +6,9 @@ import type { AppConfig } from '@nuxt/schema'
6
6
  import _appConfig from '#build/app.config'
7
7
  import theme from '#build/ui/input-menu'
8
8
  import type { UseComponentIconsProps } from '../composables/useComponentIcons'
9
+ import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
9
10
  import type { AvatarProps, ChipProps, InputProps } from '../types'
10
- import type { AcceptableValue, ArrayOrWrapped, PartialString } from '../types/utils'
11
+ import type { AcceptableValue, ArrayOrWrapped, PartialString, MaybeArrayOfArray, MaybeArrayOfArrayItem, SelectModelValue, SelectModelValueEmits, SelectItemKey } from '../types/utils'
11
12
 
12
13
  const appConfig = _appConfig as AppConfig & { ui: { inputMenu: Partial<typeof theme> } }
13
14
 
@@ -29,7 +30,7 @@ export interface InputMenuItem {
29
30
 
30
31
  type InputMenuVariants = VariantProps<typeof inputMenu>
31
32
 
32
- export interface InputMenuProps<T> extends Pick<ComboboxRootProps<T>, 'modelValue' | 'defaultValue' | 'selectedValue' | 'open' | 'defaultOpen' | 'searchTerm' | 'multiple' | 'disabled' | 'name' | 'resetSearchTermOnBlur'>, UseComponentIconsProps {
33
+ export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<InputMenuItem | AcceptableValue> = MaybeArrayOfArray<InputMenuItem | AcceptableValue>, V extends SelectItemKey<T> | undefined = undefined, M extends boolean = false> extends Pick<ComboboxRootProps<T>, 'defaultValue' | 'selectedValue' | 'open' | 'defaultOpen' | 'searchTerm' | 'disabled' | 'name' | 'resetSearchTermOnBlur'>, UseComponentIconsProps {
33
34
  /**
34
35
  * The element or component this component should render as.
35
36
  * @defaultValue 'div'
@@ -77,33 +78,44 @@ export interface InputMenuProps<T> extends Pick<ComboboxRootProps<T>, 'modelValu
77
78
  */
78
79
  portal?: boolean
79
80
  /**
80
- * Whether to filter items or not, can be an array of fields to filter.
81
- * When `false`, items will not be filtered which is useful for custom filtering.
82
- * @defaultValue ['label']
81
+ * Whether to filter items or not, can be an array of fields to filter. Defaults to `[labelKey]`.
82
+ * When `false`, items will not be filtered which is useful for custom filtering (useAsyncData, useFetch, etc.).
83
+ * `['label']`{lang="ts-type"}
84
+ * @defaultValue true
83
85
  */
84
86
  filter?: boolean | string[]
85
87
  /**
86
88
  * When `items` is an array of objects, select the field to use as the value instead of the object itself.
87
89
  * @defaultValue undefined
88
90
  */
89
- valueKey?: keyof T
91
+ valueKey?: V
90
92
  /**
91
93
  * When `items` is an array of objects, select the field to use as the label.
92
94
  * @defaultValue 'label'
93
95
  */
94
- labelKey?: keyof T
95
- items?: T[] | T[][]
96
+ labelKey?: V
97
+ items?: I
96
98
  /** Highlight the ring color like a focus state. */
97
99
  highlight?: boolean
100
+ /**
101
+ * Determines if custom user input that does not exist in options can be added.
102
+ * @defaultValue false
103
+ */
104
+ createItem?: boolean | 'always' | { placement?: 'top' | 'bottom', when?: 'empty' | 'always' }
98
105
  class?: any
99
106
  ui?: PartialString<typeof inputMenu.slots>
107
+ /** The controlled value of the Combobox. Can be binded-with with `v-model`. */
108
+ modelValue?: SelectModelValue<T, V, M>
109
+ /** Whether multiple options can be selected or not. */
110
+ multiple?: M
100
111
  }
101
112
 
102
- export type InputMenuEmits<T> = ComboboxRootEmits<T> & {
113
+ export type InputMenuEmits<T, V, M extends boolean> = Omit<ComboboxRootEmits<T>, 'update:modelValue'> & {
103
114
  change: [payload: Event]
104
115
  blur: [payload: FocusEvent]
105
116
  focus: [payload: FocusEvent]
106
- }
117
+ create: [payload: Event, item: T]
118
+ } & SelectModelValueEmits<T, V, M>
107
119
 
108
120
  type SlotProps<T> = (props: { item: T, index: number }) => any
109
121
 
@@ -117,19 +129,23 @@ export interface InputMenuSlots<T> {
117
129
  'item-trailing': SlotProps<T>
118
130
  'tags-item-text': SlotProps<T>
119
131
  'tags-item-delete': SlotProps<T>
132
+ 'create-item-label'(props: { item: T }): any
120
133
  }
134
+
135
+ extendDevtoolsMeta({ defaultProps: { items: ['Option 1', 'Option 2', 'Option 3'] } })
121
136
  </script>
122
137
 
123
- <script setup lang="ts" generic="T extends InputMenuItem | AcceptableValue">
124
- import { computed, ref, toRef, onMounted } from 'vue'
125
- import { ComboboxRoot, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxViewport, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, TagsInputRoot, TagsInputItem, TagsInputItemText, TagsInputItemDelete, TagsInputInput, useForwardPropsEmits } from 'radix-vue'
138
+ <script setup lang="ts" generic="T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<InputMenuItem | AcceptableValue> = MaybeArrayOfArray<InputMenuItem | AcceptableValue>, V extends SelectItemKey<T> | undefined = undefined, M extends boolean = false">
139
+ import { computed, ref, toRef, onMounted, toRaw } from 'vue'
140
+ import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxViewport, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, TagsInputRoot, TagsInputItem, TagsInputItemText, TagsInputItemDelete, TagsInputInput, useForwardPropsEmits } from 'radix-vue'
126
141
  import { defu } from 'defu'
127
- import isEqual from 'fast-deep-equal'
128
- import { reactivePick } from '@vueuse/core'
142
+ import { isEqual } from 'ohash'
143
+ import { reactivePick, createReusableTemplate } from '@vueuse/core'
129
144
  import { useAppConfig } from '#imports'
130
145
  import { useButtonGroup } from '../composables/useButtonGroup'
131
146
  import { useComponentIcons } from '../composables/useComponentIcons'
132
147
  import { useFormField } from '../composables/useFormField'
148
+ import { useLocale } from '../composables/useLocale'
133
149
  import { get, escapeRegExp } from '../utils'
134
150
  import UIcon from './Icon.vue'
135
151
  import UAvatar from './Avatar.vue'
@@ -137,25 +153,32 @@ import UChip from './Chip.vue'
137
153
 
138
154
  defineOptions({ inheritAttrs: false })
139
155
 
140
- const props = withDefaults(defineProps<InputMenuProps<T>>(), {
156
+ const props = withDefaults(defineProps<InputMenuProps<T, I, V, M>>(), {
141
157
  type: 'text',
142
158
  autofocusDelay: 0,
143
159
  portal: true,
144
- filter: () => ['label'],
145
- labelKey: 'label' as keyof T
160
+ filter: true,
161
+ labelKey: 'label' as never
146
162
  })
147
- const emits = defineEmits<InputMenuEmits<T>>()
163
+ const emits = defineEmits<InputMenuEmits<T, V, M>>()
148
164
  const slots = defineSlots<InputMenuSlots<T>>()
149
165
 
150
166
  const searchTerm = defineModel<string>('searchTerm', { default: '' })
151
167
 
152
168
  const appConfig = useAppConfig()
153
- const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'selectedValue', 'open', 'defaultOpen', 'multiple', 'resetSearchTermOnBlur'), emits)
169
+ const { t } = useLocale()
170
+ const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'selectedValue', 'open', 'defaultOpen', 'resetSearchTermOnBlur'), emits)
154
171
  const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, position: 'popper' }) as ComboboxContentProps)
172
+ const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
173
+ // This is a hack due to generic boolean casting (see https://github.com/nuxt/ui/issues/2541)
174
+ const multiple = toRef(() => typeof props.multiple === 'string' ? true : props.multiple)
175
+
155
176
  const { emitFormBlur, emitFormChange, emitFormInput, size: formGroupSize, color, id, name, highlight, disabled } = useFormField<InputProps>(props)
156
177
  const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
157
178
  const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown })))
158
179
 
180
+ const [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate()
181
+
159
182
  const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value)
160
183
 
161
184
  const ui = computed(() => inputMenu({
@@ -166,33 +189,41 @@ const ui = computed(() => inputMenu({
166
189
  highlight: highlight.value,
167
190
  leading: isLeading.value || !!props.avatar || !!slots.leading,
168
191
  trailing: isTrailing.value || !!slots.trailing,
169
- multiple: props.multiple,
192
+ multiple: multiple.value,
170
193
  buttonGroup: orientation.value
171
194
  }))
172
195
 
173
- function displayValue(value: AcceptableValue): string {
174
- const item = items.value.find(item => props.valueKey ? isEqual(get(item as Record<string, any>, props.valueKey as string), value) : isEqual(item, value))
196
+ function displayValue(value: T): string {
197
+ if (!props.valueKey) {
198
+ return value && (typeof value === 'object' ? get(value, props.labelKey as string) : value)
199
+ }
200
+
201
+ const item = items.value.find(item => isEqual(get(item as Record<string, any>, props.valueKey as string), value))
175
202
 
176
203
  return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
177
204
  }
178
205
 
179
- function filterFunction(items: ArrayOrWrapped<AcceptableValue>, searchTerm: string): ArrayOrWrapped<AcceptableValue> {
206
+ function filterFunction(
207
+ inputItems: ArrayOrWrapped<T> = items.value as ArrayOrWrapped<T>,
208
+ filterSearchTerm: string = searchTerm.value,
209
+ comparator = (item: any, term: string) => String(item).search(new RegExp(term, 'i')) !== -1
210
+ ): ArrayOrWrapped<T> {
180
211
  if (props.filter === false) {
181
- return items
212
+ return inputItems
182
213
  }
183
214
 
184
215
  const fields = Array.isArray(props.filter) ? props.filter : [props.labelKey]
185
- const escapedSearchTerm = escapeRegExp(searchTerm)
216
+ const escapedSearchTerm = escapeRegExp(filterSearchTerm ?? '')
186
217
 
187
- return items.filter((item) => {
218
+ return inputItems.filter((item) => {
188
219
  if (typeof item !== 'object') {
189
- return String(item).search(new RegExp(escapedSearchTerm, 'i')) !== -1
220
+ return comparator(item, escapedSearchTerm)
190
221
  }
191
222
 
192
223
  return fields.some((field) => {
193
224
  const child = get(item, field as string)
194
225
 
195
- return child !== null && child !== undefined && String(child).search(new RegExp(escapedSearchTerm, 'i')) !== -1
226
+ return child !== null && child !== undefined && comparator(child, escapedSearchTerm)
196
227
  })
197
228
  }) as ArrayOrWrapped<T>
198
229
  }
@@ -201,6 +232,36 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
201
232
  // eslint-disable-next-line vue/no-dupe-keys
202
233
  const items = computed(() => groups.value.flatMap(group => group) as T[])
203
234
 
235
+ const creatable = computed(() => {
236
+ if (!props.createItem) {
237
+ return false
238
+ }
239
+
240
+ const isModelValueCustom = props.modelValue && filterFunction((props.multiple && Array.isArray(props.modelValue) ? props.modelValue : [props.modelValue]) as ArrayOrWrapped<T>, searchTerm.value, (item, term) => String(item) === term).length === 1
241
+
242
+ if (isModelValueCustom) {
243
+ return false
244
+ }
245
+
246
+ const filteredItems = filterFunction()
247
+ const newItem = searchTerm.value && {
248
+ item: props.valueKey ? { [props.valueKey]: searchTerm.value, [props.labelKey ?? 'label']: searchTerm.value } : searchTerm.value,
249
+ position: ((typeof props.createItem === 'object' && props.createItem.placement) || 'bottom') as 'top' | 'bottom'
250
+ }
251
+
252
+ if ((typeof props.createItem === 'object' && props.createItem.when === 'always') || props.createItem === 'always') {
253
+ return (filteredItems.length === 1 && filterFunction(filteredItems, searchTerm.value, (item, term) => String(item) === term).length === 1) ? false : newItem
254
+ }
255
+
256
+ return filteredItems.length > 0 ? false : newItem
257
+ })
258
+
259
+ const rootItems = computed(() => [
260
+ ...(creatable.value && creatable.value.position === 'top' ? [creatable.value.item] : []),
261
+ ...filterFunction(),
262
+ ...(creatable.value && creatable.value.position === 'bottom' ? [creatable.value.item] : [])
263
+ ] as ArrayOrWrapped<T>)
264
+
204
265
  const inputRef = ref<InstanceType<typeof ComboboxInput> | null>(null)
205
266
 
206
267
  function autoFocus() {
@@ -216,6 +277,9 @@ onMounted(() => {
216
277
  })
217
278
 
218
279
  function onUpdate(value: any) {
280
+ if (toRaw(props.modelValue) === value) {
281
+ return
282
+ }
219
283
  // @ts-expect-error - 'target' does not exist in type 'EventInit'
220
284
  const event = new Event('change', { target: { value } })
221
285
  emits('change', event)
@@ -249,6 +313,22 @@ defineExpose({
249
313
  </script>
250
314
 
251
315
  <template>
316
+ <DefineCreateItemTemplate>
317
+ <ComboboxGroup v-if="creatable" :class="ui.group({ class: props.ui?.group })">
318
+ <ComboboxItem
319
+ :class="ui.item({ class: props.ui?.item })"
320
+ :value="valueKey && typeof creatable.item === 'object' ? get(creatable.item, props.valueKey as string) : creatable.item"
321
+ @select="e => emits('create', e, (creatable as any).item as T)"
322
+ >
323
+ <span :class="ui.itemLabel({ class: props.ui?.itemLabel })">
324
+ <slot name="create-item-label" :item="(creatable.item as T)">
325
+ {{ t('inputMenu.create', { label: typeof creatable.item === 'object' ? get(creatable.item, props.labelKey as string) : creatable.item }) }}
326
+ </slot>
327
+ </span>
328
+ </ComboboxItem>
329
+ </ComboboxGroup>
330
+ </DefineCreateItemTemplate>
331
+
252
332
  <ComboboxRoot
253
333
  :id="id"
254
334
  v-slot="{ modelValue, open }"
@@ -256,8 +336,9 @@ defineExpose({
256
336
  v-model:search-term="searchTerm"
257
337
  :name="name"
258
338
  :disabled="disabled"
339
+ :multiple="multiple"
259
340
  :display-value="displayValue"
260
- :filter-function="filterFunction"
341
+ :filter-function="() => rootItems"
261
342
  :class="ui.root({ class: [props.class, props.ui?.root] })"
262
343
  :as-child="!!multiple"
263
344
  @update:model-value="onUpdate"
@@ -331,11 +412,13 @@ defineExpose({
331
412
  <ComboboxContent :class="ui.content({ class: props.ui?.content })" v-bind="contentProps">
332
413
  <ComboboxEmpty :class="ui.empty({ class: props.ui?.empty })">
333
414
  <slot name="empty" :search-term="searchTerm">
334
- {{ searchTerm ? `No results for ${searchTerm}` : 'No results' }}
415
+ {{ searchTerm ? t('inputMenu.noMatch', { searchTerm }) : t('inputMenu.noData') }}
335
416
  </slot>
336
417
  </ComboboxEmpty>
337
418
 
338
419
  <ComboboxViewport :class="ui.viewport({ class: props.ui?.viewport })">
420
+ <ReuseCreateItemTemplate v-if="creatable && creatable.position === 'top'" />
421
+
339
422
  <ComboboxGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
340
423
  <template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
341
424
  <ComboboxLabel v-if="item?.type === 'label'" :class="ui.label({ class: props.ui?.label })">
@@ -382,7 +465,11 @@ defineExpose({
382
465
  </ComboboxItem>
383
466
  </template>
384
467
  </ComboboxGroup>
468
+
469
+ <ReuseCreateItemTemplate v-if="creatable && creatable.position === 'bottom'" />
385
470
  </ComboboxViewport>
471
+
472
+ <ComboboxArrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow({ class: props.ui?.arrow })" />
386
473
  </ComboboxContent>
387
474
  </ComboboxPortal>
388
475
  </ComboboxRoot>
@@ -0,0 +1,192 @@
1
+ <script lang="ts">
2
+ import { tv, type VariantProps } from 'tailwind-variants'
3
+ import type { NumberFieldRootProps } from 'radix-vue'
4
+ import type { AppConfig } from '@nuxt/schema'
5
+ import _appConfig from '#build/app.config'
6
+ import theme from '#build/ui/input-number'
7
+ import type { ButtonProps } from '../types'
8
+
9
+ const appConfig = _appConfig as AppConfig & { ui: { inputNumber: Partial<typeof theme> } }
10
+
11
+ const inputNumber = tv({ extend: tv(theme), ...(appConfig.ui?.inputNumber || {}) })
12
+
13
+ type InputNumberVariants = VariantProps<typeof inputNumber>
14
+
15
+ export interface InputNumberProps extends Pick<NumberFieldRootProps, 'modelValue' | 'defaultValue' | 'min' | 'max' | 'step' | 'disabled' | 'required' | 'id' | 'name' | 'formatOptions'> {
16
+ /**
17
+ * The element or component this component should render as.
18
+ * @defaultValue 'div'
19
+ */
20
+ as?: any
21
+ class?: any
22
+ /** The placeholder text when the input is empty. */
23
+ placeholder?: string
24
+ ui?: Partial<typeof inputNumber.slots>
25
+ color?: InputNumberVariants['color']
26
+ variant?: InputNumberVariants['variant']
27
+ size?: InputNumberVariants['size']
28
+ /** Highlight the ring color like a focus state. */
29
+ highlight?: boolean
30
+ /**
31
+ * The orientation of the input menu.
32
+ * @defaultValue 'horizontal'
33
+ */
34
+ orientation?: 'vertical' | 'horizontal'
35
+ /**
36
+ * Configure the increment button. The `color` and `size` are inherited.
37
+ * @defaultValue { variant: 'link' }
38
+ */
39
+ increment?: ButtonProps
40
+ /**
41
+ * The icon displayed to increment the value.
42
+ * @defaultValue appConfig.ui.icons.plus
43
+ */
44
+ incrementIcon?: string
45
+ /**
46
+ * Configure the decrement button. The `color` and `size` are inherited.
47
+ * @defaultValue { variant: 'link' }
48
+ */
49
+ decrement?: ButtonProps
50
+ /**
51
+ * The icon displayed to decrement the value.
52
+ * @defaultValue appConfig.ui.icons.minus
53
+ */
54
+ decrementIcon?: string
55
+ autofocus?: boolean
56
+ autofocusDelay?: number
57
+ /**
58
+ * The locale to use for formatting and parsing numbers.
59
+ * @defaultValue UApp.locale.code
60
+ */
61
+ locale?: string
62
+ }
63
+
64
+ export interface InputNumberEmits {
65
+ (e: 'update:modelValue', payload: number): void
66
+ (e: 'blur', event: FocusEvent): void
67
+ (e: 'change', payload: Event): void
68
+ }
69
+
70
+ export interface InputNumberSlots {
71
+ increment(props?: {}): any
72
+ decrement(props?: {}): any
73
+ }
74
+ </script>
75
+
76
+ <script setup lang="ts">
77
+ import { onMounted, ref, computed } from 'vue'
78
+ import { NumberFieldRoot, NumberFieldInput, NumberFieldDecrement, NumberFieldIncrement, useForwardPropsEmits } from 'radix-vue'
79
+ import { reactivePick } from '@vueuse/core'
80
+ import { useFormField } from '../composables/useFormField'
81
+ import { useLocale } from '../composables/useLocale'
82
+ import UButton from './Button.vue'
83
+
84
+ defineOptions({ inheritAttrs: false })
85
+
86
+ const props = withDefaults(defineProps<InputNumberProps>(), {
87
+ orientation: 'horizontal'
88
+ })
89
+ const emits = defineEmits<InputNumberEmits>()
90
+ defineSlots<InputNumberSlots>()
91
+
92
+ const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'min', 'max', 'step', 'formatOptions'), emits)
93
+
94
+ const { emitFormBlur, emitFormChange, emitFormInput, id, color, size, name, highlight, disabled } = useFormField<InputNumberProps>(props)
95
+
96
+ const { t, code: codeLocale } = useLocale()
97
+ const locale = computed(() => props.locale || codeLocale.value)
98
+
99
+ const ui = computed(() => inputNumber({
100
+ color: color.value,
101
+ variant: props.variant,
102
+ size: size.value,
103
+ highlight: highlight.value,
104
+ orientation: props.orientation
105
+ }))
106
+
107
+ const incrementIcon = computed(() => props.incrementIcon || (props.orientation === 'horizontal' ? appConfig.ui.icons.plus : appConfig.ui.icons.chevronUp))
108
+ const decrementIcon = computed(() => props.decrementIcon || (props.orientation === 'horizontal' ? appConfig.ui.icons.minus : appConfig.ui.icons.chevronDown))
109
+
110
+ const inputRef = ref<InstanceType<typeof NumberFieldInput> | null>(null)
111
+
112
+ function autoFocus() {
113
+ if (props.autofocus) {
114
+ inputRef.value?.$el?.focus()
115
+ }
116
+ }
117
+
118
+ onMounted(() => {
119
+ setTimeout(() => {
120
+ autoFocus()
121
+ }, props.autofocusDelay)
122
+ })
123
+
124
+ function onUpdate(value: number) {
125
+ // @ts-expect-error - 'target' does not exist in type 'EventInit'
126
+ const event = new Event('change', { target: { value } })
127
+ emits('change', event)
128
+
129
+ emitFormChange()
130
+ emitFormInput()
131
+ }
132
+
133
+ function onBlur(event: FocusEvent) {
134
+ emitFormBlur()
135
+ emits('blur', event)
136
+ }
137
+
138
+ defineExpose({
139
+ inputRef
140
+ })
141
+ </script>
142
+
143
+ <template>
144
+ <NumberFieldRoot
145
+ v-bind="rootProps"
146
+ :id="id"
147
+ :class="ui.root({ class: [props.class, props.ui?.root] })"
148
+ :name="name"
149
+ :disabled="disabled"
150
+ :locale="locale"
151
+ @update:model-value="onUpdate"
152
+ >
153
+ <NumberFieldInput
154
+ v-bind="$attrs"
155
+ ref="inputRef"
156
+ :placeholder="placeholder"
157
+ :required="required"
158
+ :class="ui.base({ class: props.ui?.base })"
159
+ @blur="onBlur"
160
+ />
161
+
162
+ <div :class="ui.increment({ class: props.ui?.increment })">
163
+ <NumberFieldIncrement as-child :disabled="disabled">
164
+ <slot name="increment">
165
+ <UButton
166
+ :icon="incrementIcon"
167
+ :color="color"
168
+ :size="size"
169
+ variant="link"
170
+ :aria-label="t('inputNumber.increment')"
171
+ v-bind="typeof increment === 'object' ? increment : undefined"
172
+ />
173
+ </slot>
174
+ </NumberFieldIncrement>
175
+ </div>
176
+
177
+ <div :class="ui.decrement({ class: props.ui?.decrement })">
178
+ <NumberFieldDecrement as-child :disabled="disabled">
179
+ <slot name="decrement">
180
+ <UButton
181
+ :icon="decrementIcon"
182
+ :color="color"
183
+ :size="size"
184
+ variant="link"
185
+ :aria-label="t('inputNumber.decrement')"
186
+ v-bind="typeof decrement === 'object' ? decrement : undefined"
187
+ />
188
+ </slot>
189
+ </NumberFieldDecrement>
190
+ </div>
191
+ </NumberFieldRoot>
192
+ </template>
@@ -4,6 +4,7 @@ import type { AppConfig } from '@nuxt/schema'
4
4
  import _appConfig from '#build/app.config'
5
5
  import theme from '#build/ui/kbd'
6
6
  import type { KbdKey } from '../composables/useKbd'
7
+ import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
7
8
 
8
9
  const appConfig = _appConfig as AppConfig & { ui: { kbd: Partial<typeof theme> } }
9
10
 
@@ -26,6 +27,7 @@ export interface KbdProps {
26
27
  export interface KbdSlots {
27
28
  default(props?: {}): any
28
29
  }
30
+ extendDevtoolsMeta({ defaultProps: { value: 'K' } })
29
31
  </script>
30
32
 
31
33
  <script setup lang="ts">
@@ -3,8 +3,55 @@ import type { ButtonHTMLAttributes } from 'vue'
3
3
  import { tv } from 'tailwind-variants'
4
4
  import type { AppConfig } from '@nuxt/schema'
5
5
  import _appConfig from '#build/app.config'
6
+ import type { RouterLinkProps, RouteLocationRaw } from 'vue-router'
6
7
  import theme from '#build/ui/link'
7
- import type { NuxtLinkProps } from '#app'
8
+ import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
9
+
10
+ interface NuxtLinkProps extends Omit<RouterLinkProps, 'to'> {
11
+ /**
12
+ * Route Location the link should navigate to when clicked on.
13
+ */
14
+ to?: RouteLocationRaw // need to manually type to avoid breaking typedPages
15
+ /**
16
+ * An alias for `to`. If used with `to`, `href` will be ignored
17
+ */
18
+ href?: NuxtLinkProps['to']
19
+ /**
20
+ * Forces the link to be considered as external (true) or internal (false). This is helpful to handle edge-cases
21
+ */
22
+ external?: boolean
23
+ /**
24
+ * Where to display the linked URL, as the name for a browsing context.
25
+ */
26
+ target?: '_blank' | '_parent' | '_self' | '_top' | (string & {}) | null
27
+ /**
28
+ * A rel attribute value to apply on the link. Defaults to "noopener noreferrer" for external links.
29
+ */
30
+ rel?: 'noopener' | 'noreferrer' | 'nofollow' | 'sponsored' | 'ugc' | (string & {}) | null
31
+ /**
32
+ * If set to true, no rel attribute will be added to the link
33
+ */
34
+ noRel?: boolean
35
+ /**
36
+ * A class to apply to links that have been prefetched.
37
+ */
38
+ prefetchedClass?: string
39
+ /**
40
+ * When enabled will prefetch middleware, layouts and payloads of links in the viewport.
41
+ */
42
+ prefetch?: boolean
43
+ /**
44
+ * Allows controlling when to prefetch links. By default, prefetch is triggered only on visibility.
45
+ */
46
+ prefetchOn?: 'visibility' | 'interaction' | Partial<{
47
+ visibility: boolean
48
+ interaction: boolean
49
+ }>
50
+ /**
51
+ * Escape hatch to disable `prefetch` attribute.
52
+ */
53
+ noPrefetch?: boolean
54
+ }
8
55
 
9
56
  const appConfig = _appConfig as AppConfig & { ui: { link: Partial<typeof theme> } }
10
57
 
@@ -26,8 +73,8 @@ export interface LinkProps extends NuxtLinkProps {
26
73
  active?: boolean
27
74
  /** Will only be active if the current route is an exact match. */
28
75
  exact?: boolean
29
- /** Will only be active if the current route query is an exact match. */
30
- exactQuery?: boolean
76
+ /** Allows controlling how the current route query sets the link as active. */
77
+ exactQuery?: boolean | 'partial'
31
78
  /** Will only be active if the current route hash is an exact match. */
32
79
  exactHash?: boolean
33
80
  /** The class to apply when the link is inactive. */
@@ -41,14 +88,17 @@ export interface LinkProps extends NuxtLinkProps {
41
88
  export interface LinkSlots {
42
89
  default(props: { active: boolean }): any
43
90
  }
91
+
92
+ extendDevtoolsMeta({ example: 'LinkExample' })
44
93
  </script>
45
94
 
46
95
  <script setup lang="ts">
47
96
  import { computed } from 'vue'
48
- import { isEqual } from 'ohash'
97
+ import { isEqual, diff } from 'ohash'
49
98
  import { useForwardProps } from 'radix-vue'
50
99
  import { reactiveOmit } from '@vueuse/core'
51
100
  import { useRoute } from '#imports'
101
+ import ULinkBase from './LinkBase.vue'
52
102
 
53
103
  defineOptions({ inheritAttrs: false })
54
104
 
@@ -74,14 +124,27 @@ const ui = computed(() => tv({
74
124
  }
75
125
  }))
76
126
 
127
+ function isPartiallyEqual(item1: any, item2: any) {
128
+ const diffedKeys = diff(item1, item2).reduce((filtered, q) => {
129
+ if (q.type === 'added') {
130
+ filtered.push(q.key)
131
+ }
132
+ return filtered
133
+ }, [] as string[])
134
+ return isEqual(item1, item2, { excludeKeys: key => diffedKeys.includes(key) })
135
+ }
136
+
77
137
  function isLinkActive({ route: linkRoute, isActive, isExactActive }: any) {
78
138
  if (props.active !== undefined) {
79
139
  return props.active
80
140
  }
81
141
 
82
- if (props.exactQuery && !isEqual(linkRoute.query, route.query)) {
83
- return false
142
+ if (props.exactQuery === 'partial') {
143
+ if (!isPartiallyEqual(linkRoute.query, route.query)) return false
144
+ } else if (props.exactQuery === true) {
145
+ if (!isEqual(linkRoute.query, route.query)) return false
84
146
  }
147
+
85
148
  if (props.exactHash && linkRoute.hash !== route.hash) {
86
149
  return false
87
150
  }
@@ -4,6 +4,7 @@ import type { DialogRootProps, DialogRootEmits, DialogContentProps } from 'radix
4
4
  import type { AppConfig } from '@nuxt/schema'
5
5
  import _appConfig from '#build/app.config'
6
6
  import theme from '#build/ui/modal'
7
+ import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
7
8
  import type { ButtonProps } from '../types'
8
9
 
9
10
  const appConfig = _appConfig as AppConfig & { ui: { modal: Partial<typeof theme> } }
@@ -67,6 +68,8 @@ export interface ModalSlots {
67
68
  body(props?: {}): any
68
69
  footer(props?: {}): any
69
70
  }
71
+
72
+ extendDevtoolsMeta({ example: 'ModalExample' })
70
73
  </script>
71
74
 
72
75
  <script setup lang="ts">
@@ -74,6 +77,7 @@ import { computed, toRef } from 'vue'
74
77
  import { DialogRoot, DialogTrigger, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose, useForwardPropsEmits } from 'radix-vue'
75
78
  import { reactivePick } from '@vueuse/core'
76
79
  import { useAppConfig } from '#imports'
80
+ import { useLocale } from '../composables/useLocale'
77
81
  import UButton from './Button.vue'
78
82
 
79
83
  const props = withDefaults(defineProps<ModalProps>(), {
@@ -92,14 +96,22 @@ const contentEvents = computed(() => {
92
96
  if (props.preventClose) {
93
97
  return {
94
98
  pointerDownOutside: (e: Event) => e.preventDefault(),
95
- interactOutside: (e: Event) => e.preventDefault()
99
+ interactOutside: (e: Event) => e.preventDefault(),
100
+ escapeKeyDown: (e: Event) => e.preventDefault()
96
101
  }
97
102
  }
98
103
 
99
- return {}
104
+ return {
105
+ interactOutside: (e: Event) => {
106
+ if (e.target instanceof Element && e.target.closest('[data-sonner-toaster]')) {
107
+ return e.preventDefault()
108
+ }
109
+ }
110
+ }
100
111
  })
101
112
 
102
113
  const appConfig = useAppConfig()
114
+ const { t } = useLocale()
103
115
 
104
116
  const ui = computed(() => modal({
105
117
  transition: props.transition,
@@ -140,7 +152,7 @@ const ui = computed(() => modal({
140
152
  size="md"
141
153
  color="neutral"
142
154
  variant="ghost"
143
- aria-label="Close"
155
+ :aria-label="t('modal.close')"
144
156
  v-bind="typeof close === 'object' ? close : undefined"
145
157
  :class="ui.close({ class: props.ui?.close })"
146
158
  />