Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/vue-controlled-vmodel-emits.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@ark-ui/vue': patch
---

Fixed missing `v-model` emit wiring for `Drawer` (`open`, `snapPoint`), `Listbox` (`highlightedValue`), `Marquee`
(`paused`), `DateInput` (`placeholderValue`), and `triggerValue` on Dialog, Drawer, HoverCard, Menu, Popover, and
Tooltip.
5 changes: 5 additions & 0 deletions .changeset/vue-drawer-snap-point-null.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ark-ui/vue": patch
---

Fixed `Drawer.Root` rejecting `null` for `snapPoint` and `defaultSnapPoint`.
5 changes: 5 additions & 0 deletions .changeset/vue-file-upload-accepted-files.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ark-ui/vue': patch
---

Fixed `FileUpload.Root` ignoring the controlled `acceptedFiles` prop.
5 changes: 5 additions & 0 deletions .changeset/vue-signature-pad-paths.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ark-ui/vue": patch
---

Fixed `SignaturePad.Root` ignoring the controlled `paths` prop.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- name: Install
uses: ./.github/composite-actions/install

- uses: actions/setup-node@v6
- uses: actions/setup-node@v7
with:
node-version: 24
registry-url: 'https://registry.npmjs.org'
Expand Down
452 changes: 177 additions & 275 deletions bun.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { SignaturePad } from '@ark-ui/react/signature-pad'
import { RotateCcwIcon } from 'lucide-react'
import { useState } from 'react'
import button from 'styles/button.module.css'
import styles from 'styles/signature-pad.module.css'

