@zag-js/react
Advanced tools
Comparing version 0.82.2 to 1.0.0
@@ -1,7 +0,8 @@ | ||
import * as _zag_js_core from '@zag-js/core'; | ||
import { StateMachine, Machine, MachineSrc } from '@zag-js/core'; | ||
export { ContextFrom, EventFrom, StateFrom, mergeProps } from '@zag-js/core'; | ||
import { BaseSchema, MachineConfig, Service } from '@zag-js/core'; | ||
export { mergeProps } from '@zag-js/core'; | ||
import * as _zag_js_types from '@zag-js/types'; | ||
import { HTMLAttributes, CSSProperties, JSX, RefObject, PropsWithChildren } from 'react'; | ||
declare function useMachine<T extends BaseSchema>(machine: MachineConfig<T>, userProps?: Partial<T["props"]>): Service<T>; | ||
type WithoutRef<T> = Omit<T, "ref">; | ||
@@ -24,6 +25,2 @@ type ElementsWithoutRef = { | ||
declare function useActor<TContext extends Record<string, any>, TState extends StateMachine.StateSchema, TEvent extends StateMachine.EventObject = StateMachine.AnyEventObject>(service: Machine<TContext, TState, TEvent>): readonly [StateMachine.State<TContext, TState, TEvent>, (evt: StateMachine.Event<TEvent>) => void]; | ||
declare function useMachine<TContext extends Record<string, any>, TState extends StateMachine.StateSchema, TEvent extends StateMachine.EventObject = StateMachine.AnyEventObject>(machine: MachineSrc<TContext, TState, TEvent>, options?: StateMachine.HookOptions<TContext, TState, TEvent>): readonly [StateMachine.State<TContext, TState, TEvent>, (evt: StateMachine.Event<TEvent>) => void, _zag_js_core.Machine<TContext, TState, TEvent>]; | ||
export { Portal, type PortalProps, type PropTypes, normalizeProps, useActor, useMachine }; | ||
export { Portal, type PortalProps, type PropTypes, normalizeProps, useMachine }; |
@@ -0,89 +1,125 @@ | ||
"use client"; | ||
'use strict'; | ||
var core = require('@zag-js/core'); | ||
var React = require('react'); | ||
var reactDom = require('react-dom'); | ||
var types = require('@zag-js/types'); | ||
var react = require('react'); | ||
var reactDom = require('react-dom'); | ||
var jsxRuntime = require('react/jsx-runtime'); | ||
var store = require('@zag-js/store'); | ||
var proxyCompare = require('proxy-compare'); | ||
var normalizeProps = types.createNormalizer((v) => v); | ||
var Portal = (props) => { | ||
const { children, container, disabled, getRootNode } = props; | ||
const isServer = typeof window === "undefined"; | ||
if (isServer || disabled) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children }); | ||
const doc = getRootNode?.().ownerDocument ?? document; | ||
const mountNode = container?.current ?? doc.body; | ||
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: react.Children.map(children, (child) => reactDom.createPortal(child, mountNode)) }); | ||
}; | ||
// ../../utilities/core/src/equal.ts | ||
var isArrayLike = (value) => value?.constructor.name === "Array"; | ||
var isArrayEqual = (a, b) => { | ||
if (a.length !== b.length) return false; | ||
for (let i = 0; i < a.length; i++) { | ||
if (!isEqual(a[i], b[i])) return false; | ||
function _interopNamespace(e) { | ||
if (e && e.__esModule) return e; | ||
var n = Object.create(null); | ||
if (e) { | ||
Object.keys(e).forEach(function (k) { | ||
if (k !== 'default') { | ||
var d = Object.getOwnPropertyDescriptor(e, k); | ||
Object.defineProperty(n, k, d.get ? d : { | ||
enumerable: true, | ||
get: function () { return e[k]; } | ||
}); | ||
} | ||
}); | ||
} | ||
return true; | ||
}; | ||
var isEqual = (a, b) => { | ||
if (Object.is(a, b)) return true; | ||
if (a == null && b != null || a != null && b == null) return false; | ||
if (typeof a?.isEqual === "function" && typeof b?.isEqual === "function") { | ||
return a.isEqual(b); | ||
} | ||
if (typeof a === "function" && typeof b === "function") { | ||
return a.toString() === b.toString(); | ||
} | ||
if (isArrayLike(a) && isArrayLike(b)) { | ||
return isArrayEqual(Array.from(a), Array.from(b)); | ||
} | ||
if (!(typeof a === "object") || !(typeof b === "object")) return false; | ||
const keys = Object.keys(b ?? /* @__PURE__ */ Object.create(null)); | ||
const length = keys.length; | ||
for (let i = 0; i < length; i++) { | ||
const hasKey = Reflect.has(a, keys[i]); | ||
if (!hasKey) return false; | ||
} | ||
for (let i = 0; i < length; i++) { | ||
const key = keys[i]; | ||
if (!isEqual(a[key], b[key])) return false; | ||
} | ||
return true; | ||
}; | ||
n.default = e; | ||
return Object.freeze(n); | ||
} | ||
var React__namespace = /*#__PURE__*/_interopNamespace(React); | ||
// src/index.ts | ||
// ../../utilities/core/src/array.ts | ||
function toArray(v) { | ||
if (!v) return []; | ||
return Array.isArray(v) ? v : [v]; | ||
} | ||
// ../../utilities/core/src/functions.ts | ||
var identity = (v) => v(); | ||
// ../../utilities/core/src/guard.ts | ||
var isDev = () => process.env.NODE_ENV !== "production"; | ||
var isString = (v) => typeof v === "string"; | ||
var isFunction = (v) => typeof v === "function"; | ||
var fnToString = Function.prototype.toString; | ||
fnToString.call(Object); | ||
// ../../utilities/core/src/object.ts | ||
function compact(obj) { | ||
if (!isPlainObject(obj) || obj === void 0) return obj; | ||
const keys = Reflect.ownKeys(obj).filter((key) => typeof key === "string"); | ||
const filtered = {}; | ||
for (const key of keys) { | ||
const value = obj[key]; | ||
if (value !== void 0) { | ||
filtered[key] = compact(value); | ||
// ../../utilities/core/src/warning.ts | ||
function warn(...a) { | ||
const m = a.length === 1 ? a[0] : a[1]; | ||
const c = a.length === 2 ? a[0] : true; | ||
if (c && process.env.NODE_ENV !== "production") { | ||
console.warn(m); | ||
} | ||
} | ||
var useSafeLayoutEffect = typeof globalThis.document !== "undefined" ? React.useLayoutEffect : React.useEffect; | ||
// src/bindable.ts | ||
function useBindable(props) { | ||
const initial = props().value ?? props().defaultValue; | ||
if (props().debug) { | ||
console.log(`[bindable > ${props().debug}] initial`, initial); | ||
} | ||
const eq = props().isEqual ?? Object.is; | ||
const [initialValue] = React.useState(initial); | ||
const [value, setValue] = React.useState(initialValue); | ||
const controlled = props().value !== void 0; | ||
const valueRef = React.useRef(value); | ||
valueRef.current = controlled ? props().value : value; | ||
const prevValue = React.useRef(valueRef.current); | ||
useSafeLayoutEffect(() => { | ||
prevValue.current = valueRef.current; | ||
}, [value, props().value]); | ||
const setFn = (value2) => { | ||
const prev = prevValue.current; | ||
const next = isFunction(value2) ? value2(prev) : value2; | ||
if (props().debug) { | ||
console.log(`[bindable > ${props().debug}] setValue`, { next, prev }); | ||
} | ||
if (!controlled) setValue(next); | ||
if (!eq(next, prev)) { | ||
props().onChange?.(next, prev); | ||
} | ||
}; | ||
function get() { | ||
return controlled ? props().value : value; | ||
} | ||
return filtered; | ||
return { | ||
initial, | ||
ref: valueRef, | ||
get, | ||
set(value2) { | ||
const exec = props().sync ? reactDom.flushSync : identity; | ||
exec(() => setFn(value2)); | ||
}, | ||
invoke(nextValue, prevValue2) { | ||
props().onChange?.(nextValue, prevValue2); | ||
}, | ||
hash(value2) { | ||
return props().hash?.(value2) ?? String(value2); | ||
} | ||
}; | ||
} | ||
var isPlainObject = (v) => { | ||
return v && typeof v === "object" && v.constructor === Object; | ||
}; | ||
function useUpdateEffect(callback, deps) { | ||
const render = react.useRef(false); | ||
const effect = react.useRef(false); | ||
react.useEffect(() => { | ||
function useRefs(refs) { | ||
const ref = React.useRef(refs); | ||
return { | ||
get(key) { | ||
return ref.current[key]; | ||
}, | ||
set(key, value) { | ||
ref.current[key] = value; | ||
} | ||
}; | ||
} | ||
var useTrack = (deps, effect) => { | ||
const render = React.useRef(false); | ||
const called = React.useRef(false); | ||
React.useEffect(() => { | ||
const mounted = render.current; | ||
const run = mounted && effect.current; | ||
if (run) { | ||
return callback(); | ||
} | ||
effect.current = true; | ||
}, deps); | ||
react.useEffect(() => { | ||
const run = mounted && called.current; | ||
if (run) return effect(); | ||
called.current = true; | ||
}, [...(deps ?? []).map((d) => typeof d === "function" ? d() : d)]); | ||
React.useEffect(() => { | ||
render.current = true; | ||
@@ -94,90 +130,227 @@ return () => { | ||
}, []); | ||
} | ||
}; | ||
// src/use-snapshot.ts | ||
var targetCache = store.globalRef("__zag__targetCache", () => /* @__PURE__ */ new WeakMap()); | ||
function useSnapshot(service, options) { | ||
const { actions, context, sync: notifyInSync } = options ?? {}; | ||
const lastSnapshot = react.useRef(void 0); | ||
const lastAffected = react.useRef(void 0); | ||
const currSnapshot = react.useSyncExternalStore( | ||
react.useCallback((callback) => store.subscribe(service.state, callback, notifyInSync), [notifyInSync]), | ||
() => { | ||
const nextSnapshot = store.snapshot(service.state); | ||
try { | ||
if (lastSnapshot.current && lastAffected.current && !proxyCompare.isChanged(lastSnapshot.current, nextSnapshot, lastAffected.current, /* @__PURE__ */ new WeakMap())) { | ||
return lastSnapshot.current; | ||
} | ||
} catch { | ||
} | ||
return nextSnapshot; | ||
// src/machine.ts | ||
function useMachine(machine, userProps = {}) { | ||
const scope = React.useMemo(() => { | ||
const { id, ids, getRootNode } = userProps; | ||
return core.createScope({ id, ids, getRootNode }); | ||
}, [userProps]); | ||
const debug = (...args) => { | ||
if (machine.debug) console.log(...args); | ||
}; | ||
const props = machine.props?.({ props: userProps, scope }) ?? userProps; | ||
const prop = useProp(props); | ||
const context = machine.context?.({ | ||
prop, | ||
bindable: useBindable, | ||
scope, | ||
flush, | ||
getContext() { | ||
return ctx; | ||
}, | ||
() => store.snapshot(service.state) | ||
); | ||
service.setOptions({ actions }); | ||
const ctx = react.useMemo(() => compact(context ?? {}), [context]); | ||
useUpdateEffect(() => { | ||
const entries = Object.entries(ctx); | ||
const previousCtx = service.contextSnapshot ?? {}; | ||
const equality = entries.map(([key, value]) => ({ | ||
key, | ||
curr: value, | ||
prev: previousCtx[key], | ||
equal: isEqual(previousCtx[key], value) | ||
})); | ||
const allEqual = equality.every(({ equal }) => equal); | ||
if (!allEqual) { | ||
service.setContext(ctx); | ||
getComputed() { | ||
return computed; | ||
} | ||
}, [ctx]); | ||
const currAffected = /* @__PURE__ */ new WeakMap(); | ||
react.useEffect(() => { | ||
lastSnapshot.current = currSnapshot; | ||
lastAffected.current = currAffected; | ||
}); | ||
const proxyCache = react.useMemo(() => /* @__PURE__ */ new WeakMap(), []); | ||
return proxyCompare.createProxy(currSnapshot, currAffected, proxyCache, targetCache); | ||
} | ||
// src/use-actor.ts | ||
function useActor(service) { | ||
const state = useSnapshot(service); | ||
return [state, service.send]; | ||
} | ||
function useConstant(fn) { | ||
const ref = react.useRef(void 0); | ||
if (!ref.current) ref.current = { v: fn() }; | ||
return ref.current.v; | ||
} | ||
var useSafeLayoutEffect = typeof document !== "undefined" ? react.useLayoutEffect : react.useEffect; | ||
// src/use-service.ts | ||
function useService(machine, options) { | ||
const { state: hydratedState, context } = options ?? {}; | ||
const service = useConstant(() => { | ||
const instance = typeof machine === "function" ? machine() : machine; | ||
if (context) instance.setContext(context); | ||
instance._created(); | ||
return instance; | ||
const contextRef = useLiveRef(context); | ||
const ctx = { | ||
get(key) { | ||
return contextRef.current?.[key].ref.current; | ||
}, | ||
set(key, value) { | ||
contextRef.current?.[key].set(value); | ||
}, | ||
initial(key) { | ||
return contextRef.current?.[key].initial; | ||
}, | ||
hash(key) { | ||
const current = contextRef.current?.[key].get(); | ||
return contextRef.current?.[key].hash(current); | ||
} | ||
}; | ||
const effects = React.useRef(/* @__PURE__ */ new Map()); | ||
const transitionRef = React.useRef(null); | ||
const previousEventRef = React.useRef(null); | ||
const eventRef = React.useRef({ type: "" }); | ||
const getEvent = () => ({ | ||
...eventRef.current, | ||
current() { | ||
return eventRef.current; | ||
}, | ||
previous() { | ||
return previousEventRef.current; | ||
} | ||
}); | ||
const snapshotRef = react.useRef(void 0); | ||
const getState = () => ({ | ||
...state, | ||
matches(...values) { | ||
return values.includes(state.ref.current); | ||
}, | ||
hasTag(tag) { | ||
return !!machine.states[state.ref.current]?.tags?.includes(tag); | ||
} | ||
}); | ||
const refs = useRefs(machine.refs?.({ prop, context: ctx }) ?? {}); | ||
const getParams = () => ({ | ||
state: getState(), | ||
context: ctx, | ||
event: getEvent(), | ||
prop, | ||
send, | ||
action, | ||
guard, | ||
track: useTrack, | ||
refs, | ||
computed, | ||
flush, | ||
scope, | ||
choose | ||
}); | ||
const action = (keys) => { | ||
const strs = isFunction(keys) ? keys(getParams()) : keys; | ||
if (!strs) return; | ||
const fns = strs.map((s) => { | ||
const fn = machine.implementations?.actions?.[s]; | ||
if (!fn) warn(`[zag-js] No implementation found for action "${JSON.stringify(s)}"`); | ||
return fn; | ||
}); | ||
for (const fn of fns) { | ||
fn?.(getParams()); | ||
} | ||
}; | ||
const guard = (str) => { | ||
if (isFunction(str)) return str(getParams()); | ||
return machine.implementations?.guards?.[str](getParams()); | ||
}; | ||
const effect = (keys) => { | ||
const strs = isFunction(keys) ? keys(getParams()) : keys; | ||
if (!strs) return; | ||
const fns = strs.map((s) => { | ||
const fn = machine.implementations?.effects?.[s]; | ||
if (!fn) warn(`[zag-js] No implementation found for effect "${JSON.stringify(s)}"`); | ||
return fn; | ||
}); | ||
const cleanups = []; | ||
for (const fn of fns) { | ||
const cleanup = fn?.(getParams()); | ||
if (cleanup) cleanups.push(cleanup); | ||
} | ||
return () => cleanups.forEach((fn) => fn?.()); | ||
}; | ||
const choose = (transitions) => { | ||
return toArray(transitions).find((t) => { | ||
let result = !t.guard; | ||
if (isString(t.guard)) result = !!guard(t.guard); | ||
else if (isFunction(t.guard)) result = t.guard(getParams()); | ||
return result; | ||
}); | ||
}; | ||
const computed = (key) => { | ||
return machine.computed?.[key]({ | ||
context: ctx, | ||
event: getEvent(), | ||
prop, | ||
refs, | ||
scope, | ||
computed | ||
}) ?? {}; | ||
}; | ||
const state = useBindable(() => ({ | ||
defaultValue: machine.initialState({ prop }), | ||
onChange(nextState, prevState) { | ||
if (prevState) { | ||
const exitEffects = effects.current.get(prevState); | ||
exitEffects?.(); | ||
effects.current.delete(prevState); | ||
} | ||
if (prevState) { | ||
action(machine.states[prevState]?.exit); | ||
} | ||
action(transitionRef.current?.actions); | ||
const cleanup = effect(machine.states[nextState]?.effects); | ||
if (cleanup) effects.current.set(nextState, cleanup); | ||
if (prevState === "__init__") { | ||
action(machine.entry); | ||
const cleanup2 = effect(machine.effects); | ||
if (cleanup2) effects.current.set("__init__", cleanup2); | ||
} | ||
action(machine.states[nextState]?.entry); | ||
} | ||
})); | ||
useSafeLayoutEffect(() => { | ||
const stateInit = hydratedState ?? snapshotRef.current; | ||
service.start(stateInit); | ||
state.invoke(state.initial, "__init__"); | ||
const fns = effects.current; | ||
return () => { | ||
if (isDev()) { | ||
snapshotRef.current = service.getHydrationState(); | ||
} | ||
service.stop(); | ||
fns.forEach((fn) => fn?.()); | ||
effects.current = /* @__PURE__ */ new Map(); | ||
transitionRef.current = null; | ||
action(machine.exit); | ||
}; | ||
}, []); | ||
return service; | ||
const getCurrentState = () => { | ||
if ("ref" in state) return state.ref.current; | ||
return state.get(); | ||
}; | ||
const send = (event) => { | ||
queueMicrotask(() => { | ||
previousEventRef.current = eventRef.current; | ||
eventRef.current = event; | ||
debug("send", event); | ||
let currentState = getCurrentState(); | ||
const transitions = ( | ||
// @ts-ignore | ||
machine.states[currentState].on?.[event.type] ?? // @ts-ignore | ||
machine.on?.[event.type] | ||
); | ||
const transition = choose(transitions); | ||
if (!transition) return; | ||
transitionRef.current = transition; | ||
const target = transition.target ?? currentState; | ||
debug("transition", transition); | ||
const changed = target !== currentState; | ||
if (changed) { | ||
reactDom.flushSync(() => state.set(target)); | ||
} else { | ||
action(transition.actions ?? []); | ||
} | ||
}); | ||
}; | ||
machine.watch?.(getParams()); | ||
return { | ||
state: getState(), | ||
send, | ||
context: ctx, | ||
prop, | ||
scope, | ||
refs, | ||
computed, | ||
event: getEvent() | ||
}; | ||
} | ||
// src/use-machine.ts | ||
function useMachine(machine, options) { | ||
const service = useService(machine, options); | ||
const state = useSnapshot(service, options); | ||
return [state, service.send, service]; | ||
function useLiveRef(value) { | ||
const ref = React.useRef(value); | ||
ref.current = value; | ||
return ref; | ||
} | ||
function useProp(value) { | ||
const ref = useLiveRef(value); | ||
return function get(key) { | ||
return ref.current[key]; | ||
}; | ||
} | ||
function flush(fn) { | ||
queueMicrotask(() => { | ||
reactDom.flushSync(() => fn()); | ||
}); | ||
} | ||
var normalizeProps = types.createNormalizer((v) => v); | ||
var Portal = (props) => { | ||
const { children, container, disabled, getRootNode } = props; | ||
const isServer = typeof window === "undefined"; | ||
if (isServer || disabled) return /* @__PURE__ */ jsxRuntime.jsx(React__namespace.Fragment, { children }); | ||
const doc = getRootNode?.().ownerDocument ?? document; | ||
const mountNode = container?.current ?? doc.body; | ||
return /* @__PURE__ */ jsxRuntime.jsx(React__namespace.Fragment, { children: React__namespace.Children.map(children, (child) => reactDom.createPortal(child, mountNode)) }); | ||
}; | ||
@@ -190,3 +363,2 @@ Object.defineProperty(exports, "mergeProps", { | ||
exports.normalizeProps = normalizeProps; | ||
exports.useActor = useActor; | ||
exports.useMachine = useMachine; |
{ | ||
"name": "@zag-js/react", | ||
"version": "0.82.2", | ||
"version": "1.0.0", | ||
"description": "The react wrapper for zag", | ||
@@ -28,6 +28,5 @@ "keywords": [ | ||
"dependencies": { | ||
"proxy-compare": "3.0.1", | ||
"@zag-js/core": "0.82.2", | ||
"@zag-js/store": "0.82.2", | ||
"@zag-js/types": "0.82.2" | ||
"@zag-js/core": "1.0.0", | ||
"@zag-js/store": "1.0.0", | ||
"@zag-js/types": "1.0.0" | ||
}, | ||
@@ -37,6 +36,11 @@ "devDependencies": { | ||
"@types/react-dom": "^18", | ||
"@types/jsdom": "^21.1.7", | ||
"react": "18.3.1", | ||
"react-dom": "18.3.1", | ||
"clean-package": "2.2.0", | ||
"@zag-js/utils": "0.82.2" | ||
"@testing-library/react": "^16.2.0", | ||
"@testing-library/jest-dom": "^6.6.3", | ||
"@vitejs/plugin-react": "^4.3.4", | ||
"jsdom": "^26.0.0", | ||
"@zag-js/utils": "1.0.0" | ||
}, | ||
@@ -53,5 +57,10 @@ "peerDependencies": { | ||
".": { | ||
"types": "./dist/index.d.ts", | ||
"import": "./dist/index.mjs", | ||
"require": "./dist/index.js" | ||
"import": { | ||
"types": "./dist/index.d.mts", | ||
"default": "./dist/index.mjs" | ||
}, | ||
"require": { | ||
"types": "./dist/index.d.ts", | ||
"default": "./dist/index.js" | ||
} | ||
}, | ||
@@ -63,4 +72,5 @@ "./package.json": "./package.json" | ||
"lint": "eslint src", | ||
"typecheck": "tsc --noEmit" | ||
"typecheck": "tsc --noEmit", | ||
"test": "vitest" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
24536
5
698
0
11
1
+ Added@zag-js/core@1.0.0(transitive)
+ Added@zag-js/dom-query@1.0.0(transitive)
+ Added@zag-js/store@1.0.0(transitive)
+ Added@zag-js/types@1.0.0(transitive)
+ Added@zag-js/utils@1.0.0(transitive)
- Removedproxy-compare@3.0.1
- Removed@zag-js/core@0.82.2(transitive)
- Removed@zag-js/store@0.82.2(transitive)
- Removed@zag-js/types@0.82.2(transitive)
- Removed@zag-js/utils@0.82.2(transitive)
Updated@zag-js/core@1.0.0
Updated@zag-js/store@1.0.0
Updated@zag-js/types@1.0.0