@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.
- package/.nuxt/ui/accordion.ts +2 -2
- package/.nuxt/ui/alert.ts +122 -95
- package/.nuxt/ui/avatar-group.ts +14 -2
- package/.nuxt/ui/avatar.ts +16 -4
- package/.nuxt/ui/badge.ts +139 -93
- package/.nuxt/ui/breadcrumb.ts +5 -5
- package/.nuxt/ui/button-group.ts +13 -0
- package/.nuxt/ui/button.ts +167 -136
- package/.nuxt/ui/calendar.ts +103 -0
- package/.nuxt/ui/card.ts +27 -1
- package/.nuxt/ui/carousel.ts +7 -5
- package/.nuxt/ui/checkbox.ts +45 -27
- package/.nuxt/ui/chip.ts +44 -15
- package/.nuxt/ui/color-picker.ts +47 -0
- package/.nuxt/ui/command-palette.ts +28 -18
- package/.nuxt/ui/container.ts +1 -1
- package/.nuxt/ui/context-menu.ts +64 -46
- package/.nuxt/ui/drawer.ts +83 -15
- package/.nuxt/ui/dropdown-menu.ts +65 -47
- package/.nuxt/ui/form-field.ts +15 -7
- package/.nuxt/ui/index.ts +5 -1
- package/.nuxt/ui/input-menu.ts +150 -116
- package/.nuxt/ui/input-number.ts +102 -71
- package/.nuxt/ui/input.ts +111 -76
- package/.nuxt/ui/kbd.ts +18 -6
- package/.nuxt/ui/link.ts +3 -3
- package/.nuxt/ui/modal.ts +8 -7
- package/.nuxt/ui/navigation-menu.ts +244 -131
- package/.nuxt/ui/pin-input.ts +77 -51
- package/.nuxt/ui/popover.ts +2 -2
- package/.nuxt/ui/progress.ts +111 -72
- package/.nuxt/ui/radio-group.ts +45 -22
- package/.nuxt/ui/select-menu.ts +122 -86
- package/.nuxt/ui/select.ts +120 -84
- package/.nuxt/ui/separator.ts +60 -31
- package/.nuxt/ui/skeleton.ts +1 -1
- package/.nuxt/ui/slideover.ts +17 -9
- package/.nuxt/ui/slider.ts +61 -38
- package/.nuxt/ui/stepper.ts +202 -0
- package/.nuxt/ui/switch.ts +40 -22
- package/.nuxt/ui/table.ts +48 -31
- package/.nuxt/ui/tabs.ts +100 -72
- package/.nuxt/ui/textarea.ts +111 -76
- package/.nuxt/ui/toast.ts +53 -33
- package/.nuxt/ui/toaster.ts +31 -15
- package/.nuxt/ui/tooltip.ts +2 -2
- package/.nuxt/ui/tree.ts +154 -0
- package/.nuxt/ui.css +92 -0
- package/README.md +19 -8
- package/cli/commands/make/component.mjs +12 -0
- package/cli/package.json +2 -2
- package/cli/templates.mjs +27 -22
- package/dist/module.cjs +9 -171
- package/dist/module.d.cts +5 -15
- package/dist/module.d.mts +5 -15
- package/dist/module.d.ts +5 -15
- package/dist/module.json +2 -2
- package/dist/module.mjs +12 -168
- package/dist/runtime/components/Accordion.vue +8 -39
- package/dist/runtime/components/Alert.vue +22 -21
- package/dist/runtime/components/App.vue +9 -15
- package/dist/runtime/components/Avatar.vue +33 -21
- package/dist/runtime/components/AvatarGroup.vue +5 -7
- package/dist/runtime/components/Badge.vue +10 -8
- package/dist/runtime/components/Breadcrumb.vue +5 -31
- package/dist/runtime/components/Button.vue +5 -8
- package/dist/runtime/components/ButtonGroup.vue +5 -7
- package/dist/runtime/components/Calendar.vue +194 -0
- package/dist/runtime/components/Card.vue +10 -9
- package/dist/runtime/components/Carousel.vue +15 -10
- package/dist/runtime/components/Checkbox.vue +32 -38
- package/dist/runtime/components/Chip.vue +6 -8
- package/dist/runtime/components/Collapsible.vue +10 -11
- package/dist/runtime/components/ColorPicker.vue +297 -0
- package/dist/runtime/components/CommandPalette.vue +111 -97
- package/dist/runtime/components/Container.vue +4 -7
- package/dist/runtime/components/ContextMenu.vue +16 -69
- package/dist/runtime/components/ContextMenuContent.vue +12 -10
- package/dist/runtime/components/Drawer.vue +23 -12
- package/dist/runtime/components/DropdownMenu.vue +18 -59
- package/dist/runtime/components/DropdownMenuContent.vue +12 -10
- package/dist/runtime/components/Form.vue +65 -34
- package/dist/runtime/components/FormField.vue +22 -13
- package/dist/runtime/components/Icon.vue +1 -1
- package/dist/runtime/components/Input.vue +15 -7
- package/dist/runtime/components/InputMenu.vue +95 -104
- package/dist/runtime/components/InputNumber.vue +14 -9
- package/dist/runtime/components/Kbd.vue +5 -6
- package/dist/runtime/components/Link.vue +5 -8
- package/dist/runtime/components/LinkBase.vue +1 -1
- package/dist/runtime/components/Modal.vue +51 -38
- package/dist/runtime/components/NavigationMenu.vue +129 -114
- package/dist/runtime/components/OverlayProvider.vue +29 -0
- package/dist/runtime/components/Pagination.vue +6 -9
- package/dist/runtime/components/PinInput.vue +10 -11
- package/dist/runtime/components/Popover.vue +16 -18
- package/dist/runtime/components/Progress.vue +28 -12
- package/dist/runtime/components/RadioGroup.vue +9 -12
- package/dist/runtime/components/Select.vue +60 -34
- package/dist/runtime/components/SelectMenu.vue +160 -165
- package/dist/runtime/components/Separator.vue +6 -5
- package/dist/runtime/components/Skeleton.vue +4 -7
- package/dist/runtime/components/Slideover.vue +51 -37
- package/dist/runtime/components/Slider.vue +8 -7
- package/dist/runtime/components/Stepper.vue +170 -0
- package/dist/runtime/components/Switch.vue +19 -23
- package/dist/runtime/components/Table.vue +146 -31
- package/dist/runtime/components/Tabs.vue +12 -31
- package/dist/runtime/components/Textarea.vue +17 -8
- package/dist/runtime/components/Toast.vue +36 -25
- package/dist/runtime/components/Toaster.vue +10 -11
- package/dist/runtime/components/Tooltip.vue +9 -12
- package/dist/runtime/components/Tree.vue +196 -0
- package/dist/runtime/composables/defineShortcuts.d.ts +1 -1
- package/dist/runtime/composables/defineShortcuts.js +2 -2
- package/dist/runtime/composables/useFormField.d.ts +7 -3
- package/dist/runtime/composables/useFormField.js +18 -8
- package/dist/runtime/composables/useLocale.js +1 -1
- package/dist/runtime/composables/useOverlay.d.ts +30 -0
- package/dist/runtime/composables/useOverlay.js +71 -0
- package/dist/runtime/composables/useToast.d.ts +4 -3
- package/dist/runtime/composables/useToast.js +20 -6
- package/dist/runtime/index.css +1 -1
- package/dist/runtime/keyframes.css +1 -1
- package/dist/runtime/locale/ar.js +9 -1
- package/dist/runtime/locale/az.js +54 -0
- package/dist/runtime/locale/bn.js +54 -0
- package/dist/runtime/locale/cs.js +9 -1
- package/dist/runtime/locale/da.d.ts +2 -0
- package/dist/runtime/locale/da.js +54 -0
- package/dist/runtime/locale/de.js +11 -3
- package/dist/runtime/locale/el.d.ts +2 -0
- package/dist/runtime/locale/el.js +54 -0
- package/dist/runtime/locale/en.js +9 -1
- package/dist/runtime/locale/es.js +9 -1
- package/dist/runtime/locale/et.d.ts +2 -0
- package/dist/runtime/locale/et.js +54 -0
- package/dist/runtime/locale/fa_ir.js +9 -1
- package/dist/runtime/locale/fi.d.ts +2 -0
- package/dist/runtime/locale/fi.js +54 -0
- package/dist/runtime/locale/fr.js +9 -1
- package/dist/runtime/locale/he.d.ts +2 -0
- package/dist/runtime/locale/he.js +53 -0
- package/dist/runtime/locale/hi.d.ts +2 -0
- package/dist/runtime/locale/hi.js +54 -0
- package/dist/runtime/locale/hu.d.ts +2 -0
- package/dist/runtime/locale/hu.js +54 -0
- package/dist/runtime/locale/id.d.ts +2 -0
- package/dist/runtime/locale/id.js +54 -0
- package/dist/runtime/locale/index.d.ts +25 -3
- package/dist/runtime/locale/index.js +25 -3
- package/dist/runtime/locale/it.js +9 -1
- package/dist/runtime/locale/ja.d.ts +2 -0
- package/dist/runtime/locale/ja.js +54 -0
- package/dist/runtime/locale/km.d.ts +2 -0
- package/dist/runtime/locale/km.js +54 -0
- package/dist/runtime/locale/ko.d.ts +2 -0
- package/dist/runtime/locale/ko.js +54 -0
- package/dist/runtime/locale/nb_no.d.ts +2 -0
- package/dist/runtime/locale/nb_no.js +54 -0
- package/dist/runtime/locale/nl.d.ts +2 -0
- package/dist/runtime/locale/nl.js +54 -0
- package/dist/runtime/locale/pl.js +9 -1
- package/dist/runtime/locale/pt.d.ts +2 -0
- package/dist/runtime/locale/pt.js +54 -0
- package/dist/runtime/locale/pt_br.d.ts +2 -0
- package/dist/runtime/locale/pt_br.js +54 -0
- package/dist/runtime/locale/ru.js +9 -1
- package/dist/runtime/locale/sk.d.ts +2 -0
- package/dist/runtime/locale/sk.js +54 -0
- package/dist/runtime/locale/sv.d.ts +2 -0
- package/dist/runtime/locale/sv.js +54 -0
- package/dist/runtime/locale/th.d.ts +2 -0
- package/dist/runtime/locale/th.js +54 -0
- package/dist/runtime/locale/tr.d.ts +2 -0
- package/dist/runtime/locale/tr.js +54 -0
- package/dist/runtime/locale/uk.d.ts +2 -0
- package/dist/runtime/locale/uk.js +54 -0
- package/dist/runtime/locale/vi.d.ts +2 -0
- package/dist/runtime/locale/vi.js +54 -0
- package/dist/runtime/locale/zh_cn.d.ts +2 -0
- package/dist/runtime/locale/{zh_hans.js → zh_cn.js} +10 -2
- package/dist/runtime/locale/zh_tw.d.ts +2 -0
- package/dist/runtime/locale/{zh_hant.js → zh_tw.js} +16 -8
- package/dist/runtime/plugins/colors.js +8 -5
- package/dist/runtime/types/form.d.ts +24 -15
- package/dist/runtime/types/form.js +2 -2
- package/dist/runtime/types/index.d.ts +5 -0
- package/dist/runtime/types/index.js +5 -0
- package/dist/runtime/types/locale.d.ts +8 -0
- package/dist/runtime/types/utils.d.ts +7 -3
- package/dist/runtime/utils/form.d.ts +4 -4
- package/dist/runtime/utils/form.js +6 -8
- package/dist/runtime/utils/index.d.ts +1 -1
- package/dist/runtime/utils/index.js +15 -2
- package/dist/runtime/utils/tv.d.ts +1 -0
- package/dist/runtime/utils/tv.js +4 -0
- package/dist/runtime/vue/components/Icon.vue +2 -2
- package/dist/runtime/vue/components/Link.vue +87 -23
- package/dist/runtime/vue/composables/useAppConfig.d.ts +1 -0
- package/dist/runtime/vue/composables/useAppConfig.js +2 -0
- package/dist/runtime/vue/plugins/head.d.ts +1 -1
- package/dist/runtime/vue/plugins/head.js +3 -3
- package/dist/runtime/vue/stubs.d.ts +19 -0
- package/dist/runtime/vue/stubs.js +37 -0
- package/dist/shared/{ui.d52SSCwA.mjs → ui.DSPu6Y8j.mjs} +905 -401
- package/dist/shared/{ui.BPnAe4QS.cjs → ui.DfK2RX6G.cjs} +908 -401
- package/dist/unplugin.cjs +62 -26
- package/dist/unplugin.d.cts +13 -1
- package/dist/unplugin.d.mts +13 -1
- package/dist/unplugin.d.ts +13 -1
- package/dist/unplugin.mjs +61 -25
- package/dist/vite.cjs +3 -2
- package/dist/vite.d.cts +2 -0
- package/dist/vite.d.mts +2 -0
- package/dist/vite.d.ts +2 -0
- package/dist/vite.mjs +3 -2
- package/package.json +103 -67
- package/dist/chunks/component-meta.cjs +0 -77249
- package/dist/chunks/component-meta.mjs +0 -77247
- package/dist/client/devtools/200.html +0 -1
- package/dist/client/devtools/404.html +0 -1
- package/dist/client/devtools/_fonts/rP2Wp2ywxg089UriCZaSExd86J3t9jz86MvyyKK58UfivUw4aw-SK92LTmKP0.woff2 +0 -0
- package/dist/client/devtools/_fonts/rP2Wp2ywxg089UriCZaSExd86J3t9jz86MvyyKy58UfivUw-9uYm8d48E4.woff2 +0 -0
- package/dist/client/devtools/_fonts/rP2Yp2ywxg089UriI5-g4vlH9VoD8Cmcqbu0-K6z9mXg-VNw7NHvqnp.woff2 +0 -0
- package/dist/client/devtools/_fonts/rP2Yp2ywxg089UriI5-g4vlH9VoD8Cmcqbu6-K6z9mXgjU0-EkYdMs3MAX.woff2 +0 -0
- package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat-7DW3w-R7n06NbhGy.woff +0 -0
- package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat-JDG3w-leGNfkTmsj.woff +0 -0
- package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat-JDW3w-ciElMF03ku.woff +0 -0
- package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat8JCm3w-sB722Uoa0f.woff +0 -0
- package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat8JDW3w-PBDI1JDjwV.woff +0 -0
- package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat8gCm3w-0q1hqVn09O.woff +0 -0
- package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat9XCm3w-ByvzuLy2fa.woff +0 -0
- package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat9uCm3w-5TKCIs6EEw.woff +0 -0
- package/dist/client/devtools/_fonts/rP2rp2ywxg089UriCZaSExd86J3t9jz86Mvy4qCRAL19DksVat_XDW3w-r2swRDGfn2.woff +0 -0
- package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAC5thTQ-ldB60l5U8r.woff +0 -0
- package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAIpthTQ-wbFKGFDrgo.woff +0 -0
- package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAIpxhTQ-LLUBv5jskj.woff +0 -0
- package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwARZthTQ-F71RdTRbNg.woff +0 -0
- package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwA_JxhTQ-6nh65gZkc0.woff +0 -0
- package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAfJthTQ-aMoWEJLmTt.woff +0 -0
- package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAkJxhTQ-WMBvmLOgKm.woff +0 -0
- package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAop1hTQ-utGC2hnWq2.woff +0 -0
- package/dist/client/devtools/_fonts/rP2tp2ywxg089UriI5-g4vlH9VoD8CmcqZG40F9JadbnoEwAopxhTQ-YpyTieVa5B.woff +0 -0
- package/dist/client/devtools/_nuxt/BDC8ywlR.js +0 -1
- package/dist/client/devtools/_nuxt/BT004Uxp.js +0 -53
- package/dist/client/devtools/_nuxt/C1H2Soz3.js +0 -1
- package/dist/client/devtools/_nuxt/builds/latest.json +0 -1
- package/dist/client/devtools/_nuxt/builds/meta/f98c13ea-d751-4b37-8ce3-94e99708745f.json +0 -1
- package/dist/client/devtools/_nuxt/entry.Bx6WJjrx.css +0 -1
- package/dist/client/devtools/_nuxt/error-404.Cq4qshcf.css +0 -1
- package/dist/client/devtools/_nuxt/error-500.ax1aM34m.css +0 -1
- package/dist/client/devtools/favicon.svg +0 -1
- package/dist/client/devtools/index.html +0 -1
- package/dist/devtools/runtime/DevtoolsRenderer.vue +0 -53
- package/dist/devtools/runtime/examples/AvatarGroupExample.vue +0 -7
- package/dist/devtools/runtime/examples/ButtonGroupExample.vue +0 -8
- package/dist/devtools/runtime/examples/CardExample.vue +0 -13
- package/dist/devtools/runtime/examples/CarouselExample.vue +0 -13
- package/dist/devtools/runtime/examples/ChipExample.vue +0 -5
- package/dist/devtools/runtime/examples/CollapsibleExample.vue +0 -8
- package/dist/devtools/runtime/examples/CommandPaletteExample.vue +0 -29
- package/dist/devtools/runtime/examples/ContainerExample.vue +0 -5
- package/dist/devtools/runtime/examples/ContextMenuExample.vue +0 -5
- package/dist/devtools/runtime/examples/DrawerExample.vue +0 -8
- package/dist/devtools/runtime/examples/DropdownMenuExample.vue +0 -5
- package/dist/devtools/runtime/examples/FormExample.vue +0 -30
- package/dist/devtools/runtime/examples/FormFieldExample.vue +0 -5
- package/dist/devtools/runtime/examples/LinkExample.vue +0 -5
- package/dist/devtools/runtime/examples/ModalExample.vue +0 -8
- package/dist/devtools/runtime/examples/PopoverExample.vue +0 -8
- package/dist/devtools/runtime/examples/SkeletonExample.vue +0 -3
- package/dist/devtools/runtime/examples/SlideoverExample.vue +0 -8
- package/dist/devtools/runtime/examples/ToasterExample.vue +0 -11
- package/dist/devtools/runtime/examples/TooltipExample.vue +0 -5
- package/dist/runtime/components/ModalProvider.vue +0 -12
- package/dist/runtime/components/SlideoverProvider.vue +0 -12
- package/dist/runtime/composables/extendDevtoolsMeta.d.ts +0 -7
- package/dist/runtime/composables/extendDevtoolsMeta.js +0 -2
- package/dist/runtime/composables/useModal.d.ts +0 -17
- package/dist/runtime/composables/useModal.js +0 -46
- package/dist/runtime/composables/useSlideover.d.ts +0 -17
- package/dist/runtime/composables/useSlideover.js +0 -46
- package/dist/runtime/plugins/modal.d.ts +0 -2
- package/dist/runtime/plugins/modal.js +0 -10
- package/dist/runtime/plugins/slideover.d.ts +0 -2
- package/dist/runtime/plugins/slideover.js +0 -10
- /package/dist/runtime/locale/{zh_hans.d.ts → az.d.ts} +0 -0
- /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 '
|
|
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 {
|
|
7
|
+
import { tv } from '../utils/tv'
|
|
9
8
|
|
|
10
|
-
const
|
|
9
|
+
const appConfigDrawer = _appConfig as AppConfig & { ui: { drawer: Partial<typeof theme> } }
|
|
11
10
|
|
|
12
|
-
const drawer = tv({ extend: tv(theme), ...(
|
|
11
|
+
const drawer = tv({ extend: tv(theme), ...(appConfigDrawer.ui?.drawer || {}) })
|
|
13
12
|
|
|
14
|
-
export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | 'closeThreshold' | 'defaultOpen' | 'direction' | '
|
|
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 '
|
|
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 {
|
|
3
|
-
import type { DropdownMenuRootProps, DropdownMenuRootEmits, DropdownMenuContentProps, DropdownMenuArrowProps } from '
|
|
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 {
|
|
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
|
|
11
|
+
const appConfigDropdownMenu = _appConfig as AppConfig & { ui: { dropdownMenu: Partial<typeof theme> } }
|
|
12
12
|
|
|
13
|
-
const dropdownMenu = tv({ extend: tv(theme), ...(
|
|
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 '
|
|
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 {
|
|
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<
|
|
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
|
|
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 '
|
|
33
|
-
import { useForwardPropsEmits } from '
|
|
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
|
-
:
|
|
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:
|
|
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 {
|
|
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
|
|
9
|
+
const appConfigForm = _appConfig as AppConfig & { ui: { form: Partial<typeof theme> } }
|
|
10
10
|
|
|
11
|
-
const form = tv({ extend: tv(theme), ...(
|
|
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 {
|
|
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
|
|
56
|
+
const bus = useEventBus<FormEvent<T>>(`form-${formId}`)
|
|
56
57
|
const parentBus = inject(
|
|
57
58
|
formBusInjectionKey,
|
|
58
59
|
undefined
|
|
@@ -60,7 +61,7 @@ const parentBus = inject(
|
|
|
60
61
|
|
|
61
62
|
provide(formBusInjectionKey, bus)
|
|
62
63
|
|
|
63
|
-
const nestedForms = ref<Map<string | number, { validate:
|
|
64
|
+
const nestedForms = ref<Map<string | number, { validate: typeof _validate }>>(new Map())
|
|
64
65
|
|
|
65
66
|
onMounted(async () => {
|
|
66
67
|
bus.on(async (event) => {
|
|
@@ -68,8 +69,24 @@ onMounted(async () => {
|
|
|
68
69
|
nestedForms.value.set(event.formId, { validate: event.validate })
|
|
69
70
|
} else if (event.type === 'detach') {
|
|
70
71
|
nestedForms.value.delete(event.formId)
|
|
71
|
-
} else if (props.validateOn?.includes(event.type
|
|
72
|
-
|
|
72
|
+
} else if (props.validateOn?.includes(event.type)) {
|
|
73
|
+
if (event.type !== 'input') {
|
|
74
|
+
await _validate({ name: event.name, silent: true, nested: false })
|
|
75
|
+
} else if (event.eager || blurredFields.has(event.name)) {
|
|
76
|
+
await _validate({ name: event.name, silent: true, nested: false })
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (event.type === 'blur') {
|
|
81
|
+
blurredFields.add(event.name)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (event.type === 'change' || event.type === 'input' || event.type === 'blur' || event.type === 'focus') {
|
|
85
|
+
touchedFields.add(event.name)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (event.type === 'change' || event.type === 'input') {
|
|
89
|
+
dirtyFields.add(event.name)
|
|
73
90
|
}
|
|
74
91
|
})
|
|
75
92
|
})
|
|
@@ -94,8 +111,12 @@ onUnmounted(() => {
|
|
|
94
111
|
const errors = ref<FormErrorWithId[]>([])
|
|
95
112
|
provide('form-errors', errors)
|
|
96
113
|
|
|
97
|
-
const inputs = ref<
|
|
98
|
-
provide(formInputsInjectionKey, inputs)
|
|
114
|
+
const inputs = ref<{ [P in keyof T]?: { id?: string, pattern?: RegExp } }>({})
|
|
115
|
+
provide(formInputsInjectionKey, inputs as any)
|
|
116
|
+
|
|
117
|
+
const dirtyFields = new Set<keyof T>()
|
|
118
|
+
const touchedFields = new Set<keyof T>()
|
|
119
|
+
const blurredFields = new Set<keyof T>()
|
|
99
120
|
|
|
100
121
|
function resolveErrorIds(errs: FormError[]): FormErrorWithId[] {
|
|
101
122
|
return errs.map(err => ({
|
|
@@ -104,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
|
|
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
|
-
|
|
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?:
|
|
123
|
-
const names = opts.name && !Array.isArray(opts.name) ? [opts.name] : opts.name as
|
|
145
|
+
async function _validate(opts: { name?: keyof T | (keyof T)[], silent?: boolean, nested?: boolean, transform?: boolean } = { silent: false, nested: true, transform: false }): Promise<T | false> {
|
|
146
|
+
const names = opts.name && !Array.isArray(opts.name) ? [opts.name] : opts.name as (keyof T)[]
|
|
124
147
|
|
|
125
148
|
const nestedValidatePromises = !names && opts.nested
|
|
126
149
|
? Array.from(nestedForms.value.values()).map(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
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?:
|
|
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?:
|
|
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 {
|
|
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 {
|
|
6
|
+
import { tv } from '../utils/tv'
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const appConfigFormField = _appConfig as AppConfig & { ui: { formField: Partial<typeof theme> } }
|
|
9
9
|
|
|
10
|
-
const formField = tv({ extend: tv(theme), ...(
|
|
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 '
|
|
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
|
-
<
|
|
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
|
-
</
|
|
122
|
+
</Primitive>
|
|
114
123
|
</template>
|