export const Controlled = () => {
const [paths, setPaths] = useState<string[]>([])

return (
<div className="stack">
<output>paths: {paths.length}</output>
<SignaturePad.Root className={styles.Root} paths={paths} onDraw={(details) => setPaths(details.paths)}>
<SignaturePad.Label className={styles.Label}>Sign below</SignaturePad.Label>
<SignaturePad.Control className={styles.Control}>
<SignaturePad.Segment className={styles.Segment} />
<SignaturePad.ClearTrigger className={styles.ClearTrigger}>
<RotateCcwIcon />
</SignaturePad.ClearTrigger>
<SignaturePad.Guide className={styles.Guide} />
</SignaturePad.Control>
</SignaturePad.Root>
<button className={button.Root} onClick={() => setPaths([])}>
Clear
</button>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const meta: Meta = {
export default meta

export { Basic } from './examples/basic.tsx'
export { Controlled } from './examples/controlled.tsx'
export { ImagePreview } from './examples/image-preview.tsx'
export { RootProvider } from './examples/root-provider.tsx'
export { WithField } from './examples/with-field.tsx'
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { SignaturePad } from '@ark-ui/solid/signature-pad'
import { RotateCcwIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/signature-pad.module.css'

export const Controlled = () => {
const [paths, setPaths] = createSignal<string[]>([])

return (
<div class="stack">
<output>paths: {paths().length}</output>
<SignaturePad.Root class={styles.Root} paths={paths()} onDraw={(details) => setPaths(details.paths)}>
<SignaturePad.Label class={styles.Label}>Sign below</SignaturePad.Label>
<SignaturePad.Control class={styles.Control}>
<SignaturePad.Segment class={styles.Segment} />
<SignaturePad.ClearTrigger class={styles.ClearTrigger}>
<RotateCcwIcon />
</SignaturePad.ClearTrigger>
<SignaturePad.Guide class={styles.Guide} />
</SignaturePad.Control>
</SignaturePad.Root>
<button class={button.Root} onClick={() => setPaths([])}>
Clear
</button>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const meta: Meta = {
export default meta

export { Basic } from './examples/basic.tsx'
export { Controlled } from './examples/controlled.tsx'
export { ImagePreview } from './examples/image-preview.tsx'
export { RootProvider } from './examples/root-provider.tsx'
export { WithField } from './examples/with-field.tsx'
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script lang="ts">
import { SignaturePad } from '@ark-ui/svelte/signature-pad'
import RotateCcwIcon from 'lucide-svelte/icons/rotate-ccw'
import button from 'styles/button.module.css'
import styles from 'styles/signature-pad.module.css'

let paths = $state<string[]>([])
</script>

<div class="stack">
<output>paths: {paths.length}</output>
<SignaturePad.Root class={styles.Root} bind:paths>
<SignaturePad.Label class={styles.Label}>Sign below</SignaturePad.Label>
<SignaturePad.Control class={styles.Control}>
<SignaturePad.Segment class={styles.Segment} />
<SignaturePad.ClearTrigger class={styles.ClearTrigger}>
<RotateCcwIcon />
</SignaturePad.ClearTrigger>
<SignaturePad.Guide class={styles.Guide} />
</SignaturePad.Control>
</SignaturePad.Root>
<button class={button.Root} onclick={() => (paths = [])}>Clear</button>
</div>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Meta } from '@storybook/svelte'
import BasicExample from './examples/basic.svelte'
import ControlledExample from './examples/controlled.svelte'
import RootProviderExample from './examples/root-provider.svelte'
import WithContextExample from './examples/with-context.svelte'

Expand All @@ -15,6 +16,12 @@ export const Basic = {
}),
}

export const Controlled = {
render: () => ({
Component: ControlledExample,
}),
}

export const RootProvider = {
render: () => ({
Component: RootProviderExample,
Expand Down
8 changes: 8 additions & 0 deletions packages/vue/src/components/date-input/date-input.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ export type RootEmits = {
* Function invoked when the focused segment changes.
*/
focusChange: [details: dateInput.FocusChangeDetails]
/**
* Function invoked when the placeholder value changes.
*/
placeholderChange: [details: dateInput.PlaceholderChangeDetails]
/**
* Function invoked when the selected value changes.
*/
Expand All @@ -128,4 +132,8 @@ export type RootEmits = {
* The callback fired when the model value changes.
*/
'update:modelValue': [value: dateInput.DateValue[]]
/**
* The callback fired when the placeholder value changes.
*/
'update:placeholderValue': [placeholderValue: dateInput.DateValue]
}
5 changes: 5 additions & 0 deletions packages/vue/src/components/date-input/use-date-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ export const useDateInput = (props: MaybeRef<UseDateInputProps> = {}, emit?: Emi
emit?.('focusChange', details)
localeProps.onFocusChange?.(details)
},
onPlaceholderChange: (details) => {
emit?.('placeholderChange', details)
emit?.('update:placeholderValue', details.placeholderValue)
localeProps.onPlaceholderChange?.(details)
},
onValueChange: (details) => {
emit?.('valueChange', details)
emit?.('update:modelValue', details.value)
Expand Down
4 changes: 4 additions & 0 deletions packages/vue/src/components/dialog/dialog.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,8 @@ export type RootEmits = {
* The callback fired when the open state changes.
*/
'update:open': [open: boolean]
/**
* The callback fired when the trigger value changes.
*/
'update:triggerValue': [triggerValue: string | null]
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { Dialog } from '@ark-ui/vue/dialog'
import { XIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import { computed, ref } from 'vue'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'
import field from 'styles/field.module.css'
Expand All @@ -18,11 +18,12 @@ const users: User[] = [
{ id: '3', name: 'Carol Davis', email: 'carol@example.com' },
]

const activeUser = ref<User | null>(null)
const triggerValue = ref<string | null>(null)
const activeUser = computed(() => users.find((u) => u.id === triggerValue.value) ?? null)
</script>

<template>
<Dialog.Root @trigger-value-change="(e) => (activeUser = users.find((u) => u.id === e.value) ?? null)">
<Dialog.Root v-model:trigger-value="triggerValue">
<div :class="button.Group">
<Dialog.Trigger v-for="user in users" :key="user.id" :value="user.id" :class="button.Root">
Edit {{ user.name }}
Expand Down
1 change: 1 addition & 0 deletions packages/vue/src/components/dialog/use-dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const useDialog = (props: MaybeRef<UseDialogProps> = {}, emit?: EmitFn<Ro
},
onTriggerValueChange: (details) => {
emit?.('triggerValueChange', details)
emit?.('update:triggerValue', details.value)
localeProps.onTriggerValueChange?.(details)
},
onEscapeKeyDown: (details) => {
Expand Down
16 changes: 14 additions & 2 deletions packages/vue/src/components/drawer/drawer.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,12 @@ export interface RootProps {
/**
* The currently active snap point.
*/
snapPoint?: number | string
snapPoint?: drawer.SnapPoint | null
/**
* The default snap point of the drawer.
* @default 1
*/
defaultSnapPoint?: number | string
defaultSnapPoint?: drawer.SnapPoint | null
/**
* The value of the trigger that currently open the drawer
*/
Expand All @@ -127,12 +127,24 @@ export type RootEmits = {
* Function called when the open state changes.
*/
openChange: [details: drawer.OpenChangeDetails]
/**
* The callback fired when the open state changes.
*/
'update:open': [open: boolean]
/**
* Callback fired when the active snap point changes.
*/
snapPointChange: [details: drawer.SnapPointChangeDetails]
/**
* The callback fired when the snap point changes.
*/
'update:snapPoint': [snapPoint: drawer.SnapPoint | null]
/**
* Function called when the trigger value changes
*/
triggerValueChange: [details: drawer.TriggerValueChangeDetails]
/**
* The callback fired when the trigger value changes.
*/
'update:triggerValue': [triggerValue: string | null]
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { Drawer } from '@ark-ui/vue/drawer'
import { XIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import { computed, ref } from 'vue'
import button from 'styles/button.module.css'
import styles from 'styles/drawer.module.css'
import field from 'styles/field.module.css'
Expand All @@ -18,14 +18,12 @@ const users: User[] = [
{ id: '3', name: 'Carol Davis', email: 'carol@example.com' },
]

const activeUser = ref<User | null>(null)
const triggerValue = ref<string | null>(null)
const activeUser = computed(() => users.find((u) => u.id === triggerValue.value) ?? null)
</script>

<template>
<Drawer.Root
swipe-direction="end"
@trigger-value-change="(e) => (activeUser = users.find((u) => u.id === e.value) ?? null)"
>
<Drawer.Root swipe-direction="end" v-model:trigger-value="triggerValue">
<div :class="button.Group">
<Drawer.Trigger v-for="user in users" :key="user.id" :value="user.id" :class="button.Root">
Edit {{ user.name }}
Expand Down
5 changes: 4 additions & 1 deletion packages/vue/src/components/drawer/use-drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { RootEmits } from './drawer.types.ts'
import { useDrawerStackStore } from './use-drawer-stack-store.ts'

export interface UseDrawerProps extends Optional<Omit<drawer.Props, 'dir' | 'getRootNode' | 'defaultSnapPoint'>, 'id'> {
defaultSnapPoint?: drawer.SnapPoint | undefined
defaultSnapPoint?: drawer.SnapPoint | null | undefined
}
export interface UseDrawerReturn extends ComputedRef<drawer.Api<PropTypes>> {}

Expand All @@ -28,14 +28,17 @@ export const useDrawer = (props: MaybeRef<UseDrawerProps> = {}, emit?: EmitFn<Ro
...cleanProps(localeProps),
onOpenChange: (details) => {
emit?.('openChange', details)
emit?.('update:open', details.open)
localeProps.onOpenChange?.(details)
},
onTriggerValueChange: (details) => {
emit?.('triggerValueChange', details)
emit?.('update:triggerValue', details.value)
localeProps.onTriggerValueChange?.(details)
},
onSnapPointChange: (details) => {
emit?.('snapPointChange', details)
emit?.('update:snapPoint', details.snapPoint)
localeProps.onSnapPointChange?.(details)
},
}
Expand Down
4 changes: 4 additions & 0 deletions packages/vue/src/components/file-upload/file-upload.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export interface RootProps {
* The default accepted files
*/
defaultAcceptedFiles?: File[]
/**
* The controlled accepted files
*/
acceptedFiles?: File[]
/**
* Whether to allow drag and drop in the dropzone element
* @default true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { HoverCard } from '@ark-ui/vue/hover-card'
import { ref } from 'vue'
import { computed, ref } from 'vue'
import styles from 'styles/hover-card.module.css'

interface Profile {
Expand Down Expand Up @@ -35,11 +35,12 @@ const profiles: Profile[] = [
},
]

const activeProfile = ref<Profile | null>(null)
const triggerValue = ref<string | null>(null)
const activeProfile = computed(() => profiles.find((p) => p.id === triggerValue.value) ?? null)
</script>

<template>
<HoverCard.Root @trigger-value-change="(e) => (activeProfile = profiles.find((p) => p.id === e.value) ?? null)">
<HoverCard.Root v-model:trigger-value="triggerValue">
<p :class="styles.Paragraph">
Reviewed by
<HoverCard.Trigger value="sarah" as-child>
Expand Down
4 changes: 4 additions & 0 deletions packages/vue/src/components/hover-card/hover-card.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,8 @@ export type RootEmits = {
* The callback fired when the open state changes.
*/
'update:open': [open: boolean]
/**
* The callback fired when the trigger value changes.
*/
'update:triggerValue': [triggerValue: string | null]
}
1 change: 1 addition & 0 deletions packages/vue/src/components/hover-card/use-hover-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const useHoverCard = (props: MaybeRef<UseHoverCardProps> = {}, emit?: Emi
},
onTriggerValueChange: (details) => {
emit?.('triggerValueChange', details)
emit?.('update:triggerValue', details.value)
localeProps.onTriggerValueChange?.(details)
},
onFocusOutside: (details) => {
Expand Down
4 changes: 4 additions & 0 deletions packages/vue/src/components/listbox/listbox.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ export type RootEmits<T extends CollectionItem> = {
* The callback fired when the highlighted item changes.
*/
highlightChange: [details: listbox.HighlightChangeDetails<T>]
/**
* The callback fired when the highlighted value changes.
*/
'update:highlightedValue': [value: string | null]
/**
* Function called when an item is selected
*/
Expand Down
1 change: 1 addition & 0 deletions packages/vue/src/components/listbox/use-listbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const useListbox = <T extends CollectionItem>(
},
onHighlightChange: (details) => {
emit?.('highlightChange', details)
emit?.('update:highlightedValue', details.highlightedValue)
localProps.onHighlightChange?.(details)
},
onSelect: (details) => {
Expand Down
4 changes: 4 additions & 0 deletions packages/vue/src/components/marquee/marquee.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,8 @@ export type RootEmits = {
* Function called when the pause status changes.
*/
pauseChange: [details: marquee.PauseStatusDetails]
/**
* The callback fired when the paused state changes.
*/
'update:paused': [paused: boolean]
}
Loading