diff --git a/.changeset/selfish-windows-warn.md b/.changeset/selfish-windows-warn.md
new file mode 100644
index 0000000000..744af4b1ef
--- /dev/null
+++ b/.changeset/selfish-windows-warn.md
@@ -0,0 +1,33 @@
+---
+"@zag-js/color-picker": minor
+"@zag-js/number-input": minor
+"@zag-js/range-slider": minor
+"@zag-js/rating-group": minor
+"@zag-js/toggle-group": minor
+"@zag-js/date-picker": minor
+"@zag-js/file-upload": minor
+"@zag-js/radio-group": minor
+"@zag-js/hover-card": minor
+"@zag-js/pagination": minor
+"@zag-js/tags-input": minor
+"@zag-js/accordion": minor
+"@zag-js/pin-input": minor
+"@zag-js/carousel": minor
+"@zag-js/checkbox": minor
+"@zag-js/combobox": minor
+"@zag-js/editable": minor
+"@zag-js/presence": minor
+"@zag-js/splitter": minor
+"@zag-js/popover": minor
+"@zag-js/tooltip": minor
+"@zag-js/avatar": minor
+"@zag-js/dialog": minor
+"@zag-js/select": minor
+"@zag-js/slider": minor
+"@zag-js/switch": minor
+"@zag-js/menu": minor
+"@zag-js/tabs": minor
+"@zag-js/types": minor
+---
+
+BREAKING: Unify all callbacks to follow a consistent naming convention
diff --git a/.xstate/date-picker.js b/.xstate/date-picker.js
index 1a5d13005c..171dc2b3b8 100644
--- a/.xstate/date-picker.js
+++ b/.xstate/date-picker.js
@@ -101,7 +101,7 @@ const fetchMachine = createMachine({
},
"TRIGGER.CLICK": {
target: "open",
- actions: ["focusFirstSelectedDate"]
+ actions: ["focusFirstSelectedDate", "invokeOnOpen"]
}
}
},
@@ -110,7 +110,7 @@ const fetchMachine = createMachine({
on: {
"TRIGGER.CLICK": {
target: "open",
- actions: ["setViewToDay", "focusFirstSelectedDate"]
+ actions: ["setViewToDay", "focusFirstSelectedDate", "invokeOnOpen"]
},
"INPUT.CHANGE": {
actions: ["focusParsedDate"]
@@ -123,7 +123,7 @@ const fetchMachine = createMachine({
},
"CELL.FOCUS": {
target: "open",
- actions: ["setView"]
+ actions: ["setView", "invokeOnOpen"]
}
}
},
@@ -153,7 +153,7 @@ const fetchMachine = createMachine({
}, {
target: "focused",
cond: "isRangePicker && isSelectingEndDate",
- actions: ["setFocusedDate", "setSelectedDate", "setStartIndex", "clearHoveredDate", "focusInputElement"]
+ actions: ["setFocusedDate", "setSelectedDate", "setStartIndex", "clearHoveredDate", "focusInputElement", "invokeOnClose"]
},
// ===
{
@@ -169,7 +169,7 @@ const fetchMachine = createMachine({
actions: ["setFocusedDate", "setSelectedDate"]
}, {
target: "focused",
- actions: ["setFocusedDate", "setSelectedDate", "focusInputElement"]
+ actions: ["setFocusedDate", "setSelectedDate", "focusInputElement", "invokeOnClose"]
}
// ===
],
@@ -193,7 +193,7 @@ const fetchMachine = createMachine({
"GRID.ESCAPE": {
cond: "!isInline",
target: "focused",
- actions: ["setViewToDay", "focusFirstSelectedDate", "focusTriggerElement"]
+ actions: ["setViewToDay", "focusFirstSelectedDate", "focusTriggerElement", "invokeOnClose"]
},
"GRID.ENTER": [{
cond: "isMonthView",
@@ -212,7 +212,7 @@ const fetchMachine = createMachine({
}, {
target: "focused",
cond: "isRangePicker && isSelectingEndDate",
- actions: ["setSelectedDate", "setStartIndex", "focusInputElement"]
+ actions: ["setSelectedDate", "setStartIndex", "focusInputElement", "invokeOnClose"]
},
// ===
{
@@ -228,7 +228,7 @@ const fetchMachine = createMachine({
actions: ["selectFocusedDate"]
}, {
target: "focused",
- actions: ["selectFocusedDate", "focusInputElement"]
+ actions: ["selectFocusedDate", "focusInputElement", "invokeOnClose"]
}
// ===
],
@@ -294,7 +294,8 @@ const fetchMachine = createMachine({
actions: ["focusSectionEnd"]
}],
"TRIGGER.CLICK": {
- target: "focused"
+ target: "focused",
+ actions: ["invokeOnClose"]
},
"VIEW.CHANGE": [{
cond: "isDayView",
@@ -306,10 +307,10 @@ const fetchMachine = createMachine({
DISMISS: [{
cond: "isTargetFocusable",
target: "idle",
- actions: ["setStartIndex"]
+ actions: ["setStartIndex", "invokeOnClose"]
}, {
target: "focused",
- actions: ["focusTriggerElement", "setStartIndex"]
+ actions: ["focusTriggerElement", "setStartIndex", "invokeOnClose"]
}]
}
}
diff --git a/.xstate/editable.js b/.xstate/editable.js
index e4bfee2d9b..06b64ea467 100644
--- a/.xstate/editable.js
+++ b/.xstate/editable.js
@@ -12,6 +12,7 @@ const {
const fetchMachine = createMachine({
id: "editable",
initial: ctx.startWithEditView ? "edit" : "preview",
+ entry: ctx.startWithEditView ? ["focusInput"] : undefined,
context: {
"activateOnDblClick": false,
"activateOnFocus": false,
diff --git a/.xstate/tags-input.js b/.xstate/tags-input.js
index 40ecd32a78..403de88624 100644
--- a/.xstate/tags-input.js
+++ b/.xstate/tags-input.js
@@ -14,24 +14,23 @@ const fetchMachine = createMachine({
initial: ctx.autoFocus ? "focused:input" : "idle",
context: {
"allowEditTag": false,
- "!isTagFocused": false,
+ "!isTagHighlighted": false,
"(!isAtMax || allowOverflow) && !isInputValueEmpty": false,
"addOnBlur": false,
"clearOnBlur": false,
- "!hasFocusedId": false,
+ "!hasHighlightedTag": false,
"addOnBlur": false,
"clearOnBlur": false,
"hasTags && isInputCaretAtStart": false,
"hasTags && isInputCaretAtStart": false,
"addOnPaste": false,
- "hasTags && isInputCaretAtStart && !isLastTagFocused": false,
- "allowEditTag && hasFocusedId": false,
- "isFirstTagFocused": false,
+ "hasTags && isInputCaretAtStart && !isLastTagHighlighted": false,
+ "allowEditTag && hasHighlightedTag": false,
+ "isFirstTagHighlighted": false,
"isInputRelatedTarget": false
},
- activities: ["trackFormControlState"],
- entry: ["setupLiveRegion"],
- exit: ["removeLiveRegion", "clearLog"],
+ activities: ["trackLiveRegion", "trackFormControlState"],
+ exit: ["clearLog"],
on: {
DOUBLE_CLICK_TAG: {
internal: true,
@@ -41,9 +40,9 @@ const fetchMachine = createMachine({
},
POINTER_DOWN_TAG: {
internal: true,
- cond: "!isTagFocused",
+ cond: "!isTagHighlighted",
target: "navigating:tag",
- actions: ["focusTag", "focusInput"]
+ actions: ["highlightTag", "focusInput"]
},
SET_INPUT_VALUE: {
actions: ["setInputValue"]
@@ -83,14 +82,14 @@ const fetchMachine = createMachine({
on: {
FOCUS: "focused:input",
POINTER_DOWN: {
- cond: "!hasFocusedId",
+ cond: "!hasHighlightedTag",
target: "focused:input"
}
}
},
"focused:input": {
tags: ["focused"],
- entry: ["focusInput", "clearFocusedId"],
+ entry: ["focusInput", "clearHighlightedId"],
activities: ["trackInteractOutside"],
on: {
TYPE: {
@@ -116,12 +115,12 @@ const fetchMachine = createMachine({
ARROW_LEFT: {
cond: "hasTags && isInputCaretAtStart",
target: "navigating:tag",
- actions: "focusLastTag"
+ actions: "highlightLastTag"
},
BACKSPACE: {
target: "navigating:tag",
cond: "hasTags && isInputCaretAtStart",
- actions: "focusLastTag"
+ actions: "highlightLastTag"
},
PASTE: {
cond: "addOnPaste",
@@ -134,20 +133,20 @@ const fetchMachine = createMachine({
activities: ["trackInteractOutside"],
on: {
ARROW_RIGHT: [{
- cond: "hasTags && isInputCaretAtStart && !isLastTagFocused",
- actions: "focusNextTag"
+ cond: "hasTags && isInputCaretAtStart && !isLastTagHighlighted",
+ actions: "highlightNextTag"
}, {
target: "focused:input"
}],
ARROW_LEFT: {
- actions: "focusPrevTag"
+ actions: "highlightPrevTag"
},
BLUR: {
target: "idle",
- actions: "clearFocusedId"
+ actions: "clearHighlightedId"
},
ENTER: {
- cond: "allowEditTag && hasFocusedId",
+ cond: "allowEditTag && hasHighlightedTag",
target: "editing:tag",
actions: ["setEditedId", "initializeEditedTagValue", "focusEditedTagInput"]
},
@@ -158,13 +157,13 @@ const fetchMachine = createMachine({
actions: "setInputValue"
},
BACKSPACE: [{
- cond: "isFirstTagFocused",
- actions: ["deleteFocusedTag", "focusFirstTag"]
+ cond: "isFirstTagHighlighted",
+ actions: ["deleteHighlightedTag", "highlightFirstTag"]
}, {
- actions: ["deleteFocusedTag", "focusPrevTag"]
+ actions: ["deleteHighlightedTag", "highlightPrevTag"]
}],
DELETE: {
- actions: ["deleteFocusedTag", "focusTagAtIndex"]
+ actions: ["deleteHighlightedTag", "highlightTagAtIndex"]
}
}
},
@@ -178,19 +177,19 @@ const fetchMachine = createMachine({
},
TAG_INPUT_ESCAPE: {
target: "navigating:tag",
- actions: ["clearEditedTagValue", "focusInput", "clearEditedId", "focusTagAtIndex"]
+ actions: ["clearEditedTagValue", "focusInput", "clearEditedId", "highlightTagAtIndex"]
},
TAG_INPUT_BLUR: [{
cond: "isInputRelatedTarget",
target: "navigating:tag",
- actions: ["clearEditedTagValue", "clearFocusedId", "clearEditedId"]
+ actions: ["clearEditedTagValue", "clearHighlightedId", "clearEditedId"]
}, {
target: "idle",
- actions: ["clearEditedTagValue", "clearFocusedId", "clearEditedId", "raiseExternalBlurEvent"]
+ actions: ["clearEditedTagValue", "clearHighlightedId", "clearEditedId", "raiseExternalBlurEvent"]
}],
TAG_INPUT_ENTER: {
target: "navigating:tag",
- actions: ["submitEditedTagValue", "focusInput", "clearEditedId", "focusTagAtIndex"]
+ actions: ["submitEditedTagValue", "focusInput", "clearEditedId", "highlightTagAtIndex"]
}
}
}
@@ -205,16 +204,16 @@ const fetchMachine = createMachine({
},
guards: {
"allowEditTag": ctx => ctx["allowEditTag"],
- "!isTagFocused": ctx => ctx["!isTagFocused"],
+ "!isTagHighlighted": ctx => ctx["!isTagHighlighted"],
"(!isAtMax || allowOverflow) && !isInputValueEmpty": ctx => ctx["(!isAtMax || allowOverflow) && !isInputValueEmpty"],
"addOnBlur": ctx => ctx["addOnBlur"],
"clearOnBlur": ctx => ctx["clearOnBlur"],
- "!hasFocusedId": ctx => ctx["!hasFocusedId"],
+ "!hasHighlightedTag": ctx => ctx["!hasHighlightedTag"],
"hasTags && isInputCaretAtStart": ctx => ctx["hasTags && isInputCaretAtStart"],
"addOnPaste": ctx => ctx["addOnPaste"],
- "hasTags && isInputCaretAtStart && !isLastTagFocused": ctx => ctx["hasTags && isInputCaretAtStart && !isLastTagFocused"],
- "allowEditTag && hasFocusedId": ctx => ctx["allowEditTag && hasFocusedId"],
- "isFirstTagFocused": ctx => ctx["isFirstTagFocused"],
+ "hasTags && isInputCaretAtStart && !isLastTagHighlighted": ctx => ctx["hasTags && isInputCaretAtStart && !isLastTagHighlighted"],
+ "allowEditTag && hasHighlightedTag": ctx => ctx["allowEditTag && hasHighlightedTag"],
+ "isFirstTagHighlighted": ctx => ctx["isFirstTagHighlighted"],
"isInputRelatedTarget": ctx => ctx["isInputRelatedTarget"]
}
});
\ No newline at end of file
diff --git a/examples/next-ts/package.json b/examples/next-ts/package.json
index 3ebb316cfb..e8cb03b47f 100644
--- a/examples/next-ts/package.json
+++ b/examples/next-ts/package.json
@@ -35,6 +35,7 @@
"@zag-js/element-rect": "workspace:*",
"@zag-js/element-size": "workspace:*",
"@zag-js/file-upload": "workspace:*",
+ "@zag-js/file-utils": "workspace:*",
"@zag-js/focus-scope": "workspace:*",
"@zag-js/focus-visible": "workspace:*",
"@zag-js/form-utils": "workspace:*",
@@ -90,4 +91,4 @@
"typescript": "5.2.2"
},
"license": "MIT"
-}
+}
\ No newline at end of file
diff --git a/examples/next-ts/pages/combobox.tsx b/examples/next-ts/pages/combobox.tsx
index c9d010e175..b1af34e789 100644
--- a/examples/next-ts/pages/combobox.tsx
+++ b/examples/next-ts/pages/combobox.tsx
@@ -21,10 +21,11 @@ export default function Page() {
combobox.machine({
id: useId(),
collection,
- onOpen() {
+ onOpenChange(details) {
+ if (!details.open) return
setOptions(comboboxData)
},
- onInputChange({ value }) {
+ onInputValueChange({ value }) {
const filtered = comboboxData.filter((item) => item.label.toLowerCase().includes(value.toLowerCase()))
setOptions(filtered.length > 0 ? filtered : comboboxData)
},
diff --git a/examples/next-ts/pages/hover-card.tsx b/examples/next-ts/pages/hover-card.tsx
index bfdf2b9199..f73edac1ac 100644
--- a/examples/next-ts/pages/hover-card.tsx
+++ b/examples/next-ts/pages/hover-card.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react/jsx-no-target-blank */
import * as hoverCard from "@zag-js/hover-card"
import { normalizeProps, Portal, useMachine } from "@zag-js/react"
import { hoverCardControls } from "@zag-js/shared"
diff --git a/examples/next-ts/pages/pagination.tsx b/examples/next-ts/pages/pagination.tsx
index 13c819a103..65199aba17 100644
--- a/examples/next-ts/pages/pagination.tsx
+++ b/examples/next-ts/pages/pagination.tsx
@@ -14,7 +14,7 @@ export default function Page() {
pagination.machine({
id: useId(),
count: paginationData.length,
- onChange: console.log,
+ onPageChange: console.log,
}),
{
context: controls.context,
diff --git a/examples/next-ts/pages/popper.tsx b/examples/next-ts/pages/popper.tsx
index 7e65654803..a1e03511ab 100644
--- a/examples/next-ts/pages/popper.tsx
+++ b/examples/next-ts/pages/popper.tsx
@@ -14,7 +14,7 @@ export default function App() {
})
}, [])
- const styles = getPlacementStyles({ measured: true })
+ const styles = getPlacementStyles({ placement: "bottom" })
return (
diff --git a/examples/next-ts/pages/radio-group.tsx b/examples/next-ts/pages/radio-group.tsx
index 7e39c88c5b..7313db72e8 100644
--- a/examples/next-ts/pages/radio-group.tsx
+++ b/examples/next-ts/pages/radio-group.tsx
@@ -50,9 +50,6 @@ export default function Page() {
-
diff --git a/examples/next-ts/pages/select.tsx b/examples/next-ts/pages/select.tsx
index b337c6eb4d..294f1716a1 100644
--- a/examples/next-ts/pages/select.tsx
+++ b/examples/next-ts/pages/select.tsx
@@ -15,14 +15,14 @@ export default function Page() {
collection: select.collection({ items: selectData }),
id: useId(),
name: "country",
- onHighlight(details) {
- console.log("onHighlight", details)
+ onHighlightChange(details) {
+ console.log("onHighlightChange", details)
},
- onChange(details) {
+ onValueChange(details) {
console.log("onChange", details)
},
- onOpen() {
- console.log("onOpen")
+ onOpenChange(details) {
+ console.log("onOpenChange", details)
},
}),
{
diff --git a/examples/nuxt-ts/package.json b/examples/nuxt-ts/package.json
index 10150907d5..aa41760266 100644
--- a/examples/nuxt-ts/package.json
+++ b/examples/nuxt-ts/package.json
@@ -34,6 +34,7 @@
"@zag-js/element-rect": "workspace:*",
"@zag-js/element-size": "workspace:*",
"@zag-js/file-upload": "workspace:*",
+ "@zag-js/file-utils": "workspace:*",
"@zag-js/focus-scope": "workspace:*",
"@zag-js/focus-visible": "workspace:*",
"@zag-js/form-utils": "workspace:*",
@@ -81,4 +82,4 @@
"@types/node": "^20.6.0",
"nuxt": "^3.7.1"
}
-}
+}
\ No newline at end of file
diff --git a/examples/nuxt-ts/pages/combobox.vue b/examples/nuxt-ts/pages/combobox.vue
index e5da5ab0be..771437f2ba 100644
--- a/examples/nuxt-ts/pages/combobox.vue
+++ b/examples/nuxt-ts/pages/combobox.vue
@@ -18,10 +18,11 @@ const [state, send] = useMachine(
combobox.machine({
id: "1",
collection: collectionRef.value,
- onOpen() {
+ onOpenChange(details) {
+ if (!details.open) return
options.value = comboboxData
},
- onInputChange({ value }) {
+ onInputValueChange({ value }) {
const filtered = comboboxData.filter((item) => item.label.toLowerCase().includes(value.toLowerCase()))
options.value = filtered.length > 0 ? filtered : comboboxData
},
diff --git a/examples/nuxt-ts/pages/radio-group.vue b/examples/nuxt-ts/pages/radio-group.vue
index d9bf378675..7c27c476c3 100644
--- a/examples/nuxt-ts/pages/radio-group.vue
+++ b/examples/nuxt-ts/pages/radio-group.vue
@@ -44,7 +44,6 @@ const api = computed(() => radio.connect(state.value, send, normalizeProps))
-
diff --git a/examples/shadow-dom/package.json b/examples/shadow-dom/package.json
index 9d760b88c0..cfc304f0ba 100644
--- a/examples/shadow-dom/package.json
+++ b/examples/shadow-dom/package.json
@@ -33,6 +33,7 @@
"@zag-js/element-rect": "workspace:*",
"@zag-js/element-size": "workspace:*",
"@zag-js/file-upload": "workspace:*",
+ "@zag-js/file-utils": "workspace:*",
"@zag-js/focus-scope": "workspace:*",
"@zag-js/focus-visible": "workspace:*",
"@zag-js/form-utils": "workspace:*",
@@ -81,4 +82,4 @@
"typescript": "^5.2.2",
"vite": "^4.4.9"
}
-}
+}
\ No newline at end of file
diff --git a/examples/solid-ts/package.json b/examples/solid-ts/package.json
index e16dcae6f9..31fea555d2 100644
--- a/examples/solid-ts/package.json
+++ b/examples/solid-ts/package.json
@@ -43,6 +43,7 @@
"@zag-js/element-rect": "workspace:*",
"@zag-js/element-size": "workspace:*",
"@zag-js/file-upload": "workspace:*",
+ "@zag-js/file-utils": "workspace:*",
"@zag-js/focus-scope": "workspace:*",
"@zag-js/focus-visible": "workspace:*",
"@zag-js/form-utils": "workspace:*",
@@ -85,4 +86,4 @@
"form-serialize": "0.7.2",
"solid-js": "1.7.11"
}
-}
+}
\ No newline at end of file
diff --git a/examples/solid-ts/src/pages/combobox.tsx b/examples/solid-ts/src/pages/combobox.tsx
index a8d0b3cdf7..2e8b998976 100644
--- a/examples/solid-ts/src/pages/combobox.tsx
+++ b/examples/solid-ts/src/pages/combobox.tsx
@@ -28,10 +28,11 @@ export default function Page() {
combobox.machine({
id: createUniqueId(),
collection: collection(),
- onOpen() {
+ onOpenChange(details) {
+ if (!details.open) return
setOptions(comboboxData)
},
- onInputChange({ value }) {
+ onInputValueChange({ value }) {
const filtered = comboboxData.filter((item) => item.label.toLowerCase().includes(value.toLowerCase()))
setOptions(filtered.length > 0 ? filtered : comboboxData)
},
diff --git a/examples/solid-ts/src/pages/radio-group.tsx b/examples/solid-ts/src/pages/radio-group.tsx
index 6f80836a9b..882527e667 100644
--- a/examples/solid-ts/src/pages/radio-group.tsx
+++ b/examples/solid-ts/src/pages/radio-group.tsx
@@ -53,9 +53,6 @@ export default function Page() {
-
diff --git a/examples/vue-ts/package.json b/examples/vue-ts/package.json
index 19e6e9a084..d6c3a0a86a 100644
--- a/examples/vue-ts/package.json
+++ b/examples/vue-ts/package.json
@@ -35,6 +35,7 @@
"@zag-js/element-rect": "workspace:*",
"@zag-js/element-size": "workspace:*",
"@zag-js/file-upload": "workspace:*",
+ "@zag-js/file-utils": "workspace:*",
"@zag-js/focus-scope": "workspace:*",
"@zag-js/focus-visible": "workspace:*",
"@zag-js/form-utils": "workspace:*",
@@ -95,4 +96,4 @@
"vue-tsc": "1.8.10"
},
"license": "MIT"
-}
+}
\ No newline at end of file
diff --git a/examples/vue-ts/src/pages/combobox.tsx b/examples/vue-ts/src/pages/combobox.tsx
index ac3f7a8d18..9d0edb98c2 100644
--- a/examples/vue-ts/src/pages/combobox.tsx
+++ b/examples/vue-ts/src/pages/combobox.tsx
@@ -23,10 +23,11 @@ export default defineComponent({
combobox.machine({
collection: collectionRef.value,
id: "1",
- onOpen() {
+ onOpenChange(details) {
+ if (!details.open) return
options.value = comboboxData
},
- onInputChange({ value }) {
+ onInputValueChange({ value }) {
const filtered = comboboxData.filter((item) => item.label.toLowerCase().includes(value.toLowerCase()))
options.value = filtered.length > 0 ? filtered : comboboxData
},
diff --git a/examples/vue-ts/src/pages/pagination.tsx b/examples/vue-ts/src/pages/pagination.tsx
index 212b89abd6..b415a2ba15 100644
--- a/examples/vue-ts/src/pages/pagination.tsx
+++ b/examples/vue-ts/src/pages/pagination.tsx
@@ -16,7 +16,7 @@ export default defineComponent({
pagination.machine({
id: "1",
count: paginationData.length,
- onChange: console.log,
+ onPageChange: console.log,
}),
{
context: controls.context,
diff --git a/examples/vue-ts/src/pages/radio-group.tsx b/examples/vue-ts/src/pages/radio-group.tsx
index b0bd473794..bfca8302ab 100644
--- a/examples/vue-ts/src/pages/radio-group.tsx
+++ b/examples/vue-ts/src/pages/radio-group.tsx
@@ -52,9 +52,6 @@ export default defineComponent({
-
diff --git a/packages/docs/api.json b/packages/docs/api.json
index f9811155b2..98a04469a5 100644
--- a/packages/docs/api.json
+++ b/packages/docs/api.json
@@ -19,19 +19,6 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ root: string; item(value: string): string; content(value: string): string; trigger(value: string): string; }>",
"description": "The ids of the elements in the accordion. Useful for composition."
@@ -54,8 +41,8 @@
"type": "boolean",
"description": "Whether the accordion items are disabled"
},
- "onChange": {
- "type": "(details: ChangeDetails) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "The callback fired when the state of opened/closed accordion items changes."
},
"onFocusChange": {
@@ -65,6 +52,19 @@
"orientation": {
"type": "\"horizontal\" | \"vertical\"",
"description": "The orientation of the accordion items."
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -92,6 +92,10 @@
}
},
"context": {
+ "onLoadingStatusChange": {
+ "type": "(details: StatusChangeDetails) => void",
+ "description": "Functional called when the image loading status changes."
+ },
"id": {
"type": "string",
"description": "The unique identifier of the machine."
@@ -137,7 +141,7 @@
"description": "Function to scroll to the previous slide"
},
"getSlideState": {
- "type": "(props: SlideProps) => { valueText: string; isCurrent: boolean; isNext: boolean; isPrevious: boolean; isInView: boolean; }",
+ "type": "(props: SlideState",
"description": "Returns the state of a specific slide"
},
"play": {
@@ -150,19 +154,6 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"orientation": {
"type": "\"horizontal\" | \"vertical\"",
"description": "The orientation of the carousel.",
@@ -189,12 +180,25 @@
"description": "The amount of space between slides."
},
"onSlideChange": {
- "type": "(details: ChangeDetails) => void",
+ "type": "(details: SlideChangeDetails) => void",
"description": "Function called when the slide changes."
},
"ids": {
"type": "Partial<{ root: string; viewport: string; slide(index: number): string; slideGroup: string; nextSlideTrigger: string; prevSlideTrigger: string; indicatorGroup: string; indicator(index: number): string; }>",
"description": "The ids of the elements in the carousel. Useful for composition."
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -230,19 +234,6 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ root: string; hiddenInput: string; control: string; label: string; }>",
"description": "The ids of the elements in the checkbox. Useful for composition."
@@ -263,8 +254,8 @@
"type": "CheckedState",
"description": "If `true`, the checkbox will be checked."
},
- "onChange": {
- "type": "(details: { checked: CheckedState; }) => void",
+ "onCheckedChange": {
+ "type": "(details: CheckedChangeDetails) => void",
"description": "The callback invoked when the checked state of the `Checkbox` changes."
},
"name": {
@@ -279,6 +270,19 @@
"type": "string",
"description": "The value of checkbox input. Useful for form submission.",
"defaultValue": "\"on\""
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -322,14 +326,6 @@
}
},
"context": {
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ content: string; area: string; areaGradient: string; areaThumb: string; channelInput(id: string): string; channelSliderTrack(id: ColorChannel): string; }>",
"description": "The ids of the elements in the color picker. Useful for composition."
@@ -350,17 +346,25 @@
"type": "boolean",
"description": "Whether the color picker is read-only"
},
- "onChange": {
- "type": "(details: ChangeDetails) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Handler that is called when the value changes, as the user drags."
},
- "onChangeEnd": {
- "type": "(details: ChangeDetails) => void",
+ "onValueChangeEnd": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Handler that is called when the user stops dragging."
},
"name": {
"type": "string",
"description": "The name for the form input"
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -387,7 +391,7 @@
"description": "The value of the highlighted item"
},
"highlightedItem": {
- "type": "CollectionItem",
+ "type": "V",
"description": "The highlighted item"
},
"highlightValue": {
@@ -395,7 +399,7 @@
"description": "The value of the combobox input"
},
"selectedItems": {
- "type": "CollectionItem[]",
+ "type": "V[]",
"description": "The selected items"
},
"hasSelectedItems": {
@@ -443,24 +447,11 @@
"description": "Function to close the combobox"
},
"setCollection": {
- "type": "(collection: Collection
) => void",
+ "type": "(collection: Collection) => void",
"description": "Function to set the collection of items"
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ root: string; label: string; control: string; input: string; content: string; trigger: string; clearTrigger: string; item(id: string, index?: number): string; positioner: string; itemGroup(id: string | number): string; itemGroupLabel(id: string | number): string; }>",
"description": "The ids of the elements in the combobox. Useful for composition."
@@ -533,42 +524,26 @@
"type": "PositioningOptions",
"description": "The positioning options to dynamically position the menu"
},
- "onInputChange": {
- "type": "(details: { value: string; }) => void",
+ "onInputValueChange": {
+ "type": "(details: InputValueChangeDetails) => void",
"description": "Function called when the input's value changes"
},
- "onChange": {
- "type": "(details: { value: string[]; items: CollectionItem[]; }) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Function called when a new item is selected"
},
- "onHighlight": {
- "type": "(details: { value: string; item: CollectionItem; }) => void",
+ "onHighlightChange": {
+ "type": "(details: HighlightChangeDetails) => void",
"description": "Function called when an item is highlighted using the pointer\nor keyboard navigation."
},
- "onOpen": {
- "type": "VoidFunction",
+ "onOpenChange": {
+ "type": "(details: OpenChangeDetails) => void",
"description": "Function called when the popup is opened"
},
- "onClose": {
- "type": "VoidFunction",
- "description": "Function called when the popup is closed"
- },
"translations": {
"type": "IntlTranslations",
"description": "Specifies the localized strings that identifies the accessibility elements and their states"
},
- "onPointerDownOutside": {
- "type": "(event: PointerDownOutsideEvent) => void",
- "description": "Function called when the pointer is pressed down outside the combobox"
- },
- "onFocusOutside": {
- "type": "(event: FocusOutsideEvent) => void",
- "description": "Function called when the focus is moved outside the combobox"
- },
- "onInteractOutside": {
- "type": "(event: InteractOutsideEvent) => void",
- "description": "Function called when an interaction happens outside the combobox"
- },
"collection": {
"type": "Collection",
"description": "The collection of items"
@@ -580,6 +555,31 @@
"closeOnSelect": {
"type": "boolean",
"description": "Whether to close the combobox when an item is selected."
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
+ },
+ "onPointerDownOutside": {
+ "type": "(event: PointerDownOutsideEvent) => void",
+ "description": "Function called when the pointer is pressed down outside the combobox"
+ },
+ "onFocusOutside": {
+ "type": "(event: FocusOutsideEvent) => void",
+ "description": "Function called when the focus is moved outside the combobox"
+ },
+ "onInteractOutside": {
+ "type": "(event: InteractOutsideEvent) => void",
+ "description": "Function called when an interaction happens outside the combobox"
}
}
},
@@ -597,10 +597,6 @@
"type": "DateView",
"description": "The current view of the date picker"
},
- "matchView": {
- "type": "typeof matchView",
- "description": "Matcher for the current view of the date picker"
- },
"getDaysInWeek": {
"type": "(weekIndex: number, from?: DateValue[]",
"description": "Returns an array of days in the week index counted from the provided start date, or the first visible date if not given."
@@ -690,11 +686,11 @@
"description": "The visible range of dates."
},
"getYears": {
- "type": "() => { label: string; value: number; }[]",
+ "type": "() => GridItem[]",
"description": "Returns the months of the year"
},
"getYearsGrid": {
- "type": "(props?: { columns?: number; }) => { label: string; value: number; }[][]",
+ "type": "(props?: YearGridValue",
"description": "Returns the years of the decade based on the columns.\nRepresented as an array of arrays of years."
},
"getDecade": {
@@ -702,11 +698,11 @@
"description": "Returns the start and end years of the decade."
},
"getMonths": {
- "type": "(props?: { format?: \"short\" | \"long\"; }) => { label: string; value: number; }[]",
+ "type": "(props?: { format?: \"short\" | \"long\"; }) => GridItem[]",
"description": "Returns the months of the year"
},
"getMonthsGrid": {
- "type": "(props?: { columns?: number; format?: \"short\" | \"long\"; }) => { label: string; value: number; }[][]",
+ "type": "(props?: MonthGridValue",
"description": "Returns the months of the year based on the columns.\nRepresented as an array of arrays of months."
},
"format": {
@@ -726,24 +722,15 @@
"description": "Goes to the previous month/year/decade."
},
"getDayCellState": {
- "type": "(props: DayCellProps) => { isInvalid: boolean; isDisabled: boolean; isSelected: boolean; isUnavailable: boolean; isOutsideRange: boolean; isInRange: boolean; isFirstInRange: boolean; isLastInRange: boolean; isToday: boolean; isWeekend: boolean; formattedDate: string; readonly isFocused: boolean; readonly ariaLabel: string; readonly isSelectable: boolean; }",
+ "type": "(props: DayCellState",
"description": "Returns the state details for a given cell."
+ },
+ "getMonthCellState": {
+ "type": "(props: CellState",
+ "description": "Returns the state details for a given month cell."
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"messages": {
"type": "IntlMessages",
"description": "The localized messages to use."
@@ -804,18 +791,22 @@
"type": "boolean",
"description": "Whether the calendar should have a fixed number of weeks.\nThis renders the calendar with 6 weeks instead of 5 or 6."
},
- "onChange": {
- "type": "(details: { value: DateValue[]; }) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Function called when the value changes."
},
"onFocusChange": {
- "type": "(details: ChangeDetails<{ focusedValue: DateView; }>) => void",
+ "type": "(details: FocusChangeDetails) => void",
"description": "Function called when the focused date changes."
},
"onViewChange": {
- "type": "(details: { view: DateView; }) => void",
+ "type": "(details: ViewChangeDetails) => void",
"description": "Function called when the view changes."
},
+ "onOpenChange": {
+ "type": "(details: OpenChangeDetails) => void",
+ "description": "Function called when the calendar opens or closes."
+ },
"isDateUnavailable": {
"type": "(date: DateValue, locale: string) => boolean",
"description": "Returns whether a date of the calendar is available."
@@ -844,6 +835,19 @@
"positioning": {
"type": "PositioningOptions",
"description": "The user provided options used to position the date picker content"
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -863,19 +867,6 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ trigger: string; container: string; backdrop: string; content: string; closeTrigger: string; title: string; description: string; }>",
"description": "The ids of the elements in the dialog. Useful for composition."
@@ -904,20 +895,16 @@
"type": "boolean",
"description": "Whether to restore focus to the element that had focus before the dialog was opened"
},
- "onClose": {
- "type": "() => void",
- "description": "Callback to be invoked when the dialog is closed"
- },
- "onOpen": {
- "type": "() => void",
- "description": "Callback to be invoked when the dialog is opened"
+ "onOpenChange": {
+ "type": "(details: OpenChangeDetails) => void",
+ "description": "Callback to be invoked when the dialog is opened or closed"
},
"closeOnOutsideClick": {
"type": "boolean",
"description": "Whether to close the dialog when the outside is clicked"
},
"onOutsideClick": {
- "type": "() => void",
+ "type": "VoidFunction",
"description": "Callback to be invoked when the outside is clicked"
},
"closeOnEsc": {
@@ -925,7 +912,7 @@
"description": "Whether to close the dialog when the escape key is pressed"
},
"onEsc": {
- "type": "() => void",
+ "type": "VoidFunction",
"description": "Callback to be invoked when the escape key is pressed"
},
"aria-label": {
@@ -940,6 +927,19 @@
"open": {
"type": "boolean",
"description": "Whether the dialog is open"
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => Node | ShadowRoot | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -979,19 +979,6 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ root: string; area: string; label: string; preview: string; input: string; controls: string; submitTrigger: string; cancelTrigger: string; editTrigger: string; }>",
"description": "The ids of the elements in the editable. Useful for composition."
@@ -1046,16 +1033,16 @@
"type": "boolean",
"description": "Whether the editable is readonly"
},
- "onChange": {
- "type": "(details: { value: string; }) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "The callback that is called when the editable's value is changed"
},
- "onCancel": {
- "type": "(details: { value: string; }) => void",
+ "onValueRevert": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "The callback that is called when the esc key is pressed or the cancel button is clicked"
},
- "onSubmit": {
- "type": "(details: { value: string; }) => void",
+ "onValueCommit": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "The callback that is called when the editable's value is submitted."
},
"onEdit": {
@@ -1073,22 +1060,47 @@
"finalFocusEl": {
"type": "() => HTMLElement",
"description": "The element that should receive focus when the editable is closed.\nBy default, it will focus on the trigger element."
- }
- }
- },
- "file-upload": {
- "api": {
- "isDragging": {
- "type": "boolean",
- "description": "Whether the user is dragging something over the root element"
},
- "isFocused": {
- "type": "boolean",
- "description": "Whether the user is focused on the root element"
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
},
- "open": {
- "type": "() => void",
- "description": "Function to open the file dialog"
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => Node | ShadowRoot | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
+ },
+ "onPointerDownOutside": {
+ "type": "(event: PointerDownOutsideEvent) => void",
+ "description": "Function called when the pointer is pressed down outside the combobox"
+ },
+ "onFocusOutside": {
+ "type": "(event: FocusOutsideEvent) => void",
+ "description": "Function called when the focus is moved outside the combobox"
+ },
+ "onInteractOutside": {
+ "type": "(event: InteractOutsideEvent) => void",
+ "description": "Function called when an interaction happens outside the combobox"
+ }
+ }
+ },
+ "file-upload": {
+ "api": {
+ "isDragging": {
+ "type": "boolean",
+ "description": "Whether the user is dragging something over the root element"
+ },
+ "isFocused": {
+ "type": "boolean",
+ "description": "Whether the user is focused on the root element"
+ },
+ "open": {
+ "type": "() => void",
+ "description": "Function to open the file dialog"
},
"deleteFile": {
"type": "(file: File) => void",
@@ -1098,24 +1110,16 @@
"type": "File[]",
"description": "The files that have been dropped or selected"
},
- "setValue": {
+ "setFiles": {
"type": "(files: File[]) => void",
"description": "Function to set the value"
},
- "clearValue": {
+ "clearFiles": {
"type": "() => void",
"description": "Function to clear the value"
}
},
"context": {
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"name": {
"type": "string",
"description": "The name of the underlying file input"
@@ -1152,9 +1156,17 @@
"type": "File[]",
"description": "The current value of the file input"
},
- "onChange": {
- "type": "(details: ChangeDetails) => void",
+ "onFilesChange": {
+ "type": "(details: FileChangeDetails) => void",
"description": "Function called when the value changes"
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -1178,30 +1190,13 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ trigger: string; content: string; positioner: string; arrow: string; }>",
"description": "The ids of the elements in the popover. Useful for composition."
},
- "onOpen": {
- "type": "VoidFunction",
- "description": "Function invoked when the hover card is opened."
- },
- "onClose": {
- "type": "VoidFunction",
- "description": "Function invoked when the hover card is closed."
+ "onOpenChange": {
+ "type": "(details: OpenChangeDetails) => void",
+ "description": "Function called when the hover card opens or closes."
},
"openDelay": {
"type": "number",
@@ -1218,6 +1213,19 @@
"positioning": {
"type": "PositioningOptions",
"description": "The user provided options used to position the popover content"
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -1269,19 +1277,6 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ trigger: string; contextTrigger: string; content: string; label(id: string): string; group(id: string): string; positioner: string; arrow: string; }>",
"description": "The ids of the elements in the menu. Useful for composition."
@@ -1291,7 +1286,7 @@
"description": "The values of radios and checkboxes in the menu."
},
"onValueChange": {
- "type": "(details: { name: string; value: string | string[]; }) => void",
+ "type": "(details: ValueChangeDetails) => void",
"description": "Callback to be called when the menu values change (for radios and checkboxes)."
},
"highlightedId": {
@@ -1299,7 +1294,7 @@
"description": "The `id` of the active menu item."
},
"onSelect": {
- "type": "(details: { value: string; }) => void",
+ "type": "(details: SelectionDetails) => void",
"description": "Function called when a menu item is selected."
},
"anchorPoint": {
@@ -1322,13 +1317,34 @@
"type": "string",
"description": "The accessibility label for the menu"
},
- "onOpen": {
- "type": "() => void",
- "description": "Function called when the menu is opened"
+ "onOpenChange": {
+ "type": "(details: OpenChangeDetails) => void",
+ "description": "Function called when the menu opens or closes"
},
- "onClose": {
- "type": "() => void",
- "description": "Function called when the menu is closed"
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
+ },
+ "onPointerDownOutside": {
+ "type": "(event: PointerDownOutsideEvent) => void",
+ "description": "Function called when the pointer is pressed down outside the combobox"
+ },
+ "onFocusOutside": {
+ "type": "(event: FocusOutsideEvent) => void",
+ "description": "Function called when the focus is moved outside the combobox"
+ },
+ "onInteractOutside": {
+ "type": "(event: InteractOutsideEvent) => void",
+ "description": "Function called when an interaction happens outside the combobox"
}
}
},
@@ -1388,19 +1404,6 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ root: string; label: string; input: string; incrementTrigger: string; decrementTrigger: string; scrubber: string; }>",
"description": "The ids of the elements in the number input. Useful for composition."
@@ -1482,26 +1485,22 @@
"description": "If using a custom display format, this converts the default format to the custom format."
},
"inputMode": {
- "type": "\"text\" | \"tel\" | \"numeric\" | \"decimal\"",
+ "type": "InputMode",
"description": "Hints at the type of data that might be entered by the user. It also determines\nthe type of keyboard shown to the user on mobile devices",
"defaultValue": "\"decimal\""
},
- "onChange": {
- "type": "(details: Value) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Function invoked when the value changes"
},
- "onInvalid": {
- "type": "(details: Value & { reason: ValidityState; }) => void",
+ "onValueInvalid": {
+ "type": "(details: ValueInvalidDetails) => void",
"description": "Function invoked when the value overflows or underflows the min/max range"
},
- "onFocus": {
- "type": "(details: Value & { srcElement: HTMLElement; }) => void",
+ "onFocusChange": {
+ "type": "(details: FocusChangeDetails) => void",
"description": "Function invoked when the number input is focused"
},
- "onBlur": {
- "type": "(details: Value) => void",
- "description": "The value of the input when it is blurred"
- },
"minFractionDigits": {
"type": "number",
"description": "The minimum number of fraction digits to use. Possible values are from 0 to 20"
@@ -1513,6 +1512,19 @@
"spinOnPress": {
"type": "boolean",
"description": "Whether to spin the value when the increment/decrement button is pressed"
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -1527,7 +1539,7 @@
"description": "The total number of pages."
},
"pages": {
- "type": "PaginationRange",
+ "type": "Pages",
"description": "The page range. Represented as an array of page numbers (including ellipsis)"
},
"previousPage": {
@@ -1539,11 +1551,11 @@
"description": "The next page."
},
"pageRange": {
- "type": "{ start: number; end: number; }",
+ "type": "PageRange",
"description": "The page range. Represented as an object with `start` and `end` properties."
},
"slice": {
- "type": "(data: T_1[]) => T_1[]",
+ "type": "(data: V[]) => V[]",
"description": "Function to slice an array of data based on the current page."
},
"isFirstPage": {
@@ -1568,19 +1580,6 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ root: string; ellipsis(index: number): string; prevPageTrigger: string; nextPageTrigger: string; pageTrigger(page: number): string; }>",
"description": "The ids of the elements in the accordion. Useful for composition."
@@ -1605,14 +1604,27 @@
"type": "number",
"description": "The active page"
},
- "onChange": {
- "type": "(details: ChangeDetails) => void",
+ "onPageChange": {
+ "type": "(details: PageChangeDetails) => void",
"description": "Called when the page number is changed, and it takes the resulting page number argument"
},
"type": {
"type": "\"button\" | \"link\"",
"description": "The type of the trigger element",
"defaultValue": "\"button\""
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -1648,19 +1660,6 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"name": {
"type": "string",
"description": "The name of the input element. Useful for form submission."
@@ -1705,16 +1704,16 @@
"type": "\"alphanumeric\" | \"numeric\" | \"alphabetic\"",
"description": "The type of value the pin-input should allow"
},
- "onComplete": {
- "type": "(details: { value: string[]; valueAsString: string; }) => void",
+ "onValueComplete": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Function called when all inputs have valid values"
},
- "onChange": {
- "type": "(details: { value: string[]; }) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Function called on input change"
},
- "onInvalid": {
- "type": "(details: { value: string; index: number; }) => void",
+ "onValueInvalid": {
+ "type": "(details: ValueInvalidDetails) => void",
"description": "Function called when an invalid value is entered"
},
"mask": {
@@ -1732,6 +1731,19 @@
"translations": {
"type": "IntlTranslations",
"description": "Specifies the localized strings that identifies the accessibility elements and their states"
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -1759,14 +1771,6 @@
}
},
"context": {
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ anchor: string; trigger: string; content: string; title: string; description: string; closeTrigger: string; positioner: string; arrow: string; }>",
"description": "The ids of the elements in the popover. Useful for composition."
@@ -1797,13 +1801,9 @@
"type": "boolean",
"description": "Whether to close the popover when the escape key is pressed."
},
- "onClose": {
- "type": "VoidFunction",
- "description": "Function invoked when the popover is closed"
- },
- "onOpen": {
- "type": "VoidFunction",
- "description": "Function invoked when the popover is opened"
+ "onOpenChange": {
+ "type": "(details: OpenChangeDetails) => void",
+ "description": "Function invoked when the popover opens or closes"
},
"positioning": {
"type": "PositioningOptions",
@@ -1812,6 +1812,30 @@
"open": {
"type": "boolean",
"description": "Whether the popover is open"
+ },
+ "onEscapeKeyDown": {
+ "type": "(event: KeyboardEvent) => void",
+ "description": "Function called when the escape key is pressed"
+ },
+ "onPointerDownOutside": {
+ "type": "(event: PointerDownOutsideEvent) => void",
+ "description": "Function called when the pointer is pressed down outside the combobox"
+ },
+ "onFocusOutside": {
+ "type": "(event: FocusOutsideEvent) => void",
+ "description": "Function called when the focus is moved outside the combobox"
+ },
+ "onInteractOutside": {
+ "type": "(event: InteractOutsideEvent) => void",
+ "description": "Function called when an interaction happens outside the combobox"
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => Node | ShadowRoot | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -1836,6 +1860,26 @@
}
},
"context": {
+ "disabled": {
+ "type": "boolean",
+ "description": "Whether the element is disabled"
+ },
+ "preventFocusOnPress": {
+ "type": "boolean",
+ "description": "Whether the target should not receive focus on press."
+ },
+ "cancelOnPointerExit": {
+ "type": "boolean",
+ "description": "Whether press events should be canceled when the pointer leaves the target while pressed.\n\nBy default, this is `false`, which means if the pointer returns back over the target while\nstill pressed, onPressStart will be fired again.\n\nIf set to `true`, the press is canceled when the pointer leaves the target and\nonPressStart will not be fired if the pointer returns."
+ },
+ "allowTextSelectionOnPress": {
+ "type": "boolean",
+ "description": "Whether text selection should be enabled on the pressable element."
+ },
+ "longPressDelay": {
+ "type": "number",
+ "description": "The amount of time (in milliseconds) to wait before firing the `onLongPress` event."
+ },
"dir": {
"type": "\"ltr\" | \"rtl\"",
"description": "The document's text/writing direction.",
@@ -1868,26 +1912,6 @@
"onLongPress": {
"type": "(event: PressEvent) => void",
"description": "Handler that is called when the element has been pressed for 500 milliseconds"
- },
- "disabled": {
- "type": "boolean",
- "description": "Whether the element is disabled"
- },
- "preventFocusOnPress": {
- "type": "boolean",
- "description": "Whether the target should not receive focus on press."
- },
- "cancelOnPointerExit": {
- "type": "boolean",
- "description": "Whether press events should be canceled when the pointer leaves the target while pressed.\n\nBy default, this is `false`, which means if the pointer returns back over the target while\nstill pressed, onPressStart will be fired again.\n\nIf set to `true`, the press is canceled when the pointer leaves the target and\nonPressStart will not be fired if the pointer returns."
- },
- "allowTextSelectionOnPress": {
- "type": "boolean",
- "description": "Whether text selection should be enabled on the pressable element."
- },
- "longPressDelay": {
- "type": "number",
- "description": "The amount of time (in milliseconds) to wait before firing the `onLongPress` event."
}
}
},
@@ -1909,29 +1933,12 @@
"type": "() => void",
"description": "Function to focus the radio group"
},
- "blur": {
- "type": "() => void",
- "description": "Function to blur the currently focused radio input in the radio group"
- },
"getRadioState": {
- "type": "(props: T_1) => { isInteractive: boolean; isInvalid: boolean; isDisabled: boolean; isChecked: boolean; isFocused: boolean; isHovered: boolean; isActive: boolean; }",
+ "type": "(props: RadioState",
"description": "Returns the state details of a radio input"
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ root: string; label: string; indicator: string; radio(value: string): string; radioLabel(value: string): string; radioControl(value: string): string; radioHiddenInput(value: string): string; }>",
"description": "The ids of the elements in the radio. Useful for composition."
@@ -1952,13 +1959,26 @@
"type": "boolean",
"description": "If `true`, the radio group will be disabled"
},
- "onChange": {
- "type": "(details: { value: string; }) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Function called once a radio is checked"
},
"orientation": {
"type": "\"horizontal\" | \"vertical\"",
"description": "Orientation of the radio group"
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -2026,19 +2046,6 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ root: string; thumb(index: number): string; control: string; track: string; range: string; label: string; output: string; marker(index: number): string; }>",
"description": "The ids of the elements in the range slider. Useful for composition."
@@ -2075,20 +2082,20 @@
"type": "boolean",
"description": "Whether the slider is invalid"
},
- "onChange": {
- "type": "(details: { value: number[]; }) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Function invoked when the value of the slider changes"
},
- "onChangeStart": {
- "type": "(details: { value: number[]; }) => void",
+ "onValueChangeStart": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Function invoked when the slider value change is started"
},
- "onChangeEnd": {
- "type": "(details: { value: number[]; }) => void",
+ "onValueChangeEnd": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Function invoked when the slider value change is done"
},
"onFocusChange": {
- "type": "(details: { index: number; value: number[]; }) => void",
+ "type": "(details: FocusChangeDetails) => void",
"description": "Function invoked when the slider's focused index changes"
},
"getAriaValueText": {
@@ -2122,6 +2129,19 @@
"thumbSize": {
"type": "{ width: number; height: number; }",
"description": "The slider thumbs dimensions"
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -2161,19 +2181,6 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ root: string; label: string; hiddenInput: string; control: string; rating(id: string): string; }>",
"description": "The ids of the elements in the rating. Useful for composition."
@@ -2214,13 +2221,26 @@
"type": "boolean",
"description": "Whether to autofocus the rating."
},
- "onChange": {
- "type": "(details: { value: number; }) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Function to be called when the rating value changes."
},
- "onHover": {
- "type": "(details: { value: number; }) => void",
+ "onHoverChange": {
+ "type": "(details: HoverChangeDetails) => void",
"description": "Function to be called when the rating value is hovered."
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -2239,7 +2259,7 @@
"description": "The value of the highlighted item"
},
"highlightedItem": {
- "type": "CollectionItem",
+ "type": "V",
"description": "The highlighted item"
},
"highlightValue": {
@@ -2247,7 +2267,7 @@
"description": "The value of the combobox input"
},
"selectedItems": {
- "type": "CollectionItem[]",
+ "type": "V[]",
"description": "The selected items"
},
"hasSelectedItems": {
@@ -2291,30 +2311,17 @@
"description": "Function to close the combobox"
},
"setCollection": {
- "type": "(collection: Collection) => void",
+ "type": "(collection: Collection) => void",
"description": "Function to set the collection of items"
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"collection": {
"type": "Collection",
"description": "The item collection"
},
"ids": {
- "type": "Partial<{ content: string; trigger: string; clearTrigger: string; label: string; hiddenSelect: string; positioner: string; item(id: string | number): string; itemGroup(id: string | number): string; itemGroupLabel(id: string | number): string; }>",
+ "type": "Partial<{ root: string; content: string; control: string; trigger: string; clearTrigger: string; label: string; hiddenSelect: string; positioner: string; item(id: string | number): string; itemGroup(id: string | number): string; itemGroupLabel(id: string | number): string; }>",
"description": "The ids of the elements in the select. Useful for composition."
},
"name": {
@@ -2345,22 +2352,18 @@
"type": "boolean",
"description": "Whether to select the highlighted item when the user presses Tab,\nand the menu is open."
},
- "onHighlight": {
- "type": "(details: HighlightChangeDetails) => void",
+ "onHighlightChange": {
+ "type": "(details: HighlightChangeDetails) => void",
"description": "The callback fired when the highlighted item changes."
},
- "onChange": {
- "type": "(details: ValueChangeDetails) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "The callback fired when the selected item changes."
},
- "onOpen": {
- "type": "VoidFunction",
+ "onOpenChange": {
+ "type": "(details: OpenChangeDetails) => void",
"description": "Function called when the popup is opened"
},
- "onClose": {
- "type": "VoidFunction",
- "description": "Function called when the popup is closed"
- },
"positioning": {
"type": "PositioningOptions",
"description": "The positioning options of the menu."
@@ -2384,6 +2387,31 @@
"open": {
"type": "boolean",
"description": "Whether the select menu is open"
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
+ },
+ "onPointerDownOutside": {
+ "type": "(event: PointerDownOutsideEvent) => void",
+ "description": "Function called when the pointer is pressed down outside the combobox"
+ },
+ "onFocusOutside": {
+ "type": "(event: FocusOutsideEvent) => void",
+ "description": "Function called when the focus is moved outside the combobox"
+ },
+ "onInteractOutside": {
+ "type": "(event: InteractOutsideEvent) => void",
+ "description": "Function called when an interaction happens outside the combobox"
}
}
},
@@ -2431,19 +2459,6 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ root: string; thumb: string; control: string; track: string; range: string; label: string; output: string; hiddenInput: string; }>",
"description": "The ids of the elements in the slider. Useful for composition."
@@ -2508,16 +2523,16 @@
"type": "(value: number) => string",
"description": "Function that returns a human readable value for the slider"
},
- "onChange": {
- "type": "(details: { value: number; }) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Function invoked when the value of the slider changes"
},
- "onChangeEnd": {
- "type": "(details: { value: number; }) => void",
+ "onValueChangeEnd": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Function invoked when the slider value change is done"
},
- "onChangeStart": {
- "type": "(details: { value: number; }) => void",
+ "onValueChangeStart": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Function invoked when the slider value change is started"
},
"thumbAlignment": {
@@ -2525,8 +2540,21 @@
"description": "The alignment of the slider thumb relative to the track\n- `center`: the thumb will extend beyond the bounds of the slider track.\n- `contain`: the thumb will be contained within the bounds of the track."
},
"thumbSize": {
- "type": "{ width: number; height: number; }",
+ "type": "Size",
"description": "The slider thumb dimensions.If not provided, the thumb size will be measured automatically."
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -2557,24 +2585,11 @@
"description": "Function to set the size of a panel."
},
"getResizeTriggerState": {
- "type": "(props: ResizeTriggerProps) => { isDisabled: boolean; isFocused: boolean; panelIds: string[]; min: number; max: number; value: number; }",
+ "type": "(props: ResizeTriggerState",
"description": "Returns the state details for a resize trigger."
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"orientation": {
"type": "\"horizontal\" | \"vertical\"",
"description": "The orientation of the splitter. Can be `horizontal` or `vertical`"
@@ -2583,21 +2598,34 @@
"type": "PanelSizeData[]",
"description": "The size data of the panels"
},
- "onResize": {
- "type": "(details: ResizeDetails) => void",
+ "onSizeChange": {
+ "type": "(details: SizeChangeDetails) => void",
"description": "Function called when the splitter is resized."
},
- "onResizeStart": {
- "type": "(details: ResizeDetails) => void",
+ "onSizeChangeStart": {
+ "type": "(details: SizeChangeDetails) => void",
"description": "Function called when the splitter resize starts."
},
- "onResizeEnd": {
- "type": "(details: ResizeDetails) => void",
+ "onSizeChangeEnd": {
+ "type": "(details: SizeChangeDetails) => void",
"description": "Function called when the splitter resize ends."
},
"ids": {
"type": "Partial<{ root: string; resizeTrigger(id: string): string; label(id: string): string; panel(id: string | number): string; }>",
"description": "The ids of the elements in the splitter. Useful for composition."
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -2625,19 +2653,6 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ root: string; input: string; control: string; label: string; thumb: string; }>",
"description": "The ids of the elements in the switch. Useful for composition."
@@ -2650,10 +2665,6 @@
"type": "boolean",
"description": "Whether the switch is disabled."
},
- "focusable": {
- "type": "boolean",
- "description": "If `true` and `disabled` is passed, the switch will\nremain tabbable but not interactive"
- },
"readOnly": {
"type": "boolean",
"description": "If `true`, the switch will be readonly"
@@ -2666,8 +2677,8 @@
"type": "boolean",
"description": "If `true`, the switch input is marked as required,"
},
- "onChange": {
- "type": "(details: { checked: boolean; }) => void",
+ "onCheckedChange": {
+ "type": "(details: CheckedChangeDetails) => void",
"description": "Function to call when the switch is clicked."
},
"checked": {
@@ -2686,6 +2697,19 @@
"type": "string | number",
"description": "The value of switch input. Useful for form submission.",
"defaultValue": "\"on\""
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -2712,24 +2736,15 @@
"description": "Clears the value of the tabs."
},
"setIndicatorRect": {
- "type": "(id: string) => void",
- "description": "Sets the indicator rect to the tab with the given id."
+ "type": "(value: string) => void",
+ "description": "Sets the indicator rect to the tab with the given value"
+ },
+ "getTriggerState": {
+ "type": "(props: TriggerState",
+ "description": "Returns the state of the trigger with the given props"
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ root: string; trigger: string; tablist: string; content: string; indicator: string; }>",
"description": "The ids of the elements in the tabs. Useful for composition."
@@ -2757,17 +2772,26 @@
"description": "The activation mode of the tabs. Can be `manual` or `automatic`\n- `manual`: Tabs are activated when clicked or press `enter` key.\n- `automatic`: Tabs are activated when receiving focus",
"defaultValue": "\"automatic\""
},
- "onChange": {
- "type": "(details: { value: string; }) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Callback to be called when the selected/active tab changes"
},
- "onFocus": {
- "type": "(details: { value: string; }) => void",
+ "onFocusChange": {
+ "type": "(details: FocusChangeDetails) => void",
"description": "Callback to be called when the focused tab changes"
},
- "onDelete": {
- "type": "(details: { value: string; }) => void",
- "description": "Callback to be called when a tab's close button is clicked"
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -2824,22 +2848,13 @@
"focus": {
"type": "() => void",
"description": "Function to focus the tags entry input."
+ },
+ "getTagState": {
+ "type": "(props: TagState",
+ "description": "Returns the state of a tag"
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ root: string; input: string; clearBtn: string; label: string; control: string; tag(opts: TagProps): string; }>",
"description": "The ids of the elements in the tags input. Useful for composition."
@@ -2885,20 +2900,20 @@
"type": "string[]",
"description": "The tag values"
},
- "onChange": {
- "type": "(details: { values: string[]; }) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Callback fired when the tag values is updated"
},
- "onFocusChange": {
- "type": "(details: { value: string; }) => void",
- "description": "Callback fired when a tag is focused by pointer or keyboard navigation"
+ "onHighlightChange": {
+ "type": "(details: HighlightChangeDetails) => void",
+ "description": "Callback fired when a tag is highlighted by pointer or keyboard navigation"
},
- "onInvalid": {
- "type": "(details: { reason: ValidityState; }) => void",
+ "onValueInvalid": {
+ "type": "(details: ValidityChangeDetails) => void",
"description": "Callback fired when the max tag count is reached or the `validateTag` function returns `false`"
},
"validate": {
- "type": "(details: { inputValue: string; values: string[]; }) => boolean",
+ "type": "(details: ValidateArgs) => boolean",
"description": "Returns a boolean that determines whether a tag can be added.\nUseful for preventing duplicates or invalid tag values."
},
"blurBehavior": {
@@ -2925,6 +2940,31 @@
"form": {
"type": "string",
"description": "The associate form of the underlying input element."
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
+ },
+ "onPointerDownOutside": {
+ "type": "(event: PointerDownOutsideEvent) => void",
+ "description": "Function called when the pointer is pressed down outside the combobox"
+ },
+ "onFocusOutside": {
+ "type": "(event: FocusOutsideEvent) => void",
+ "description": "Function called when the focus is moved outside the combobox"
+ },
+ "onInteractOutside": {
+ "type": "(event: InteractOutsideEvent) => void",
+ "description": "Function called when an interaction happens outside the combobox"
}
}
},
@@ -2977,53 +3017,6 @@
},
"context": {}
},
- "toggle": {
- "api": {
- "isPressed": {
- "type": "boolean",
- "description": "Whether the toggle is pressed."
- },
- "setPressed": {
- "type": "(pressed: boolean) => void",
- "description": "Function to set the pressed state of the toggle."
- }
- },
- "context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
- "ids": {
- "type": "Partial<{ button: string; }>",
- "description": "The ids of the elements in the toggle. Useful for composition."
- },
- "aria-label": {
- "type": "string",
- "description": "Specifies the localized strings that identifies the accessibility elements and their states"
- },
- "disabled": {
- "type": "boolean",
- "description": "Whether the toggle is disabled."
- },
- "onChange": {
- "type": "(details: { pressed: boolean; }) => void",
- "description": "Function to call when the toggle is clicked."
- },
- "pressed": {
- "type": "boolean",
- "description": "Whether the toggle is initially pressed."
- }
- }
- },
"toggle-group": {
"api": {
"value": {
@@ -3036,19 +3029,6 @@
}
},
"context": {
- "dir": {
- "type": "\"ltr\" | \"rtl\"",
- "description": "The document's text/writing direction.",
- "defaultValue": "\"ltr\""
- },
- "id": {
- "type": "string",
- "description": "The unique identifier of the machine."
- },
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ root: string; toggle(value: string): string; }>",
"description": "The ids of the elements in the toggle. Useful for composition."
@@ -3061,8 +3041,8 @@
"type": "string[]",
"description": "The values of the toggles in the group."
},
- "onChange": {
- "type": "(details: { value: string[]; }) => void",
+ "onValueChange": {
+ "type": "(details: ValueChangeDetails) => void",
"description": "Function to call when the toggle is clicked."
},
"loop": {
@@ -3080,6 +3060,19 @@
"multiple": {
"type": "boolean",
"description": "Whether to allow multiple toggles to be selected."
+ },
+ "dir": {
+ "type": "\"ltr\" | \"rtl\"",
+ "description": "The document's text/writing direction.",
+ "defaultValue": "\"ltr\""
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier of the machine."
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
},
@@ -3103,14 +3096,14 @@
}
},
"context": {
- "getRootNode": {
- "type": "() => ShadowRoot | Node | Document",
- "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
- },
"ids": {
"type": "Partial<{ trigger: string; content: string; arrow: string; positioner: string; }>",
"description": "The ids of the elements in the tooltip. Useful for composition."
},
+ "id": {
+ "type": "string",
+ "description": "The `id` of the tooltip."
+ },
"openDelay": {
"type": "number",
"description": "The open delay of the tooltip."
@@ -3131,14 +3124,10 @@
"type": "boolean",
"description": "Whether the tooltip's content is interactive.\nIn this mode, the tooltip will remain open when user hovers over the content."
},
- "onOpen": {
- "type": "VoidFunction",
+ "onOpenChange": {
+ "type": "(details: OpenChangeDetails) => void",
"description": "Function called when the tooltip is opened."
},
- "onClose": {
- "type": "VoidFunction",
- "description": "Function called when the tooltip is closed."
- },
"aria-label": {
"type": "string",
"description": "Custom label for the tooltip."
@@ -3154,6 +3143,10 @@
"open": {
"type": "boolean",
"description": "Whether the tooltip is open"
+ },
+ "getRootNode": {
+ "type": "() => ShadowRoot | Node | Document",
+ "description": "A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."
}
}
}
diff --git a/packages/machines/accordion/src/accordion.connect.ts b/packages/machines/accordion/src/accordion.connect.ts
index 2c45777db7..f8c58bc906 100644
--- a/packages/machines/accordion/src/accordion.connect.ts
+++ b/packages/machines/accordion/src/accordion.connect.ts
@@ -39,28 +39,28 @@ export function connect(state: State, send: Send, normalize
}),
getItemProps(props: ItemProps) {
- const { isOpen, isFocused, isDisabled } = getItemState(props)
+ const itemState = getItemState(props)
return normalize.element({
...parts.item.attrs,
id: dom.getItemId(state.context, props.value),
- "data-state": isOpen ? "open" : "closed",
- "data-focus": dataAttr(isFocused),
- "data-disabled": dataAttr(isDisabled),
+ "data-state": itemState.isOpen ? "open" : "closed",
+ "data-focus": dataAttr(itemState.isFocused),
+ "data-disabled": dataAttr(itemState.isDisabled),
"data-orientation": state.context.orientation,
})
},
getContentProps(props: ItemProps) {
- const { isOpen, isFocused, isDisabled } = getItemState(props)
+ const itemState = getItemState(props)
return normalize.element({
...parts.content.attrs,
role: "region",
id: dom.getContentId(state.context, props.value),
"aria-labelledby": dom.getTriggerId(state.context, props.value),
- hidden: !isOpen,
- "data-state": isOpen ? "open" : "closed",
- "data-disabled": dataAttr(isDisabled),
- "data-focus": dataAttr(isFocused),
+ hidden: !itemState.isOpen,
+ "data-state": itemState.isOpen ? "open" : "closed",
+ "data-disabled": dataAttr(itemState.isDisabled),
+ "data-focus": dataAttr(itemState.isFocused),
"data-orientation": state.context.orientation,
})
},
diff --git a/packages/machines/accordion/src/accordion.machine.ts b/packages/machines/accordion/src/accordion.machine.ts
index 5a363c4eb3..1f65903385 100644
--- a/packages/machines/accordion/src/accordion.machine.ts
+++ b/packages/machines/accordion/src/accordion.machine.ts
@@ -131,7 +131,7 @@ export function machine(userContext: UserDefinedContext) {
const invoke = {
change(ctx: MachineContext) {
- ctx.onChange?.({ value: Array.from(ctx.value) })
+ ctx.onValueChange?.({ value: Array.from(ctx.value) })
},
focusChange(ctx: MachineContext) {
ctx.onFocusChange?.({ value: ctx.focusedValue })
diff --git a/packages/machines/accordion/src/accordion.types.ts b/packages/machines/accordion/src/accordion.types.ts
index 8741cf49f7..15718f6298 100644
--- a/packages/machines/accordion/src/accordion.types.ts
+++ b/packages/machines/accordion/src/accordion.types.ts
@@ -1,6 +1,22 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface ValueChangeDetails {
+ value: string[]
+}
+
+export interface FocusChangeDetails {
+ value: string | null
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
type ElementIds = Partial<{
root: string
item(value: string): string
@@ -8,52 +24,43 @@ type ElementIds = Partial<{
trigger(value: string): string
}>
-export type ChangeDetails = {
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The ids of the elements in the accordion. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * Whether multple accordion items can be open at the same time.
+ * @default false
+ */
+ multiple?: boolean
+ /**
+ * Whether an accordion item can be collapsed after it has been opened.
+ * @default false
+ */
+ collapsible?: boolean
+ /**
+ * The `id` of the accordion item that is currently being opened.
+ */
value: string[]
+ /**
+ * Whether the accordion items are disabled
+ */
+ disabled?: boolean
+ /**
+ * The callback fired when the state of opened/closed accordion items changes.
+ */
+ onValueChange?: (details: ValueChangeDetails) => void
+ /**
+ * The callback fired when the focused accordion item changes.
+ */
+ onFocusChange?: (details: FocusChangeDetails) => void
+ /**
+ * The orientation of the accordion items.
+ */
+ orientation?: "horizontal" | "vertical"
}
-export type FocusChangeDetails = {
- value: string | null
-}
-
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The ids of the elements in the accordion. Useful for composition.
- */
- ids?: ElementIds
- /**
- * Whether multple accordion items can be open at the same time.
- * @default false
- */
- multiple?: boolean
- /**
- * Whether an accordion item can be collapsed after it has been opened.
- * @default false
- */
- collapsible?: boolean
- /**
- * The `id` of the accordion item that is currently being opened.
- */
- value: string[]
- /**
- * Whether the accordion items are disabled
- */
- disabled?: boolean
- /**
- * The callback fired when the state of opened/closed accordion items changes.
- */
- onChange?: (details: ChangeDetails) => void
- /**
- * The callback fired when the focused accordion item changes.
- */
- onFocusChange?: (details: FocusChangeDetails) => void
- /**
- * The orientation of the accordion items.
- */
- orientation?: "horizontal" | "vertical"
- }
-
export type UserDefinedContext = RequiredBy
type ComputedContext = Readonly<{
@@ -72,9 +79,9 @@ type PrivateContext = Context<{
focusedValue: string | null
}>
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle" | "focused"
}
@@ -82,18 +89,22 @@ export type State = S.State
export type Send = S.Send
-export type ItemProps = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface ItemProps {
value: string
disabled?: boolean
}
-export type ItemState = {
+export interface ItemState {
isOpen: boolean
isFocused: boolean
isDisabled: boolean
}
-export type MachineApi = {
+export interface MachineApi {
/**
* The value of the focused accordion item.
*/
diff --git a/packages/machines/avatar/src/avatar.machine.ts b/packages/machines/avatar/src/avatar.machine.ts
index 2866f89097..f41f311662 100644
--- a/packages/machines/avatar/src/avatar.machine.ts
+++ b/packages/machines/avatar/src/avatar.machine.ts
@@ -79,10 +79,10 @@ export function machine(userContext: UserDefinedContext) {
},
actions: {
invokeOnLoad(ctx) {
- ctx.onLoad?.()
+ ctx.onLoadingStatusChange?.({ status: "loaded" })
},
invokeOnError(ctx) {
- ctx.onError?.()
+ ctx.onLoadingStatusChange?.({ status: "error" })
},
checkImgStatus(ctx, _evt, { send }) {
const img = dom.getImageEl(ctx)
diff --git a/packages/machines/avatar/src/avatar.types.ts b/packages/machines/avatar/src/avatar.types.ts
index 7b0a827c95..70140a7fcd 100644
--- a/packages/machines/avatar/src/avatar.types.ts
+++ b/packages/machines/avatar/src/avatar.types.ts
@@ -1,9 +1,23 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, PropTypes, RequiredBy } from "@zag-js/types"
-type PublicContext = CommonProperties & {
- onLoad?: () => void
- onError?: () => void
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface StatusChangeDetails {
+ status: "loaded" | "error"
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
+interface PublicContext extends CommonProperties {
+ /**
+ * Functional called when the image loading status changes.
+ */
+ onLoadingStatusChange?: (details: StatusChangeDetails) => void
}
type PrivateContext = Context<{}>
@@ -12,9 +26,9 @@ type ComputedContext = Readonly<{}>
export type UserDefinedContext = RequiredBy
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "loading" | "error" | "loaded"
}
@@ -22,7 +36,11 @@ export type State = S.State
export type Send = S.Send
-export type MachineApi = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface MachineApi {
/**
* Whether the image is loaded.
*/
@@ -43,6 +61,7 @@ export type MachineApi = {
* Function to set error state.
*/
setError(): void
+
rootProps: T["element"]
imageProps: T["img"]
fallbackProps: T["element"]
diff --git a/packages/machines/carousel/src/carousel.types.ts b/packages/machines/carousel/src/carousel.types.ts
index b4828c60ee..0a656228aa 100644
--- a/packages/machines/carousel/src/carousel.types.ts
+++ b/packages/machines/carousel/src/carousel.types.ts
@@ -1,15 +1,20 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
-export type SlideProps = {
- index: number
-}
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
-export type SlideIndicatorProps = {
+export interface SlideChangeDetails {
index: number
- readOnly?: boolean
}
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
+type RectEdge = "top" | "right" | "bottom" | "left"
+
type ElementIds = Partial<{
root: string
viewport: string
@@ -21,44 +26,41 @@ type ElementIds = Partial<{
indicator(index: number): string
}>
-type ChangeDetails = { index: number }
-
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The orientation of the carousel.
- * @default "horizontal"
- */
- orientation: "horizontal" | "vertical"
- /**
- * The alignment of the slides in the carousel.
- */
- align: "start" | "center" | "end"
- /**
- * The number of slides to show at a time.
- */
- slidesPerView: number | "auto"
- /**
- * Whether the carousel should loop around.
- */
- loop: boolean
- /**
- * The current slide index.
- */
- index: number
- /**
- * The amount of space between slides.
- */
- spacing: string
- /**
- * Function called when the slide changes.
- */
- onSlideChange?: (details: ChangeDetails) => void
- /**
- * The ids of the elements in the carousel. Useful for composition.
- */
- ids?: ElementIds
- }
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The orientation of the carousel.
+ * @default "horizontal"
+ */
+ orientation: "horizontal" | "vertical"
+ /**
+ * The alignment of the slides in the carousel.
+ */
+ align: "start" | "center" | "end"
+ /**
+ * The number of slides to show at a time.
+ */
+ slidesPerView: number | "auto"
+ /**
+ * Whether the carousel should loop around.
+ */
+ loop: boolean
+ /**
+ * The current slide index.
+ */
+ index: number
+ /**
+ * The amount of space between slides.
+ */
+ spacing: string
+ /**
+ * Function called when the slide changes.
+ */
+ onSlideChange?: (details: SlideChangeDetails) => void
+ /**
+ * The ids of the elements in the carousel. Useful for composition.
+ */
+ ids?: ElementIds
+}
type PrivateContext = Context<{
slideRects: DOMRect[]
@@ -68,8 +70,6 @@ type PrivateContext = Context<{
scrollProgress: number
}>
-type RectEdge = "top" | "right" | "bottom" | "left"
-
type ComputedContext = Readonly<{
isRtl: boolean
isHorizontal: boolean
@@ -83,9 +83,9 @@ type ComputedContext = Readonly<{
export type UserDefinedContext = RequiredBy
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle" | "dragging" | "autoplay"
}
@@ -93,7 +93,28 @@ export type State = S.State
export type Send = S.Send
-export type MachineApi = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface SlideProps {
+ index: number
+}
+
+export interface SlideState {
+ valueText: string
+ isCurrent: boolean
+ isNext: boolean
+ isPrevious: boolean
+ isInView: boolean
+}
+
+export interface SlideIndicatorProps {
+ index: number
+ readOnly?: boolean
+}
+
+export interface MachineApi {
/**
* The current index of the carousel
*/
@@ -129,13 +150,7 @@ export type MachineApi = {
/**
* Returns the state of a specific slide
*/
- getSlideState: (props: SlideProps) => {
- valueText: string
- isCurrent: boolean
- isNext: boolean
- isPrevious: boolean
- isInView: boolean
- }
+ getSlideState(props: SlideProps): SlideState
/**
* Function to start/resume autoplay
*/
diff --git a/packages/machines/checkbox/src/checkbox.machine.ts b/packages/machines/checkbox/src/checkbox.machine.ts
index 38b8f49267..8d9a0d3529 100644
--- a/packages/machines/checkbox/src/checkbox.machine.ts
+++ b/packages/machines/checkbox/src/checkbox.machine.ts
@@ -120,7 +120,7 @@ function isChecked(checked?: CheckedState): checked is boolean {
const invoke = {
change: (ctx: MachineContext) => {
- ctx.onChange?.({ checked: ctx.checked })
+ ctx.onCheckedChange?.({ checked: ctx.checked })
},
}
diff --git a/packages/machines/checkbox/src/checkbox.types.ts b/packages/machines/checkbox/src/checkbox.types.ts
index a539e28582..0e05514096 100644
--- a/packages/machines/checkbox/src/checkbox.types.ts
+++ b/packages/machines/checkbox/src/checkbox.types.ts
@@ -1,6 +1,20 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export type CheckedState = boolean | "indeterminate"
+
+export interface CheckedChangeDetails {
+ checked: CheckedState
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
type ElementIds = Partial<{
root: string
hiddenInput: string
@@ -8,48 +22,45 @@ type ElementIds = Partial<{
label: string
}>
-export type CheckedState = boolean | "indeterminate"
-
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The ids of the elements in the checkbox. Useful for composition.
- */
- ids?: ElementIds
- /**
- * If `true`, the checkbox will be disabled
- */
- disabled?: boolean
- /**
- * If `true`, the checkbox is marked as invalid.
- */
- invalid?: boolean
- /**
- * If `true`, the checkbox input is marked as required,
- */
- required?: boolean
- /**
- * If `true`, the checkbox will be checked.
- */
- checked: CheckedState
- /**
- * The callback invoked when the checked state of the `Checkbox` changes.
- */
- onChange?: (details: { checked: CheckedState }) => void
- /**
- * The name of the input field in a checkbox. Useful for form submission.
- */
- name?: string
- /**
- * The id of the form that the checkbox belongs to.
- */
- form?: string
- /**
- * The value of checkbox input. Useful for form submission.
- * @default "on"
- */
- value: string
- }
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The ids of the elements in the checkbox. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * If `true`, the checkbox will be disabled
+ */
+ disabled?: boolean
+ /**
+ * If `true`, the checkbox is marked as invalid.
+ */
+ invalid?: boolean
+ /**
+ * If `true`, the checkbox input is marked as required,
+ */
+ required?: boolean
+ /**
+ * If `true`, the checkbox will be checked.
+ */
+ checked: CheckedState
+ /**
+ * The callback invoked when the checked state of the `Checkbox` changes.
+ */
+ onCheckedChange?(details: CheckedChangeDetails): void
+ /**
+ * The name of the input field in a checkbox. Useful for form submission.
+ */
+ name?: string
+ /**
+ * The id of the form that the checkbox belongs to.
+ */
+ form?: string
+ /**
+ * The value of checkbox input. Useful for form submission.
+ * @default "on"
+ */
+ value: string
+}
export type UserDefinedContext = RequiredBy
@@ -91,9 +102,9 @@ type PrivateContext = Context<{
fieldsetDisabled: boolean
}>
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "ready"
}
@@ -101,7 +112,11 @@ export type State = S.State
export type Send = S.Send
-export type MachineApi = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface MachineApi {
/**
* Whether the checkbox is checked
*/
diff --git a/packages/machines/color-picker/src/color-picker.machine.ts b/packages/machines/color-picker/src/color-picker.machine.ts
index 00c0d94011..acff89ecd9 100644
--- a/packages/machines/color-picker/src/color-picker.machine.ts
+++ b/packages/machines/color-picker/src/color-picker.machine.ts
@@ -220,7 +220,7 @@ export function machine(userContext: UserDefinedContext) {
const format = ctx.valueAsColor.getColorSpace()
const color = parseColor(sRGBHex).toFormat(format)
set.value(ctx, color)
- ctx.onChangeEnd?.({ value: ctx.value, valueAsColor: color })
+ ctx.onValueChangeEnd?.({ value: ctx.value, valueAsColor: color })
})
.catch(() => void 0)
},
@@ -368,17 +368,18 @@ export function machine(userContext: UserDefinedContext) {
)
}
-const getDetails = (ctx: MachineContext) => ({
- value: ctx.value,
- valueAsColor: ctx.valueAsColor,
-})
-
const invoke = {
changeEnd(ctx: MachineContext) {
- ctx.onChangeEnd?.(getDetails(ctx))
+ ctx.onValueChangeEnd?.({
+ value: ctx.value,
+ valueAsColor: ctx.valueAsColor,
+ })
},
change(ctx: MachineContext) {
- ctx.onChange?.(getDetails(ctx))
+ ctx.onValueChange?.({
+ value: ctx.value,
+ valueAsColor: ctx.valueAsColor,
+ })
},
}
diff --git a/packages/machines/color-picker/src/color-picker.types.ts b/packages/machines/color-picker/src/color-picker.types.ts
index 49553b8378..e76c7637f8 100644
--- a/packages/machines/color-picker/src/color-picker.types.ts
+++ b/packages/machines/color-picker/src/color-picker.types.ts
@@ -2,33 +2,21 @@ import type { Color, ColorAxes, ColorChannel, ColorFormat, ColorType } from "@za
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, Orientation, PropTypes, RequiredBy } from "@zag-js/types"
-export type ColorChannelProps = {
- channel: ColorChannel
- orientation?: Orientation
-}
-
-export type ExtendedColorChannel = ColorChannel | "hex" | "css"
-
-export type ColorChannelInputProps = {
- channel: ExtendedColorChannel
- orientation?: Orientation
-}
-
-export type ColorAreaProps = {
- xChannel: ColorChannel
- yChannel: ColorChannel
-}
-
-export type ColorSwatchProps = {
- readOnly?: boolean
- value: string | Color
-}
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
-type ChangeDetails = {
+export interface ValueChangeDetails {
value: string
valueAsColor: Color
}
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
+export type ExtendedColorChannel = ColorChannel | "hex" | "css"
+
type ElementIds = Partial<{
content: string
area: string
@@ -39,9 +27,7 @@ type ElementIds = Partial<{
channelSliderThumb(id: ColorChannel): string
}>
-export type { Color, ColorAxes, ColorChannel, ColorFormat, ColorType }
-
-type PublicContext = CommonProperties & {
+interface PublicContext extends CommonProperties {
/**
* The ids of the elements in the color picker. Useful for composition.
*/
@@ -65,11 +51,11 @@ type PublicContext = CommonProperties & {
/**
* Handler that is called when the value changes, as the user drags.
*/
- onChange?: (details: ChangeDetails) => void
+ onValueChange?: (details: ValueChangeDetails) => void
/**
* Handler that is called when the user stops dragging.
*/
- onChangeEnd?: (details: ChangeDetails) => void
+ onValueChangeEnd?: (details: ValueChangeDetails) => void
/**
* The name for the form input
*/
@@ -124,9 +110,9 @@ type ComputedContext = Readonly<{
export type UserDefinedContext = RequiredBy
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle" | "focused" | "dragging"
}
@@ -134,7 +120,31 @@ export type State = S.State
export type Send = S.Send
-export type MachineApi = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface ColorChannelProps {
+ channel: ColorChannel
+ orientation?: Orientation
+}
+
+export interface ColorChannelInputProps {
+ channel: ExtendedColorChannel
+ orientation?: Orientation
+}
+
+export interface ColorAreaProps {
+ xChannel: ColorChannel
+ yChannel: ColorChannel
+}
+
+export interface ColorSwatchProps {
+ readOnly?: boolean
+ value: string | Color
+}
+
+export interface MachineApi {
/**
* Whether the color picker is being dragged
*/
@@ -171,6 +181,7 @@ export type MachineApi = {
* Function to set the color alpha
*/
setAlpha(value: number): void
+
contentProps: T["element"]
hiddenInputProps: T["input"]
getAreaProps(props: ColorAreaProps): T["element"]
@@ -184,3 +195,9 @@ export type MachineApi = {
getSwatchBackgroundProps(props: ColorSwatchProps): T["element"]
getSwatchProps(props: ColorSwatchProps): T["element"]
}
+
+/* -----------------------------------------------------------------------------
+ * Re-exported types
+ * -----------------------------------------------------------------------------*/
+
+export type { Color, ColorAxes, ColorChannel, ColorFormat, ColorType }
diff --git a/packages/machines/combobox/src/combobox.machine.ts b/packages/machines/combobox/src/combobox.machine.ts
index 290a3aff91..21c8e4dedc 100644
--- a/packages/machines/combobox/src/combobox.machine.ts
+++ b/packages/machines/combobox/src/combobox.machine.ts
@@ -529,10 +529,10 @@ export function machine(userContext: UserDefinedContex
contentEl.scrollTop = 0
},
invokeOnOpen(ctx) {
- ctx.onOpen?.()
+ ctx.onOpenChange?.({ open: true })
},
invokeOnClose(ctx) {
- ctx.onClose?.()
+ ctx.onOpenChange?.({ open: false })
},
highlightFirstItem(ctx) {
const value = ctx.collection.first()
@@ -570,7 +570,10 @@ export function machine(userContext: UserDefinedContex
const invoke = {
selectionChange: (ctx: MachineContext) => {
- ctx.onChange?.({ value: Array.from(ctx.value), items: ctx.selectedItems })
+ ctx.onValueChange?.({
+ value: Array.from(ctx.value),
+ items: ctx.selectedItems,
+ })
// side effect: sync inputValue
ctx.inputValue = match(ctx.selectionBehavior, {
@@ -580,10 +583,13 @@ const invoke = {
})
},
highlightChange: (ctx: MachineContext) => {
- ctx.onHighlight?.({ value: ctx.highlightedValue, item: ctx.highlightedItem })
+ ctx.onHighlightChange?.({
+ highlightedValue: ctx.highlightedValue,
+ highligtedItem: ctx.highlightedItem,
+ })
},
inputChange: (ctx: MachineContext) => {
- ctx.onInputChange?.({ value: ctx.inputValue })
+ ctx.onInputValueChange?.({ value: ctx.inputValue })
},
}
diff --git a/packages/machines/combobox/src/combobox.types.ts b/packages/machines/combobox/src/combobox.types.ts
index 716ce7262c..ebe9ecd17a 100644
--- a/packages/machines/combobox/src/combobox.types.ts
+++ b/packages/machines/combobox/src/combobox.types.ts
@@ -4,9 +4,33 @@ import type { InteractOutsideHandlers } from "@zag-js/dismissable"
import type { Placement, PositioningOptions } from "@zag-js/popper"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
-export type { CollectionOptions, CollectionItem }
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
-type IntlTranslations = {
+export interface ValueChangeDetails {
+ value: string[]
+ items: T[]
+}
+
+export interface HighlightChangeDetails {
+ highlightedValue: string | null
+ highligtedItem: T | null
+}
+
+export interface InputValueChangeDetails {
+ value: string
+}
+
+export interface OpenChangeDetails {
+ open: boolean
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
+interface IntlTranslations {
triggerLabel?: string
clearTriggerLabel?: string
}
@@ -25,145 +49,125 @@ type ElementIds = Partial<{
itemGroupLabel(id: string | number): string
}>
-export type ValueChangeDetails = {
+interface PublicContext
+ extends DirectionProperty,
+ CommonProperties,
+ InteractOutsideHandlers {
+ /**
+ * The ids of the elements in the combobox. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * The current value of the combobox's input
+ */
+ inputValue: string
+ /**
+ * The `name` attribute of the combobox's input. Useful for form submission
+ */
+ name?: string
+ /**
+ * The associate form of the combobox.
+ */
+ form?: string
+ /**
+ * Whether the combobox is disabled
+ */
+ disabled?: boolean
+ /**
+ * Whether the combobox is readonly. This puts the combobox in a "non-editable" mode
+ * but the user can still interact with it
+ */
+ readOnly?: boolean
+ /**
+ * Whether the combobox is required
+ */
+ invalid?: boolean
+ /**
+ * The placeholder text of the combobox's input
+ */
+ placeholder?: string
+ /**
+ * The active item's id. Used to set the `aria-activedescendant` attribute
+ */
+ highlightedValue: string | null
+ /**
+ * The keys of the selected items
+ */
value: string[]
- items: T[]
-}
-
-export type HighlightChangeDetails = {
- value: string | null
- item: T | null
-}
-
-type InputValueChangeDetails = {
- value: string
+ /**
+ * Defines the auto-completion behavior of the combobox.
+ *
+ * - `autohighlight`: The first focused item is highlighted as the user types
+ * - `autocomplete`: Navigating the listbox with the arrow keys selects the item and the input is updated
+ */
+ inputBehavior: "autohighlight" | "autocomplete" | "none"
+ /**
+ * The behavior of the combobox input when an item is selected
+ *
+ * - `replace`: The selected item string is set as the input value
+ * - `clear`: The input value is cleared
+ * - `preserve`: The input value is preserved
+ */
+ selectionBehavior: "clear" | "replace" | "preserve"
+ /**
+ * Whether to select the higlighted item on interaction outside the combobox
+ */
+ selectOnBlur: boolean
+ /**
+ * Whether to autofocus the input on mount
+ */
+ autoFocus?: boolean
+ /**
+ * Whether to open the combobox popup on initial click on the input
+ */
+ openOnClick?: boolean
+ /**
+ * Whether to allow custom values or free values in the input
+ */
+ allowCustomValue?: boolean
+ /**
+ * Whether to loop the keyboard navigation through the items
+ */
+ loop?: boolean
+ /**
+ * The positioning options to dynamically position the menu
+ */
+ positioning: PositioningOptions
+ /**
+ * Function called when the input's value changes
+ */
+ onInputValueChange?: (details: InputValueChangeDetails) => void
+ /**
+ * Function called when a new item is selected
+ */
+ onValueChange?: (details: ValueChangeDetails) => void
+ /**
+ * Function called when an item is highlighted using the pointer
+ * or keyboard navigation.
+ */
+ onHighlightChange?: (details: HighlightChangeDetails) => void
+ /**
+ * Function called when the popup is opened
+ */
+ onOpenChange?: (details: OpenChangeDetails) => void
+ /**
+ * Specifies the localized strings that identifies the accessibility elements and their states
+ */
+ translations: IntlTranslations
+ /**
+ * The collection of items
+ */
+ collection: Collection
+ /**
+ * Whether to allow multiple selection
+ */
+ multiple?: boolean
+ /**
+ * Whether to close the combobox when an item is selected.
+ */
+ closeOnSelect?: boolean
}
-type PublicContext = DirectionProperty &
- CommonProperties &
- InteractOutsideHandlers & {
- /**
- * The ids of the elements in the combobox. Useful for composition.
- */
- ids?: ElementIds
- /**
- * The current value of the combobox's input
- */
- inputValue: string
- /**
- * The `name` attribute of the combobox's input. Useful for form submission
- */
- name?: string
- /**
- * The associate form of the combobox.
- */
- form?: string
- /**
- * Whether the combobox is disabled
- */
- disabled?: boolean
- /**
- * Whether the combobox is readonly. This puts the combobox in a "non-editable" mode
- * but the user can still interact with it
- */
- readOnly?: boolean
- /**
- * Whether the combobox is required
- */
- invalid?: boolean
- /**
- * The placeholder text of the combobox's input
- */
- placeholder?: string
- /**
- * The active item's id. Used to set the `aria-activedescendant` attribute
- */
- highlightedValue: string | null
- /**
- * The keys of the selected items
- */
- value: string[]
- /**
- * Defines the auto-completion behavior of the combobox.
- *
- * - `autohighlight`: The first focused item is highlighted as the user types
- * - `autocomplete`: Navigating the listbox with the arrow keys selects the item and the input is updated
- */
- inputBehavior: "autohighlight" | "autocomplete" | "none"
- /**
- * The behavior of the combobox input when an item is selected
- *
- * - `replace`: The selected item string is set as the input value
- * - `clear`: The input value is cleared
- * - `preserve`: The input value is preserved
- */
- selectionBehavior: "clear" | "replace" | "preserve"
- /**
- * Whether to select the higlighted item on interaction outside the combobox
- */
- selectOnBlur: boolean
- /**
- * Whether to autofocus the input on mount
- */
- autoFocus?: boolean
- /**
- * Whether to open the combobox popup on initial click on the input
- */
- openOnClick?: boolean
- /**
- * Whether to allow custom values or free values in the input
- */
- allowCustomValue?: boolean
- /**
- * Whether to loop the keyboard navigation through the items
- */
- loop?: boolean
- /**
- * The positioning options to dynamically position the menu
- */
- positioning: PositioningOptions
- /**
- * Function called when the input's value changes
- */
- onInputChange?: (details: InputValueChangeDetails) => void
- /**
- * Function called when a new item is selected
- */
- onChange?: (details: ValueChangeDetails) => void
- /**
- * Function called when an item is highlighted using the pointer
- * or keyboard navigation.
- */
- onHighlight?: (details: HighlightChangeDetails) => void
- /**
- * Function called when the popup is opened
- */
- onOpen?: VoidFunction
- /**
- * Function called when the popup is closed
- */
- onClose?: VoidFunction
- /**
- * Specifies the localized strings that identifies the accessibility elements and their states
- */
- translations: IntlTranslations
- /**
- * The collection of items
- */
- collection: Collection
- /**
- * Whether to allow multiple selection
- */
- multiple?: boolean
- /**
- * Whether to close the combobox when an item is selected.
- */
- closeOnSelect?: boolean
- }
-
-/**
- * This is the actual context exposed to the user.
- */
export type UserDefinedContext = RequiredBy<
PublicContext,
"id" | "collection"
@@ -222,9 +226,9 @@ type PrivateContext = Context<{
composing: boolean
}>
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle" | "focused" | "suggesting" | "interacting"
tags: "open" | "focused" | "idle" | "closed"
}
@@ -233,28 +237,30 @@ export type State = S.State
export type Send = S.Send
-export type ItemProps = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface ItemProps {
item: CollectionItem
}
-export type ItemState = {
+export interface ItemState {
value: string
isDisabled: boolean
isSelected: boolean
isHighlighted: boolean
}
-export type ItemGroupProps = {
+export interface ItemGroupProps {
id: string
}
-export type ItemGroupLabelProps = {
+export interface ItemGroupLabelProps {
htmlFor: string
}
-export type { Placement, PositioningOptions }
-
-export type MachineApi = {
+export interface MachineApi {
/**
* Whether the combobox is focused
*/
@@ -350,3 +356,9 @@ export type MachineApi value.toString().split("T")[0]
@@ -94,8 +93,6 @@ export function connect(state: State, send: Send, normalize
isOpen,
view: state.context.view,
- matchView,
-
getDaysInWeek(weekIndex: number, from = startValue) {
return getDaysInWeek(weekIndex, from, locale, startOfWeek)
},
diff --git a/packages/machines/date-picker/src/date-picker.machine.ts b/packages/machines/date-picker/src/date-picker.machine.ts
index d027aeda18..1aff9a44cd 100644
--- a/packages/machines/date-picker/src/date-picker.machine.ts
+++ b/packages/machines/date-picker/src/date-picker.machine.ts
@@ -40,7 +40,7 @@ const getInitialContext = (ctx: Partial): MachineContext => {
value = value.map((date) => constrainValue(date, ctx.min, ctx.max))
// get initial focused value
- let focusedValue = value.at(0) || ctx.focusedValue || getTodayDate(timeZone)
+ let focusedValue = value[0] || ctx.focusedValue || getTodayDate(timeZone)
focusedValue = constrainValue(focusedValue, ctx.min, ctx.max)
// get initial start value for visible range
@@ -145,7 +145,7 @@ export function machine(userContext: UserDefinedContext) {
},
"TRIGGER.CLICK": {
target: "open",
- actions: ["focusFirstSelectedDate"],
+ actions: ["focusFirstSelectedDate", "invokeOnOpen"],
},
},
},
@@ -155,7 +155,7 @@ export function machine(userContext: UserDefinedContext) {
on: {
"TRIGGER.CLICK": {
target: "open",
- actions: ["setViewToDay", "focusFirstSelectedDate"],
+ actions: ["setViewToDay", "focusFirstSelectedDate", "invokeOnOpen"],
},
"INPUT.CHANGE": {
actions: ["focusParsedDate"],
@@ -168,7 +168,7 @@ export function machine(userContext: UserDefinedContext) {
},
"CELL.FOCUS": {
target: "open",
- actions: ["setView"],
+ actions: ["setView", "invokeOnOpen"],
},
},
},
@@ -209,6 +209,7 @@ export function machine(userContext: UserDefinedContext) {
"setStartIndex",
"clearHoveredDate",
"focusInputElement",
+ "invokeOnClose",
],
},
// ===
@@ -227,7 +228,7 @@ export function machine(userContext: UserDefinedContext) {
},
{
target: "focused",
- actions: ["setFocusedDate", "setSelectedDate", "focusInputElement"],
+ actions: ["setFocusedDate", "setSelectedDate", "focusInputElement", "invokeOnClose"],
},
// ===
],
@@ -250,7 +251,7 @@ export function machine(userContext: UserDefinedContext) {
"GRID.ESCAPE": {
guard: not("isInline"),
target: "focused",
- actions: ["setViewToDay", "focusFirstSelectedDate", "focusTriggerElement"],
+ actions: ["setViewToDay", "focusFirstSelectedDate", "focusTriggerElement", "invokeOnClose"],
},
"GRID.ENTER": [
{
@@ -273,7 +274,7 @@ export function machine(userContext: UserDefinedContext) {
{
target: "focused",
guard: and("isRangePicker", "isSelectingEndDate"),
- actions: ["setSelectedDate", "setStartIndex", "focusInputElement"],
+ actions: ["setSelectedDate", "setStartIndex", "focusInputElement", "invokeOnClose"],
},
// ===
{
@@ -291,7 +292,7 @@ export function machine(userContext: UserDefinedContext) {
},
{
target: "focused",
- actions: ["selectFocusedDate", "focusInputElement"],
+ actions: ["selectFocusedDate", "focusInputElement", "invokeOnClose"],
},
// ===
],
@@ -333,6 +334,7 @@ export function machine(userContext: UserDefinedContext) {
],
"TRIGGER.CLICK": {
target: "focused",
+ actions: ["invokeOnClose"],
},
"VIEW.CHANGE": [
{
@@ -348,11 +350,11 @@ export function machine(userContext: UserDefinedContext) {
{
guard: "isTargetFocusable",
target: "idle",
- actions: ["setStartIndex"],
+ actions: ["setStartIndex", "invokeOnClose"],
},
{
target: "focused",
- actions: ["focusTriggerElement", "setStartIndex"],
+ actions: ["focusTriggerElement", "setStartIndex", "invokeOnClose"],
},
],
},
@@ -660,6 +662,12 @@ export function machine(userContext: UserDefinedContext) {
const startValue = alignDate(ctx.focusedValue, "start", { months: ctx.numOfMonths }, ctx.locale)
ctx.startValue = startValue
},
+ invokeOnOpen(ctx) {
+ ctx.onOpenChange?.({ open: true })
+ },
+ invokeOnClose(ctx) {
+ ctx.onOpenChange?.({ open: false })
+ },
},
compareFns: {
startValue: (a, b) => a.toString() === b.toString(),
@@ -672,16 +680,20 @@ export function machine(userContext: UserDefinedContext) {
const invoke = {
change(ctx: MachineContext) {
- const details = { value: Array.from(ctx.value), view: ctx.view }
- ctx.onChange?.(details)
+ ctx.onValueChange?.({
+ value: Array.from(ctx.value),
+ view: ctx.view,
+ })
},
focusChange(ctx: MachineContext) {
- const details = { focusedValue: ctx.focusedValue, value: Array.from(ctx.value), view: ctx.view }
- ctx.onFocusChange?.(details)
+ ctx.onFocusChange?.({
+ focusedValue: ctx.focusedValue,
+ value: Array.from(ctx.value),
+ view: ctx.view,
+ })
},
viewChange(ctx: MachineContext) {
- const details = { view: ctx.view }
- ctx.onViewChange?.(details)
+ ctx.onViewChange?.({ view: ctx.view })
},
}
diff --git a/packages/machines/date-picker/src/date-picker.types.ts b/packages/machines/date-picker/src/date-picker.types.ts
index 509fd72131..9742bad974 100644
--- a/packages/machines/date-picker/src/date-picker.types.ts
+++ b/packages/machines/date-picker/src/date-picker.types.ts
@@ -11,13 +11,38 @@ import type { Placement, PositioningOptions } from "@zag-js/popper"
import type { StateMachine as S } from "@zag-js/core"
import type { LiveRegion } from "@zag-js/live-region"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
-import type { matchView } from "./date-picker.utils"
-type ChangeDetails = T & {
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export type DateView = "day" | "month" | "year"
+
+export interface ValueChangeDetails {
value: DateValue[]
+ view: DateView
+}
+
+export interface FocusChangeDetails extends ValueChangeDetails {
+ focusedValue: DateValue
+ view: DateView
+}
+
+export interface ViewChangeDetails {
+ view: DateView
}
-type IntlMessages = {
+export interface OpenChangeDetails {
+ open: boolean
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
+export type SelectionMode = "single" | "multiple" | "range"
+
+interface IntlMessages {
placeholder: (locale: string) => { year: string; month: string; day: string }
}
@@ -38,165 +63,154 @@ type ElementIds = Partial<{
positioner: string
}>
-export type SelectionMode = "single" | "multiple" | "range"
-
-export type Offset = {
- amount: number
- visibleRange: { start: DateValue; end: DateValue }
-}
-
-export type DayCellProps = {
- value: DateValue
- disabled?: boolean
- offset?: Offset
-}
-
-export type MachineState = {
- tags: "open" | "closed"
- value: "idle" | "focused" | "open"
-}
-
-export type GridProps = {
- view?: DateView
- columns?: number
- id?: string
-}
-
-export type CellProps = {
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The localized messages to use.
+ */
+ messages?: IntlMessages
+ /**
+ * The ids of the elements in the date picker. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * The `name` attribute of the input element.
+ */
+ name?: string
+ /**
+ * The locale (BCP 47 language tag) to use when formatting the date.
+ */
+ locale: string
+ /**
+ * The time zone to use
+ */
+ timeZone: string
+ /**
+ * Whether the calendar is disabled.
+ */
disabled?: boolean
- value: number
-}
-
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The localized messages to use.
- */
- messages?: IntlMessages
- /**
- * The ids of the elements in the date picker. Useful for composition.
- */
- ids?: ElementIds
- /**
- * The `name` attribute of the input element.
- */
- name?: string
- /**
- * The locale (BCP 47 language tag) to use when formatting the date.
- */
- locale: string
- /**
- * The time zone to use
- */
- timeZone: string
- /**
- * Whether the calendar is disabled.
- */
- disabled?: boolean
- /**
- * Whether the calendar is read-only.
- */
- readOnly?: boolean
- /**
- * The minimum date that can be selected.
- */
- min?: DateValue
- /**
- * The maximum date that can be selected.
- */
- max?: DateValue
- /**
- * Whether the calendar should be displayed inline.
- */
- inline?: boolean
- /**
- * The selected date(s).
- */
- value: DateValue[]
- /**
- * The focused date.
- */
- focusedValue: DateValue
- /**
- * The number of months to display.
- */
- numOfMonths: number
- /**
- * The first day of the week.
- * `0` - Sunday
- * `1` - Monday
- * `2` - Tuesday
- * `3` - Wednesday
- * `4` - Thursday
- * `5` - Friday
- * `6` - Saturday
- */
- startOfWeek?: number
- /**
- * Whether the calendar should have a fixed number of weeks.
- * This renders the calendar with 6 weeks instead of 5 or 6.
- */
- fixedWeeks?: boolean
- /**
- * Function called when the value changes.
- */
- onChange?: (details: ChangeDetails) => void
- /**
- * Function called when the focused date changes.
- */
- onFocusChange?: (details: ChangeDetails<{ focusedValue: DateValue; view: DateView }>) => void
- /**
- * Function called when the view changes.
- */
- onViewChange?: (details: { view: DateView }) => void
- /**
- * Returns whether a date of the calendar is available.
- */
- isDateUnavailable?: (date: DateValue, locale: string) => boolean
- /**
- * The selection mode of the calendar.
- * - `single` - only one date can be selected
- * - `multiple` - multiple dates can be selected
- * - `range` - a range of dates can be selected
- */
- selectionMode: SelectionMode
- /**
- * The format of the date to display in the input.
- */
- format?: (date: DateValue[]) => string
- /**
- * The format of the date to display in the input.
- */
- parse?: (value: string) => DateValue[]
- /**
- * The view of the calendar
- * @default "day"
- */
- view: DateView
- /**
- * Whether the calendar should be modal. This means that the calendar will
- * block interaction with the rest of the page, and trap focus within it.
- */
- modal?: boolean
- /**
- * The user provided options used to position the date picker content
- */
- positioning: PositioningOptions
- }
-
-export type DateView = "day" | "month" | "year"
-
-export type ViewProps = {
- view?: DateView
+ /**
+ * Whether the calendar is read-only.
+ */
+ readOnly?: boolean
+ /**
+ * The minimum date that can be selected.
+ */
+ min?: DateValue
+ /**
+ * The maximum date that can be selected.
+ */
+ max?: DateValue
+ /**
+ * Whether the calendar should be displayed inline.
+ */
+ inline?: boolean
+ /**
+ * The selected date(s).
+ */
+ value: DateValue[]
+ /**
+ * The focused date.
+ */
+ focusedValue: DateValue
+ /**
+ * The number of months to display.
+ */
+ numOfMonths: number
+ /**
+ * The first day of the week.
+ * `0` - Sunday
+ * `1` - Monday
+ * `2` - Tuesday
+ * `3` - Wednesday
+ * `4` - Thursday
+ * `5` - Friday
+ * `6` - Saturday
+ */
+ startOfWeek?: number
+ /**
+ * Whether the calendar should have a fixed number of weeks.
+ * This renders the calendar with 6 weeks instead of 5 or 6.
+ */
+ fixedWeeks?: boolean
+ /**
+ * Function called when the value changes.
+ */
+ onValueChange?: (details: ValueChangeDetails) => void
+ /**
+ * Function called when the focused date changes.
+ */
+ onFocusChange?: (details: FocusChangeDetails) => void
+ /**
+ * Function called when the view changes.
+ */
+ onViewChange?: (details: ViewChangeDetails) => void
+ /**
+ * Function called when the calendar opens or closes.
+ */
+ onOpenChange?: (details: OpenChangeDetails) => void
+ /**
+ * Returns whether a date of the calendar is available.
+ */
+ isDateUnavailable?: (date: DateValue, locale: string) => boolean
+ /**
+ * The selection mode of the calendar.
+ * - `single` - only one date can be selected
+ * - `multiple` - multiple dates can be selected
+ * - `range` - a range of dates can be selected
+ */
+ selectionMode: SelectionMode
+ /**
+ * The format of the date to display in the input.
+ */
+ format?: (date: DateValue[]) => string
+ /**
+ * The format of the date to display in the input.
+ */
+ parse?: (value: string) => DateValue[]
+ /**
+ * The view of the calendar
+ * @default "day"
+ */
+ view: DateView
+ /**
+ * Whether the calendar should be modal. This means that the calendar will
+ * block interaction with the rest of the page, and trap focus within it.
+ */
+ modal?: boolean
+ /**
+ * The user provided options used to position the date picker content
+ */
+ positioning: PositioningOptions
}
type PrivateContext = Context<{
+ /**
+ * @internal
+ * The start date of the current visible duration.
+ */
startValue: DateValue
+ /**
+ * @internal
+ * Whether the calendar has focus
+ */
hasFocus?: boolean
+ /**
+ * @internal
+ * The live region to announce changes
+ */
announcer?: LiveRegion
+ /**
+ * @internal
+ * The input element's value
+ */
inputValue: string
+ /**
+ * @internal
+ * The current hovered date. Useful for range selection mode.
+ */
hoveredValue: DateValue | null
/**
+ * @internal
* The index of the currently active date.
* Used in range selection mode.
*/
@@ -253,15 +267,91 @@ type ComputedContext = Readonly<{
export type UserDefinedContext = RequiredBy
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
+
+export interface MachineState {
+ tags: "open" | "closed"
+ value: "idle" | "focused" | "open"
+}
export type State = S.State
export type Send = S.Send
-export type { Calendar, CalendarDate, CalendarDateTime, DateDuration, DateFormatter, DateValue, ZonedDateTime }
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface Offset {
+ amount: number
+ visibleRange: { start: DateValue; end: DateValue }
+}
+
+export interface CellProps {
+ disabled?: boolean
+ value: number
+}
-export type MachineApi = {
+export interface CellState {
+ isFocused: boolean
+ isSelectable: boolean
+ isSelected: boolean
+ valueText: string
+ readonly isDisabled: boolean
+}
+
+export interface DayCellProps {
+ value: DateValue
+ disabled?: boolean
+ offset?: Offset
+}
+
+export interface DayCellState {
+ isInvalid: boolean
+ isDisabled: boolean
+ isSelected: boolean
+ isUnavailable: boolean
+ isOutsideRange: boolean
+ isInRange: boolean
+ isFirstInRange: boolean
+ isLastInRange: boolean
+ isToday: boolean
+ isWeekend: boolean
+ formattedDate: string
+ readonly isFocused: boolean
+ readonly ariaLabel: string
+ readonly isSelectable: boolean
+}
+
+export interface GridProps {
+ view?: DateView
+ columns?: number
+ id?: string
+}
+
+export interface ViewProps {
+ view?: DateView
+}
+
+export interface MonthGridProps {
+ columns?: number
+ format?: "short" | "long"
+}
+
+interface GridItem {
+ label: string
+ value: number
+}
+
+export type MonthGridValue = GridItem[][]
+
+export interface YearGridProps {
+ columns?: number
+}
+
+export type YearGridValue = GridItem[][]
+
+export interface MachineApi {
/**
* Whether the input is focused
*/
@@ -274,10 +364,6 @@ export type MachineApi = {
* The current view of the date picker
*/
view: DateView
- /**
- * Matcher for the current view of the date picker
- */
- matchView: typeof matchView
/**
* Returns an array of days in the week index counted from the provided start date, or the first visible date if not given.
*/
@@ -388,18 +474,12 @@ export type MachineApi = {
/**
* Returns the months of the year
*/
- getYears(): {
- label: string
- value: number
- }[]
+ getYears(): GridItem[]
/**
* Returns the years of the decade based on the columns.
* Represented as an array of arrays of years.
*/
- getYearsGrid(props?: { columns?: number }): {
- label: string
- value: number
- }[][]
+ getYearsGrid(props?: YearGridProps): YearGridValue
/**
* Returns the start and end years of the decade.
*/
@@ -410,18 +490,12 @@ export type MachineApi = {
/**
* Returns the months of the year
*/
- getMonths(props?: { format?: "short" | "long" }): {
- label: string
- value: number
- }[]
+ getMonths(props?: { format?: "short" | "long" }): GridItem[]
/**
* Returns the months of the year based on the columns.
* Represented as an array of arrays of months.
*/
- getMonthsGrid(props?: { columns?: number; format?: "short" | "long" }): {
- label: string
- value: number
- }[][]
+ getMonthsGrid(props?: MonthGridProps): MonthGridValue
/**
* Formats the given date value based on the provided options.
*/
@@ -438,48 +512,24 @@ export type MachineApi = {
* Goes to the previous month/year/decade.
*/
goToPrev(): void
+ /**
+ * Returns the state details for a given cell.
+ */
+ getDayCellState(props: DayCellProps): DayCellState
+ /**
+ * Returns the state details for a given month cell.
+ */
+ getMonthCellState(props: CellProps): CellState
+
controlProps: T["element"]
contentProps: T["element"]
positionerProps: T["element"]
-
getGridProps(props?: GridProps): T["element"]
- /**
- * Returns the state details for a given cell.
- */
- getDayCellState(props: DayCellProps): {
- isInvalid: boolean
- isDisabled: boolean
- isSelected: boolean
- isUnavailable: boolean
- isOutsideRange: boolean
- isInRange: boolean
- isFirstInRange: boolean
- isLastInRange: boolean
- isToday: boolean
- isWeekend: boolean
- formattedDate: string
- readonly isFocused: boolean
- readonly ariaLabel: string
- readonly isSelectable: boolean
- }
getDayCellProps(props: DayCellProps): T["element"]
getDayCellTriggerProps(props: DayCellProps): T["element"]
- getMonthCellState(props: CellProps): {
- isFocused: boolean
- isSelectable: boolean
- isSelected: boolean
- valueText: string
- readonly isDisabled: boolean
- }
getMonthCellProps(props: CellProps): T["element"]
getMonthCellTriggerProps(props: CellProps): T["element"]
- getYearCellState(props: CellProps): {
- isFocused: boolean
- isSelectable: boolean
- isSelected: boolean
- valueText: string
- readonly isDisabled: boolean
- }
+ getYearCellState(props: CellProps): CellState
getYearCellProps(props: CellProps): T["element"]
getYearCellTriggerProps(props: CellProps): T["element"]
getNextTriggerProps(props?: ViewProps): T["button"]
@@ -492,3 +542,9 @@ export type MachineApi = {
monthSelectProps: T["select"]
yearSelectProps: T["select"]
}
+
+/* -----------------------------------------------------------------------------
+ * Re-exported types
+ * -----------------------------------------------------------------------------*/
+
+export type { Calendar, CalendarDate, CalendarDateTime, DateDuration, DateFormatter, DateValue, ZonedDateTime }
diff --git a/packages/machines/date-picker/src/date-picker.utils.ts b/packages/machines/date-picker/src/date-picker.utils.ts
index 4027485623..ae1d14ce16 100644
--- a/packages/machines/date-picker/src/date-picker.utils.ts
+++ b/packages/machines/date-picker/src/date-picker.utils.ts
@@ -1,4 +1,5 @@
import { DateFormatter, type DateValue } from "@internationalized/date"
+import { match } from "@zag-js/utils"
import type { DateView, MachineContext } from "./date-picker.types"
export function adjustStartAndEndDate(value: DateValue[]) {
@@ -17,12 +18,6 @@ export function sortDates(values: DateValue[]) {
return values.sort((a, b) => a.compare(b))
}
-export function matchView(view: DateView, values: { year: T; month: T; day: T }) {
- if (view === "year") return values.year
- if (view === "month") return values.month
- return values.day
-}
-
export function formatValue(ctx: Pick) {
const formatter = new DateFormatter(ctx.locale, {
timeZone: ctx.timeZone,
@@ -47,7 +42,7 @@ export function formatValue(ctx: Pick
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The ids of the elements in the dialog. Useful for composition.
- */
- ids?: ElementIds
- /**
- * Whether to trap focus inside the dialog when it's opened
- */
- trapFocus: boolean
- /**
- * Whether to prevent scrolling behind the dialog when it's opened
- */
- preventScroll: boolean
- /**
- * Whether to prevent pointer interaction outside the element and hide all content below it
- */
- modal?: boolean
- /**
- * Element to receive focus when the dialog is opened
- */
- initialFocusEl?: MaybeElement | (() => MaybeElement)
- /**
- * Element to receive focus when the dialog is closed
- */
- finalFocusEl?: MaybeElement | (() => MaybeElement)
- /**
- * Whether to restore focus to the element that had focus before the dialog was opened
- */
- restoreFocus?: boolean
- /**
- * Callback to be invoked when the dialog is closed
- */
- onClose?: () => void
- /**
- * Callback to be invoked when the dialog is opened
- */
- onOpen?: () => void
- /**
- * Whether to close the dialog when the outside is clicked
- */
- closeOnOutsideClick: boolean
- /**
- * Callback to be invoked when the outside is clicked
- */
- onOutsideClick?: () => void
- /**
- * Whether to close the dialog when the escape key is pressed
- */
- closeOnEsc: boolean
- /**
- * Callback to be invoked when the escape key is pressed
- */
- onEsc?: () => void
- /**
- * Human readable label for the dialog, in event the dialog title is not rendered
- */
- "aria-label"?: string
- /**
- * The dialog's role
- * @default "dialog"
- */
- role: "dialog" | "alertdialog"
- /**
- * Whether the dialog is open
- */
- open?: boolean
- }
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The ids of the elements in the dialog. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * Whether to trap focus inside the dialog when it's opened
+ */
+ trapFocus: boolean
+ /**
+ * Whether to prevent scrolling behind the dialog when it's opened
+ */
+ preventScroll: boolean
+ /**
+ * Whether to prevent pointer interaction outside the element and hide all content below it
+ */
+ modal?: boolean
+ /**
+ * Element to receive focus when the dialog is opened
+ */
+ initialFocusEl?: MaybeElement | (() => MaybeElement)
+ /**
+ * Element to receive focus when the dialog is closed
+ */
+ finalFocusEl?: MaybeElement | (() => MaybeElement)
+ /**
+ * Whether to restore focus to the element that had focus before the dialog was opened
+ */
+ restoreFocus?: boolean
+ /**
+ * Callback to be invoked when the dialog is opened or closed
+ */
+ onOpenChange?: (details: OpenChangeDetails) => void
+ /**
+ * Whether to close the dialog when the outside is clicked
+ */
+ closeOnOutsideClick: boolean
+ /**
+ * Callback to be invoked when the outside is clicked
+ */
+ onOutsideClick?: VoidFunction
+ /**
+ * Whether to close the dialog when the escape key is pressed
+ */
+ closeOnEsc: boolean
+ /**
+ * Callback to be invoked when the escape key is pressed
+ */
+ onEsc?: VoidFunction
+ /**
+ * Human readable label for the dialog, in event the dialog title is not rendered
+ */
+ "aria-label"?: string
+ /**
+ * The dialog's role
+ * @default "dialog"
+ */
+ role: "dialog" | "alertdialog"
+ /**
+ * Whether the dialog is open
+ */
+ open?: boolean
+}
export type UserDefinedContext = RequiredBy
@@ -95,9 +102,9 @@ type PrivateContext = Context<{
}
}>
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "open" | "closed"
}
@@ -105,7 +112,11 @@ export type State = S.State
export type Send = S.Send
-export type MachineApi = {
+/* -----------------------------------------------------------------------------
+ * Component props
+ * -----------------------------------------------------------------------------*/
+
+export interface MachineApi {
/**
* Whether the dialog is open
*/
@@ -118,6 +129,7 @@ export type MachineApi = {
* Function to close the dialog
*/
close(): void
+
triggerProps: T["button"]
backdropProps: T["element"]
containerProps: T["element"]
diff --git a/packages/machines/editable/src/editable.machine.ts b/packages/machines/editable/src/editable.machine.ts
index 8183d35ec8..35d4b41113 100644
--- a/packages/machines/editable/src/editable.machine.ts
+++ b/packages/machines/editable/src/editable.machine.ts
@@ -12,7 +12,10 @@ export function machine(userContext: UserDefinedContext) {
return createMachine(
{
id: "editable",
+
initial: ctx.startWithEditView ? "edit" : "preview",
+ entry: ctx.startWithEditView ? ["focusInput"] : undefined,
+
context: {
activationMode: "focus",
submitMode: "both",
@@ -144,7 +147,6 @@ export function machine(userContext: UserDefinedContext) {
raf(() => {
const inputEl = dom.getInputEl(ctx)
if (!inputEl) return
-
if (ctx.selectOnFocus) {
inputEl.select()
} else {
@@ -153,18 +155,17 @@ export function machine(userContext: UserDefinedContext) {
})
},
invokeOnCancel(ctx) {
- ctx.onCancel?.({ value: ctx.previousValue })
+ ctx.onValueRevert?.({ value: ctx.previousValue })
},
invokeOnSubmit(ctx) {
- ctx.onSubmit?.({ value: ctx.value })
+ ctx.onValueCommit?.({ value: ctx.value })
},
invokeOnEdit(ctx) {
ctx.onEdit?.()
},
syncInputValue(ctx) {
- const input = dom.getInputEl(ctx)
- if (!input) return
- input.value = ctx.value
+ const inputEl = dom.getInputEl(ctx)
+ dom.setValue(inputEl, ctx.value)
},
setValue(ctx, evt) {
set.value(ctx, evt.value)
@@ -185,7 +186,7 @@ export function machine(userContext: UserDefinedContext) {
const invoke = {
change(ctx: MachineContext) {
- ctx.onChange?.({ value: ctx.value })
+ ctx.onValueChange?.({ value: ctx.value })
},
}
diff --git a/packages/machines/editable/src/editable.types.ts b/packages/machines/editable/src/editable.types.ts
index d9eb0fa054..a223e4e17c 100644
--- a/packages/machines/editable/src/editable.types.ts
+++ b/packages/machines/editable/src/editable.types.ts
@@ -1,7 +1,19 @@
import type { StateMachine as S } from "@zag-js/core"
-import type { FocusOutsideEvent, InteractOutsideEvent, PointerDownOutsideEvent } from "@zag-js/interact-outside"
+import type { InteractOutsideHandlers } from "@zag-js/interact-outside"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface ValueChangeDetails {
+ value: string
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
export type ActivationMode = "focus" | "dblclick" | "none"
export type SubmitMode = "enter" | "blur" | "both" | "none"
@@ -25,107 +37,102 @@ type ElementIds = Partial<{
editTrigger: string
}>
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The ids of the elements in the editable. Useful for composition.
- */
- ids?: ElementIds
- /**
- * Whether the input's value is invalid.
- */
- invalid?: boolean
- /**
- * The name attribute of the editable component. Used for form submission.
- */
- name?: string
- /**
- * The associate form of the underlying input.
- */
- form?: string
- /**
- * Whether the editable should auto-resize to fit the content.
- */
- autoResize?: boolean
- /**
- * The activation mode for the preview element.
- *
- * - "focus" - Enter edit mode when the preview element is focused
- * - "dblclick" - Enter edit mode when the preview element is double-clicked
- * - "none" - No interaction with the preview element will trigger edit mode.
- *
- * @default "focus"
- */
- activationMode: ActivationMode
- /**
- * The action that triggers submit in the edit mode:
- *
- * - "enter" - Trigger submit when the enter key is pressed
- * - "blur" - Trigger submit when the editable is blurred
- * - "none" - No action will trigger submit. You need to use the submit button
- * - "both" - Pressing `Enter` and blurring the input will trigger submit
- *
- * @default "enter"
- */
- submitMode: SubmitMode
- /**
- * Whether to start with the edit mode active.
- */
- startWithEditView?: boolean
- /**
- * Whether to select the text in the input when it is focused.
- */
- selectOnFocus?: boolean
- /**
- * The value of the editable in both edit and preview mode
- */
- value: string
- /**
- * The maximum number of characters allowed in the editable
- */
- maxLength?: number
- /**
- * Whether the editable is disabled
- */
- disabled?: boolean
- /**
- * Whether the editable is readonly
- */
- readOnly?: boolean
- /**
- * The callback that is called when the editable's value is changed
- */
- onChange?: (details: { value: string }) => void
- /**
- * The callback that is called when the esc key is pressed or the cancel button is clicked
- */
- onCancel?: (details: { value: string }) => void
- /**
- * The callback that is called when the editable's value is submitted.
- */
- onSubmit?: (details: { value: string }) => void
- /**
- * The callback that is called when in the edit mode.
- */
- onEdit?: () => void
- /**
- * The placeholder value to show when the `value` is empty
- */
- placeholder?: string | { edit: string; preview: string }
- /**
- * Specifies the localized strings that identifies the accessibility elements and their states
- */
- translations: IntlTranslations
- /**
- * The element that should receive focus when the editable is closed.
- * By default, it will focus on the trigger element.
- */
- finalFocusEl?: () => HTMLElement | null
-
- onPointerDownOutside?: (event: PointerDownOutsideEvent) => void
- onFocusOutside?: (event: FocusOutsideEvent) => void
- onInteractOutside?: (event: InteractOutsideEvent) => void
- }
+interface PublicContext extends DirectionProperty, CommonProperties, InteractOutsideHandlers {
+ /**
+ * The ids of the elements in the editable. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * Whether the input's value is invalid.
+ */
+ invalid?: boolean
+ /**
+ * The name attribute of the editable component. Used for form submission.
+ */
+ name?: string
+ /**
+ * The associate form of the underlying input.
+ */
+ form?: string
+ /**
+ * Whether the editable should auto-resize to fit the content.
+ */
+ autoResize?: boolean
+ /**
+ * The activation mode for the preview element.
+ *
+ * - "focus" - Enter edit mode when the preview element is focused
+ * - "dblclick" - Enter edit mode when the preview element is double-clicked
+ * - "none" - No interaction with the preview element will trigger edit mode.
+ *
+ * @default "focus"
+ */
+ activationMode: ActivationMode
+ /**
+ * The action that triggers submit in the edit mode:
+ *
+ * - "enter" - Trigger submit when the enter key is pressed
+ * - "blur" - Trigger submit when the editable is blurred
+ * - "none" - No action will trigger submit. You need to use the submit button
+ * - "both" - Pressing `Enter` and blurring the input will trigger submit
+ *
+ * @default "enter"
+ */
+ submitMode: SubmitMode
+ /**
+ * Whether to start with the edit mode active.
+ */
+ startWithEditView?: boolean
+ /**
+ * Whether to select the text in the input when it is focused.
+ */
+ selectOnFocus?: boolean
+ /**
+ * The value of the editable in both edit and preview mode
+ */
+ value: string
+ /**
+ * The maximum number of characters allowed in the editable
+ */
+ maxLength?: number
+ /**
+ * Whether the editable is disabled
+ */
+ disabled?: boolean
+ /**
+ * Whether the editable is readonly
+ */
+ readOnly?: boolean
+ /**
+ * The callback that is called when the editable's value is changed
+ */
+ onValueChange?: (details: ValueChangeDetails) => void
+ /**
+ * The callback that is called when the esc key is pressed or the cancel button is clicked
+ */
+ onValueRevert?: (details: ValueChangeDetails) => void
+ /**
+ * The callback that is called when the editable's value is submitted.
+ */
+ onValueCommit?: (details: ValueChangeDetails) => void
+ /**
+ * The callback that is called when in the edit mode.
+ */
+ onEdit?: () => void
+ /**
+ * The placeholder value to show when the `value` is empty
+ */
+ placeholder?: string | { edit: string; preview: string }
+ /**
+ * Specifies the localized strings that identifies the accessibility elements and their states
+ */
+ translations: IntlTranslations
+ /**
+ * The element that should receive focus when the editable is closed.
+ * By default, it will focus on the trigger element.
+ */
+ finalFocusEl?: () => HTMLElement | null
+}
export type UserDefinedContext = RequiredBy
@@ -165,9 +172,9 @@ type PrivateContext = Context<{
previousValue: string
}>
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "preview" | "edit"
}
@@ -175,9 +182,11 @@ export type State = S.State
export type Send = S.Send
-export type { InteractOutsideEvent }
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
-export type MachineApi = {
+export interface MachineApi {
/**
* Whether the editable is in edit mode
*/
@@ -210,6 +219,7 @@ export type MachineApi = {
* Function to exit edit mode, and submit any changes
*/
submit(): void
+
rootProps: T["element"]
areaProps: T["element"]
labelProps: T["label"]
diff --git a/packages/machines/editable/src/index.ts b/packages/machines/editable/src/index.ts
index 09bb1df5d2..7678bd66de 100644
--- a/packages/machines/editable/src/index.ts
+++ b/packages/machines/editable/src/index.ts
@@ -1,4 +1,4 @@
export { anatomy } from "./editable.anatomy"
export { connect } from "./editable.connect"
export { machine } from "./editable.machine"
-export type { UserDefinedContext as Context, MachineApi as Api, InteractOutsideEvent } from "./editable.types"
+export type { UserDefinedContext as Context, MachineApi as Api } from "./editable.types"
diff --git a/packages/machines/file-upload/src/file-upload.connect.ts b/packages/machines/file-upload/src/file-upload.connect.ts
index 375f4bdbd7..977bc634a1 100644
--- a/packages/machines/file-upload/src/file-upload.connect.ts
+++ b/packages/machines/file-upload/src/file-upload.connect.ts
@@ -22,11 +22,11 @@ export function connect(state: State, send: Send, normalize
send({ type: "FILE.DELETE", file })
},
files: state.context.files,
- setValue(files: File[]) {
+ setFiles(files: File[]) {
const count = files.length
send({ type: "VALUE.SET", files, count })
},
- clearValue() {
+ clearFiles() {
send({ type: "VALUE.SET", files: [] })
},
diff --git a/packages/machines/file-upload/src/file-upload.machine.ts b/packages/machines/file-upload/src/file-upload.machine.ts
index ea679ee10f..b2a025c1ef 100644
--- a/packages/machines/file-upload/src/file-upload.machine.ts
+++ b/packages/machines/file-upload/src/file-upload.machine.ts
@@ -125,12 +125,6 @@ export function machine(userContext: UserDefinedContext) {
const nextFiles = ctx.files.filter((file) => file !== evt.file)
set.files(ctx, nextFiles)
},
- invokeOnChange(ctx) {
- ctx.onChange?.({
- acceptedFiles: ctx.files,
- rejectedFiles: ctx.rejectedFiles,
- })
- },
},
compareFns: {
files: (a, b) => a.length === b.length && a.every((file, i) => file === b[i]),
@@ -141,7 +135,10 @@ export function machine(userContext: UserDefinedContext) {
const invoke = {
change: (ctx: MachineContext) => {
- ctx.onChange?.({ acceptedFiles: ctx.files, rejectedFiles: ctx.rejectedFiles })
+ ctx.onFilesChange?.({
+ acceptedFiles: ctx.files,
+ rejectedFiles: ctx.rejectedFiles,
+ })
},
}
diff --git a/packages/machines/file-upload/src/file-upload.types.ts b/packages/machines/file-upload/src/file-upload.types.ts
index e14160732c..0513cde4a7 100644
--- a/packages/machines/file-upload/src/file-upload.types.ts
+++ b/packages/machines/file-upload/src/file-upload.types.ts
@@ -1,7 +1,25 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, PropTypes, RequiredBy } from "@zag-js/types"
-type PublicContext = CommonProperties & {
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface RejectedFile {
+ file: File
+ errors: (string | null)[]
+}
+
+export interface FileChangeDetails {
+ acceptedFiles: File[]
+ rejectedFiles: RejectedFile[]
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
+interface PublicContext extends CommonProperties {
/**
* The name of the underlying file input
*/
@@ -41,40 +59,40 @@ type PublicContext = CommonProperties & {
/**
* Function called when the value changes
*/
- onChange?: (details: ChangeDetails) => void
-}
-
-export type RejectedFile = {
- file: File
- errors: (string | null)[]
-}
-
-type ChangeDetails = {
- acceptedFiles: File[]
- rejectedFiles: RejectedFile[]
+ onFilesChange?: (details: FileChangeDetails) => void
}
-type PrivateContext = {
+interface PrivateContext {
/**
+ * @internal
* Whether the files includes any rejection
*/
invalid: boolean
/**
+ * @internal
* The rejected files
*/
rejectedFiles: RejectedFile[]
}
type ComputedContext = Readonly<{
+ /**
+ * @computed
+ * The accept attribute as a string
+ */
acceptAttr: string | undefined
+ /**
+ * @computed
+ * Whether the file can select multiple files
+ */
multiple: boolean
}>
export type UserDefinedContext = RequiredBy
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle" | "focused" | "open" | "dragging"
}
@@ -82,7 +100,11 @@ export type State = S.State
export type Send = S.Send
-export type MachineApi = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface MachineApi {
/**
* Whether the user is dragging something over the root element
*/
@@ -106,11 +128,12 @@ export type MachineApi = {
/**
* Function to set the value
*/
- setValue(files: File[]): void
+ setFiles(files: File[]): void
/**
* Function to clear the value
*/
- clearValue(): void
+ clearFiles(): void
+
rootProps: T["element"]
dropzoneProps: T["element"]
triggerProps: T["button"]
diff --git a/packages/machines/hover-card/src/hover-card.machine.ts b/packages/machines/hover-card/src/hover-card.machine.ts
index b8d731239e..bca76bb71e 100644
--- a/packages/machines/hover-card/src/hover-card.machine.ts
+++ b/packages/machines/hover-card/src/hover-card.machine.ts
@@ -148,10 +148,10 @@ export function machine(userContext: UserDefinedContext) {
},
actions: {
invokeOnClose(ctx) {
- ctx.onClose?.()
+ ctx.onOpenChange?.({ open: false })
},
invokeOnOpen(ctx) {
- ctx.onOpen?.()
+ ctx.onOpenChange?.({ open: true })
},
setIsPointer(ctx) {
ctx.isPointer = true
diff --git a/packages/machines/hover-card/src/hover-card.types.ts b/packages/machines/hover-card/src/hover-card.types.ts
index 368f2eedc3..f39dcc6515 100644
--- a/packages/machines/hover-card/src/hover-card.types.ts
+++ b/packages/machines/hover-card/src/hover-card.types.ts
@@ -2,6 +2,18 @@ import type { StateMachine as S } from "@zag-js/core"
import type { Placement, PositioningOptions } from "@zag-js/popper"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface OpenChangeDetails {
+ open: boolean
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
type ElementIds = Partial<{
trigger: string
content: string
@@ -9,60 +21,31 @@ type ElementIds = Partial<{
arrow: string
}>
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The ids of the elements in the popover. Useful for composition.
- */
- ids?: ElementIds
- /**
- * Function invoked when the hover card is opened.
- */
- onOpen?: VoidFunction
- /**
- * Function invoked when the hover card is closed.
- */
- onClose?: VoidFunction
- /**
- * The duration from when the mouse enters the trigger until the hover card opens.
- */
- openDelay: number
- /**
- * The duration from when the mouse leaves the trigger or content until the hover card closes.
- */
- closeDelay: number
- /**
- * Whether the hover card is open
- */
- open?: boolean
- /**
- * The user provided options used to position the popover content
- */
- positioning: PositioningOptions
- }
-
-export type MachineApi = {
+interface PublicContext extends DirectionProperty, CommonProperties {
/**
- * Whether the hover card is open
+ * The ids of the elements in the popover. Useful for composition.
*/
- isOpen: boolean
+ ids?: ElementIds
/**
- * Function to open the hover card
+ * Function called when the hover card opens or closes.
*/
- open(): void
+ onOpenChange?: (details: OpenChangeDetails) => void
/**
- * Function to close the hover card
+ * The duration from when the mouse enters the trigger until the hover card opens.
*/
- close(): void
+ openDelay: number
/**
- * Function to reposition the popover
+ * The duration from when the mouse leaves the trigger or content until the hover card closes.
*/
- setPositioning(options?: Partial): void
- arrowProps: T["element"]
- arrowTipProps: T["element"]
- triggerProps: T["element"]
- positionerProps: T["element"]
- contentProps: T["element"]
+ closeDelay: number
+ /**
+ * Whether the hover card is open
+ */
+ open?: boolean
+ /**
+ * The user provided options used to position the popover content
+ */
+ positioning: PositioningOptions
}
type PrivateContext = Context<{
@@ -84,7 +67,7 @@ export type UserDefinedContext = RequiredBy
export type MachineContext = PublicContext & PrivateContext & ComputedContext
-export type MachineState = {
+export interface MachineState {
value: "opening" | "open" | "closing" | "closed"
tags: "open" | "closed"
}
@@ -93,4 +76,36 @@ export type State = S.State
export type Send = S.Send
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface MachineApi {
+ /**
+ * Whether the hover card is open
+ */
+ isOpen: boolean
+ /**
+ * Function to open the hover card
+ */
+ open(): void
+ /**
+ * Function to close the hover card
+ */
+ close(): void
+ /**
+ * Function to reposition the popover
+ */
+ setPositioning(options?: Partial): void
+ arrowProps: T["element"]
+ arrowTipProps: T["element"]
+ triggerProps: T["element"]
+ positionerProps: T["element"]
+ contentProps: T["element"]
+}
+
+/* -----------------------------------------------------------------------------
+ * Re-exported types
+ * -----------------------------------------------------------------------------*/
+
export type { Placement, PositioningOptions }
diff --git a/packages/machines/menu/src/menu.machine.ts b/packages/machines/menu/src/menu.machine.ts
index e7a78a51eb..1aca6baa31 100644
--- a/packages/machines/menu/src/menu.machine.ts
+++ b/packages/machines/menu/src/menu.machine.ts
@@ -536,10 +536,10 @@ export function machine(userContext: UserDefinedContext) {
ctx.parent?.send("RESTORE_FOCUS")
},
invokeOnOpen(ctx) {
- ctx.onOpen?.()
+ ctx.onOpenChange?.({ open: true })
},
invokeOnClose(ctx) {
- ctx.onClose?.()
+ ctx.onOpenChange?.({ open: false })
},
},
},
diff --git a/packages/machines/menu/src/menu.types.ts b/packages/machines/menu/src/menu.types.ts
index cbe910d755..f213cb696f 100644
--- a/packages/machines/menu/src/menu.types.ts
+++ b/packages/machines/menu/src/menu.types.ts
@@ -5,6 +5,27 @@ import type { Placement, PositioningOptions } from "@zag-js/popper"
import type { Point } from "@zag-js/rect-utils"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface OpenChangeDetails {
+ open: boolean
+}
+
+export interface ValueChangeDetails {
+ name: string
+ value: string | string[]
+}
+
+export interface SelectionDetails {
+ value: string
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
type ElementIds = Partial<{
trigger: string
contextTrigger: string
@@ -15,58 +36,52 @@ type ElementIds = Partial<{
arrow: string
}>
-type PublicContext = DirectionProperty &
- CommonProperties &
- InteractOutsideHandlers & {
- /**
- * The ids of the elements in the menu. Useful for composition.
- */
- ids?: ElementIds
- /**
- * The values of radios and checkboxes in the menu.
- */
- value?: Record
- /**
- * Callback to be called when the menu values change (for radios and checkboxes).
- */
- onValueChange?: (details: { name: string; value: string | string[] }) => void
- /**
- * The `id` of the active menu item.
- */
- highlightedId: string | null
- /**
- * Function called when a menu item is selected.
- */
- onSelect?: (details: { value: string }) => void
- /**
- * The positioning point for the menu. Can be set by the context menu trigger or the button trigger.
- */
- anchorPoint: Point | null
- /**
- * Whether to loop the keyboard navigation.
- */
- loop: boolean
- /**
- * The options used to dynamically position the menu
- */
- positioning: PositioningOptions
- /**
- * Whether to close the menu when an option is selected
- */
- closeOnSelect: boolean
- /**
- * The accessibility label for the menu
- */
- "aria-label"?: string
- /**
- * Function called when the menu is opened
- */
- onOpen?: () => void
- /**
- * Function called when the menu is closed
- */
- onClose?: () => void
- }
+interface PublicContext extends DirectionProperty, CommonProperties, InteractOutsideHandlers {
+ /**
+ * The ids of the elements in the menu. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * The values of radios and checkboxes in the menu.
+ */
+ value?: Record
+ /**
+ * Callback to be called when the menu values change (for radios and checkboxes).
+ */
+ onValueChange?: (details: ValueChangeDetails) => void
+ /**
+ * The `id` of the active menu item.
+ */
+ highlightedId: string | null
+ /**
+ * Function called when a menu item is selected.
+ */
+ onSelect?: (details: SelectionDetails) => void
+ /**
+ * The positioning point for the menu. Can be set by the context menu trigger or the button trigger.
+ */
+ anchorPoint: Point | null
+ /**
+ * Whether to loop the keyboard navigation.
+ */
+ loop: boolean
+ /**
+ * The options used to dynamically position the menu
+ */
+ positioning: PositioningOptions
+ /**
+ * Whether to close the menu when an option is selected
+ */
+ closeOnSelect: boolean
+ /**
+ * The accessibility label for the menu
+ */
+ "aria-label"?: string
+ /**
+ * Function called when the menu opens or closes
+ */
+ onOpenChange?: (details: OpenChangeDetails) => void
+}
export type UserDefinedContext = RequiredBy
@@ -132,9 +147,9 @@ type PrivateContext = Context<{
focusTriggerOnClose?: boolean
}>
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle" | "open" | "closed" | "opening" | "closing" | "opening:contextmenu"
tags: "visible"
}
@@ -145,12 +160,16 @@ export type Send = S.Send
export type Service = Machine
-export type Api = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface Api {
getItemProps: (opts: ItemProps) => Record
triggerProps: Record
}
-export type ItemProps = {
+export interface ItemProps {
/**
* The `id` of the menu item option.
*/
@@ -170,7 +189,7 @@ export type ItemProps = {
closeOnSelect?: boolean
}
-export type OptionItemProps = Partial & {
+export interface OptionItemProps extends Partial {
/**
* The option's name as specified in menu's `context.values` object
*/
@@ -189,21 +208,21 @@ export type OptionItemProps = Partial & {
onCheckedChange?: (checked: boolean) => void
}
-export type GroupProps = {
+export interface GroupProps {
/**
* The `id` of the element that provides accessibility label to the option group
*/
id: string
}
-export type LabelProps = {
+export interface LabelProps {
/**
* The `id` of the group this refers to
*/
htmlFor: string
}
-export type MachineApi = {
+export interface MachineApi {
/**
* Whether the menu is open
*/
diff --git a/packages/machines/number-input/src/number-input.machine.ts b/packages/machines/number-input/src/number-input.machine.ts
index b15550efcb..83020fbd09 100644
--- a/packages/machines/number-input/src/number-input.machine.ts
+++ b/packages/machines/number-input/src/number-input.machine.ts
@@ -3,7 +3,7 @@ import { addDomEvent, requestPointerLock } from "@zag-js/dom-event"
import { isSafari, raf } from "@zag-js/dom-query"
import { observeAttributes } from "@zag-js/mutation-observer"
import { isAtMax, isAtMin, isWithinRange, valueOf } from "@zag-js/number-utils"
-import { callAll, compact, isEqual } from "@zag-js/utils"
+import { callAll, compact, isEqual, match } from "@zag-js/utils"
import { dom } from "./number-input.dom"
import type { MachineContext, MachineState, UserDefinedContext } from "./number-input.types"
import { utils } from "./number-input.utils"
@@ -327,29 +327,29 @@ export function machine(userContext: UserDefinedContext) {
ctx.hint = "set"
},
invokeOnFocus(ctx, evt) {
- let srcElement: HTMLElement | null = null
-
- if (evt.type === "PRESS_DOWN") {
- srcElement = dom.getPressedTriggerEl(ctx, evt.hint)
- } else if (evt.type === "FOCUS") {
- srcElement = dom.getInputEl(ctx)
- } else if (evt.type === "PRESS_DOWN_SCRUBBER") {
- srcElement = dom.getScrubberEl(ctx)
- }
-
- ctx.onFocus?.({
- value: ctx.value,
- valueAsNumber: ctx.valueAsNumber,
+ const srcElement: HTMLElement | null = match(evt.type, {
+ PRESS_DOWN: dom.getPressedTriggerEl(ctx, evt.hint),
+ FOCUS: dom.getInputEl(ctx),
+ PRESS_DOWN_SCRUBBER: dom.getScrubberEl(ctx),
+ })
+ ctx.onFocusChange?.({
+ focused: true,
srcElement,
+ value: ctx.formattedValue,
+ valueAsNumber: ctx.valueAsNumber,
})
},
invokeOnBlur(ctx) {
- ctx.onBlur?.({ value: ctx.value, valueAsNumber: ctx.valueAsNumber })
+ ctx.onFocusChange?.({
+ focused: false,
+ value: ctx.formattedValue,
+ valueAsNumber: ctx.valueAsNumber,
+ })
},
invokeOnInvalid(ctx) {
if (!ctx.isOutOfRange) return
const reason = ctx.valueAsNumber > ctx.max ? "rangeOverflow" : "rangeUnderflow"
- ctx.onInvalid?.({
+ ctx.onValueInvalid?.({
reason,
value: ctx.formattedValue,
valueAsNumber: ctx.valueAsNumber,
@@ -385,7 +385,7 @@ export function machine(userContext: UserDefinedContext) {
const invoke = {
onChange: (ctx: MachineContext) => {
- ctx.onChange?.({ value: ctx.value, valueAsNumber: ctx.valueAsNumber })
+ ctx.onValueChange?.({ value: ctx.value, valueAsNumber: ctx.valueAsNumber })
},
}
diff --git a/packages/machines/number-input/src/number-input.types.ts b/packages/machines/number-input/src/number-input.types.ts
index 4ce9b579b7..206b39143f 100644
--- a/packages/machines/number-input/src/number-input.types.ts
+++ b/packages/machines/number-input/src/number-input.types.ts
@@ -1,8 +1,32 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface ValueChangeDetails {
+ value: string
+ valueAsNumber: number
+}
+
+export interface FocusChangeDetails extends ValueChangeDetails {
+ focused: boolean
+ srcElement?: HTMLElement | null
+}
+
type ValidityState = "rangeUnderflow" | "rangeOverflow"
+export interface ValueInvalidDetails extends ValueChangeDetails {
+ reason: ValidityState
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
+type InputMode = "text" | "tel" | "numeric" | "decimal"
+
type ElementIds = Partial<{
root: string
label: string
@@ -28,132 +52,122 @@ type IntlTranslations = {
decrementLabel: string
}
-type Value = {
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The ids of the elements in the number input. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * The name attribute of the number input. Useful for form submission.
+ */
+ name?: string
+ /**
+ * The associate form of the input element.
+ */
+ form?: string
+ /**
+ * Whether the number input is disabled.
+ */
+ disabled?: boolean
+ /**
+ * Whether the number input is readonly
+ */
+ readOnly?: boolean
+ /**
+ * Whether the number input value is invalid.
+ */
+ invalid?: boolean
+ /**
+ * The pattern used to check the element's value against
+ *
+ * @default
+ * "[0-9]*(.[0-9]+)?"
+ */
+ pattern: string
+ /**
+ * The value of the input
+ */
value: string
- valueAsNumber: number
+ /**
+ * The minimum value of the number input
+ */
+ min: number
+ /**
+ * The maximum value of the number input
+ */
+ max: number
+ /**
+ * The amount to increment or decrement the value by
+ */
+ step: number
+ /**
+ * Whether to allow mouse wheel to change the value
+ */
+ allowMouseWheel?: boolean
+ /**
+ * Whether to allow the value overflow the min/max range
+ * @default true
+ */
+ allowOverflow: boolean
+ /**
+ * Whether the pressed key should be allowed in the input.
+ * The default behavior is to allow DOM floating point characters defined by /^[Ee0-9+\-.]$/
+ */
+ validateCharacter?: (char: string) => boolean
+ /**
+ * Whether to clamp the value when the input loses focus (blur)
+ * @default true
+ */
+ clampValueOnBlur: boolean
+ /**
+ * Whether to focus input when the value changes
+ * @default true
+ */
+ focusInputOnChange: boolean
+ /**
+ * Specifies the localized strings that identifies the accessibility elements and their states
+ */
+ translations: IntlTranslations
+ /**
+ * If using a custom display format, this converts the custom format to a format `parseFloat` understands.
+ */
+ parse?: (value: string) => string
+ /**
+ * If using a custom display format, this converts the default format to the custom format.
+ */
+ format?: (value: string) => string | number
+ /**
+ * Hints at the type of data that might be entered by the user. It also determines
+ * the type of keyboard shown to the user on mobile devices
+ * @default "decimal"
+ */
+ inputMode: InputMode
+ /**
+ * Function invoked when the value changes
+ */
+ onValueChange?: (details: ValueChangeDetails) => void
+ /**
+ * Function invoked when the value overflows or underflows the min/max range
+ */
+ onValueInvalid?: (details: ValueInvalidDetails) => void
+ /**
+ * Function invoked when the number input is focused
+ */
+ onFocusChange?: (details: FocusChangeDetails) => void
+ /**
+ * The minimum number of fraction digits to use. Possible values are from 0 to 20
+ */
+ minFractionDigits?: number
+ /**
+ * The maximum number of fraction digits to use. Possible values are from 0 to 20;
+ */
+ maxFractionDigits?: number
+ /**
+ * Whether to spin the value when the increment/decrement button is pressed
+ */
+ spinOnPress?: boolean
}
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The ids of the elements in the number input. Useful for composition.
- */
- ids?: ElementIds
- /**
- * The name attribute of the number input. Useful for form submission.
- */
- name?: string
- /**
- * The associate form of the input element.
- */
- form?: string
- /**
- * Whether the number input is disabled.
- */
- disabled?: boolean
- /**
- * Whether the number input is readonly
- */
- readOnly?: boolean
- /**
- * Whether the number input value is invalid.
- */
- invalid?: boolean
- /**
- * The pattern used to check the element's value against
- *
- * @default
- * "[0-9]*(.[0-9]+)?"
- */
- pattern: string
- /**
- * The value of the input
- */
- value: string
- /**
- * The minimum value of the number input
- */
- min: number
- /**
- * The maximum value of the number input
- */
- max: number
- /**
- * The amount to increment or decrement the value by
- */
- step: number
- /**
- * Whether to allow mouse wheel to change the value
- */
- allowMouseWheel?: boolean
- /**
- * Whether to allow the value overflow the min/max range
- * @default true
- */
- allowOverflow: boolean
- /**
- * Whether the pressed key should be allowed in the input.
- * The default behavior is to allow DOM floating point characters defined by /^[Ee0-9+\-.]$/
- */
- validateCharacter?: (char: string) => boolean
- /**
- * Whether to clamp the value when the input loses focus (blur)
- * @default true
- */
- clampValueOnBlur: boolean
- /**
- * Whether to focus input when the value changes
- * @default true
- */
- focusInputOnChange: boolean
- /**
- * Specifies the localized strings that identifies the accessibility elements and their states
- */
- translations: IntlTranslations
- /**
- * If using a custom display format, this converts the custom format to a format `parseFloat` understands.
- */
- parse?: (value: string) => string
- /**
- * If using a custom display format, this converts the default format to the custom format.
- */
- format?: (value: string) => string | number
- /**
- * Hints at the type of data that might be entered by the user. It also determines
- * the type of keyboard shown to the user on mobile devices
- * @default "decimal"
- */
- inputMode: "text" | "tel" | "numeric" | "decimal"
- /**
- * Function invoked when the value changes
- */
- onChange?: (details: Value) => void
- /**
- * Function invoked when the value overflows or underflows the min/max range
- */
- onInvalid?: (details: Value & { reason: ValidityState }) => void
- /**
- * Function invoked when the number input is focused
- */
- onFocus?: (details: Value & { srcElement: HTMLElement | null }) => void
- /**
- * The value of the input when it is blurred
- */
- onBlur?: (details: Value) => void
- /**
- * The minimum number of fraction digits to use. Possible values are from 0 to 20
- */
- minFractionDigits?: number
- /**
- * The maximum number of fraction digits to use. Possible values are from 0 to 20;
- */
- maxFractionDigits?: number
- /**
- * Whether to spin the value when the increment/decrement button is pressed
- */
- spinOnPress?: boolean
- }
-
export type UserDefinedContext = RequiredBy
type ComputedContext = Readonly<{
@@ -222,9 +236,9 @@ type PrivateContext = Context<{
scrubberCursorPoint: { x: number; y: number } | null
}>
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle" | "focused" | "spinning" | "before:spin" | "scrubbing"
tags: "focus"
}
@@ -233,7 +247,11 @@ export type State = S.State
export type Send = S.Send
-export type MachineApi = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface MachineApi {
/**
* Whether the input is focused.
*/
diff --git a/packages/machines/pagination/src/pagination.anatomy.ts b/packages/machines/pagination/src/pagination.anatomy.ts
index 52b8523167..80be17e893 100644
--- a/packages/machines/pagination/src/pagination.anatomy.ts
+++ b/packages/machines/pagination/src/pagination.anatomy.ts
@@ -7,4 +7,5 @@ export const anatomy = createAnatomy("pagination").parts(
"prevPageTrigger",
"nextPageTrigger",
)
+
export const parts = anatomy.build()
diff --git a/packages/machines/pagination/src/pagination.connect.ts b/packages/machines/pagination/src/pagination.connect.ts
index 248bb2e4a8..02228aec92 100644
--- a/packages/machines/pagination/src/pagination.connect.ts
+++ b/packages/machines/pagination/src/pagination.connect.ts
@@ -2,8 +2,8 @@ import { dataAttr } from "@zag-js/dom-query"
import type { NormalizeProps, PropTypes } from "@zag-js/types"
import { parts } from "./pagination.anatomy"
import { dom } from "./pagination.dom"
-import type { EllipsisProps, PageTriggerProps, MachineApi, Send, State } from "./pagination.types"
-import { utils } from "./pagination.utils"
+import type { MachineApi, Send, State } from "./pagination.types"
+import * as utils from "./pagination.utils"
export function connect(state: State, send: Send, normalize: NormalizeProps): MachineApi {
const totalPages = state.context.totalPages
@@ -30,19 +30,19 @@ export function connect(state: State, send: Send, normalize
isFirstPage,
isLastPage,
- slice(data: T[]) {
+ slice(data) {
return data.slice(pageRange.start, pageRange.end)
},
- setCount(count: number) {
+ setCount(count) {
send({ type: "SET_COUNT", count })
},
- setPageSize(size: number) {
+ setPageSize(size) {
send({ type: "SET_PAGE_SIZE", size })
},
- setPage(page: number) {
+ setPage(page) {
send({ type: "SET_PAGE", page })
},
@@ -52,14 +52,14 @@ export function connect(state: State, send: Send, normalize
"aria-label": translations.rootLabel,
}),
- getEllipsisProps(props: EllipsisProps) {
+ getEllipsisProps(props) {
return normalize.element({
id: dom.getEllipsisId(state.context, props.index),
...parts.ellipsis.attrs,
})
},
- getPageTriggerProps(page: PageTriggerProps) {
+ getPageTriggerProps(page) {
const index = page.value
const isCurrentPage = index === state.context.page
diff --git a/packages/machines/pagination/src/pagination.machine.ts b/packages/machines/pagination/src/pagination.machine.ts
index 01a22f495b..b61fbcacd4 100644
--- a/packages/machines/pagination/src/pagination.machine.ts
+++ b/packages/machines/pagination/src/pagination.machine.ts
@@ -112,6 +112,6 @@ const set = {
page: (ctx: MachineContext, value: number) => {
if (isEqual(ctx.page, value)) return
ctx.page = value
- ctx.onChange?.({ page: ctx.page, pageSize: ctx.pageSize })
+ ctx.onPageChange?.({ page: ctx.page, pageSize: ctx.pageSize })
},
}
diff --git a/packages/machines/pagination/src/pagination.types.ts b/packages/machines/pagination/src/pagination.types.ts
index d309d2961a..3691f4b5e8 100644
--- a/packages/machines/pagination/src/pagination.types.ts
+++ b/packages/machines/pagination/src/pagination.types.ts
@@ -1,16 +1,20 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
-export type PageTriggerProps = {
- type: "page"
- value: number
-}
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
-export type EllipsisProps = {
- index: number
+export interface PageChangeDetails {
+ page: number
+ pageSize: number
}
-type IntlTranslations = {
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
+interface IntlTranslations {
rootLabel?: string
prevPageTriggerLabel?: string
nextPageTriggerLabel?: string
@@ -25,50 +29,42 @@ type ElementIds = Partial<{
pageTrigger(page: number): string
}>
-export type ChangeDetails = {
- page: number
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The ids of the elements in the accordion. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * Specifies the localized strings that identifies the accessibility elements and their states
+ */
+ translations: IntlTranslations
+ /**
+ * Total number of data items
+ */
+ count: number
+ /**
+ * Number of data items per page
+ */
pageSize: number
+ /**
+ * Number of pages to show beside active page
+ */
+ siblingCount: number
+ /**
+ * The active page
+ */
+ page: number
+ /**
+ * Called when the page number is changed, and it takes the resulting page number argument
+ */
+ onPageChange?: (details: PageChangeDetails) => void
+ /**
+ * The type of the trigger element
+ * @default "button"
+ */
+ type: "button" | "link"
}
-export type PaginationRange = ({ type: "ellipsis" } | { type: "page"; value: number })[]
-
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The ids of the elements in the accordion. Useful for composition.
- */
- ids?: ElementIds
- /**
- * Specifies the localized strings that identifies the accessibility elements and their states
- */
- translations: IntlTranslations
- /**
- * Total number of data items
- */
- count: number
- /**
- * Number of data items per page
- */
- pageSize: number
- /**
- * Number of pages to show beside active page
- */
- siblingCount: number
- /**
- * The active page
- */
- page: number
- /**
- * Called when the page number is changed, and it takes the resulting page number argument
- */
- onChange?: (details: ChangeDetails) => void
- /**
- * The type of the trigger element
- * @default "button"
- */
- type: "button" | "link"
- }
-
type PrivateContext = Context<{}>
type ComputedContext = Readonly<{
@@ -81,7 +77,7 @@ type ComputedContext = Readonly<{
* @computed
* Pages to render in pagination
*/
- items: PaginationRange
+ items: Pages
/**
* @computed
* Index of first and last data items on current page
@@ -106,7 +102,7 @@ type ComputedContext = Readonly<{
export type UserDefinedContext = RequiredBy
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
export type MachineState = {
value: "idle"
@@ -116,7 +112,27 @@ export type State = S.State
export type Send = S.Send
-export type MachineApi = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface PageTriggerProps {
+ type: "page"
+ value: number
+}
+
+export interface EllipsisProps {
+ index: number
+}
+
+export type Pages = Array<{ type: "ellipsis" } | { type: "page"; value: number }>
+
+interface PageRange {
+ start: number
+ end: number
+}
+
+export interface MachineApi {
/**
* The current page.
*/
@@ -128,7 +144,7 @@ export type MachineApi = {
/**
* The page range. Represented as an array of page numbers (including ellipsis)
*/
- pages: PaginationRange
+ pages: Pages
/**
* The previous page.
*/
@@ -140,14 +156,11 @@ export type MachineApi = {
/**
* The page range. Represented as an object with `start` and `end` properties.
*/
- pageRange: {
- start: number
- end: number
- }
+ pageRange: PageRange
/**
* Function to slice an array of data based on the current page.
*/
- slice(data: T_1[]): T_1[]
+ slice(data: V[]): V[]
/**
* Whether the current page is the first page.
*/
diff --git a/packages/machines/pagination/src/pagination.utils.ts b/packages/machines/pagination/src/pagination.utils.ts
index d9cae8c3a2..06473128a2 100644
--- a/packages/machines/pagination/src/pagination.utils.ts
+++ b/packages/machines/pagination/src/pagination.utils.ts
@@ -1,49 +1,49 @@
-import type { MachineContext as Ctx, PaginationRange } from "./pagination.types"
-
-export const utils = {
- range: (start: number, end: number) => {
- let length = end - start + 1
- return Array.from({ length }, (_, idx) => idx + start)
- },
- transform: (items: (string | number)[]): PaginationRange => {
- return items.map((value) => {
- if (typeof value === "number") return { type: "page", value }
- return { type: "ellipsis" }
- })
- },
- getRange: (ctx: Omit) => {
- const totalPageNumbers = ctx.siblingCount + 5
- if (totalPageNumbers >= ctx.totalPages) return utils.transform(utils.range(1, ctx.totalPages))
-
- const ELLIPSIS = "ellipsis"
-
- const leftSiblingIndex = Math.max(ctx.page - ctx.siblingCount, 1)
- const rightSiblingIndex = Math.min(ctx.page + ctx.siblingCount, ctx.totalPages)
-
- const showLeftEllipsis = leftSiblingIndex > 2
- const showRightEllipsis = rightSiblingIndex < ctx.totalPages - 2
-
- const firstPageIndex = 1
- const lastPageIndex = ctx.totalPages
-
- if (!showLeftEllipsis && showRightEllipsis) {
- let leftItemCount = 3 + 2 * ctx.siblingCount
- let leftRange = utils.range(1, leftItemCount)
-
- return utils.transform([...leftRange, ELLIPSIS, ctx.totalPages])
- }
-
- if (showLeftEllipsis && !showRightEllipsis) {
- let rightItemCount = 3 + 2 * ctx.siblingCount
- let rightRange = utils.range(ctx.totalPages - rightItemCount + 1, ctx.totalPages)
- return utils.transform([firstPageIndex, ELLIPSIS, ...rightRange])
- }
-
- if (showLeftEllipsis && showRightEllipsis) {
- let middleRange = utils.range(leftSiblingIndex, rightSiblingIndex)
- return utils.transform([firstPageIndex, ELLIPSIS, ...middleRange, ELLIPSIS, lastPageIndex])
- }
-
- return []
- },
+import type { MachineContext as Ctx, Pages } from "./pagination.types"
+
+export const range = (start: number, end: number) => {
+ let length = end - start + 1
+ return Array.from({ length }, (_, idx) => idx + start)
+}
+
+export const transform = (items: (string | number)[]): Pages => {
+ return items.map((value) => {
+ if (typeof value === "number") return { type: "page", value }
+ return { type: "ellipsis" }
+ })
+}
+
+export const getRange = (ctx: Omit) => {
+ const totalPageNumbers = ctx.siblingCount + 5
+ if (totalPageNumbers >= ctx.totalPages) return transform(range(1, ctx.totalPages))
+
+ const ELLIPSIS = "ellipsis"
+
+ const leftSiblingIndex = Math.max(ctx.page - ctx.siblingCount, 1)
+ const rightSiblingIndex = Math.min(ctx.page + ctx.siblingCount, ctx.totalPages)
+
+ const showLeftEllipsis = leftSiblingIndex > 2
+ const showRightEllipsis = rightSiblingIndex < ctx.totalPages - 2
+
+ const firstPageIndex = 1
+ const lastPageIndex = ctx.totalPages
+
+ if (!showLeftEllipsis && showRightEllipsis) {
+ let leftItemCount = 3 + 2 * ctx.siblingCount
+ let leftRange = range(1, leftItemCount)
+
+ return transform([...leftRange, ELLIPSIS, ctx.totalPages])
+ }
+
+ if (showLeftEllipsis && !showRightEllipsis) {
+ let rightItemCount = 3 + 2 * ctx.siblingCount
+ let rightRange = range(ctx.totalPages - rightItemCount + 1, ctx.totalPages)
+ return transform([firstPageIndex, ELLIPSIS, ...rightRange])
+ }
+
+ if (showLeftEllipsis && showRightEllipsis) {
+ let middleRange = range(leftSiblingIndex, rightSiblingIndex)
+ return transform([firstPageIndex, ELLIPSIS, ...middleRange, ELLIPSIS, lastPageIndex])
+ }
+
+ return []
}
diff --git a/packages/machines/pagination/tests/pagination.utils.test.ts b/packages/machines/pagination/tests/pagination.utils.test.ts
index cdb24c7812..811a25217c 100644
--- a/packages/machines/pagination/tests/pagination.utils.test.ts
+++ b/packages/machines/pagination/tests/pagination.utils.test.ts
@@ -1,5 +1,5 @@
import { expect, describe, test } from "vitest"
-import { utils } from "../src/pagination.utils"
+import * as utils from "../src/pagination.utils"
describe("@zag-js/pagination utils", () => {
test("range method", () => {
diff --git a/packages/machines/pin-input/src/pin-input.machine.ts b/packages/machines/pin-input/src/pin-input.machine.ts
index fe3c41ea0e..2c8d2ee015 100644
--- a/packages/machines/pin-input/src/pin-input.machine.ts
+++ b/packages/machines/pin-input/src/pin-input.machine.ts
@@ -169,10 +169,16 @@ export function machine(userContext: UserDefinedContext) {
},
invokeOnComplete(ctx) {
if (!ctx.isValueComplete) return
- ctx.onComplete?.({ value: Array.from(ctx.value), valueAsString: ctx.valueAsString })
+ ctx.onValueComplete?.({
+ value: Array.from(ctx.value),
+ valueAsString: ctx.valueAsString,
+ })
},
invokeOnInvalid(ctx, evt) {
- ctx.onInvalid?.({ value: evt.value, index: ctx.focusedIndex })
+ ctx.onValueInvalid?.({
+ value: evt.value,
+ index: ctx.focusedIndex,
+ })
},
clearFocusedIndex(ctx) {
ctx.focusedIndex = -1
@@ -280,7 +286,10 @@ function getNextValue(current: string, next: string) {
const invoke = {
change(ctx: MachineContext) {
// callback
- ctx.onChange?.({ value: Array.from(ctx.value) })
+ ctx.onValueChange?.({
+ value: Array.from(ctx.value),
+ valueAsString: ctx.valueAsString,
+ })
// form event
const inputEl = dom.getHiddenInputEl(ctx)
diff --git a/packages/machines/pin-input/src/pin-input.types.ts b/packages/machines/pin-input/src/pin-input.types.ts
index 066f313043..4fd81c389d 100644
--- a/packages/machines/pin-input/src/pin-input.types.ts
+++ b/packages/machines/pin-input/src/pin-input.types.ts
@@ -1,6 +1,24 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface ValueChangeDetails {
+ value: string[]
+ valueAsString: string
+}
+
+export interface ValueInvalidDetails {
+ value: string
+ index: number
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
type IntlTranslations = {
inputLabel: (index: number, length: number) => string
}
@@ -13,82 +31,81 @@ type ElementIds = Partial<{
input(id: string): string
}>
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The name of the input element. Useful for form submission.
- */
- name?: string
- /**
- * The associate form of the underlying input element.
- */
- form?: string
- /**
- * The regular expression that the user-entered input value is checked against.
- */
- pattern?: string
- /**
- * The ids of the elements in the pin input. Useful for composition.
- */
- ids?: ElementIds
- /**
- * Whether the inputs are disabled
- */
- disabled?: boolean
- /**
- * The placeholder text for the input
- */
- placeholder?: string
- /**
- * Whether to auto-focus the first input.
- */
- autoFocus?: boolean
- /**
- * Whether the pin input is in the invalid state
- */
- invalid?: boolean
- /**
- * If `true`, the pin input component signals to its fields that they should
- * use `autocomplete="one-time-code"`.
- */
- otp?: boolean
- /**
- * The value of the the pin input.
- */
- value: string[]
- /**
- * The type of value the pin-input should allow
- */
- type?: "alphanumeric" | "numeric" | "alphabetic"
- /**
- * Function called when all inputs have valid values
- */
- onComplete?: (details: { value: string[]; valueAsString: string }) => void
- /**
- * Function called on input change
- */
- onChange?: (details: { value: string[] }) => void
- /**
- * Function called when an invalid value is entered
- */
- onInvalid?: (details: { value: string; index: number }) => void
- /**
- * If `true`, the input's value will be masked just like `type=password`
- */
- mask?: boolean
- /**
- * Whether to blur the input when the value is complete
- */
- blurOnComplete?: boolean
- /**
- * Whether to select input value when input is focused
- */
- selectOnFocus?: boolean
- /**
- * Specifies the localized strings that identifies the accessibility elements and their states
- */
- translations: IntlTranslations
- }
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The name of the input element. Useful for form submission.
+ */
+ name?: string
+ /**
+ * The associate form of the underlying input element.
+ */
+ form?: string
+ /**
+ * The regular expression that the user-entered input value is checked against.
+ */
+ pattern?: string
+ /**
+ * The ids of the elements in the pin input. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * Whether the inputs are disabled
+ */
+ disabled?: boolean
+ /**
+ * The placeholder text for the input
+ */
+ placeholder?: string
+ /**
+ * Whether to auto-focus the first input.
+ */
+ autoFocus?: boolean
+ /**
+ * Whether the pin input is in the invalid state
+ */
+ invalid?: boolean
+ /**
+ * If `true`, the pin input component signals to its fields that they should
+ * use `autocomplete="one-time-code"`.
+ */
+ otp?: boolean
+ /**
+ * The value of the the pin input.
+ */
+ value: string[]
+ /**
+ * The type of value the pin-input should allow
+ */
+ type?: "alphanumeric" | "numeric" | "alphabetic"
+ /**
+ * Function called when all inputs have valid values
+ */
+ onValueComplete?: (details: ValueChangeDetails) => void
+ /**
+ * Function called on input change
+ */
+ onValueChange?: (details: ValueChangeDetails) => void
+ /**
+ * Function called when an invalid value is entered
+ */
+ onValueInvalid?: (details: ValueInvalidDetails) => void
+ /**
+ * If `true`, the input's value will be masked just like `type=password`
+ */
+ mask?: boolean
+ /**
+ * Whether to blur the input when the value is complete
+ */
+ blurOnComplete?: boolean
+ /**
+ * Whether to select input value when input is focused
+ */
+ selectOnFocus?: boolean
+ /**
+ * Specifies the localized strings that identifies the accessibility elements and their states
+ */
+ translations: IntlTranslations
+}
export type UserDefinedContext = RequiredBy
@@ -128,9 +145,9 @@ type PrivateContext = Context<{
focusedIndex: number
}>
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle" | "focused"
}
@@ -138,7 +155,11 @@ export type State = S.State
export type Send = S.Send
-export type MachineApi = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface MachineApi {
/**
* The value of the input as an array of strings.
*/
diff --git a/packages/machines/popover/src/popover.machine.ts b/packages/machines/popover/src/popover.machine.ts
index 3e23fd41ff..0c622bc1aa 100644
--- a/packages/machines/popover/src/popover.machine.ts
+++ b/packages/machines/popover/src/popover.machine.ts
@@ -205,10 +205,10 @@ export function machine(userContext: UserDefinedContext) {
})
},
invokeOnOpen(ctx) {
- ctx.onOpen?.()
+ ctx.onOpenChange?.({ open: true })
},
invokeOnClose(ctx) {
- ctx.onClose?.()
+ ctx.onOpenChange?.({ open: false })
},
toggleVisibility(ctx, _evt, { send }) {
send({ type: ctx.open ? "OPEN" : "CLOSE", src: "controlled" })
diff --git a/packages/machines/popover/src/popover.types.ts b/packages/machines/popover/src/popover.types.ts
index df6a8556ae..f8f01ee633 100644
--- a/packages/machines/popover/src/popover.types.ts
+++ b/packages/machines/popover/src/popover.types.ts
@@ -3,6 +3,18 @@ import type { DismissableElementHandlers } from "@zag-js/dismissable"
import type { Placement, PositioningOptions } from "@zag-js/popper"
import type { CommonProperties, Context, MaybeElement, PropTypes, RequiredBy } from "@zag-js/types"
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface OpenChangeDetails {
+ open: boolean
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
type ElementIds = Partial<{
anchor: string
trigger: string
@@ -14,62 +26,57 @@ type ElementIds = Partial<{
arrow: string
}>
-type PublicContext = DismissableElementHandlers &
- CommonProperties & {
- /**
- * The ids of the elements in the popover. Useful for composition.
- */
- ids?: ElementIds
- /**
- * Whether the popover should be modal. When set to `true`:
- * - interaction with outside elements will be disabled
- * - only popover content will be visible to screen readers
- * - scrolling is blocked
- * - focus is trapped within the popover
- *
- * @default false
- */
- modal?: boolean
- /**
- * Whether the popover is rendered in a portal
- *
- * @default true
- */
- portalled?: boolean
- /**
- * Whether to automatically set focus on the first focusable
- * content within the popover when opened.
- */
- autoFocus?: boolean
- /**
- * The element to focus on when the popover is opened.
- */
- initialFocusEl?: MaybeElement | (() => MaybeElement)
- /**
- * Whether to close the popover when the user clicks outside of the popover.
- */
- closeOnInteractOutside?: boolean
- /**
- * Whether to close the popover when the escape key is pressed.
- */
- closeOnEsc?: boolean
- /**
- * Function invoked when the popover is closed
- */
- onClose?: VoidFunction
- /**
- * Function invoked when the popover is opened
- */
- onOpen?: VoidFunction
- /**
- * The user provided options used to position the popover content
- */
- positioning: PositioningOptions
- /**
- * Whether the popover is open
- */
- open?: boolean
- }
+interface PublicContext extends DismissableElementHandlers, CommonProperties {
+ /**
+ * The ids of the elements in the popover. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * Whether the popover should be modal. When set to `true`:
+ * - interaction with outside elements will be disabled
+ * - only popover content will be visible to screen readers
+ * - scrolling is blocked
+ * - focus is trapped within the popover
+ *
+ * @default false
+ */
+ modal?: boolean
+ /**
+ * Whether the popover is rendered in a portal
+ *
+ * @default true
+ */
+ portalled?: boolean
+ /**
+ * Whether to automatically set focus on the first focusable
+ * content within the popover when opened.
+ */
+ autoFocus?: boolean
+ /**
+ * The element to focus on when the popover is opened.
+ */
+ initialFocusEl?: MaybeElement | (() => MaybeElement)
+ /**
+ * Whether to close the popover when the user clicks outside of the popover.
+ */
+ closeOnInteractOutside?: boolean
+ /**
+ * Whether to close the popover when the escape key is pressed.
+ */
+ closeOnEsc?: boolean
+ /**
+ * Function invoked when the popover opens or closes
+ */
+ onOpenChange?: (details: OpenChangeDetails) => void
+ /**
+ * The user provided options used to position the popover content
+ */
+ positioning: PositioningOptions
+ /**
+ * Whether the popover is open
+ */
+ open?: boolean
+}
export type UserDefinedContext = RequiredBy
@@ -97,9 +104,9 @@ type PrivateContext = Context<{
currentPlacement?: Placement
}>
-export type MachineContext = PublicContext & ComputedContext & PrivateContext
+export interface MachineContext extends PublicContext, ComputedContext, PrivateContext {}
-export type MachineState = {
+export interface MachineState {
value: "open" | "closed"
}
@@ -107,9 +114,11 @@ export type State = S.State
export type Send = S.Send
-export type { Placement, PositioningOptions }
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
-export type MachineApi = {
+export interface MachineApi {
/**
* Whether the popover is portalled
*/
@@ -140,3 +149,9 @@ export type MachineApi = {
descriptionProps: T["element"]
closeTriggerProps: T["button"]
}
+
+/* -----------------------------------------------------------------------------
+ * Re-exported types
+ * -----------------------------------------------------------------------------*/
+
+export type { Placement, PositioningOptions }
diff --git a/packages/machines/presence/src/presence.types.ts b/packages/machines/presence/src/presence.types.ts
index 09d4a1693e..d67ebd3430 100644
--- a/packages/machines/presence/src/presence.types.ts
+++ b/packages/machines/presence/src/presence.types.ts
@@ -1,22 +1,15 @@
import type { StateMachine as S } from "@zag-js/core"
-type PublicContext = {
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
+interface PublicContext {
present: boolean
onExitComplete?: () => void
}
-export type MachineApi = {
- /**
- * Whether the node is present in the DOM.
- */
- isPresent: boolean
- /**
- * Function to set the node (as early as possible)
- */
- setNode(node: HTMLElement | null): void
-}
-
-type PrivateContext = {
+interface PrivateContext {
node: HTMLElement | null
styles: CSSStyleDeclaration | null
prevPresent?: boolean
@@ -25,12 +18,27 @@ type PrivateContext = {
export type UserDefinedContext = PublicContext
-export type MachineContext = PublicContext & PrivateContext
+export interface MachineContext extends PublicContext, PrivateContext {}
-export type MachineState = {
+export interface MachineState {
value: "mounted" | "unmountSuspended" | "unmounted"
}
export type State = S.State
export type Send = S.Send
+
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface MachineApi {
+ /**
+ * Whether the node is present in the DOM.
+ */
+ isPresent: boolean
+ /**
+ * Function to set the node (as early as possible)
+ */
+ setNode(node: HTMLElement | null): void
+}
diff --git a/packages/machines/pressable/src/pressable.types.ts b/packages/machines/pressable/src/pressable.types.ts
index e3dbbdff1c..856662f72e 100644
--- a/packages/machines/pressable/src/pressable.types.ts
+++ b/packages/machines/pressable/src/pressable.types.ts
@@ -1,6 +1,10 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
export type PointerType = "mouse" | "pen" | "touch" | "keyboard" | "virtual"
export interface Rect {
@@ -34,7 +38,7 @@ export interface PressEvent {
target: HTMLElement
}
-export type PressHandlers = {
+export interface PressHandlers {
/**
* Handler that is called when the press is released over the target.
*/
@@ -59,43 +63,33 @@ export type PressHandlers = {
onLongPress?: (event: PressEvent) => void
}
-type PublicContext = DirectionProperty &
- CommonProperties &
- PressHandlers & {
- /**
- * Whether the element is disabled
- */
- disabled?: boolean
- /**
- * Whether the target should not receive focus on press.
- */
- preventFocusOnPress?: boolean
- /**
- * Whether press events should be canceled when the pointer leaves the target while pressed.
- *
- * By default, this is `false`, which means if the pointer returns back over the target while
- * still pressed, onPressStart will be fired again.
- *
- * If set to `true`, the press is canceled when the pointer leaves the target and
- * onPressStart will not be fired if the pointer returns.
- */
- cancelOnPointerExit?: boolean
- /**
- * Whether text selection should be enabled on the pressable element.
- */
- allowTextSelectionOnPress?: boolean
- /**
- * The amount of time (in milliseconds) to wait before firing the `onLongPress` event.
- */
- longPressDelay: number
- }
-
-export type MachineApi = {
+interface PublicContext extends DirectionProperty, CommonProperties, PressHandlers {
/**
- * Whether the element is pressed.
+ * Whether the element is disabled
*/
- isPressed: boolean
- pressableProps: T["element"]
+ disabled?: boolean
+ /**
+ * Whether the target should not receive focus on press.
+ */
+ preventFocusOnPress?: boolean
+ /**
+ * Whether press events should be canceled when the pointer leaves the target while pressed.
+ *
+ * By default, this is `false`, which means if the pointer returns back over the target while
+ * still pressed, onPressStart will be fired again.
+ *
+ * If set to `true`, the press is canceled when the pointer leaves the target and
+ * onPressStart will not be fired if the pointer returns.
+ */
+ cancelOnPointerExit?: boolean
+ /**
+ * Whether text selection should be enabled on the pressable element.
+ */
+ allowTextSelectionOnPress?: boolean
+ /**
+ * The amount of time (in milliseconds) to wait before firing the `onLongPress` event.
+ */
+ longPressDelay: number
}
interface FocusableElement extends HTMLElement, HTMLOrSVGElement {}
@@ -113,9 +107,9 @@ export type UserDefinedContext = RequiredBy
type ComputedContext = Readonly<{}>
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle" | "pressed:in" | "pressed:out"
tags: "pressed"
}
@@ -123,3 +117,15 @@ export type MachineState = {
export type State = S.State
export type Send = S.Send
+
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface MachineApi {
+ /**
+ * Whether the element is pressed.
+ */
+ isPressed: boolean
+ pressableProps: T["element"]
+}
diff --git a/packages/machines/radio-group/src/radio-group.machine.ts b/packages/machines/radio-group/src/radio-group.machine.ts
index 27d0a467e9..0ce5fcfd31 100644
--- a/packages/machines/radio-group/src/radio-group.machine.ts
+++ b/packages/machines/radio-group/src/radio-group.machine.ts
@@ -151,7 +151,7 @@ export function machine(userContext: UserDefinedContext) {
const invoke = {
change: (ctx: MachineContext) => {
if (ctx.value == null) return
- ctx.onChange?.({ value: ctx.value })
+ ctx.onValueChange?.({ value: ctx.value })
},
}
diff --git a/packages/machines/radio-group/src/radio-group.types.ts b/packages/machines/radio-group/src/radio-group.types.ts
index 2103c5aa97..817a898775 100644
--- a/packages/machines/radio-group/src/radio-group.types.ts
+++ b/packages/machines/radio-group/src/radio-group.types.ts
@@ -1,6 +1,14 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
+export interface ValueChangeDetails {
+ value: string
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
type ElementIds = Partial<{
root: string
label: string
@@ -11,40 +19,39 @@ type ElementIds = Partial<{
radioHiddenInput(value: string): string
}>
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The ids of the elements in the radio. Useful for composition.
- */
- ids?: ElementIds
- /**
- * The value of the checked radio
- */
- value: string | null
-
- /**
- * The name of the input fields in the radio
- * (Useful for form submission).
- */
- name?: string
- /**
- * The associate form of the underlying input.
- */
- form?: string
- /**
- * If `true`, the radio group will be disabled
- */
- disabled?: boolean
- /**
- * Function called once a radio is checked
- * @param value the value of the checked radio
- */
- onChange?(details: { value: string }): void
- /**
- * Orientation of the radio group
- */
- orientation?: "horizontal" | "vertical"
- }
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The ids of the elements in the radio. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * The value of the checked radio
+ */
+ value: string | null
+
+ /**
+ * The name of the input fields in the radio
+ * (Useful for form submission).
+ */
+ name?: string
+ /**
+ * The associate form of the underlying input.
+ */
+ form?: string
+ /**
+ * If `true`, the radio group will be disabled
+ */
+ disabled?: boolean
+ /**
+ * Function called once a radio is checked
+ * @param value the value of the checked radio
+ */
+ onValueChange?(details: ValueChangeDetails): void
+ /**
+ * Orientation of the radio group
+ */
+ orientation?: "horizontal" | "vertical"
+}
type PrivateContext = Context<{
/**
@@ -88,14 +95,15 @@ export type UserDefinedContext = RequiredBy
type ComputedContext = Readonly<{
/**
+ * @computed
* Whether the radio group is disabled
*/
isDisabled: boolean
}>
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle"
}
@@ -103,19 +111,17 @@ export type State = S.State
export type Send = S.Send
-export type RadioProps = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface RadioProps {
value: string
- /**
- * If `true`, the radio will be disabled
- */
disabled?: boolean
- /**
- * If `true`, the radio is marked as invalid.
- */
invalid?: boolean
}
-export type RadioState = {
+export interface RadioState {
isInteractive: boolean
isInvalid: boolean
isDisabled: boolean
@@ -125,7 +131,11 @@ export type RadioState = {
isActive: boolean
}
-export type MachineApi = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface MachineApi {
/**
* The current value of the radio group
*/
diff --git a/packages/machines/range-slider/src/range-slider.machine.ts b/packages/machines/range-slider/src/range-slider.machine.ts
index 63ba2f59bf..9d73b4b6b4 100644
--- a/packages/machines/range-slider/src/range-slider.machine.ts
+++ b/packages/machines/range-slider/src/range-slider.machine.ts
@@ -208,10 +208,10 @@ export function machine(userContext: UserDefinedContext) {
})
},
invokeOnChangeStart(ctx) {
- ctx.onChangeStart?.({ value: ctx.value })
+ ctx.onValueChangeStart?.({ value: ctx.value })
},
invokeOnChangeEnd(ctx) {
- ctx.onChangeEnd?.({ value: ctx.value })
+ ctx.onValueChangeEnd?.({ value: ctx.value })
},
setClosestThumbIndex(ctx, evt) {
const pointValue = dom.getValueFromPoint(ctx, evt.point)
@@ -274,11 +274,16 @@ export function machine(userContext: UserDefinedContext) {
const invoke = {
change: (ctx: MachineContext) => {
- ctx.onChange?.({ value: Array.from(ctx.value) })
+ ctx.onValueChange?.({
+ value: Array.from(ctx.value),
+ })
dom.dispatchChangeEvent(ctx)
},
focusChange: (ctx: MachineContext) => {
- ctx.onFocusChange?.({ value: Array.from(ctx.value), index: ctx.focusedIndex })
+ ctx.onFocusChange?.({
+ value: Array.from(ctx.value),
+ focusedIndex: ctx.focusedIndex,
+ })
},
}
diff --git a/packages/machines/range-slider/src/range-slider.types.ts b/packages/machines/range-slider/src/range-slider.types.ts
index 60298de84d..7b27201ba4 100644
--- a/packages/machines/range-slider/src/range-slider.types.ts
+++ b/packages/machines/range-slider/src/range-slider.types.ts
@@ -1,6 +1,23 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface ValueChangeDetails {
+ value: number[]
+}
+
+export interface FocusChangeDetails {
+ focusedIndex: number
+ value: number[]
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
type ElementIds = Partial<{
root: string
thumb(index: number): string
@@ -12,95 +29,94 @@ type ElementIds = Partial<{
marker(index: number): string
}>
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The ids of the elements in the range slider. Useful for composition.
- */
- ids?: ElementIds
- /**
- * The aria-label of each slider thumb. Useful for providing an accessible name to the slider
- */
- "aria-label"?: string[]
- /**
- * The `id` of the elements that labels each slider thumb. Useful for providing an accessible name to the slider
- */
- "aria-labelledby"?: string[]
- /**
- * The name associated with each slider thumb (when used in a form)
- */
- name?: string
- /**
- * The associate form of the underlying input element.
- */
- form?: string
- /**
- * The value of the range slider
- */
- value: number[]
- /**
- * Whether the slider is disabled
- */
- disabled?: boolean
- /**
- * Whether the slider is read-only
- */
- readOnly?: boolean
- /**
- * Whether the slider is invalid
- */
- invalid?: boolean
- /**
- * Function invoked when the value of the slider changes
- */
- onChange?(details: { value: number[] }): void
- /**
- * Function invoked when the slider value change is started
- */
- onChangeStart?(details: { value: number[] }): void
- /**
- * Function invoked when the slider value change is done
- */
- onChangeEnd?(details: { value: number[] }): void
- /**
- * Function invoked when the slider's focused index changes
- */
- onFocusChange?(details: { index: number; value: number[] }): void
- /**
- * Function that returns a human readable value for the slider thumb
- */
- getAriaValueText?(value: number, index: number): string
- /**
- * The minimum value of the slider
- */
- min: number
- /**
- * The maximum value of the slider
- */
- max: number
- /**
- * The step value of the slider
- */
- step: number
- /**
- * The minimum permitted steps between multiple thumbs.
- */
- minStepsBetweenThumbs: number
- /**
- * The orientation of the slider
- */
- orientation: "vertical" | "horizontal"
- /**
- * The alignment of the slider thumb relative to the track
- * - `center`: the thumb will extend beyond the bounds of the slider track.
- * - `contain`: the thumb will be contained within the bounds of the track.
- */
- thumbAlignment?: "contain" | "center"
- /**
- * The slider thumbs dimensions
- */
- thumbSize: { width: number; height: number } | null
- }
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The ids of the elements in the range slider. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * The aria-label of each slider thumb. Useful for providing an accessible name to the slider
+ */
+ "aria-label"?: string[]
+ /**
+ * The `id` of the elements that labels each slider thumb. Useful for providing an accessible name to the slider
+ */
+ "aria-labelledby"?: string[]
+ /**
+ * The name associated with each slider thumb (when used in a form)
+ */
+ name?: string
+ /**
+ * The associate form of the underlying input element.
+ */
+ form?: string
+ /**
+ * The value of the range slider
+ */
+ value: number[]
+ /**
+ * Whether the slider is disabled
+ */
+ disabled?: boolean
+ /**
+ * Whether the slider is read-only
+ */
+ readOnly?: boolean
+ /**
+ * Whether the slider is invalid
+ */
+ invalid?: boolean
+ /**
+ * Function invoked when the value of the slider changes
+ */
+ onValueChange?(details: ValueChangeDetails): void
+ /**
+ * Function invoked when the slider value change is started
+ */
+ onValueChangeStart?(details: ValueChangeDetails): void
+ /**
+ * Function invoked when the slider value change is done
+ */
+ onValueChangeEnd?(details: ValueChangeDetails): void
+ /**
+ * Function invoked when the slider's focused index changes
+ */
+ onFocusChange?(details: FocusChangeDetails): void
+ /**
+ * Function that returns a human readable value for the slider thumb
+ */
+ getAriaValueText?(value: number, index: number): string
+ /**
+ * The minimum value of the slider
+ */
+ min: number
+ /**
+ * The maximum value of the slider
+ */
+ max: number
+ /**
+ * The step value of the slider
+ */
+ step: number
+ /**
+ * The minimum permitted steps between multiple thumbs.
+ */
+ minStepsBetweenThumbs: number
+ /**
+ * The orientation of the slider
+ */
+ orientation: "vertical" | "horizontal"
+ /**
+ * The alignment of the slider thumb relative to the track
+ * - `center`: the thumb will extend beyond the bounds of the slider track.
+ * - `contain`: the thumb will be contained within the bounds of the track.
+ */
+ thumbAlignment?: "contain" | "center"
+ /**
+ * The slider thumbs dimensions
+ */
+ thumbSize: { width: number; height: number } | null
+}
export type UserDefinedContext = RequiredBy
@@ -166,9 +182,9 @@ type PrivateContext = Context<{
fieldsetDisabled: boolean
}>
-export type MachineContext = PublicContext & ComputedContext & PrivateContext
+export interface MachineContext extends PublicContext, ComputedContext, PrivateContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle" | "dragging" | "focus"
}
@@ -176,7 +192,15 @@ export type State = S.State
export type Send = S.Send
-export type MachineApi = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface MarkerProps {
+ value: number
+}
+
+export interface MachineApi {
/**
* The value of the slider.
*/
@@ -246,5 +270,5 @@ export type MachineApi = {
rangeProps: T["element"]
controlProps: T["element"]
markerGroupProps: T["element"]
- getMarkerProps({ value }: { value: number }): T["element"]
+ getMarkerProps(props: MarkerProps): T["element"]
}
diff --git a/packages/machines/rating-group/src/rating-group.connect.ts b/packages/machines/rating-group/src/rating-group.connect.ts
index d659b1c576..062e7172f6 100644
--- a/packages/machines/rating-group/src/rating-group.connect.ts
+++ b/packages/machines/rating-group/src/rating-group.connect.ts
@@ -20,7 +20,23 @@ export function connect(state: State, send: Send, normalize
const hoveredValue = state.context.hoveredValue
const translations = state.context.translations
- const api = {
+ function getRatingState(props: ItemProps): ItemState {
+ const value = state.context.isHovering ? state.context.hoveredValue : state.context.value
+ const isEqual = Math.ceil(value) === props.index
+
+ const isHighlighted = props.index <= value || isEqual
+ const isHalf = isEqual && Math.abs(value - props.index) === 0.5
+
+ return {
+ isEqual,
+ isValueEmpty: state.context.value === -1,
+ isHighlighted,
+ isHalf,
+ isChecked: isEqual || (state.context.value === -1 && props.index === 1),
+ }
+ }
+
+ return {
setValue(value: number) {
send({ type: "SET_VALUE", value })
},
@@ -34,22 +50,7 @@ export function connect(state: State, send: Send, normalize
hoveredValue,
size: state.context.max,
sizeArray: Array.from({ length: state.context.max }).map((_, index) => index + 1),
-
- getRatingState(props: ItemProps): ItemState {
- const value = state.context.isHovering ? state.context.hoveredValue : state.context.value
- const isEqual = Math.ceil(value) === props.index
-
- const isHighlighted = props.index <= value || isEqual
- const isHalf = isEqual && Math.abs(value - props.index) === 0.5
-
- return {
- isEqual,
- isValueEmpty: state.context.value === -1,
- isHighlighted,
- isHalf,
- isChecked: isEqual || (state.context.value === -1 && props.index === 1),
- }
- },
+ getRatingState,
rootProps: normalize.element({
dir: state.context.dir,
@@ -92,14 +93,14 @@ export function connect(state: State, send: Send, normalize
getRatingProps(props: ItemProps) {
const { index } = props
- const { isHalf, isHighlighted, isChecked } = api.getRatingState(props)
+ const itemState = getRatingState(props)
const valueText = translations.ratingValueText(index)
return normalize.element({
...parts.rating.attrs,
id: dom.getRatingId(state.context, index.toString()),
role: "radio",
- tabIndex: isDisabled ? undefined : isChecked ? 0 : -1,
+ tabIndex: isDisabled ? undefined : itemState.isChecked ? 0 : -1,
"aria-roledescription": "rating",
"aria-label": valueText,
"aria-disabled": isDisabled,
@@ -107,11 +108,11 @@ export function connect(state: State, send: Send, normalize
"aria-readonly": ariaAttr(state.context.readOnly),
"data-readonly": dataAttr(state.context.readOnly),
"aria-setsize": state.context.max,
- "aria-checked": isChecked,
- "data-checked": dataAttr(isChecked),
+ "aria-checked": itemState.isChecked,
+ "data-checked": dataAttr(itemState.isChecked),
"aria-posinset": index,
- "data-highlighted": dataAttr(isHighlighted),
- "data-half": dataAttr(isHalf),
+ "data-highlighted": dataAttr(itemState.isHighlighted),
+ "data-half": dataAttr(itemState.isHalf),
onPointerDown(event) {
if (!isInteractive) return
const evt = getNativeEvent(event)
@@ -179,6 +180,4 @@ export function connect(state: State, send: Send, normalize
})
},
}
-
- return api
}
diff --git a/packages/machines/rating-group/src/rating-group.machine.ts b/packages/machines/rating-group/src/rating-group.machine.ts
index 60e4edda1e..e201b710be 100644
--- a/packages/machines/rating-group/src/rating-group.machine.ts
+++ b/packages/machines/rating-group/src/rating-group.machine.ts
@@ -181,11 +181,11 @@ export function machine(userContext: UserDefinedContext) {
const invoke = {
change: (ctx: MachineContext) => {
- ctx.onChange?.({ value: ctx.value })
+ ctx.onValueChange?.({ value: ctx.value })
dom.dispatchChangeEvent(ctx)
},
hoverChange: (ctx: MachineContext) => {
- ctx.onHover?.({ value: ctx.hoveredValue })
+ ctx.onHoverChange?.({ hoveredValue: ctx.hoveredValue })
},
}
diff --git a/packages/machines/rating-group/src/rating-group.types.ts b/packages/machines/rating-group/src/rating-group.types.ts
index 9a61b6a979..bdb0e37960 100644
--- a/packages/machines/rating-group/src/rating-group.types.ts
+++ b/packages/machines/rating-group/src/rating-group.types.ts
@@ -1,20 +1,24 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
-type IntlTranslations = {
- ratingValueText(index: number): string
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface ValueChangeDetails {
+ value: number
}
-export type ItemProps = {
- index: number
+export interface HoverChangeDetails {
+ hoveredValue: number
}
-export type ItemState = {
- isEqual: boolean
- isValueEmpty: boolean
- isHighlighted: boolean
- isHalf: boolean
- isChecked: boolean
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
+interface IntlTranslations {
+ ratingValueText(index: number): string
}
type ElementIds = Partial<{
@@ -25,57 +29,56 @@ type ElementIds = Partial<{
rating(id: string): string
}>
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The ids of the elements in the rating. Useful for composition.
- */
- ids?: ElementIds
- /**
- * Specifies the localized strings that identifies the accessibility elements and their states
- */
- translations: IntlTranslations
- /**
- * The maximum rating value.
- */
- max: number
- /**
- * The name attribute of the rating element (used in forms).
- */
- name?: string
- /**
- * The associate form of the underlying input element.
- */
- form?: string
- /**
- * The current rating value.
- */
- value: number
- /**
- * Whether the rating is readonly.
- */
- readOnly?: boolean
- /**
- * Whether the rating is disabled.
- */
- disabled?: boolean
- /**
- * Whether to allow half stars.
- */
- allowHalf?: boolean
- /**
- * Whether to autofocus the rating.
- */
- autoFocus?: boolean
- /**
- * Function to be called when the rating value changes.
- */
- onChange?: (details: { value: number }) => void
- /**
- * Function to be called when the rating value is hovered.
- */
- onHover?: (details: { value: number }) => void
- }
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The ids of the elements in the rating. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * Specifies the localized strings that identifies the accessibility elements and their states
+ */
+ translations: IntlTranslations
+ /**
+ * The maximum rating value.
+ */
+ max: number
+ /**
+ * The name attribute of the rating element (used in forms).
+ */
+ name?: string
+ /**
+ * The associate form of the underlying input element.
+ */
+ form?: string
+ /**
+ * The current rating value.
+ */
+ value: number
+ /**
+ * Whether the rating is readonly.
+ */
+ readOnly?: boolean
+ /**
+ * Whether the rating is disabled.
+ */
+ disabled?: boolean
+ /**
+ * Whether to allow half stars.
+ */
+ allowHalf?: boolean
+ /**
+ * Whether to autofocus the rating.
+ */
+ autoFocus?: boolean
+ /**
+ * Function to be called when the rating value changes.
+ */
+ onValueChange?: (details: ValueChangeDetails) => void
+ /**
+ * Function to be called when the rating value is hovered.
+ */
+ onHoverChange?: (details: HoverChangeDetails) => void
+}
export type UserDefinedContext = RequiredBy
@@ -108,9 +111,9 @@ type PrivateContext = Context<{
fieldsetDisabled: boolean
}>
-export type MachineContext = PublicContext & ComputedContext & PrivateContext
+export interface MachineContext extends PublicContext, ComputedContext, PrivateContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle" | "hover" | "focus"
}
@@ -118,7 +121,23 @@ export type State = S.State
export type Send = S.Send
-export type MachineApi = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface ItemProps {
+ index: number
+}
+
+export interface ItemState {
+ isEqual: boolean
+ isValueEmpty: boolean
+ isHighlighted: boolean
+ isHalf: boolean
+ isChecked: boolean
+}
+
+export interface MachineApi {
/**
* Sets the value of the rating group
*/
@@ -151,6 +170,7 @@ export type MachineApi = {
* Returns the state of a rating item
*/
getRatingState(props: ItemProps): ItemState
+
rootProps: T["element"]
hiddenInputProps: T["input"]
labelProps: T["element"]
diff --git a/packages/machines/select/src/select.machine.ts b/packages/machines/select/src/select.machine.ts
index 4b8036a877..489f1dc7dc 100644
--- a/packages/machines/select/src/select.machine.ts
+++ b/packages/machines/select/src/select.machine.ts
@@ -452,10 +452,10 @@ export function machine(userContext: UserDefinedContex
dom.getContentEl(ctx)?.scrollTo(0, 0)
},
invokeOnOpen(ctx) {
- ctx.onOpen?.()
+ ctx.onOpenChange?.({ open: true })
},
invokeOnClose(ctx) {
- ctx.onClose?.()
+ ctx.onOpenChange?.({ open: false })
},
syncSelectElement(ctx) {
const selectEl = dom.getHiddenSelectEl(ctx)
@@ -480,11 +480,17 @@ function dispatchChangeEvent(ctx: MachineContext) {
const invoke = {
change: (ctx: MachineContext) => {
- ctx.onChange?.({ value: Array.from(ctx.value), items: ctx.selectedItems })
+ ctx.onValueChange?.({
+ value: Array.from(ctx.value),
+ items: ctx.selectedItems,
+ })
dispatchChangeEvent(ctx)
},
highlightChange: (ctx: MachineContext) => {
- ctx.onHighlight?.({ value: ctx.highlightedValue, item: ctx.highlightedItem })
+ ctx.onHighlightChange?.({
+ highlightedValue: ctx.highlightedValue,
+ highlightedItem: ctx.highlightedItem,
+ })
},
}
diff --git a/packages/machines/select/src/select.types.ts b/packages/machines/select/src/select.types.ts
index fb67d8edf1..b7a33198bb 100644
--- a/packages/machines/select/src/select.types.ts
+++ b/packages/machines/select/src/select.types.ts
@@ -5,7 +5,27 @@ import type { TypeaheadState } from "@zag-js/dom-query"
import type { Placement, PositioningOptions } from "@zag-js/popper"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
-export type { CollectionOptions, CollectionItem }
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface ValueChangeDetails {
+ value: string[]
+ items: T[]
+}
+
+export interface HighlightChangeDetails {
+ highlightedValue: string | null
+ highlightedItem: T | null
+}
+
+export interface OpenChangeDetails {
+ open: boolean
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
type ElementIds = Partial<{
root: string
@@ -21,98 +41,85 @@ type ElementIds = Partial<{
itemGroupLabel(id: string | number): string
}>
-export type ValueChangeDetails = {
+interface PublicContext
+ extends DirectionProperty,
+ CommonProperties,
+ InteractOutsideHandlers {
+ /**
+ * The item collection
+ */
+ collection: Collection
+ /**
+ * The ids of the elements in the select. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * The `name` attribute of the underlying select.
+ */
+ name?: string
+ /**
+ * The associate form of the underlying select.
+ */
+ form?: string
+ /**
+ * Whether the select is disabled
+ */
+ disabled?: boolean
+ /**
+ * Whether the select is invalid
+ */
+ invalid?: boolean
+ /**
+ * Whether the select is read-only
+ */
+ readOnly?: boolean
+ /**
+ * Whether the select should close after an item is selected
+ */
+ closeOnSelect?: boolean
+ /**
+ * Whether to select the highlighted item when the user presses Tab,
+ * and the menu is open.
+ */
+ selectOnBlur?: boolean
+ /**
+ * The callback fired when the highlighted item changes.
+ */
+ onHighlightChange?: (details: HighlightChangeDetails) => void
+ /**
+ * The callback fired when the selected item changes.
+ */
+ onValueChange?: (details: ValueChangeDetails) => void
+ /**
+ * Function called when the popup is opened
+ */
+ onOpenChange?: (details: OpenChangeDetails) => void
+ /**
+ * The positioning options of the menu.
+ */
+ positioning: PositioningOptions
+ /**
+ * The keys of the selected items
+ */
value: string[]
- items: T[]
-}
-
-export type HighlightChangeDetails = {
- value: string | null
- item: T | null
+ /**
+ * The key of the highlighted item
+ */
+ highlightedValue: string | null
+ /**
+ * Whether to loop the keyboard navigation through the options
+ */
+ loop?: boolean
+ /**
+ * Whether to allow multiple selection
+ */
+ multiple?: boolean
+ /**
+ * Whether the select menu is open
+ */
+ open?: boolean
}
-type PublicContext = DirectionProperty &
- CommonProperties &
- InteractOutsideHandlers & {
- /**
- * The item collection
- */
- collection: Collection
- /**
- * The ids of the elements in the select. Useful for composition.
- */
- ids?: ElementIds
- /**
- * The `name` attribute of the underlying select.
- */
- name?: string
- /**
- * The associate form of the underlying select.
- */
- form?: string
- /**
- * Whether the select is disabled
- */
- disabled?: boolean
- /**
- * Whether the select is invalid
- */
- invalid?: boolean
- /**
- * Whether the select is read-only
- */
- readOnly?: boolean
- /**
- * Whether the select should close after an item is selected
- */
- closeOnSelect?: boolean
- /**
- * Whether to select the highlighted item when the user presses Tab,
- * and the menu is open.
- */
- selectOnBlur?: boolean
- /**
- * The callback fired when the highlighted item changes.
- */
- onHighlight?: (details: HighlightChangeDetails) => void
- /**
- * The callback fired when the selected item changes.
- */
- onChange?: (details: ValueChangeDetails) => void
- /**
- * Function called when the popup is opened
- */
- onOpen?: VoidFunction
- /**
- * Function called when the popup is closed
- */
- onClose?: VoidFunction
- /**
- * The positioning options of the menu.
- */
- positioning: PositioningOptions
- /**
- * The keys of the selected items
- */
- value: string[]
- /**
- * The key of the highlighted item
- */
- highlightedValue: string | null
- /**
- * Whether to loop the keyboard navigation through the options
- */
- loop?: boolean
- /**
- * Whether to allow multiple selection
- */
- multiple?: boolean
- /**
- * Whether the select menu is open
- */
- open?: boolean
- }
-
type PrivateContext = Context<{
/**
* @internal
@@ -173,36 +180,40 @@ export type UserDefinedContext = Requ
"id" | "collection"
>
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type ItemProps = {
+export interface MachineState {
+ value: "idle" | "focused" | "open"
+}
+
+export type State = S.State
+
+export type Send = S.Send
+
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface ItemProps {
item: T
}
-export type ItemState = {
+export interface ItemState {
value: string
isDisabled: boolean
isSelected: boolean
isHighlighted: boolean
}
-export type MachineState = {
- value: "idle" | "focused" | "open"
-}
-
-export type State = S.State
-
-export type Send = S.Send
-
-export type ItemGroupProps = {
+export interface ItemGroupProps {
id: string
}
-export type ItemGroupLabelProps = {
+export interface ItemGroupLabelProps {
htmlFor: string
}
-export type MachineApi = {
+export interface MachineApi {
/**
* Whether the select is focused
*/
@@ -286,3 +297,9 @@ export type MachineApi {
- ctx.onChange?.({ value: ctx.value })
+ ctx.onValueChange?.({ value: ctx.value })
dom.dispatchChangeEvent(ctx)
},
}
diff --git a/packages/machines/slider/src/slider.types.ts b/packages/machines/slider/src/slider.types.ts
index 226ab4d7a9..6d49bd1e98 100644
--- a/packages/machines/slider/src/slider.types.ts
+++ b/packages/machines/slider/src/slider.types.ts
@@ -1,6 +1,18 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface ValueChangeDetails {
+ value: number
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
type ElementIds = Partial<{
root: string
thumb: string
@@ -12,96 +24,95 @@ type ElementIds = Partial<{
hiddenInput: string
}>
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The ids of the elements in the slider. Useful for composition.
- */
- ids?: ElementIds
- /**
- * The value of the slider
- */
- value: number
- /**
- * The name associated with the slider (when used in a form)
- */
- name?: string
- /**
- * The associate form of the underlying input element.
- */
- form?: string
- /**
- * Whether the slider is disabled
- */
- disabled?: boolean
- /**
- * Whether the slider is read-only
- */
- readOnly?: boolean
- /**
- * Whether the slider value is invalid
- */
- invalid?: boolean
- /**
- * The minimum value of the slider
- */
- min: number
- /**
- * The maximum value of the slider
- */
- max: number
- /**
- * The step value of the slider
- */
- step: number
- /**
- * The orientation of the slider
- */
- orientation?: "vertical" | "horizontal"
- /**
- * - "start": Useful when the value represents an absolute value
- * - "center": Useful when the value represents an offset (relative)
- */
- origin?: "start" | "center"
- /**
- * The aria-label of the slider. Useful for providing an accessible name to the slider
- */
- "aria-label"?: string
- /**
- * The `id` of the element that labels the slider. Useful for providing an accessible name to the slider
- */
- "aria-labelledby"?: string
- /**
- * Whether to focus the slider thumb after interaction (scrub and keyboard)
- */
- focusThumbOnChange?: boolean
- /**
- * Function that returns a human readable value for the slider
- */
- getAriaValueText?(value: number): string
- /**
- * Function invoked when the value of the slider changes
- */
- onChange?(details: { value: number }): void
- /**
- * Function invoked when the slider value change is done
- */
- onChangeEnd?(details: { value: number }): void
- /**
- * Function invoked when the slider value change is started
- */
- onChangeStart?(details: { value: number }): void
- /**
- * The alignment of the slider thumb relative to the track
- * - `center`: the thumb will extend beyond the bounds of the slider track.
- * - `contain`: the thumb will be contained within the bounds of the track.
- */
- thumbAlignment?: "contain" | "center"
- /**
- * The slider thumb dimensions.If not provided, the thumb size will be measured automatically.
- */
- thumbSize: { width: number; height: number } | null
- }
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The ids of the elements in the slider. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * The value of the slider
+ */
+ value: number
+ /**
+ * The name associated with the slider (when used in a form)
+ */
+ name?: string
+ /**
+ * The associate form of the underlying input element.
+ */
+ form?: string
+ /**
+ * Whether the slider is disabled
+ */
+ disabled?: boolean
+ /**
+ * Whether the slider is read-only
+ */
+ readOnly?: boolean
+ /**
+ * Whether the slider value is invalid
+ */
+ invalid?: boolean
+ /**
+ * The minimum value of the slider
+ */
+ min: number
+ /**
+ * The maximum value of the slider
+ */
+ max: number
+ /**
+ * The step value of the slider
+ */
+ step: number
+ /**
+ * The orientation of the slider
+ */
+ orientation?: "vertical" | "horizontal"
+ /**
+ * - "start": Useful when the value represents an absolute value
+ * - "center": Useful when the value represents an offset (relative)
+ */
+ origin?: "start" | "center"
+ /**
+ * The aria-label of the slider. Useful for providing an accessible name to the slider
+ */
+ "aria-label"?: string
+ /**
+ * The `id` of the element that labels the slider. Useful for providing an accessible name to the slider
+ */
+ "aria-labelledby"?: string
+ /**
+ * Whether to focus the slider thumb after interaction (scrub and keyboard)
+ */
+ focusThumbOnChange?: boolean
+ /**
+ * Function that returns a human readable value for the slider
+ */
+ getAriaValueText?(value: number): string
+ /**
+ * Function invoked when the value of the slider changes
+ */
+ onValueChange?(details: ValueChangeDetails): void
+ /**
+ * Function invoked when the slider value change is done
+ */
+ onValueChangeEnd?(details: ValueChangeDetails): void
+ /**
+ * Function invoked when the slider value change is started
+ */
+ onValueChangeStart?(details: ValueChangeDetails): void
+ /**
+ * The alignment of the slider thumb relative to the track
+ * - `center`: the thumb will extend beyond the bounds of the slider track.
+ * - `contain`: the thumb will be contained within the bounds of the track.
+ */
+ thumbAlignment?: "contain" | "center"
+ /**
+ * The slider thumb dimensions.If not provided, the thumb size will be measured automatically.
+ */
+ thumbSize: Size | null
+}
export type UserDefinedContext = RequiredBy
@@ -156,9 +167,9 @@ type PrivateContext = Context<{
fieldsetDisabled: boolean
}>
-export type MachineContext = PublicContext & ComputedContext & PrivateContext
+export interface MachineContext extends PublicContext, ComputedContext, PrivateContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle" | "dragging" | "focus"
}
@@ -166,27 +177,25 @@ export type State = S.State
export type Send = S.Send
-export type SharedContext = {
- min: number
- max: number
- step: number
- dir?: "ltr" | "rtl"
- isRtl: boolean
- isVertical: boolean
- isHorizontal: boolean
- value: number
- thumbSize: { width: number; height: number } | null
- thumbAlignment?: "contain" | "center"
- orientation?: "horizontal" | "vertical"
- readonly hasMeasuredThumbSize: boolean
-}
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
-export type Point = {
+export interface Point {
x: number
y: number
}
-export type MachineApi = {
+interface Size {
+ width: number
+ height: number
+}
+
+interface MarkerProps {
+ value: number
+}
+
+export interface MachineApi {
/**
* Whether the slider is focused.
*/
@@ -227,6 +236,7 @@ export type MachineApi = {
* Function to decrement the value of the slider by the step.
*/
decrement(): void
+
rootProps: T["element"]
labelProps: T["label"]
thumbProps: T["element"]
@@ -236,5 +246,24 @@ export type MachineApi = {
rangeProps: T["element"]
controlProps: T["element"]
markerGroupProps: T["element"]
- getMarkerProps({ value }: { value: number }): T["element"]
+ getMarkerProps(props: MarkerProps): T["element"]
+}
+
+/* -----------------------------------------------------------------------------
+ * Re-exported types
+ * -----------------------------------------------------------------------------*/
+
+export interface SharedContext {
+ min: number
+ max: number
+ step: number
+ dir?: "ltr" | "rtl"
+ isRtl: boolean
+ isVertical: boolean
+ isHorizontal: boolean
+ value: number
+ thumbSize: Size | null
+ thumbAlignment?: "contain" | "center"
+ orientation?: "horizontal" | "vertical"
+ readonly hasMeasuredThumbSize: boolean
}
diff --git a/packages/machines/splitter/src/splitter.connect.ts b/packages/machines/splitter/src/splitter.connect.ts
index 76161b2ca4..84a0c06eee 100644
--- a/packages/machines/splitter/src/splitter.connect.ts
+++ b/packages/machines/splitter/src/splitter.connect.ts
@@ -3,7 +3,7 @@ import { dataAttr } from "@zag-js/dom-query"
import type { NormalizeProps, PropTypes } from "@zag-js/types"
import { parts } from "./splitter.anatomy"
import { dom } from "./splitter.dom"
-import type { PanelId, PanelProps, MachineApi, ResizeTriggerProps, Send, State } from "./splitter.types"
+import type { MachineApi, ResizeTriggerProps, Send, State } from "./splitter.types"
import { getHandleBounds } from "./splitter.utils"
export function connect(state: State, send: Send, normalize: NormalizeProps): MachineApi {
@@ -12,40 +12,42 @@ export function connect(state: State, send: Send, normalize
const isDragging = state.matches("dragging")
const panels = state.context.panels
- const api = {
+ function getResizeTriggerState(props: ResizeTriggerProps) {
+ const { id, disabled } = props
+ const ids = id.split(":")
+ const panelIds = ids.map((id) => dom.getPanelId(state.context, id))
+ const panels = getHandleBounds(state.context, id)
+
+ return {
+ isDisabled: !!disabled,
+ isFocused: state.context.activeResizeId === id && isFocused,
+ panelIds,
+ min: panels?.min,
+ max: panels?.max,
+ value: 0,
+ }
+ }
+
+ return {
isFocused,
isDragging,
bounds: getHandleBounds(state.context),
- setToMinSize(id: PanelId) {
+ setToMinSize(id) {
const panel = panels.find((panel) => panel.id === id)
send({ type: "SET_PANEL_SIZE", id, size: panel?.minSize, src: "setToMinSize" })
},
- setToMaxSize(id: PanelId) {
+ setToMaxSize(id) {
const panel = panels.find((panel) => panel.id === id)
send({ type: "SET_PANEL_SIZE", id, size: panel?.maxSize, src: "setToMaxSize" })
},
- setSize(id: PanelId, size: number) {
+ setSize(id, size) {
send({ type: "SET_PANEL_SIZE", id, size })
},
- getResizeTriggerState(props: ResizeTriggerProps) {
- const { id, disabled } = props
- const ids = id.split(":")
- const panelIds = ids.map((id) => dom.getPanelId(state.context, id))
- const panels = getHandleBounds(state.context, id)
-
- return {
- isDisabled: !!disabled,
- isFocused: state.context.activeResizeId === id && isFocused,
- panelIds,
- min: panels?.min,
- max: panels?.max,
- value: 0,
- }
- },
+ getResizeTriggerState,
rootProps: normalize.element({
...parts.root.attrs,
@@ -61,7 +63,7 @@ export function connect(state: State, send: Send, normalize
},
}),
- getPanelProps(props: PanelProps) {
+ getPanelProps(props) {
const { id } = props
return normalize.element({
...parts.panel.attrs,
@@ -72,9 +74,9 @@ export function connect(state: State, send: Send, normalize
})
},
- getResizeTriggerProps(props: ResizeTriggerProps) {
+ getResizeTriggerProps(props) {
const { id, disabled, step = 1 } = props
- const triggerState = api.getResizeTriggerState(props)
+ const triggerState = getResizeTriggerState(props)
return normalize.element({
...parts.resizeTrigger.attrs,
@@ -164,6 +166,4 @@ export function connect(state: State, send: Send, normalize
})
},
}
-
- return api
}
diff --git a/packages/machines/splitter/src/splitter.machine.ts b/packages/machines/splitter/src/splitter.machine.ts
index 81d742dc4d..13b3a2fc96 100644
--- a/packages/machines/splitter/src/splitter.machine.ts
+++ b/packages/machines/splitter/src/splitter.machine.ts
@@ -170,13 +170,13 @@ export function machine(userContext: UserDefinedContext) {
dom.removeGlobalCursor(ctx)
},
invokeOnResize(ctx) {
- ctx.onResize?.({ size: ctx.size, activeHandleId: ctx.activeResizeId })
+ ctx.onSizeChange?.({ size: Array.from(ctx.size), activeHandleId: ctx.activeResizeId })
},
invokeOnResizeStart(ctx) {
- ctx.onResizeStart?.({ size: ctx.size, activeHandleId: ctx.activeResizeId })
+ ctx.onSizeChangeStart?.({ size: Array.from(ctx.size), activeHandleId: ctx.activeResizeId })
},
invokeOnResizeEnd(ctx) {
- ctx.onResizeEnd?.({ size: ctx.size, activeHandleId: ctx.activeResizeId })
+ ctx.onSizeChangeEnd?.({ size: Array.from(ctx.size), activeHandleId: ctx.activeResizeId })
},
setActiveHandleId(ctx, evt) {
ctx.activeResizeId = evt.id
diff --git a/packages/machines/splitter/src/splitter.types.ts b/packages/machines/splitter/src/splitter.types.ts
index 4125562eb9..586474ca58 100644
--- a/packages/machines/splitter/src/splitter.types.ts
+++ b/packages/machines/splitter/src/splitter.types.ts
@@ -1,20 +1,28 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
export type PanelId = string | number
-type PanelSizeData = {
+interface PanelSizeData {
id: PanelId
size?: number
minSize?: number
maxSize?: number
}
-type ResizeDetails = {
+export interface SizeChangeDetails {
size: PanelSizeData[]
activeHandleId: string | null
}
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
type ElementIds = Partial<{
root: string
resizeTrigger(id: string): string
@@ -22,33 +30,32 @@ type ElementIds = Partial<{
panel(id: string | number): string
}>
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The orientation of the splitter. Can be `horizontal` or `vertical`
- */
- orientation: "horizontal" | "vertical"
- /**
- * The size data of the panels
- */
- size: PanelSizeData[]
- /**
- * Function called when the splitter is resized.
- */
- onResize?: (details: ResizeDetails) => void
- /**
- * Function called when the splitter resize starts.
- */
- onResizeStart?: (details: ResizeDetails) => void
- /**
- * Function called when the splitter resize ends.
- */
- onResizeEnd?: (details: ResizeDetails) => void
- /**
- * The ids of the elements in the splitter. Useful for composition.
- */
- ids?: ElementIds
- }
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The orientation of the splitter. Can be `horizontal` or `vertical`
+ */
+ orientation: "horizontal" | "vertical"
+ /**
+ * The size data of the panels
+ */
+ size: PanelSizeData[]
+ /**
+ * Function called when the splitter is resized.
+ */
+ onSizeChange?: (details: SizeChangeDetails) => void
+ /**
+ * Function called when the splitter resize starts.
+ */
+ onSizeChangeStart?: (details: SizeChangeDetails) => void
+ /**
+ * Function called when the splitter resize ends.
+ */
+ onSizeChangeEnd?: (details: SizeChangeDetails) => void
+ /**
+ * The ids of the elements in the splitter. Useful for composition.
+ */
+ ids?: ElementIds
+}
export type UserDefinedContext = RequiredBy
@@ -76,9 +83,9 @@ type PrivateContext = Context<{
initialSize: Array>>
}>
-export type MachineContext = PublicContext & ComputedContext & PrivateContext
+export interface MachineContext extends PublicContext, ComputedContext, PrivateContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle" | "hover:temp" | "hover" | "dragging" | "focused"
tags: "focus"
}
@@ -87,23 +94,36 @@ export type State = S.State
export type Send = S.Send
-export type PanelProps = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface PanelProps {
id: PanelId
snapSize?: number
}
-export type ResizeTriggerProps = {
+export interface ResizeTriggerProps {
id: `${PanelId}:${PanelId}`
step?: number
disabled?: boolean
}
-export type PanelBounds = {
+export interface ResizeTriggerState {
+ isDisabled: boolean
+ isFocused: boolean
+ panelIds: string[]
+ min: number | undefined
+ max: number | undefined
+ value: number
+}
+
+export interface PanelBounds {
min: number
max: number
}
-export type MachineApi = {
+export interface MachineApi {
/**
* Whether the splitter is focused.
*/
@@ -131,14 +151,7 @@ export type MachineApi = {
/**
* Returns the state details for a resize trigger.
*/
- getResizeTriggerState(props: ResizeTriggerProps): {
- isDisabled: boolean
- isFocused: boolean
- panelIds: string[]
- min: number | undefined
- max: number | undefined
- value: number
- }
+ getResizeTriggerState(props: ResizeTriggerProps): ResizeTriggerState
rootProps: T["element"]
getPanelProps(props: PanelProps): T["element"]
getResizeTriggerProps(props: ResizeTriggerProps): T["element"]
diff --git a/packages/machines/switch/src/switch.machine.ts b/packages/machines/switch/src/switch.machine.ts
index 1bf1ab041d..e4ad39884d 100644
--- a/packages/machines/switch/src/switch.machine.ts
+++ b/packages/machines/switch/src/switch.machine.ts
@@ -109,7 +109,7 @@ export function machine(userContext: UserDefinedContext) {
const invoke = {
change: (ctx: MachineContext) => {
- ctx.onChange?.({ checked: ctx.checked })
+ ctx.onCheckedChange?.({ checked: ctx.checked })
},
}
diff --git a/packages/machines/switch/src/switch.types.ts b/packages/machines/switch/src/switch.types.ts
index 6e96e61707..ec959d4021 100644
--- a/packages/machines/switch/src/switch.types.ts
+++ b/packages/machines/switch/src/switch.types.ts
@@ -1,6 +1,18 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface CheckedChangeDetails {
+ checked: boolean
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
type ElementIds = Partial<{
root: string
input: string
@@ -9,55 +21,54 @@ type ElementIds = Partial<{
thumb: string
}>
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The ids of the elements in the switch. Useful for composition.
- */
- ids?: ElementIds
- /**
- * Specifies the localized strings that identifies the accessibility elements and their states
- */
- label: string
- /**
- * Whether the switch is disabled.
- */
- disabled?: boolean
- /**
- * If `true`, the switch will be readonly
- */
- readOnly?: boolean
- /**
- * If `true`, the switch is marked as invalid.
- */
- invalid?: boolean
- /**
- * If `true`, the switch input is marked as required,
- */
- required?: boolean
- /**
- * Function to call when the switch is clicked.
- */
- onChange?: (details: { checked: boolean }) => void
- /**
- * Whether the switch is checked.
- */
- checked: boolean
- /**
- * The name of the input field in a switch
- * (Useful for form submission).
- */
- name?: string
- /**
- * The id of the form that the switch belongs to
- */
- form?: string
- /**
- * The value of switch input. Useful for form submission.
- * @default "on"
- */
- value?: string | number
- }
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The ids of the elements in the switch. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * Specifies the localized strings that identifies the accessibility elements and their states
+ */
+ label: string
+ /**
+ * Whether the switch is disabled.
+ */
+ disabled?: boolean
+ /**
+ * If `true`, the switch will be readonly
+ */
+ readOnly?: boolean
+ /**
+ * If `true`, the switch is marked as invalid.
+ */
+ invalid?: boolean
+ /**
+ * If `true`, the switch input is marked as required,
+ */
+ required?: boolean
+ /**
+ * Function to call when the switch is clicked.
+ */
+ onCheckedChange?: (details: CheckedChangeDetails) => void
+ /**
+ * Whether the switch is checked.
+ */
+ checked: boolean
+ /**
+ * The name of the input field in a switch
+ * (Useful for form submission).
+ */
+ name?: string
+ /**
+ * The id of the form that the switch belongs to
+ */
+ form?: string
+ /**
+ * The value of switch input. Useful for form submission.
+ * @default "on"
+ */
+ value?: string | number
+}
export type UserDefinedContext = RequiredBy
@@ -91,9 +102,9 @@ type PrivateContext = Context<{
fieldsetDisabled: boolean
}>
-export type MachineContext = PublicContext & PrivateContext & ComputedContext
+export interface MachineContext extends PublicContext, PrivateContext, ComputedContext {}
-export type MachineState = {
+export interface MachineState {
value: "ready"
}
@@ -101,7 +112,11 @@ export type State = S.State
export type Send = S.Send
-export type MachineApi = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface MachineApi {
/**
* Whether the checkbox is checked
*/
diff --git a/packages/machines/tabs/src/tabs.connect.ts b/packages/machines/tabs/src/tabs.connect.ts
index a83849640e..d008564da3 100644
--- a/packages/machines/tabs/src/tabs.connect.ts
+++ b/packages/machines/tabs/src/tabs.connect.ts
@@ -3,18 +3,26 @@ import { dataAttr, isSafari, isSelfEvent } from "@zag-js/dom-query"
import type { NormalizeProps, PropTypes } from "@zag-js/types"
import { parts } from "./tabs.anatomy"
import { dom } from "./tabs.dom"
-import type { ContentProps, MachineApi, Send, State, TriggerProps } from "./tabs.types"
+import type { MachineApi, Send, State, TriggerProps } from "./tabs.types"
export function connect(state: State, send: Send, normalize: NormalizeProps): MachineApi {
const translations = state.context.translations
const isFocused = state.matches("focused")
+ function getTriggerState(props: TriggerProps) {
+ return {
+ isSelected: state.context.value === props.value,
+ isFocused: state.context.focusedValue === props.value,
+ isDisabled: !!props.disabled,
+ }
+ }
+
return {
value: state.context.value,
focusedValue: state.context.focusedValue,
previousValues: Array.from(state.context.previousValues),
- setValue(value: string) {
+ setValue(value) {
send({ type: "SET_VALUE", value })
},
@@ -22,10 +30,13 @@ export function connect(state: State, send: Send, normalize
send({ type: "CLEAR_VALUE" })
},
- setIndicatorRect(id: string | null | undefined) {
+ setIndicatorRect(value) {
+ const id = dom.getTriggerId(state.context, value)
send({ type: "SET_INDICATOR_RECT", id })
},
+ getTriggerState,
+
rootProps: normalize.element({
...parts.root.attrs,
id: dom.getRootId(state.context),
@@ -38,6 +49,7 @@ export function connect(state: State, send: Send, normalize
...parts.list.attrs,
id: dom.getTablistId(state.context),
role: "tablist",
+ dir: state.context.dir,
"data-focus": dataAttr(isFocused),
"aria-orientation": state.context.orientation,
"data-orientation": state.context.orientation,
@@ -79,25 +91,27 @@ export function connect(state: State, send: Send, normalize
},
}),
- getTriggerProps(props: TriggerProps) {
+ getTriggerProps(props) {
const { value, disabled } = props
- const selected = state.context.value === value
+ const triggerState = getTriggerState(props)
return normalize.button({
...parts.trigger.attrs,
role: "tab",
type: "button",
disabled,
+ dir: state.context.dir,
"data-orientation": state.context.orientation,
"data-disabled": dataAttr(disabled),
"aria-disabled": disabled,
"data-value": value,
- "aria-selected": selected,
- "data-selected": dataAttr(selected),
+ "aria-selected": triggerState.isSelected,
+ "data-selected": dataAttr(triggerState.isSelected),
+ "data-focus": dataAttr(triggerState.isFocused),
"aria-controls": dom.getContentId(state.context, value),
"data-ownedby": dom.getTablistId(state.context),
id: dom.getTriggerId(state.context, value),
- tabIndex: selected ? 0 : -1,
+ tabIndex: triggerState.isSelected ? 0 : -1,
onFocus() {
send({ type: "TAB_FOCUS", value })
},
@@ -117,16 +131,19 @@ export function connect(state: State, send: Send, normalize
})
},
- getContentProps({ value }: ContentProps) {
+ getContentProps(props) {
+ const { value } = props
const selected = state.context.value === value
return normalize.element({
...parts.content.attrs,
+ dir: state.context.dir,
id: dom.getContentId(state.context, value),
tabIndex: 0,
"aria-labelledby": dom.getTriggerId(state.context, value),
role: "tabpanel",
"data-ownedby": dom.getTablistId(state.context),
"data-selected": dataAttr(selected),
+ "data-orientation": state.context.orientation,
hidden: !selected,
})
},
diff --git a/packages/machines/tabs/src/tabs.machine.ts b/packages/machines/tabs/src/tabs.machine.ts
index acc2775369..b2208811a1 100644
--- a/packages/machines/tabs/src/tabs.machine.ts
+++ b/packages/machines/tabs/src/tabs.machine.ts
@@ -245,11 +245,11 @@ function pushUnique(arr: string[], value: any) {
const invoke = {
change: (ctx: MachineContext) => {
if (ctx.value == null) return
- ctx.onChange?.({ value: ctx.value })
+ ctx.onValueChange?.({ value: ctx.value })
},
focusChange: (ctx: MachineContext) => {
if (ctx.focusedValue == null) return
- ctx.onFocus?.({ value: ctx.focusedValue })
+ ctx.onFocusChange?.({ focusedValue: ctx.focusedValue })
},
}
diff --git a/packages/machines/tabs/src/tabs.types.ts b/packages/machines/tabs/src/tabs.types.ts
index 13b63aa744..01ddcfe55c 100644
--- a/packages/machines/tabs/src/tabs.types.ts
+++ b/packages/machines/tabs/src/tabs.types.ts
@@ -1,7 +1,23 @@
import type { StateMachine as S } from "@zag-js/core"
import type { CommonProperties, Context, DirectionProperty, PropTypes, RequiredBy } from "@zag-js/types"
-type IntlTranslations = {
+/* -----------------------------------------------------------------------------
+ * Callback details
+ * -----------------------------------------------------------------------------*/
+
+export interface ValueChangeDetails {
+ value: string
+}
+
+export interface FocusChangeDetails {
+ focusedValue: string
+}
+
+/* -----------------------------------------------------------------------------
+ * Machine context
+ * -----------------------------------------------------------------------------*/
+
+interface IntlTranslations {
tablistLabel?: string
}
@@ -13,59 +29,49 @@ type ElementIds = Partial<{
indicator: string
}>
-export type TriggerProps = {
- value: string
- disabled?: boolean
-}
-
-export type ContentProps = {
- value: string
+interface PublicContext extends DirectionProperty, CommonProperties {
+ /**
+ * The ids of the elements in the tabs. Useful for composition.
+ */
+ ids?: ElementIds
+ /**
+ * Specifies the localized strings that identifies the accessibility elements and their states
+ */
+ translations: IntlTranslations
+ /**
+ * Whether the keyboard navigation will loop from last tab to first, and vice versa.
+ * @default true
+ */
+ loop: boolean
+ /**
+ * The selected tab id
+ */
+ value: string | null
+ /**
+ * The orientation of the tabs. Can be `horizontal` or `vertical`
+ * - `horizontal`: only left and right arrow key navigation will work.
+ * - `vertical`: only up and down arrow key navigation will work.
+ *
+ * @default "horizontal"
+ */
+ orientation?: "horizontal" | "vertical"
+ /**
+ * The activation mode of the tabs. Can be `manual` or `automatic`
+ * - `manual`: Tabs are activated when clicked or press `enter` key.
+ * - `automatic`: Tabs are activated when receiving focus
+ * @default "automatic"
+ */
+ activationMode?: "manual" | "automatic"
+ /**
+ * Callback to be called when the selected/active tab changes
+ */
+ onValueChange?: (details: ValueChangeDetails) => void
+ /**
+ * Callback to be called when the focused tab changes
+ */
+ onFocusChange?: (details: FocusChangeDetails) => void
}
-type PublicContext = DirectionProperty &
- CommonProperties & {
- /**
- * The ids of the elements in the tabs. Useful for composition.
- */
- ids?: ElementIds
- /**
- * Specifies the localized strings that identifies the accessibility elements and their states
- */
- translations: IntlTranslations
- /**
- * Whether the keyboard navigation will loop from last tab to first, and vice versa.
- * @default true
- */
- loop: boolean
- /**
- * The selected tab id
- */
- value: string | null
- /**
- * The orientation of the tabs. Can be `horizontal` or `vertical`
- * - `horizontal`: only left and right arrow key navigation will work.
- * - `vertical`: only up and down arrow key navigation will work.
- *
- * @default "horizontal"
- */
- orientation?: "horizontal" | "vertical"
- /**
- * The activation mode of the tabs. Can be `manual` or `automatic`
- * - `manual`: Tabs are activated when clicked or press `enter` key.
- * - `automatic`: Tabs are activated when receiving focus
- * @default "automatic"
- */
- activationMode?: "manual" | "automatic"
- /**
- * Callback to be called when the selected/active tab changes
- */
- onChange?: (details: { value: string }) => void
- /**
- * Callback to be called when the focused tab changes
- */
- onFocus?: (details: { value: string }) => void
- }
-
export type UserDefinedContext = RequiredBy
type ComputedContext = Readonly<{
@@ -114,9 +120,9 @@ type PrivateContext = Context<{
indicatorCleanup?: VoidFunction | null
}>
-export type MachineContext = PublicContext & ComputedContext & PrivateContext
+export interface MachineContext extends PublicContext, ComputedContext, PrivateContext {}
-export type MachineState = {
+export interface MachineState {
value: "idle" | "focused"
}
@@ -124,7 +130,25 @@ export type State = S.State
export type Send = S.Send
-export type MachineApi = {
+/* -----------------------------------------------------------------------------
+ * Component API
+ * -----------------------------------------------------------------------------*/
+
+export interface TriggerProps {
+ value: string
+ disabled?: boolean
+}
+
+export interface TriggerState {
+ isSelected: boolean
+ isFocused: boolean
+}
+
+export interface ContentProps {
+ value: string
+}
+
+export interface MachineApi {
/**
* The current value of the tabs.
*/
@@ -146,12 +170,17 @@ export type MachineApi = {
*/
clearValue(): void
/**
- * Sets the indicator rect to the tab with the given id.
+ * Sets the indicator rect to the tab with the given value
*/
- setIndicatorRect(id: string | null | undefined): void
+ setIndicatorRect(value: string): void
+ /**
+ * Returns the state of the trigger with the given props
+ */
+ getTriggerState(props: TriggerProps): TriggerState
+
rootProps: T["element"]
tablistProps: T["element"]
getTriggerProps(props: TriggerProps): T["button"]
- getContentProps({ value }: ContentProps): T["element"]
+ getContentProps(props: ContentProps): T["element"]
indicatorProps: T["element"]
}
diff --git a/packages/machines/tags-input/src/index.ts b/packages/machines/tags-input/src/index.ts
index 220ed72d2a..ec0d279319 100644
--- a/packages/machines/tags-input/src/index.ts
+++ b/packages/machines/tags-input/src/index.ts
@@ -1,4 +1,4 @@
export { anatomy } from "./tags-input.anatomy"
export { connect } from "./tags-input.connect"
export { machine } from "./tags-input.machine"
-export type { UserDefinedContext as Context, InteractOutsideEvent, MachineApi as Api } from "./tags-input.types"
+export type { UserDefinedContext as Context, MachineApi as Api } from "./tags-input.types"
diff --git a/packages/machines/tags-input/src/tags-input.connect.ts b/packages/machines/tags-input/src/tags-input.connect.ts
index 214d60335f..0d16b752ed 100644
--- a/packages/machines/tags-input/src/tags-input.connect.ts
+++ b/packages/machines/tags-input/src/tags-input.connect.ts
@@ -17,6 +17,16 @@ export function connect(state: State, send: Send, normalize
const isEditingTag = state.matches("editing:tag")
const isEmpty = state.context.count === 0
+ function getTagState(options: TagProps) {
+ const id = dom.getTagId(state.context, options)
+ return {
+ id,
+ isEditing: isEditingTag && state.context.editedTagId === id,
+ isHighlighted: id === state.context.highlightedTagId,
+ isDisabled: options.disabled || isDisabled,
+ }
+ }
+
return {
isEmpty,
inputValue: state.context.trimmedInputValue,
@@ -57,6 +67,8 @@ export function connect(state: State, send: Send, normalize
dom.getInputEl(state.context)?.focus()
},
+ getTagState,
+
rootProps: normalize.element({
dir: state.context.dir,
...parts.root.attrs,
@@ -140,7 +152,7 @@ export function connect(state: State, send: Send, normalize
send("ARROW_LEFT")
},
ArrowRight() {
- if (state.context.focusedId) {
+ if (state.context.highlightedTagId) {
event.preventDefault()
}
if (isCombobox && isExpanded) return
@@ -181,46 +193,44 @@ export function connect(state: State, send: Send, normalize
defaultValue: state.context.valueAsString,
}),
- getTagProps(options: TagProps) {
- const { value } = options
- const id = dom.getTagId(state.context, options)
+ getTagProps(props) {
+ const tagState = getTagState(props)
return normalize.element({
...parts.tag.attrs,
- id,
- hidden: isEditingTag ? state.context.editedTagId === id : false,
- "data-value": value,
+ id: tagState.id,
+ hidden: tagState.isEditing,
+ "data-value": props.value,
"data-disabled": dataAttr(isDisabled),
- "data-highlighted": dataAttr(id === state.context.focusedId),
+ "data-highlighted": dataAttr(tagState.isHighlighted),
onPointerDown(event) {
- if (!isInteractive) return
+ if (!isInteractive || tagState.isDisabled) return
event.preventDefault()
- send({ type: "POINTER_DOWN_TAG", id })
+ send({ type: "POINTER_DOWN_TAG", id: tagState.id })
},
onDoubleClick() {
- if (!isInteractive) return
- send({ type: "DOUBLE_CLICK_TAG", id })
+ if (!isInteractive || tagState.isDisabled) return
+ send({ type: "DOUBLE_CLICK_TAG", id: tagState.id })
},
})
},
- getTagInputProps(options: TagProps) {
- const id = dom.getTagId(state.context, options)
- const active = state.context.editedTagId === id
+ getTagInputProps(props) {
+ const tagState = getTagState(props)
+
return normalize.input({
...parts.tagInput.attrs,
- "aria-label": translations.tagEdited(options.value),
+ "aria-label": translations.tagEdited(props.value),
"aria-hidden": true,
disabled: isDisabled,
- id: dom.getTagInputId(state.context, options),
- type: "text",
+ id: dom.getTagInputId(state.context, props),
tabIndex: -1,
- hidden: isEditingTag ? !active : true,
- defaultValue: active ? state.context.editedTagValue : "",
+ hidden: !tagState.isEditing,
+ defaultValue: tagState.isEditing ? state.context.editedTagValue : "",
onChange(event) {
send({ type: "TAG_INPUT_TYPE", value: event.target.value })
},
onBlur(event) {
- send({ type: "TAG_INPUT_BLUR", target: event.relatedTarget, id })
+ send({ type: "TAG_INPUT_BLUR", target: event.relatedTarget, id: tagState.id })
},
onKeyDown(event) {
const keyMap: EventKeyMap = {
@@ -242,14 +252,14 @@ export function connect(state: State, send: Send, normalize
})
},
- getTagDeleteTriggerProps(options: TagProps) {
- const id = dom.getTagId(state.context, options)
+ getTagDeleteTriggerProps(props) {
+ const id = dom.getTagId(state.context, props)
return normalize.button({
...parts.tagDeleteTrigger.attrs,
- id: dom.getTagDeleteTriggerId(state.context, options),
+ id: dom.getTagDeleteTriggerId(state.context, props),
type: "button",
disabled: isDisabled,
- "aria-label": translations.deleteTagTriggerLabel(options.value),
+ "aria-label": translations.deleteTagTriggerLabel(props.value),
tabIndex: -1,
onPointerDown(event) {
if (!isInteractive) {
diff --git a/packages/machines/tags-input/src/tags-input.dom.ts b/packages/machines/tags-input/src/tags-input.dom.ts
index 50466ca46e..3c8f34d352 100644
--- a/packages/machines/tags-input/src/tags-input.dom.ts
+++ b/packages/machines/tags-input/src/tags-input.dom.ts
@@ -30,21 +30,20 @@ export const dom = createScope({
getIndexOfId: (ctx: Ctx, id: string) => indexOfId(dom.getTagElements(ctx), id),
isInputFocused: (ctx: Ctx) => dom.getDoc(ctx).activeElement === dom.getInputEl(ctx),
- getFocusedTagValue: (ctx: Ctx) => {
- if (!ctx.focusedId) return null
- const idx = dom.getIndexOfId(ctx, ctx.focusedId)
- if (idx === -1) return null
- return dom.getTagElements(ctx)[idx].dataset.value ?? null
+ getHighlightedTagValue: (ctx: Ctx) => {
+ if (!ctx.highlightedTagId) return null
+ const tagEl = dom.getById(ctx, ctx.highlightedTagId)
+ return tagEl?.dataset.value ?? null
},
setHoverIntent: (el: Element) => {
- const tag = el.closest("[data-part=tag]")
- if (!tag) return
- tag.dataset.deleteIntent = ""
+ const tagEl = el.closest("[data-part=tag]")
+ if (!tagEl) return
+ tagEl.dataset.deleteIntent = ""
},
clearHoverIntent: (el: Element) => {
- const tag = el.closest("[data-part=tag]")
- if (!tag) return
- delete tag.dataset.deleteIntent
+ const tagEl = el.closest("[data-part=tag]")
+ if (!tagEl) return
+ delete tagEl.dataset.deleteIntent
},
dispatchInputEvent(ctx: Ctx) {
const inputEl = dom.getHiddenInputEl(ctx)
diff --git a/packages/machines/tags-input/src/tags-input.machine.ts b/packages/machines/tags-input/src/tags-input.machine.ts
index 8e25ffc25b..cbbff8f935 100644
--- a/packages/machines/tags-input/src/tags-input.machine.ts
+++ b/packages/machines/tags-input/src/tags-input.machine.ts
@@ -21,7 +21,7 @@ export function machine(userContext: UserDefinedContext) {
inputValue: "",
editedTagValue: "",
editedTagId: null,
- focusedId: null,
+ highlightedTagId: null,
value: [],
dir: "ltr",
max: Infinity,
@@ -56,18 +56,16 @@ export function machine(userContext: UserDefinedContext) {
isOverflowing: (ctx) => ctx.count > ctx.max,
},
watch: {
- focusedId: "logFocusedTag",
+ highlightedTagId: "logHighlightedTag",
isOverflowing: "invokeOnInvalid",
log: "announceLog",
inputValue: "syncInputValue",
editedTagValue: "syncEditedTagInputValue",
},
- activities: ["trackFormControlState"],
+ activities: ["trackLiveRegion", "trackFormControlState"],
- entry: ["setupLiveRegion"],
-
- exit: ["removeLiveRegion", "clearLog"],
+ exit: ["clearLog"],
on: {
DOUBLE_CLICK_TAG: {
@@ -78,9 +76,9 @@ export function machine(userContext: UserDefinedContext) {
},
POINTER_DOWN_TAG: {
internal: true,
- guard: not("isTagFocused"),
+ guard: not("isTagHighlighted"),
target: "navigating:tag",
- actions: ["focusTag", "focusInput"],
+ actions: ["highlightTag", "focusInput"],
},
SET_INPUT_VALUE: {
actions: ["setInputValue"],
@@ -113,7 +111,7 @@ export function machine(userContext: UserDefinedContext) {
on: {
FOCUS: "focused:input",
POINTER_DOWN: {
- guard: not("hasFocusedId"),
+ guard: not("hasHighlightedTag"),
target: "focused:input",
},
},
@@ -121,7 +119,7 @@ export function machine(userContext: UserDefinedContext) {
"focused:input": {
tags: ["focused"],
- entry: ["focusInput", "clearFocusedId"],
+ entry: ["focusInput", "clearHighlightedId"],
activities: ["trackInteractOutside"],
on: {
TYPE: {
@@ -149,12 +147,12 @@ export function machine(userContext: UserDefinedContext) {
ARROW_LEFT: {
guard: and("hasTags", "isInputCaretAtStart"),
target: "navigating:tag",
- actions: "focusLastTag",
+ actions: "highlightLastTag",
},
BACKSPACE: {
target: "navigating:tag",
guard: and("hasTags", "isInputCaretAtStart"),
- actions: "focusLastTag",
+ actions: "highlightLastTag",
},
PASTE: {
guard: "addOnPaste",
@@ -169,20 +167,20 @@ export function machine(userContext: UserDefinedContext) {
on: {
ARROW_RIGHT: [
{
- guard: and("hasTags", "isInputCaretAtStart", not("isLastTagFocused")),
- actions: "focusNextTag",
+ guard: and("hasTags", "isInputCaretAtStart", not("isLastTagHighlighted")),
+ actions: "highlightNextTag",
},
{ target: "focused:input" },
],
ARROW_LEFT: {
- actions: "focusPrevTag",
+ actions: "highlightPrevTag",
},
BLUR: {
target: "idle",
- actions: "clearFocusedId",
+ actions: "clearHighlightedId",
},
ENTER: {
- guard: and("allowEditTag", "hasFocusedId"),
+ guard: and("allowEditTag", "hasHighlightedTag"),
target: "editing:tag",
actions: ["setEditedId", "initializeEditedTagValue", "focusEditedTagInput"],
},
@@ -194,15 +192,15 @@ export function machine(userContext: UserDefinedContext) {
},
BACKSPACE: [
{
- guard: "isFirstTagFocused",
- actions: ["deleteFocusedTag", "focusFirstTag"],
+ guard: "isFirstTagHighlighted",
+ actions: ["deleteHighlightedTag", "highlightFirstTag"],
},
{
- actions: ["deleteFocusedTag", "focusPrevTag"],
+ actions: ["deleteHighlightedTag", "highlightPrevTag"],
},
],
DELETE: {
- actions: ["deleteFocusedTag", "focusTagAtIndex"],
+ actions: ["deleteHighlightedTag", "highlightTagAtIndex"],
},
},
},
@@ -217,22 +215,22 @@ export function machine(userContext: UserDefinedContext) {
},
TAG_INPUT_ESCAPE: {
target: "navigating:tag",
- actions: ["clearEditedTagValue", "focusInput", "clearEditedId", "focusTagAtIndex"],
+ actions: ["clearEditedTagValue", "focusInput", "clearEditedId", "highlightTagAtIndex"],
},
TAG_INPUT_BLUR: [
{
guard: "isInputRelatedTarget",
target: "navigating:tag",
- actions: ["clearEditedTagValue", "clearFocusedId", "clearEditedId"],
+ actions: ["clearEditedTagValue", "clearHighlightedId", "clearEditedId"],
},
{
target: "idle",
- actions: ["clearEditedTagValue", "clearFocusedId", "clearEditedId", "raiseExternalBlurEvent"],
+ actions: ["clearEditedTagValue", "clearHighlightedId", "clearEditedId", "raiseExternalBlurEvent"],
},
],
TAG_INPUT_ENTER: {
target: "navigating:tag",
- actions: ["submitEditedTagValue", "focusInput", "clearEditedId", "focusTagAtIndex"],
+ actions: ["submitEditedTagValue", "focusInput", "clearEditedId", "highlightTagAtIndex"],
},
},
},
@@ -242,10 +240,10 @@ export function machine(userContext: UserDefinedContext) {
guards: {
isInputRelatedTarget: (ctx, evt) => evt.relatedTarget === dom.getInputEl(ctx),
isAtMax: (ctx) => ctx.isAtMax,
- hasFocusedId: (ctx) => ctx.focusedId !== null,
- isTagFocused: (ctx, evt) => ctx.focusedId === evt.id,
- isFirstTagFocused: (ctx) => dom.getFirstEl(ctx)?.id === ctx.focusedId,
- isLastTagFocused: (ctx) => dom.getLastEl(ctx)?.id === ctx.focusedId,
+ hasHighlightedTag: (ctx) => ctx.highlightedTagId !== null,
+ isTagHighlighted: (ctx, evt) => ctx.highlightedTagId === evt.id,
+ isFirstTagHighlighted: (ctx) => dom.getFirstEl(ctx)?.id === ctx.highlightedTagId,
+ isLastTagHighlighted: (ctx) => dom.getLastEl(ctx)?.id === ctx.highlightedTagId,
isInputValueEmpty: (ctx) => ctx.trimmedInputValue.length === 0,
hasTags: (ctx) => ctx.value.length > 0,
allowOverflow: (ctx) => !!ctx.allowOverflow,
@@ -295,6 +293,13 @@ export function machine(userContext: UserDefinedContext) {
const input = dom.getTagInputEl(ctx, { value: ctx.editedTagValue, index: ctx.idx })
return autoResizeInput(input)
},
+ trackLiveRegion(ctx) {
+ ctx.liveRegion = createLiveRegion({
+ level: "assertive",
+ document: dom.getDoc(ctx),
+ })
+ return () => ctx.liveRegion?.destroy()
+ },
},
actions: {
@@ -307,46 +312,40 @@ export function machine(userContext: UserDefinedContext) {
dispatchChangeEvent(ctx) {
dom.dispatchInputEvent(ctx)
},
- setupLiveRegion(ctx) {
- ctx.liveRegion = createLiveRegion({
- level: "assertive",
- document: dom.getDoc(ctx),
- })
- },
- focusNextTag(ctx) {
- if (ctx.focusedId == null) return
- const next = dom.getNextEl(ctx, ctx.focusedId)
+ highlightNextTag(ctx) {
+ if (ctx.highlightedTagId == null) return
+ const next = dom.getNextEl(ctx, ctx.highlightedTagId)
if (next == null) return
- set.focusedId(ctx, next.id)
+ set.highlightedId(ctx, next.id)
},
- focusFirstTag(ctx) {
+ highlightFirstTag(ctx) {
raf(() => {
const first = dom.getFirstEl(ctx)
if (first == null) return
- set.focusedId(ctx, first.id)
+ set.highlightedId(ctx, first.id)
})
},
- focusLastTag(ctx) {
+ highlightLastTag(ctx) {
const last = dom.getLastEl(ctx)
if (last == null) return
- set.focusedId(ctx, last.id)
+ set.highlightedId(ctx, last.id)
},
- focusPrevTag(ctx) {
- if (ctx.focusedId == null) return
- const prev = dom.getPrevEl(ctx, ctx.focusedId)
- set.focusedId(ctx, prev?.id || null)
+ highlightPrevTag(ctx) {
+ if (ctx.highlightedTagId == null) return
+ const prev = dom.getPrevEl(ctx, ctx.highlightedTagId)
+ set.highlightedId(ctx, prev?.id || null)
},
- focusTag(ctx, evt) {
- set.focusedId(ctx, evt.id)
+ highlightTag(ctx, evt) {
+ set.highlightedId(ctx, evt.id)
},
- focusTagAtIndex(ctx) {
+ highlightTagAtIndex(ctx) {
raf(() => {
if (ctx.idx == null) return
const tagEl = dom.getTagElAtIndex(ctx, ctx.idx)
if (tagEl == null) return
- set.focusedId(ctx, tagEl.id)
+ set.highlightedId(ctx, tagEl.id)
ctx.idx = undefined
})
},
@@ -360,9 +359,9 @@ export function machine(userContext: UserDefinedContext) {
set.value(ctx, removeAt(ctx.value, index))
},
- deleteFocusedTag(ctx) {
- if (ctx.focusedId == null) return
- const index = dom.getIndexOfId(ctx, ctx.focusedId)
+ deleteHighlightedTag(ctx) {
+ if (ctx.highlightedTagId == null) return
+ const index = dom.getIndexOfId(ctx, ctx.highlightedTagId)
ctx.idx = index
const value = ctx.value[index]
@@ -373,7 +372,7 @@ export function machine(userContext: UserDefinedContext) {
set.value(ctx, removeAt(ctx.value, index))
},
setEditedId(ctx, evt) {
- ctx.editedTagId = evt.id ?? ctx.focusedId
+ ctx.editedTagId = evt.id ?? ctx.highlightedTagId
ctx.idx = dom.getIndexOfId(ctx, ctx.editedTagId!)
},
clearEditedId(ctx) {
@@ -418,8 +417,8 @@ export function machine(userContext: UserDefinedContext) {
setInputValue(ctx, evt) {
ctx.inputValue = evt.value
},
- clearFocusedId(ctx) {
- ctx.focusedId = null
+ clearHighlightedId(ctx) {
+ ctx.highlightedTagId = null
},
focusInput(ctx) {
raf(() => {
@@ -434,27 +433,27 @@ export function machine(userContext: UserDefinedContext) {
dom.setValue(inputEl, ctx.inputValue)
},
syncEditedTagInputValue(ctx, evt) {
- const id = ctx.editedTagId || ctx.focusedId || evt.id
+ const id = ctx.editedTagId || ctx.highlightedTagId || evt.id
if (id == null) return
const editTagInputEl = dom.getById