@zag-js/core 1.35.0 → 1.35.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/dist/state.js +31 -2
- package/dist/state.mjs +31 -2
- package/dist/types.d.mts +3 -1
- package/dist/types.d.ts +3 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -102,6 +102,45 @@ const machine = createMachine({
|
|
|
102
102
|
// service.state.matches("dialog.open") === true when nested state is active
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
+
### State IDs and `#id` targets
|
|
106
|
+
|
|
107
|
+
Use `id` on a state node when you want to target it explicitly from anywhere in the machine.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { createMachine } from "@zag-js/core"
|
|
111
|
+
|
|
112
|
+
const machine = createMachine({
|
|
113
|
+
initialState() {
|
|
114
|
+
return "dialog"
|
|
115
|
+
},
|
|
116
|
+
states: {
|
|
117
|
+
dialog: {
|
|
118
|
+
initial: "open",
|
|
119
|
+
states: {
|
|
120
|
+
focused: {
|
|
121
|
+
id: "dialogFocused",
|
|
122
|
+
},
|
|
123
|
+
open: {
|
|
124
|
+
initial: "idle",
|
|
125
|
+
states: {
|
|
126
|
+
idle: {
|
|
127
|
+
on: {
|
|
128
|
+
CLOSE: { target: "#dialogFocused" },
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
})
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Notes:
|
|
140
|
+
|
|
141
|
+
- `#id` targets resolve by state node `id` (XState-style).
|
|
142
|
+
- State IDs must be unique within a machine.
|
|
143
|
+
|
|
105
144
|
## API
|
|
106
145
|
|
|
107
146
|
### `createMachine(config, options)`
|
package/dist/state.js
CHANGED
|
@@ -31,20 +31,36 @@ __export(state_exports, {
|
|
|
31
31
|
});
|
|
32
32
|
module.exports = __toCommonJS(state_exports);
|
|
33
33
|
var STATE_DELIMITER = ".";
|
|
34
|
+
var ABSOLUTE_PREFIX = "#";
|
|
34
35
|
var stateIndexCache = /* @__PURE__ */ new WeakMap();
|
|
36
|
+
var stateIdIndexCache = /* @__PURE__ */ new WeakMap();
|
|
35
37
|
function joinStatePath(parts) {
|
|
36
38
|
return parts.join(STATE_DELIMITER);
|
|
37
39
|
}
|
|
38
40
|
function isAbsoluteStatePath(value) {
|
|
39
41
|
return value.includes(STATE_DELIMITER);
|
|
40
42
|
}
|
|
43
|
+
function isExplicitAbsoluteStatePath(value) {
|
|
44
|
+
return value.startsWith(ABSOLUTE_PREFIX);
|
|
45
|
+
}
|
|
46
|
+
function stripAbsolutePrefix(value) {
|
|
47
|
+
return isExplicitAbsoluteStatePath(value) ? value.slice(ABSOLUTE_PREFIX.length) : value;
|
|
48
|
+
}
|
|
41
49
|
function appendStatePath(base, segment) {
|
|
42
50
|
return base ? `${base}${STATE_DELIMITER}${segment}` : segment;
|
|
43
51
|
}
|
|
44
52
|
function buildStateIndex(machine) {
|
|
45
53
|
const index = /* @__PURE__ */ new Map();
|
|
54
|
+
const idIndex = /* @__PURE__ */ new Map();
|
|
46
55
|
const visit = (basePath, state) => {
|
|
47
56
|
index.set(basePath, state);
|
|
57
|
+
const stateId = state.id;
|
|
58
|
+
if (stateId) {
|
|
59
|
+
if (idIndex.has(stateId)) {
|
|
60
|
+
throw new Error(`Duplicate state id: ${stateId}`);
|
|
61
|
+
}
|
|
62
|
+
idIndex.set(stateId, basePath);
|
|
63
|
+
}
|
|
48
64
|
const childStates = state.states;
|
|
49
65
|
if (!childStates) return;
|
|
50
66
|
for (const [childKey, childState] of Object.entries(childStates)) {
|
|
@@ -57,15 +73,20 @@ function buildStateIndex(machine) {
|
|
|
57
73
|
if (!topState) continue;
|
|
58
74
|
visit(topKey, topState);
|
|
59
75
|
}
|
|
60
|
-
return index;
|
|
76
|
+
return { index, idIndex };
|
|
61
77
|
}
|
|
62
78
|
function ensureStateIndex(machine) {
|
|
63
79
|
const cached = stateIndexCache.get(machine);
|
|
64
80
|
if (cached) return cached;
|
|
65
|
-
const index = buildStateIndex(machine);
|
|
81
|
+
const { index, idIndex } = buildStateIndex(machine);
|
|
66
82
|
stateIndexCache.set(machine, index);
|
|
83
|
+
stateIdIndexCache.set(machine, idIndex);
|
|
67
84
|
return index;
|
|
68
85
|
}
|
|
86
|
+
function getStatePathById(machine, stateId) {
|
|
87
|
+
ensureStateIndex(machine);
|
|
88
|
+
return stateIdIndexCache.get(machine)?.get(stateId);
|
|
89
|
+
}
|
|
69
90
|
function toSegments(value) {
|
|
70
91
|
if (!value) return [];
|
|
71
92
|
return String(value).split(STATE_DELIMITER).filter(Boolean);
|
|
@@ -112,6 +133,14 @@ function hasStatePath(machine, value) {
|
|
|
112
133
|
}
|
|
113
134
|
function resolveStateValue(machine, value, source) {
|
|
114
135
|
const stateValue = String(value);
|
|
136
|
+
if (isExplicitAbsoluteStatePath(stateValue)) {
|
|
137
|
+
const stateId = stripAbsolutePrefix(stateValue);
|
|
138
|
+
const statePath = getStatePathById(machine, stateId);
|
|
139
|
+
if (!statePath) {
|
|
140
|
+
throw new Error(`Unknown state id: ${stateId}`);
|
|
141
|
+
}
|
|
142
|
+
return resolveAbsoluteStateValue(machine, statePath);
|
|
143
|
+
}
|
|
115
144
|
if (!isAbsoluteStatePath(stateValue) && source) {
|
|
116
145
|
const sourceSegments = toSegments(source);
|
|
117
146
|
for (let index = sourceSegments.length; index >= 1; index--) {
|
package/dist/state.mjs
CHANGED
|
@@ -1,19 +1,35 @@
|
|
|
1
1
|
// src/state.ts
|
|
2
2
|
var STATE_DELIMITER = ".";
|
|
3
|
+
var ABSOLUTE_PREFIX = "#";
|
|
3
4
|
var stateIndexCache = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
var stateIdIndexCache = /* @__PURE__ */ new WeakMap();
|
|
4
6
|
function joinStatePath(parts) {
|
|
5
7
|
return parts.join(STATE_DELIMITER);
|
|
6
8
|
}
|
|
7
9
|
function isAbsoluteStatePath(value) {
|
|
8
10
|
return value.includes(STATE_DELIMITER);
|
|
9
11
|
}
|
|
12
|
+
function isExplicitAbsoluteStatePath(value) {
|
|
13
|
+
return value.startsWith(ABSOLUTE_PREFIX);
|
|
14
|
+
}
|
|
15
|
+
function stripAbsolutePrefix(value) {
|
|
16
|
+
return isExplicitAbsoluteStatePath(value) ? value.slice(ABSOLUTE_PREFIX.length) : value;
|
|
17
|
+
}
|
|
10
18
|
function appendStatePath(base, segment) {
|
|
11
19
|
return base ? `${base}${STATE_DELIMITER}${segment}` : segment;
|
|
12
20
|
}
|
|
13
21
|
function buildStateIndex(machine) {
|
|
14
22
|
const index = /* @__PURE__ */ new Map();
|
|
23
|
+
const idIndex = /* @__PURE__ */ new Map();
|
|
15
24
|
const visit = (basePath, state) => {
|
|
16
25
|
index.set(basePath, state);
|
|
26
|
+
const stateId = state.id;
|
|
27
|
+
if (stateId) {
|
|
28
|
+
if (idIndex.has(stateId)) {
|
|
29
|
+
throw new Error(`Duplicate state id: ${stateId}`);
|
|
30
|
+
}
|
|
31
|
+
idIndex.set(stateId, basePath);
|
|
32
|
+
}
|
|
17
33
|
const childStates = state.states;
|
|
18
34
|
if (!childStates) return;
|
|
19
35
|
for (const [childKey, childState] of Object.entries(childStates)) {
|
|
@@ -26,15 +42,20 @@ function buildStateIndex(machine) {
|
|
|
26
42
|
if (!topState) continue;
|
|
27
43
|
visit(topKey, topState);
|
|
28
44
|
}
|
|
29
|
-
return index;
|
|
45
|
+
return { index, idIndex };
|
|
30
46
|
}
|
|
31
47
|
function ensureStateIndex(machine) {
|
|
32
48
|
const cached = stateIndexCache.get(machine);
|
|
33
49
|
if (cached) return cached;
|
|
34
|
-
const index = buildStateIndex(machine);
|
|
50
|
+
const { index, idIndex } = buildStateIndex(machine);
|
|
35
51
|
stateIndexCache.set(machine, index);
|
|
52
|
+
stateIdIndexCache.set(machine, idIndex);
|
|
36
53
|
return index;
|
|
37
54
|
}
|
|
55
|
+
function getStatePathById(machine, stateId) {
|
|
56
|
+
ensureStateIndex(machine);
|
|
57
|
+
return stateIdIndexCache.get(machine)?.get(stateId);
|
|
58
|
+
}
|
|
38
59
|
function toSegments(value) {
|
|
39
60
|
if (!value) return [];
|
|
40
61
|
return String(value).split(STATE_DELIMITER).filter(Boolean);
|
|
@@ -81,6 +102,14 @@ function hasStatePath(machine, value) {
|
|
|
81
102
|
}
|
|
82
103
|
function resolveStateValue(machine, value, source) {
|
|
83
104
|
const stateValue = String(value);
|
|
105
|
+
if (isExplicitAbsoluteStatePath(stateValue)) {
|
|
106
|
+
const stateId = stripAbsolutePrefix(stateValue);
|
|
107
|
+
const statePath = getStatePathById(machine, stateId);
|
|
108
|
+
if (!statePath) {
|
|
109
|
+
throw new Error(`Unknown state id: ${stateId}`);
|
|
110
|
+
}
|
|
111
|
+
return resolveAbsoluteStateValue(machine, statePath);
|
|
112
|
+
}
|
|
84
113
|
if (!isAbsoluteStatePath(stateValue) && source) {
|
|
85
114
|
const sourceSegments = toSegments(source);
|
|
86
115
|
for (let index = sourceSegments.length; index >= 1; index--) {
|
package/dist/types.d.mts
CHANGED
|
@@ -109,8 +109,9 @@ type ChildStateKey<S extends string, Parent extends string> = S extends `${Paren
|
|
|
109
109
|
type ParentPath<S extends string> = S extends `${infer Parent}.${string}` ? Parent : never;
|
|
110
110
|
type AncestorPaths<S extends string> = S | (ParentPath<S> extends never ? never : AncestorPaths<ParentPath<S>>);
|
|
111
111
|
type RelativeStateTarget<S extends string, Source extends string> = ChildStateKey<S, AncestorPaths<Source>>;
|
|
112
|
+
type StateIdTarget = `#${string}`;
|
|
112
113
|
interface Transition<T extends Dict, Source extends string | undefined = string | undefined> {
|
|
113
|
-
target?: T["state"] | (Source extends string ? RelativeStateTarget<T["state"], Source> : never) | undefined;
|
|
114
|
+
target?: T["state"] | StateIdTarget | (Source extends string ? RelativeStateTarget<T["state"], Source> : never) | undefined;
|
|
114
115
|
actions?: T["action"][] | undefined;
|
|
115
116
|
guard?: T["guard"] | GuardFn<T> | undefined;
|
|
116
117
|
reenter?: boolean | undefined;
|
|
@@ -134,6 +135,7 @@ interface RefsParams<T extends Dict> {
|
|
|
134
135
|
type ActionsOrFn<T extends Dict> = T["action"][] | ((params: Params<T>) => T["action"][] | undefined);
|
|
135
136
|
type EffectsOrFn<T extends Dict> = T["effect"][] | ((params: Params<T>) => T["effect"][] | undefined);
|
|
136
137
|
interface MachineState<T extends Dict, Parent extends string = string> {
|
|
138
|
+
id?: string | undefined;
|
|
137
139
|
tags?: T["tag"][] | undefined;
|
|
138
140
|
entry?: ActionsOrFn<T> | undefined;
|
|
139
141
|
exit?: ActionsOrFn<T> | undefined;
|
package/dist/types.d.ts
CHANGED
|
@@ -109,8 +109,9 @@ type ChildStateKey<S extends string, Parent extends string> = S extends `${Paren
|
|
|
109
109
|
type ParentPath<S extends string> = S extends `${infer Parent}.${string}` ? Parent : never;
|
|
110
110
|
type AncestorPaths<S extends string> = S | (ParentPath<S> extends never ? never : AncestorPaths<ParentPath<S>>);
|
|
111
111
|
type RelativeStateTarget<S extends string, Source extends string> = ChildStateKey<S, AncestorPaths<Source>>;
|
|
112
|
+
type StateIdTarget = `#${string}`;
|
|
112
113
|
interface Transition<T extends Dict, Source extends string | undefined = string | undefined> {
|
|
113
|
-
target?: T["state"] | (Source extends string ? RelativeStateTarget<T["state"], Source> : never) | undefined;
|
|
114
|
+
target?: T["state"] | StateIdTarget | (Source extends string ? RelativeStateTarget<T["state"], Source> : never) | undefined;
|
|
114
115
|
actions?: T["action"][] | undefined;
|
|
115
116
|
guard?: T["guard"] | GuardFn<T> | undefined;
|
|
116
117
|
reenter?: boolean | undefined;
|
|
@@ -134,6 +135,7 @@ interface RefsParams<T extends Dict> {
|
|
|
134
135
|
type ActionsOrFn<T extends Dict> = T["action"][] | ((params: Params<T>) => T["action"][] | undefined);
|
|
135
136
|
type EffectsOrFn<T extends Dict> = T["effect"][] | ((params: Params<T>) => T["effect"][] | undefined);
|
|
136
137
|
interface MachineState<T extends Dict, Parent extends string = string> {
|
|
138
|
+
id?: string | undefined;
|
|
137
139
|
tags?: T["tag"][] | undefined;
|
|
138
140
|
entry?: ActionsOrFn<T> | undefined;
|
|
139
141
|
exit?: ActionsOrFn<T> | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zag-js/core",
|
|
3
|
-
"version": "1.35.
|
|
3
|
+
"version": "1.35.1",
|
|
4
4
|
"description": "A minimal implementation of xstate fsm for UI machines",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-machines",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"url": "https://github.com/chakra-ui/zag/issues"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@zag-js/utils": "1.35.
|
|
29
|
-
"@zag-js/dom-query": "1.35.
|
|
28
|
+
"@zag-js/utils": "1.35.1",
|
|
29
|
+
"@zag-js/dom-query": "1.35.1"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"clean-package": "2.2.0"
|