Skip to content

Commit 01566a1

Browse files
committed
refactor: bindable equality check
1 parent db279c7 commit 01566a1

File tree

16 files changed

+47
-19
lines changed

16 files changed

+47
-19
lines changed

.changeset/tender-ducks-beg.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
"@zag-js/navigation-menu": patch
3+
"@zag-js/time-picker": patch
4+
"@zag-js/tags-input": patch
5+
"@zag-js/pin-input": patch
6+
"@zag-js/tree-view": patch
7+
"@zag-js/svelte": patch
8+
"@zag-js/combobox": patch
9+
"@zag-js/react": patch
10+
"@zag-js/solid": patch
11+
"@zag-js/select": patch
12+
"@zag-js/slider": patch
13+
"@zag-js/vue": patch
14+
---
15+
16+
Fix issue where machines that hold complex objects

.xstate/pin-input.js

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const fetchMachine = createMachine({
3636
value: bindable(() => ({
3737
value: prop("value"),
3838
defaultValue: prop("defaultValue"),
39+
isEqual,
3940
onChange(value) {
4041
prop("onValueChange")?.({
4142
value,

.xstate/slider.js

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const fetchMachine = createMachine({
4141
value: bindable(() => ({
4242
defaultValue: prop("defaultValue"),
4343
value: prop("value"),
44+
isEqual,
4445
hash(a) {
4546
return a.join(",");
4647
},

packages/frameworks/react/src/bindable.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import type { Bindable, BindableParams } from "@zag-js/core"
2-
import { identity, isEqual, isFunction } from "@zag-js/utils"
2+
import { identity, isFunction } from "@zag-js/utils"
33
import { useRef, useState } from "react"
44
import { flushSync } from "react-dom"
55
import { useSafeLayoutEffect } from "./use-layout-effect"
66

77
export function useBindable<T>(props: () => BindableParams<T>): Bindable<T> {
88
const initial = props().value ?? props().defaultValue
99

10-
const eq = props().isEqual ?? isEqual
10+
const eq = props().isEqual ?? Object.is
1111

1212
const [initialValue] = useState(initial)
1313
const [value, setValue] = useState(initialValue)

packages/frameworks/solid/src/bindable.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import type { Bindable, BindableParams } from "@zag-js/core"
2-
import { isEqual, isFunction } from "@zag-js/utils"
2+
import { isFunction } from "@zag-js/utils"
33
import { createEffect, createMemo, createSignal, type Accessor } from "solid-js"
44

55
export function createBindable<T>(props: Accessor<BindableParams<T>>): Bindable<T> {
66
const initial = props().value ?? props().defaultValue
77

8-
const eq = props().isEqual ?? isEqual
8+
const eq = props().isEqual ?? Object.is
99

1010
const [value, setValue] = createSignal(initial as T)
1111
const controlled = createMemo(() => props().value != undefined)

packages/frameworks/svelte/src/bindable.svelte.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { type Bindable, type BindableParams } from "@zag-js/core"
2-
import { identity, isEqual, isFunction } from "@zag-js/utils"
2+
import { identity, isFunction } from "@zag-js/utils"
33
import { flushSync } from "svelte"
44

55
export function bindable<T>(props: () => BindableParams<T>): Bindable<T> {
66
const initial = props().defaultValue ?? props().value
7-
const eq = props().isEqual ?? isEqual
7+
const eq = props().isEqual ?? Object.is
88

99
let value = $state(initial)
1010
const controlled = $derived(props().value !== undefined)

packages/frameworks/vue/src/bindable.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { Bindable, BindableParams } from "@zag-js/core"
2-
import { isFunction, isEqual } from "@zag-js/utils"
3-
import { shallowRef, computed as __computed } from "vue"
2+
import { isFunction } from "@zag-js/utils"
3+
import { computed as __computed, shallowRef } from "vue"
44

55
export function bindable<T>(props: () => BindableParams<T>): Bindable<T> {
66
const initial = props().defaultValue ?? props().value
7-
const eq = props().isEqual ?? isEqual
7+
const eq = props().isEqual ?? Object.is
88

99
const v = shallowRef(initial)
1010
const controlled = __computed(() => props().value !== undefined)

packages/machines/combobox/src/combobox.machine.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { createGuards, createMachine, type Params } from "@zag-js/core"
33
import { trackDismissableElement } from "@zag-js/dismissable"
44
import { clickIfLink, observeAttributes, observeChildren, raf, scrollIntoView } from "@zag-js/dom-query"
55
import { getPlacement } from "@zag-js/popper"
6-
import { addOrRemove, isBoolean, match, remove } from "@zag-js/utils"
6+
import { addOrRemove, isBoolean, isEqual, match, remove } from "@zag-js/utils"
77
import { collection } from "./combobox.collection"
88
import * as dom from "./combobox.dom"
99
import type { ComboboxSchema, Placement } from "./combobox.types"
@@ -54,6 +54,7 @@ export const machine = createMachine<ComboboxSchema>({
5454
value: bindable(() => ({
5555
defaultValue: prop("defaultValue"),
5656
value: prop("value"),
57+
isEqual,
5758
hash(value) {
5859
return value.join(",")
5960
},

packages/machines/navigation-menu/src/navigation-menu.machine.ts

-4
Original file line numberDiff line numberDiff line change
@@ -63,24 +63,20 @@ export const machine = createMachine({
6363
// nodes
6464
contentNode: bindable<HTMLElement | null>(() => ({
6565
defaultValue: null,
66-
isEqual: Object.is,
6766
})),
6867
triggerRect: bindable<Rect | null>(() => ({
6968
defaultValue: null,
7069
sync: true,
7170
})),
7271
triggerNode: bindable<HTMLElement | null>(() => ({
73-
isEqual: Object.is,
7472
defaultValue: null,
7573
})),
7674

7775
// nesting
7876
parent: bindable<NavigationMenuService | null>(() => ({
79-
isEqual: Object.is,
8077
defaultValue: null,
8178
})),
8279
children: bindable<Record<string, NavigationMenuService | null>>(() => ({
83-
isEqual: Object.is,
8480
defaultValue: {},
8581
})),
8682
}

packages/machines/pin-input/src/pin-input.machine.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { setup } from "@zag-js/core"
22
import { dispatchInputValueEvent, raf } from "@zag-js/dom-query"
3-
import { setValueAtIndex } from "@zag-js/utils"
3+
import { isEqual, setValueAtIndex } from "@zag-js/utils"
44
import * as dom from "./pin-input.dom"
55
import type { PinInputSchema } from "./pin-input.types"
66

@@ -30,6 +30,7 @@ export const machine = createMachine({
3030
value: bindable(() => ({
3131
value: prop("value"),
3232
defaultValue: prop("defaultValue"),
33+
isEqual,
3334
onChange(value) {
3435
prop("onValueChange")?.({ value, valueAsString: value.join("") })
3536
},

packages/machines/select/src/select.machine.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
trackFormControl,
1010
} from "@zag-js/dom-query"
1111
import { getPlacement, type Placement } from "@zag-js/popper"
12-
import { addOrRemove } from "@zag-js/utils"
12+
import { addOrRemove, isEqual } from "@zag-js/utils"
1313
import { collection } from "./select.collection"
1414
import * as dom from "./select.dom"
1515
import type { CollectionItem, SelectSchema } from "./select.types"
@@ -38,6 +38,7 @@ export const machine = createMachine<SelectSchema>({
3838
value: bindable(() => ({
3939
defaultValue: prop("defaultValue"),
4040
value: prop("value"),
41+
isEqual,
4142
onChange(value) {
4243
const items = prop("collection").findMany(value)
4344
return prop("onValueChange")?.({ value, items })

packages/machines/slider/src/slider.machine.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createMachine } from "@zag-js/core"
22
import { raf, setElementValue, trackFormControl, trackPointerMove } from "@zag-js/dom-query"
33
import { trackElementsSize, type ElementSize } from "@zag-js/element-size"
4-
import { getValuePercent, setValueAtIndex } from "@zag-js/utils"
4+
import { getValuePercent, isEqual, setValueAtIndex } from "@zag-js/utils"
55
import * as dom from "./slider.dom"
66
import type { SliderSchema } from "./slider.types"
77
import { constrainValue, decrement, getClosestIndex, getRangeAtIndex, increment, normalizeValues } from "./slider.utils"
@@ -38,6 +38,7 @@ export const machine = createMachine<SliderSchema>({
3838
value: bindable(() => ({
3939
defaultValue: prop("defaultValue"),
4040
value: prop("value"),
41+
isEqual,
4142
hash(a) {
4243
return a.join(",")
4344
},

packages/machines/tags-input/src/tags-input.machine.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { createGuards, createMachine } from "@zag-js/core"
33
import { contains, raf, setElementValue, trackFormControl } from "@zag-js/dom-query"
44
import { trackInteractOutside } from "@zag-js/interact-outside"
55
import { createLiveRegion } from "@zag-js/live-region"
6-
import { removeAt, uniq, warn } from "@zag-js/utils"
6+
import { isEqual, removeAt, uniq, warn } from "@zag-js/utils"
77
import * as dom from "./tags-input.dom"
88
import type { TagsInputSchema } from "./tags-input.types"
99

@@ -51,6 +51,7 @@ export const machine = createMachine<TagsInputSchema>({
5151
value: bindable(() => ({
5252
defaultValue: prop("defaultValue"),
5353
value: prop("value"),
54+
isEqual,
5455
hash(value) {
5556
return value.join(", ")
5657
},

packages/machines/time-picker/src/time-picker.machine.ts

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
getStringifiedValue,
1414
getTimeValue,
1515
is12HourFormat,
16+
isTimeEqual,
1617
} from "./time-picker.utils"
1718

1819
const { and } = createGuards<TimePickerSchema>()
@@ -43,6 +44,7 @@ export const machine = createMachine<TimePickerSchema>({
4344
hash(a) {
4445
return a?.toString() ?? ""
4546
},
47+
isEqual: isTimeEqual,
4648
onChange(value) {
4749
const computed = getComputed()
4850
prop("onValueChange")?.({

packages/machines/time-picker/src/time-picker.utils.ts

+5
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,8 @@ export function clampTime(value: Time, min: Time | undefined, max: Time | undefi
106106
}
107107
return time
108108
}
109+
110+
export function isTimeEqual(a: Time | null, b: Time | undefined | null) {
111+
if (!a || !b) return false
112+
return a.hour === b.hour && a.minute === b.minute && a.second === b.second
113+
}

packages/machines/tree-view/src/tree-view.machine.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createGuards, createMachine } from "@zag-js/core"
22
import { getByTypeahead } from "@zag-js/dom-query"
3-
import { add, addOrRemove, first, remove, uniq } from "@zag-js/utils"
3+
import { add, addOrRemove, first, isEqual, remove, uniq } from "@zag-js/utils"
44
import { collection } from "./tree-view.collection"
55
import * as dom from "./tree-view.dom"
66
import type { TreeViewSchema } from "./tree-view.types"
@@ -30,6 +30,7 @@ export const machine = createMachine<TreeViewSchema>({
3030
expandedValue: bindable(() => ({
3131
defaultValue: prop("defaultExpandedValue"),
3232
value: prop("expandedValue"),
33+
isEqual,
3334
onChange(value) {
3435
const focusedValue = getContext().get("focusedValue")
3536
prop("onExpandedChange")?.({ expandedValue: value, focusedValue })
@@ -38,6 +39,7 @@ export const machine = createMachine<TreeViewSchema>({
3839
selectedValue: bindable(() => ({
3940
defaultValue: prop("defaultSelectedValue"),
4041
value: prop("selectedValue"),
42+
isEqual,
4143
onChange(value) {
4244
const focusedValue = getContext().get("focusedValue")
4345
prop("onSelectionChange")?.({ selectedValue: value, focusedValue })

0 commit comments

Comments
 (0)