@nuxt/ui 3.0.0-alpha.11 → 3.0.0-alpha.13
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 +47 -37
- package/.nuxt/ui/avatar-group.ts +1 -1
- package/.nuxt/ui/avatar.ts +4 -4
- package/.nuxt/ui/badge.ts +37 -28
- package/.nuxt/ui/breadcrumb.ts +5 -5
- package/.nuxt/ui/button.ts +42 -43
- package/.nuxt/ui/calendar.ts +15 -15
- package/.nuxt/ui/card.ts +27 -1
- package/.nuxt/ui/carousel.ts +2 -2
- package/.nuxt/ui/checkbox.ts +18 -18
- package/.nuxt/ui/chip.ts +8 -8
- package/.nuxt/ui/color-picker.ts +2 -2
- package/.nuxt/ui/command-palette.ts +12 -12
- package/.nuxt/ui/container.ts +1 -1
- package/.nuxt/ui/context-menu.ts +32 -32
- package/.nuxt/ui/drawer.ts +72 -11
- package/.nuxt/ui/dropdown-menu.ts +33 -33
- package/.nuxt/ui/form-field.ts +6 -6
- package/.nuxt/ui/input-menu.ts +41 -41
- package/.nuxt/ui/input-number.ts +20 -20
- package/.nuxt/ui/input.ts +23 -23
- package/.nuxt/ui/kbd.ts +4 -4
- package/.nuxt/ui/link.ts +3 -3
- package/.nuxt/ui/modal.ts +5 -5
- package/.nuxt/ui/navigation-menu.ts +144 -77
- package/.nuxt/ui/pin-input.ts +20 -20
- package/.nuxt/ui/popover.ts +2 -2
- package/.nuxt/ui/progress.ts +17 -17
- package/.nuxt/ui/radio-group.ts +20 -20
- package/.nuxt/ui/select-menu.ts +33 -32
- package/.nuxt/ui/select.ts +31 -31
- package/.nuxt/ui/separator.ts +8 -8
- package/.nuxt/ui/skeleton.ts +1 -1
- package/.nuxt/ui/slideover.ts +4 -4
- package/.nuxt/ui/slider.ts +16 -16
- package/.nuxt/ui/stepper.ts +18 -18
- package/.nuxt/ui/switch.ts +20 -20
- package/.nuxt/ui/table.ts +16 -16
- package/.nuxt/ui/tabs.ts +33 -33
- package/.nuxt/ui/textarea.ts +23 -23
- package/.nuxt/ui/toast.ts +42 -32
- package/.nuxt/ui/toaster.ts +4 -4
- package/.nuxt/ui/tooltip.ts +2 -2
- package/.nuxt/ui.css +90 -0
- package/README.md +2 -2
- package/cli/package.json +2 -2
- package/cli/templates.mjs +7 -7
- package/dist/chunks/component-meta.cjs +2590 -2511
- package/dist/chunks/component-meta.mjs +2590 -2511
- package/dist/client/devtools/200.html +1 -1
- package/dist/client/devtools/404.html +1 -1
- package/dist/client/devtools/_nuxt/CG6Dc4jp.js +1 -0
- package/dist/client/devtools/_nuxt/{Dtz-o9GB.js → DrNJgD08.js} +1 -1
- package/dist/client/devtools/_nuxt/{DNZFyo26.js → ElqrUh1r.js} +1 -1
- package/dist/client/devtools/_nuxt/builds/latest.json +1 -1
- package/dist/client/devtools/_nuxt/builds/meta/255c8d0b-7281-4d74-b4b6-ec96e6721aa0.json +1 -0
- package/dist/client/devtools/_nuxt/entry.Hn8drx4g.css +1 -0
- package/dist/client/devtools/_nuxt/error-404.Dew7w9Ee.css +1 -0
- package/dist/client/devtools/_nuxt/error-500.Dep59i31.css +1 -0
- package/dist/client/devtools/_nuxt/mC2e7kOs.js +57 -0
- package/dist/client/devtools/index.html +1 -1
- package/dist/devtools/runtime/examples/CardExample.vue +3 -3
- package/dist/devtools/runtime/examples/CollapsibleExample.vue +1 -1
- package/dist/devtools/runtime/examples/ContainerExample.vue +1 -1
- package/dist/devtools/runtime/examples/ContextMenuExample.vue +1 -1
- package/dist/devtools/runtime/examples/PopoverExample.vue +1 -1
- package/dist/devtools/runtime/examples/TooltipExample.vue +1 -1
- package/dist/module.cjs +2 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +2 -1
- package/dist/runtime/components/Accordion.vue +2 -2
- package/dist/runtime/components/Alert.vue +15 -12
- package/dist/runtime/components/App.vue +3 -3
- package/dist/runtime/components/Avatar.vue +31 -10
- package/dist/runtime/components/AvatarGroup.vue +2 -2
- package/dist/runtime/components/Badge.vue +7 -3
- package/dist/runtime/components/Breadcrumb.vue +2 -2
- package/dist/runtime/components/Button.vue +2 -2
- package/dist/runtime/components/ButtonGroup.vue +2 -2
- package/dist/runtime/components/Calendar.vue +6 -3
- package/dist/runtime/components/Card.vue +8 -4
- package/dist/runtime/components/Carousel.vue +2 -2
- package/dist/runtime/components/Checkbox.vue +4 -4
- package/dist/runtime/components/Chip.vue +2 -2
- package/dist/runtime/components/Collapsible.vue +2 -2
- package/dist/runtime/components/ColorPicker.vue +46 -15
- package/dist/runtime/components/CommandPalette.vue +48 -41
- package/dist/runtime/components/Container.vue +2 -2
- package/dist/runtime/components/ContextMenu.vue +10 -2
- package/dist/runtime/components/ContextMenuContent.vue +4 -2
- package/dist/runtime/components/Drawer.vue +9 -3
- package/dist/runtime/components/DropdownMenu.vue +10 -2
- package/dist/runtime/components/DropdownMenuContent.vue +4 -2
- package/dist/runtime/components/Form.vue +46 -17
- package/dist/runtime/components/FormField.vue +12 -6
- package/dist/runtime/components/Input.vue +5 -4
- package/dist/runtime/components/InputMenu.vue +6 -5
- package/dist/runtime/components/InputNumber.vue +10 -6
- package/dist/runtime/components/Kbd.vue +2 -2
- package/dist/runtime/components/Link.vue +2 -2
- package/dist/runtime/components/Modal.vue +17 -3
- package/dist/runtime/components/NavigationMenu.vue +110 -67
- package/dist/runtime/components/Pagination.vue +2 -2
- package/dist/runtime/components/PinInput.vue +5 -4
- package/dist/runtime/components/Popover.vue +2 -2
- package/dist/runtime/components/Progress.vue +2 -2
- package/dist/runtime/components/RadioGroup.vue +4 -4
- package/dist/runtime/components/Select.vue +9 -7
- package/dist/runtime/components/SelectMenu.vue +67 -64
- package/dist/runtime/components/Separator.vue +2 -2
- package/dist/runtime/components/Skeleton.vue +2 -2
- package/dist/runtime/components/Slideover.vue +17 -3
- package/dist/runtime/components/Slider.vue +4 -4
- package/dist/runtime/components/Stepper.vue +3 -3
- package/dist/runtime/components/Switch.vue +4 -4
- package/dist/runtime/components/Table.vue +96 -18
- package/dist/runtime/components/Tabs.vue +2 -2
- package/dist/runtime/components/Textarea.vue +7 -5
- package/dist/runtime/components/Toast.vue +28 -16
- package/dist/runtime/components/Toaster.vue +4 -4
- package/dist/runtime/components/Tooltip.vue +2 -2
- package/dist/runtime/composables/useFormField.d.ts +6 -2
- package/dist/runtime/composables/useFormField.js +18 -11
- 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/locale/az.js +54 -0
- package/dist/runtime/locale/bn.js +54 -0
- package/dist/runtime/locale/es.js +2 -2
- 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/index.d.ts +8 -2
- package/dist/runtime/locale/index.js +8 -2
- package/dist/runtime/locale/km.d.ts +2 -0
- package/dist/runtime/locale/km.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/zh_cn.d.ts +2 -0
- package/dist/runtime/locale/{zh_hans.js → zh_cn.js} +1 -1
- package/dist/runtime/locale/zh_tw.d.ts +2 -0
- package/dist/runtime/locale/{zh_hant.js → zh_tw.js} +1 -1
- package/dist/runtime/plugins/colors.js +8 -5
- package/dist/runtime/types/form.d.ts +21 -12
- package/dist/runtime/types/index.d.ts +1 -0
- package/dist/runtime/types/index.js +1 -0
- package/dist/runtime/types/utils.d.ts +5 -0
- package/dist/runtime/utils/form.js +3 -5
- package/dist/runtime/utils/tv.js +2 -2
- package/dist/runtime/vue/components/Link.vue +77 -18
- package/dist/shared/{ui.DCjJwb6l.cjs → ui.C5FzRWM5.cjs} +529 -366
- package/dist/shared/{ui.BKsd22-Q.mjs → ui.DCZM2pl8.mjs} +525 -366
- package/dist/unplugin.cjs +44 -20
- package/dist/unplugin.d.cts +10 -0
- package/dist/unplugin.d.mts +10 -0
- package/dist/unplugin.d.ts +10 -0
- package/dist/unplugin.mjs +43 -19
- 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 +67 -41
- package/dist/client/devtools/_nuxt/CQ-XZFu7.js +0 -54
- package/dist/client/devtools/_nuxt/builds/meta/1f5fbd7b-9d97-480e-9ae4-e02b4ff7018b.json +0 -1
- package/dist/client/devtools/_nuxt/entry.qOdop8tq.css +0 -1
- package/dist/client/devtools/_nuxt/error-404.D-rS9BDk.css +0 -1
- package/dist/client/devtools/_nuxt/error-500.CfcCHs91.css +0 -1
- /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
|
@@ -5,10 +5,11 @@ import type { AppConfig } from '@nuxt/schema'
|
|
|
5
5
|
import _appConfig from '#build/app.config'
|
|
6
6
|
import theme from '#build/ui/color-picker'
|
|
7
7
|
import { tv } from '../utils/tv'
|
|
8
|
+
import type { HSLObject } from 'colortranslator'
|
|
8
9
|
|
|
9
|
-
const
|
|
10
|
+
const appConfigColorPicker = _appConfig as AppConfig & { ui: { colorPicker: Partial<typeof theme> } }
|
|
10
11
|
|
|
11
|
-
const colorPicker = tv({ extend: tv(theme), ...(
|
|
12
|
+
const colorPicker = tv({ extend: tv(theme), ...(appConfigColorPicker.ui?.colorPicker || {}) })
|
|
12
13
|
|
|
13
14
|
type ColorPickerVariants = VariantProps<typeof colorPicker>
|
|
14
15
|
|
|
@@ -18,6 +19,27 @@ type HSVColor = {
|
|
|
18
19
|
v: number
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
function HSLtoHSV(hsl: HSLObject): HSVColor {
|
|
23
|
+
const x = hsl.S * (hsl.L < 50 ? hsl.L : 100 - hsl.L)
|
|
24
|
+
const v = hsl.L + (x / 100)
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
h: hsl.H,
|
|
28
|
+
s: hsl.L === 0 ? hsl.S : 2 * x / v,
|
|
29
|
+
v
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function HSVtoHSL(hsv: HSVColor): HSLObject {
|
|
34
|
+
const x = (200 - hsv.s) * hsv.v / 100
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
H: hsv.h,
|
|
38
|
+
S: x === 0 || x === 200 ? 0 : Math.round(hsv.s * hsv.v / (x <= 100 ? x : 200 - x)),
|
|
39
|
+
L: Math.round(x / 2)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
21
43
|
export type ColorPickerProps = {
|
|
22
44
|
/**
|
|
23
45
|
* The element or component this component should render as.
|
|
@@ -40,7 +62,7 @@ export type ColorPickerProps = {
|
|
|
40
62
|
* Format of the color
|
|
41
63
|
* @defaultValue 'hex'
|
|
42
64
|
*/
|
|
43
|
-
format?: 'hex' | 'rgb' | 'hsl' | '
|
|
65
|
+
format?: 'hex' | 'rgb' | 'hsl' | 'cmyk' | 'lab'
|
|
44
66
|
size?: ColorPickerVariants['size']
|
|
45
67
|
class?: any
|
|
46
68
|
ui?: Partial<typeof colorPicker.slots>
|
|
@@ -52,7 +74,7 @@ import { ref, nextTick, computed, toValue } from 'vue'
|
|
|
52
74
|
import { Primitive } from 'reka-ui'
|
|
53
75
|
import { useEventListener, useElementBounding, watchThrottled, watchPausable } from '@vueuse/core'
|
|
54
76
|
import { isClient } from '@vueuse/shared'
|
|
55
|
-
import
|
|
77
|
+
import { ColorTranslator } from 'colortranslator'
|
|
56
78
|
|
|
57
79
|
const props = withDefaults(defineProps<ColorPickerProps>(), {
|
|
58
80
|
format: 'hex',
|
|
@@ -64,28 +86,37 @@ const modelValue = defineModel<string>(undefined)
|
|
|
64
86
|
const pickedColor = computed<HSVColor>({
|
|
65
87
|
get() {
|
|
66
88
|
try {
|
|
67
|
-
const color =
|
|
68
|
-
|
|
89
|
+
const color = new ColorTranslator(modelValue.value || props.defaultValue)
|
|
90
|
+
|
|
91
|
+
return HSLtoHSV(color.HSLObject)
|
|
69
92
|
} catch (_) {
|
|
70
93
|
return { h: 0, s: 0, v: 100 }
|
|
71
94
|
}
|
|
72
95
|
},
|
|
73
96
|
set(value) {
|
|
74
|
-
const color =
|
|
97
|
+
const color = new ColorTranslator(HSVtoHSL(value), {
|
|
98
|
+
decimals: 2,
|
|
99
|
+
labUnit: 'percent',
|
|
100
|
+
cmykUnit: 'percent',
|
|
101
|
+
cmykFunction: 'cmyk'
|
|
102
|
+
})
|
|
75
103
|
|
|
76
104
|
switch (props.format) {
|
|
77
105
|
case 'rgb':
|
|
78
|
-
modelValue.value = color.
|
|
106
|
+
modelValue.value = color.RGB
|
|
79
107
|
break
|
|
80
108
|
case 'hsl':
|
|
81
|
-
modelValue.value = color.
|
|
109
|
+
modelValue.value = color.HSL
|
|
110
|
+
break
|
|
111
|
+
case 'cmyk':
|
|
112
|
+
modelValue.value = color.CMYK
|
|
82
113
|
break
|
|
83
|
-
case '
|
|
84
|
-
modelValue.value = color.
|
|
114
|
+
case 'lab':
|
|
115
|
+
modelValue.value = color.CIELab
|
|
85
116
|
break
|
|
86
117
|
case 'hex':
|
|
87
118
|
default:
|
|
88
|
-
modelValue.value = color.
|
|
119
|
+
modelValue.value = color.HEX
|
|
89
120
|
}
|
|
90
121
|
}
|
|
91
122
|
})
|
|
@@ -202,18 +233,18 @@ watchThrottled([selectorThumbPosition, trackThumbPosition], () => {
|
|
|
202
233
|
nextTick(resumeWatchColor)
|
|
203
234
|
}, { throttle: () => props.throttle })
|
|
204
235
|
|
|
205
|
-
const trackThumbColor = computed(() =>
|
|
236
|
+
const trackThumbColor = computed(() => new ColorTranslator(HSVtoHSL({
|
|
206
237
|
h: normalizeHue(trackThumbPosition.value.y),
|
|
207
238
|
s: 100,
|
|
208
239
|
v: 100
|
|
209
|
-
}).
|
|
240
|
+
})).HEX)
|
|
210
241
|
|
|
211
242
|
const selectorStyle = computed(() => ({
|
|
212
243
|
backgroundColor: trackThumbColor.value
|
|
213
244
|
}))
|
|
214
245
|
|
|
215
246
|
const selectorThumbStyle = computed(() => ({
|
|
216
|
-
backgroundColor:
|
|
247
|
+
backgroundColor: new ColorTranslator(modelValue.value || props.defaultValue).HEX,
|
|
217
248
|
left: `${selectorThumbPosition.value.x}%`,
|
|
218
249
|
top: `${selectorThumbPosition.value.y}%`
|
|
219
250
|
}))
|
|
@@ -8,14 +8,14 @@ import theme from '#build/ui/command-palette'
|
|
|
8
8
|
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
|
9
9
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
10
10
|
import { tv } from '../utils/tv'
|
|
11
|
-
import type { AvatarProps, ButtonProps, ChipProps, KbdProps, InputProps } from '../types'
|
|
11
|
+
import type { AvatarProps, ButtonProps, ChipProps, KbdProps, InputProps, LinkProps } from '../types'
|
|
12
12
|
import type { DynamicSlots, PartialString } from '../types/utils'
|
|
13
13
|
|
|
14
|
-
const
|
|
14
|
+
const appConfigCommandPalette = _appConfig as AppConfig & { ui: { commandPalette: Partial<typeof theme> } }
|
|
15
15
|
|
|
16
|
-
const commandPalette = tv({ extend: tv(theme), ...(
|
|
16
|
+
const commandPalette = tv({ extend: tv(theme), ...(appConfigCommandPalette.ui?.commandPalette || {}) })
|
|
17
17
|
|
|
18
|
-
export interface CommandPaletteItem {
|
|
18
|
+
export interface CommandPaletteItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'> {
|
|
19
19
|
prefix?: string
|
|
20
20
|
label?: string
|
|
21
21
|
suffix?: string
|
|
@@ -136,12 +136,15 @@ import { useAppConfig } from '#imports'
|
|
|
136
136
|
import { useLocale } from '../composables/useLocale'
|
|
137
137
|
import { omit, get } from '../utils'
|
|
138
138
|
import { highlight } from '../utils/fuse'
|
|
139
|
+
import { pickLinkProps } from '../utils/link'
|
|
139
140
|
import UIcon from './Icon.vue'
|
|
140
141
|
import UAvatar from './Avatar.vue'
|
|
141
142
|
import UButton from './Button.vue'
|
|
142
143
|
import UChip from './Chip.vue'
|
|
143
|
-
import
|
|
144
|
+
import ULinkBase from './LinkBase.vue'
|
|
145
|
+
import ULink from './Link.vue'
|
|
144
146
|
import UInput from './Input.vue'
|
|
147
|
+
import UKbd from './Kbd.vue'
|
|
145
148
|
|
|
146
149
|
const props = withDefaults(defineProps<CommandPaletteProps<G, T>>(), {
|
|
147
150
|
modelValue: '',
|
|
@@ -281,47 +284,51 @@ const groups = computed(() => {
|
|
|
281
284
|
:key="`group-${groupIndex}-${index}`"
|
|
282
285
|
:value="omit(item, ['matches' as any, 'group' as any, 'onSelect', 'labelHtml', 'suffixHtml'])"
|
|
283
286
|
:disabled="item.disabled"
|
|
284
|
-
|
|
287
|
+
as-child
|
|
285
288
|
@select="item.onSelect"
|
|
286
289
|
>
|
|
287
|
-
<slot
|
|
288
|
-
<
|
|
289
|
-
<
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
<span v-if="item.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
290
|
+
<ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item)" custom>
|
|
291
|
+
<ULinkBase v-bind="slotProps" :class="ui.item({ class: props.ui?.item, active: active || item.active })">
|
|
292
|
+
<slot :name="item.slot || group.slot || 'item'" :item="item" :index="index">
|
|
293
|
+
<slot :name="item.slot ? `${item.slot}-leading` : group.slot ? `${group.slot}-leading` : `item-leading`" :item="item" :index="index">
|
|
294
|
+
<UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon, loading: true })" />
|
|
295
|
+
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon, active: active || item.active })" />
|
|
296
|
+
<UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar, active: active || item.active })" />
|
|
297
|
+
<UChip
|
|
298
|
+
v-else-if="item.chip"
|
|
299
|
+
:size="((props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()) as ChipProps['size'])"
|
|
300
|
+
inset
|
|
301
|
+
standalone
|
|
302
|
+
v-bind="item.chip"
|
|
303
|
+
:class="ui.itemLeadingChip({ class: props.ui?.itemLeadingChip, active: active || item.active })"
|
|
304
|
+
/>
|
|
305
|
+
</slot>
|
|
306
|
+
|
|
307
|
+
<span v-if="item.labelHtml || get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`]" :class="ui.itemLabel({ class: props.ui?.itemLabel, active: active || item.active })">
|
|
308
|
+
<slot :name="item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`" :item="item" :index="index">
|
|
309
|
+
<span v-if="item.prefix" :class="ui.itemLabelPrefix({ class: props.ui?.itemLabelPrefix })">{{ item.prefix }}</span>
|
|
310
|
+
|
|
311
|
+
<span :class="ui.itemLabelBase({ class: props.ui?.itemLabelBase, active: active || item.active })" v-html="item.labelHtml || get(item, props.labelKey as string)" />
|
|
312
|
+
|
|
313
|
+
<span :class="ui.itemLabelSuffix({ class: props.ui?.itemLabelSuffix, active: active || item.active })" v-html="item.suffixHtml || item.suffix" />
|
|
314
|
+
</slot>
|
|
315
|
+
</span>
|
|
311
316
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
317
|
+
<span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
|
|
318
|
+
<slot :name="item.slot ? `${item.slot}-trailing` : group.slot ? `${group.slot}-trailing` : `item-trailing`" :item="item" :index="index">
|
|
319
|
+
<span v-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: props.ui?.itemTrailingKbds })">
|
|
320
|
+
<UKbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" :size="((props.ui?.itemTrailingKbdsSize || ui.itemTrailingKbdsSize()) as KbdProps['size'])" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" />
|
|
321
|
+
</span>
|
|
322
|
+
<UIcon v-else-if="group.highlightedIcon" :name="group.highlightedIcon" :class="ui.itemTrailingHighlightedIcon({ class: props.ui?.itemTrailingHighlightedIcon })" />
|
|
323
|
+
</slot>
|
|
324
|
+
|
|
325
|
+
<ListboxItemIndicator as-child>
|
|
326
|
+
<UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
|
|
327
|
+
</ListboxItemIndicator>
|
|
316
328
|
</span>
|
|
317
|
-
<UIcon v-else-if="group.highlightedIcon" :name="group.highlightedIcon" :class="ui.itemTrailingHighlightedIcon({ class: props.ui?.itemTrailingHighlightedIcon })" />
|
|
318
329
|
</slot>
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
<UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
|
|
322
|
-
</ListboxItemIndicator>
|
|
323
|
-
</span>
|
|
324
|
-
</slot>
|
|
330
|
+
</ULinkBase>
|
|
331
|
+
</ULink>
|
|
325
332
|
</ListboxItem>
|
|
326
333
|
</ListboxGroup>
|
|
327
334
|
</div>
|
|
@@ -5,9 +5,9 @@ import theme from '#build/ui/container'
|
|
|
5
5
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
6
6
|
import { tv } from '../utils/tv'
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const appConfigContainer = _appConfig as AppConfig & { ui: { container: Partial<typeof theme> } }
|
|
9
9
|
|
|
10
|
-
const container = tv({ extend: tv(theme), ...(
|
|
10
|
+
const container = tv({ extend: tv(theme), ...(appConfigContainer.ui?.container || {}) })
|
|
11
11
|
|
|
12
12
|
export interface ContainerProps {
|
|
13
13
|
/**
|
|
@@ -9,9 +9,9 @@ import { tv } from '../utils/tv'
|
|
|
9
9
|
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
|
10
10
|
import type { DynamicSlots, PartialString } from '../types/utils'
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const appConfigContextMenu = _appConfig as AppConfig & { ui: { contextMenu: Partial<typeof theme> } }
|
|
13
13
|
|
|
14
|
-
const contextMenu = tv({ extend: tv(theme), ...(
|
|
14
|
+
const contextMenu = tv({ extend: tv(theme), ...(appConfigContextMenu.ui?.contextMenu || {}) })
|
|
15
15
|
|
|
16
16
|
type ContextMenuVariants = VariantProps<typeof contextMenu>
|
|
17
17
|
|
|
@@ -51,6 +51,12 @@ export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'> {
|
|
|
51
51
|
* @defaultValue appConfig.ui.icons.loading
|
|
52
52
|
*/
|
|
53
53
|
loadingIcon?: string
|
|
54
|
+
/**
|
|
55
|
+
* The icon displayed when the item is an external link.
|
|
56
|
+
* Set to `false` to hide the external icon.
|
|
57
|
+
* @defaultValue appConfig.ui.icons.external
|
|
58
|
+
*/
|
|
59
|
+
externalIcon?: boolean | string
|
|
54
60
|
/** The content of the menu. */
|
|
55
61
|
content?: Omit<ContextMenuContentProps, 'as' | 'asChild' | 'forceMount'>
|
|
56
62
|
/**
|
|
@@ -152,6 +158,7 @@ import UContextMenuContent from './ContextMenuContent.vue'
|
|
|
152
158
|
const props = withDefaults(defineProps<ContextMenuProps<T>>(), {
|
|
153
159
|
portal: true,
|
|
154
160
|
modal: true,
|
|
161
|
+
externalIcon: true,
|
|
155
162
|
labelKey: 'label'
|
|
156
163
|
})
|
|
157
164
|
const emits = defineEmits<ContextMenuEmits>()
|
|
@@ -182,6 +189,7 @@ const ui = computed(() => contextMenu({
|
|
|
182
189
|
:label-key="labelKey"
|
|
183
190
|
:checked-icon="checkedIcon"
|
|
184
191
|
:loading-icon="loadingIcon"
|
|
192
|
+
:external-icon="externalIcon"
|
|
185
193
|
>
|
|
186
194
|
<template v-for="(_, name) in proxySlots" #[name]="slotData: any">
|
|
187
195
|
<slot :name="name" v-bind="slotData" />
|
|
@@ -13,6 +13,7 @@ interface ContextMenuContentProps<T> extends Omit<RekaContextMenuContentProps, '
|
|
|
13
13
|
labelKey: string
|
|
14
14
|
checkedIcon?: string
|
|
15
15
|
loadingIcon?: string
|
|
16
|
+
externalIcon?: boolean | string
|
|
16
17
|
class?: any
|
|
17
18
|
ui: typeof _contextMenu
|
|
18
19
|
uiOverride?: any
|
|
@@ -42,7 +43,7 @@ const emits = defineEmits<ContextMenuContentEmits>()
|
|
|
42
43
|
const slots = defineSlots<ContextMenuSlots<T>>()
|
|
43
44
|
|
|
44
45
|
const appConfig = useAppConfig()
|
|
45
|
-
const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'class', 'ui'), emits)
|
|
46
|
+
const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'labelKey', 'checkedIcon', 'loadingIcon', 'externalIcon', 'class', 'ui', 'uiOverride'), emits)
|
|
46
47
|
const proxySlots = omit(slots, ['default']) as Record<string, ContextMenuSlots<T>[string]>
|
|
47
48
|
|
|
48
49
|
const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: ContextMenuItem, active?: boolean, index: number }>()
|
|
@@ -64,7 +65,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
64
65
|
{{ get(item, props.labelKey as string) }}
|
|
65
66
|
</slot>
|
|
66
67
|
|
|
67
|
-
<UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.itemLabelExternalIcon({ class: uiOverride?.itemLabelExternalIcon, color: item?.color, active })" />
|
|
68
|
+
<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 })" />
|
|
68
69
|
</span>
|
|
69
70
|
|
|
70
71
|
<span :class="ui.itemTrailing({ class: uiOverride?.itemTrailing })">
|
|
@@ -112,6 +113,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
112
113
|
:label-key="labelKey"
|
|
113
114
|
:checked-icon="checkedIcon"
|
|
114
115
|
:loading-icon="loadingIcon"
|
|
116
|
+
:external-icon="externalIcon"
|
|
115
117
|
v-bind="item.content"
|
|
116
118
|
>
|
|
117
119
|
<template v-for="(_, name) in proxySlots" #[name]="slotData: any">
|
|
@@ -7,9 +7,9 @@ import theme from '#build/ui/drawer'
|
|
|
7
7
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
8
8
|
import { tv } from '../utils/tv'
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const appConfigDrawer = _appConfig as AppConfig & { ui: { drawer: Partial<typeof theme> } }
|
|
11
11
|
|
|
12
|
-
const drawer = tv({ extend: tv(theme), ...(
|
|
12
|
+
const drawer = tv({ extend: tv(theme), ...(appConfigDrawer.ui?.drawer || {}) })
|
|
13
13
|
|
|
14
14
|
export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | 'closeThreshold' | 'defaultOpen' | 'direction' | 'fadeFromIndex' | 'fixed' | 'modal' | 'nested' | 'direction' | 'open' | 'scrollLockTimeout' | 'shouldScaleBackground' | 'snapPoints'> {
|
|
15
15
|
/**
|
|
@@ -19,6 +19,11 @@ export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | '
|
|
|
19
19
|
as?: any
|
|
20
20
|
title?: string
|
|
21
21
|
description?: string
|
|
22
|
+
/**
|
|
23
|
+
* Whether to inset the drawer from the edges.
|
|
24
|
+
* @defaultValue false
|
|
25
|
+
*/
|
|
26
|
+
inset?: boolean
|
|
22
27
|
/** The content of the drawer. */
|
|
23
28
|
content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'>
|
|
24
29
|
/**
|
|
@@ -80,7 +85,8 @@ const rootProps = useForwardPropsEmits(reactivePick(props, 'activeSnapPoint', 'c
|
|
|
80
85
|
const contentProps = toRef(() => props.content)
|
|
81
86
|
|
|
82
87
|
const ui = computed(() => drawer({
|
|
83
|
-
direction: props.direction
|
|
88
|
+
direction: props.direction,
|
|
89
|
+
inset: props.inset
|
|
84
90
|
}))
|
|
85
91
|
</script>
|
|
86
92
|
|
|
@@ -9,9 +9,9 @@ import { tv } from '../utils/tv'
|
|
|
9
9
|
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
|
10
10
|
import type { DynamicSlots, PartialString } from '../types/utils'
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const appConfigDropdownMenu = _appConfig as AppConfig & { ui: { dropdownMenu: Partial<typeof theme> } }
|
|
13
13
|
|
|
14
|
-
const dropdownMenu = tv({ extend: tv(theme), ...(
|
|
14
|
+
const dropdownMenu = tv({ extend: tv(theme), ...(appConfigDropdownMenu.ui?.dropdownMenu || {}) })
|
|
15
15
|
|
|
16
16
|
type DropdownMenuVariants = VariantProps<typeof dropdownMenu>
|
|
17
17
|
|
|
@@ -51,6 +51,12 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
|
|
|
51
51
|
* @defaultValue appConfig.ui.icons.loading
|
|
52
52
|
*/
|
|
53
53
|
loadingIcon?: string
|
|
54
|
+
/**
|
|
55
|
+
* The icon displayed when the item is an external link.
|
|
56
|
+
* Set to `false` to hide the external icon.
|
|
57
|
+
* @defaultValue appConfig.ui.icons.external
|
|
58
|
+
*/
|
|
59
|
+
externalIcon?: boolean | string
|
|
54
60
|
/**
|
|
55
61
|
* The content of the menu.
|
|
56
62
|
* @defaultValue { side: 'bottom', sideOffset: 8, collisionPadding: 8 }
|
|
@@ -149,6 +155,7 @@ import UDropdownMenuContent from './DropdownMenuContent.vue'
|
|
|
149
155
|
const props = withDefaults(defineProps<DropdownMenuProps<T>>(), {
|
|
150
156
|
portal: true,
|
|
151
157
|
modal: true,
|
|
158
|
+
externalIcon: true,
|
|
152
159
|
labelKey: 'label'
|
|
153
160
|
})
|
|
154
161
|
const emits = defineEmits<DropdownMenuEmits>()
|
|
@@ -180,6 +187,7 @@ const ui = computed(() => dropdownMenu({
|
|
|
180
187
|
:label-key="labelKey"
|
|
181
188
|
:checked-icon="checkedIcon"
|
|
182
189
|
:loading-icon="loadingIcon"
|
|
190
|
+
:external-icon="externalIcon"
|
|
183
191
|
>
|
|
184
192
|
<template v-for="(_, name) in proxySlots" #[name]="slotData: any">
|
|
185
193
|
<slot :name="name" v-bind="slotData" />
|
|
@@ -14,6 +14,7 @@ interface DropdownMenuContentProps<T> extends Omit<RekaDropdownMenuContentProps,
|
|
|
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
|
|
@@ -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">
|
|
@@ -5,10 +5,11 @@ import theme from '#build/ui/form'
|
|
|
5
5
|
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
|
6
6
|
import { tv } from '../utils/tv'
|
|
7
7
|
import type { FormSchema, FormError, FormInputEvents, FormErrorEvent, FormSubmitEvent, FormEvent, Form, FormErrorWithId } from '../types/form'
|
|
8
|
+
import type { DeepReadonly } from 'vue'
|
|
8
9
|
|
|
9
|
-
const
|
|
10
|
+
const appConfigForm = _appConfig as AppConfig & { ui: { form: Partial<typeof theme> } }
|
|
10
11
|
|
|
11
|
-
const form = tv({ extend: tv(theme), ...(
|
|
12
|
+
const form = tv({ extend: tv(theme), ...(appConfigForm.ui?.form || {}) })
|
|
12
13
|
|
|
13
14
|
export interface FormProps<T extends object> {
|
|
14
15
|
id?: string | number
|
|
@@ -52,7 +53,7 @@ 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 => ({
|
|
@@ -121,12 +142,12 @@ async function getErrors(): Promise<FormErrorWithId[]> {
|
|
|
121
142
|
return resolveErrorIds(errs)
|
|
122
143
|
}
|
|
123
144
|
|
|
124
|
-
async function _validate(opts: { name?:
|
|
125
|
-
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)[]
|
|
126
147
|
|
|
127
148
|
const nestedValidatePromises = !names && opts.nested
|
|
128
149
|
? Array.from(nestedForms.value.values()).map(
|
|
129
|
-
({ validate }) => validate().then(() => undefined).catch((error: Error) => {
|
|
150
|
+
({ validate }) => validate(opts).then(() => undefined).catch((error: Error) => {
|
|
130
151
|
if (!(error instanceof FormValidationException)) {
|
|
131
152
|
throw error
|
|
132
153
|
}
|
|
@@ -151,13 +172,17 @@ async function _validate(opts: { name?: string | string[], silent?: boolean, nes
|
|
|
151
172
|
errors.value = await getErrors()
|
|
152
173
|
}
|
|
153
174
|
|
|
154
|
-
const childErrors = (await Promise.all(nestedValidatePromises)).filter(val => val)
|
|
175
|
+
const childErrors = (await Promise.all(nestedValidatePromises)).filter(val => val !== undefined)
|
|
155
176
|
|
|
156
177
|
if (errors.value.length + childErrors.length > 0) {
|
|
157
178
|
if (opts.silent) return false
|
|
158
179
|
throw new FormValidationException(formId, errors.value, childErrors)
|
|
159
180
|
}
|
|
160
181
|
|
|
182
|
+
if (opts.transform) {
|
|
183
|
+
Object.assign(props.state, transformedState.value)
|
|
184
|
+
}
|
|
185
|
+
|
|
161
186
|
return props.state as T
|
|
162
187
|
}
|
|
163
188
|
|
|
@@ -170,8 +195,7 @@ async function onSubmitWrapper(payload: Event) {
|
|
|
170
195
|
const event = payload as FormSubmitEvent<any>
|
|
171
196
|
|
|
172
197
|
try {
|
|
173
|
-
await _validate({ nested: true })
|
|
174
|
-
event.data = props.schema ? transformedState.value : props.state
|
|
198
|
+
event.data = await _validate({ nested: true, transform: true })
|
|
175
199
|
await props.onSubmit?.(event)
|
|
176
200
|
} catch (error) {
|
|
177
201
|
if (!(error instanceof FormValidationException)) {
|
|
@@ -200,7 +224,7 @@ defineExpose<Form<T>>({
|
|
|
200
224
|
validate: _validate,
|
|
201
225
|
errors,
|
|
202
226
|
|
|
203
|
-
setErrors(errs: FormError[], name?:
|
|
227
|
+
setErrors(errs: FormError[], name?: keyof T) {
|
|
204
228
|
if (name) {
|
|
205
229
|
errors.value = errors.value
|
|
206
230
|
.filter(error => error.name !== name)
|
|
@@ -214,7 +238,7 @@ defineExpose<Form<T>>({
|
|
|
214
238
|
await onSubmitWrapper(new Event('submit'))
|
|
215
239
|
},
|
|
216
240
|
|
|
217
|
-
getErrors(name?:
|
|
241
|
+
getErrors(name?: keyof T) {
|
|
218
242
|
if (name) {
|
|
219
243
|
return errors.value.filter(err => err.name === name)
|
|
220
244
|
}
|
|
@@ -229,7 +253,12 @@ defineExpose<Form<T>>({
|
|
|
229
253
|
}
|
|
230
254
|
},
|
|
231
255
|
|
|
232
|
-
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>>
|
|
233
262
|
})
|
|
234
263
|
</script>
|
|
235
264
|
|