@menapo/machine 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +135 -2
- package/dist/chart-BWjU2VF4.js +395 -0
- package/dist/compiled.d.ts +58 -0
- package/dist/compiled.js +43 -0
- package/dist/component-DDXOyYUI.d.ts +147 -0
- package/dist/dev-BAz6e3W-.js +79 -0
- package/dist/dom.d.ts +187 -0
- package/dist/dom.js +659 -0
- package/dist/glue/preact.d.ts +4 -0
- package/dist/glue/preact.js +54 -0
- package/dist/glue/react.d.ts +4 -0
- package/dist/glue/react.js +50 -0
- package/dist/glue/solid.d.ts +4 -0
- package/dist/glue/solid.js +42 -0
- package/dist/glue/svelte.svelte.d.ts +4 -0
- package/dist/glue/svelte.svelte.js +57 -0
- package/dist/glue/vanilla.d.ts +4 -0
- package/dist/glue/vanilla.js +83 -0
- package/dist/glue/vue.d.ts +4 -0
- package/dist/glue/vue.js +41 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.js +582 -0
- package/dist/inspect-BmyLUhcy.js +74 -0
- package/dist/inspect-C3qosuCm.d.ts +22 -0
- package/dist/instance-BM2UqeXW.js +418 -0
- package/dist/machine-BstWHtWL.d.ts +335 -0
- package/dist/naming-Csj5186j.js +492 -0
- package/dist/records-7FFepzo9.js +10 -0
- package/dist/source-DdFTPjID.js +15 -0
- package/dist/test.d.ts +63 -0
- package/dist/test.js +262 -0
- package/dist/types-Bk8svtO6.d.ts +580 -0
- package/dist/vicss.d.ts +19 -0
- package/dist/vicss.js +51 -0
- package/dist/vite.d.ts +27 -0
- package/dist/vite.js +4281 -0
- package/package.json +87 -3
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
//#region src/parts/types.d.ts
|
|
2
|
+
type Global = typeof globalThis;
|
|
3
|
+
/**
|
|
4
|
+
* An event where the DOM's types are not loaded (a server's program, this
|
|
5
|
+
* package's own): what handlers read most, one shape for every name.
|
|
6
|
+
*/
|
|
7
|
+
interface BareEvent {
|
|
8
|
+
readonly type: string;
|
|
9
|
+
readonly key: string;
|
|
10
|
+
preventDefault(): void;
|
|
11
|
+
}
|
|
12
|
+
/** The DOM's event class `N` where the DOM's types are loaded, else a {@link BareEvent}. */
|
|
13
|
+
type DomEvent<N extends string> = Global extends { [K in N]: {
|
|
14
|
+
prototype: infer E;
|
|
15
|
+
}; } ? E : BareEvent;
|
|
16
|
+
/**
|
|
17
|
+
* The DOM event a handler gets, by its name's prefix (`onKeyDown` a
|
|
18
|
+
* `KeyboardEvent`, `onPointerMove` a `PointerEvent`); a framework passes its
|
|
19
|
+
* own event object (React's synthetic event), which has the same fields.
|
|
20
|
+
*/
|
|
21
|
+
interface PartEvents {
|
|
22
|
+
readonly onKey: DomEvent<"KeyboardEvent">;
|
|
23
|
+
readonly onClick: DomEvent<"MouseEvent">;
|
|
24
|
+
readonly onDblClick: DomEvent<"MouseEvent">;
|
|
25
|
+
readonly onDoubleClick: DomEvent<"MouseEvent">;
|
|
26
|
+
readonly onAuxClick: DomEvent<"MouseEvent">;
|
|
27
|
+
readonly onContextMenu: DomEvent<"MouseEvent">;
|
|
28
|
+
readonly onMouse: DomEvent<"MouseEvent">;
|
|
29
|
+
readonly onPointer: DomEvent<"PointerEvent">;
|
|
30
|
+
readonly onWheel: DomEvent<"WheelEvent">;
|
|
31
|
+
readonly onTouch: DomEvent<"TouchEvent">;
|
|
32
|
+
readonly onDrag: DomEvent<"DragEvent">;
|
|
33
|
+
readonly onDrop: DomEvent<"DragEvent">;
|
|
34
|
+
readonly onFocus: DomEvent<"FocusEvent">;
|
|
35
|
+
readonly onBlur: DomEvent<"FocusEvent">;
|
|
36
|
+
readonly onBeforeInput: DomEvent<"InputEvent">;
|
|
37
|
+
readonly onInput: DomEvent<"Event">;
|
|
38
|
+
readonly onChange: DomEvent<"Event">;
|
|
39
|
+
readonly onSubmit: DomEvent<"SubmitEvent">;
|
|
40
|
+
readonly onReset: DomEvent<"Event">;
|
|
41
|
+
readonly onScroll: DomEvent<"Event">;
|
|
42
|
+
readonly onSelect: DomEvent<"Event">;
|
|
43
|
+
readonly onInvalid: DomEvent<"Event">;
|
|
44
|
+
readonly onToggle: DomEvent<"Event">;
|
|
45
|
+
readonly onCopy: DomEvent<"ClipboardEvent">;
|
|
46
|
+
readonly onCut: DomEvent<"ClipboardEvent">;
|
|
47
|
+
readonly onPaste: DomEvent<"ClipboardEvent">;
|
|
48
|
+
readonly onComposition: DomEvent<"CompositionEvent">;
|
|
49
|
+
readonly onAnimation: DomEvent<"AnimationEvent">;
|
|
50
|
+
readonly onTransition: DomEvent<"TransitionEvent">;
|
|
51
|
+
}
|
|
52
|
+
/** The event handler `K` gets: `PartEvent<"onKeyDown">` is a `KeyboardEvent`. */
|
|
53
|
+
type PartEvent<K extends string = string> = { [P in keyof PartEvents]: K extends `${P}${string}` ? PartEvents[P] : never; }[keyof PartEvents] extends (infer E) ? [E] extends [never] ? DomEvent<"Event"> : E : never;
|
|
54
|
+
/**
|
|
55
|
+
* A handler: a function of the view and the DOM event. Written as a method,
|
|
56
|
+
* so a handler of a narrower event (a `KeyboardEvent`) stands where one of
|
|
57
|
+
* any event is expected.
|
|
58
|
+
*/
|
|
59
|
+
type Handler<V, E> = {
|
|
60
|
+
h(view: V, event: E): unknown;
|
|
61
|
+
}["h"];
|
|
62
|
+
/** What a part's key holds when no handler prefix names it: a static value or an attribute function. */
|
|
63
|
+
type Attribute<V> = string | number | boolean | null | undefined | Handler<V, unknown>;
|
|
64
|
+
/**
|
|
65
|
+
* A part as written: static values, attribute functions of the view and
|
|
66
|
+
* `on*` handlers (one of the machine's events `Ev`, or a function of the view
|
|
67
|
+
* and the DOM event, typed by the handler's name: {@link PartEvents}).
|
|
68
|
+
*/
|
|
69
|
+
type PartConfig<V, Ev extends string = string> = {
|
|
70
|
+
readonly [key: string]: Attribute<V>;
|
|
71
|
+
readonly [key: `onKey${string}`]: Ev | Handler<V, PartEvents["onKey"]>;
|
|
72
|
+
readonly [key: `onClick${string}`]: Ev | Handler<V, PartEvents["onClick"]>;
|
|
73
|
+
readonly [key: `onDblClick${string}`]: Ev | Handler<V, PartEvents["onDblClick"]>;
|
|
74
|
+
readonly [key: `onDoubleClick${string}`]: Ev | Handler<V, PartEvents["onDoubleClick"]>;
|
|
75
|
+
readonly [key: `onAuxClick${string}`]: Ev | Handler<V, PartEvents["onAuxClick"]>;
|
|
76
|
+
readonly [key: `onContextMenu${string}`]: Ev | Handler<V, PartEvents["onContextMenu"]>;
|
|
77
|
+
readonly [key: `onMouse${string}`]: Ev | Handler<V, PartEvents["onMouse"]>;
|
|
78
|
+
readonly [key: `onPointer${string}`]: Ev | Handler<V, PartEvents["onPointer"]>;
|
|
79
|
+
readonly [key: `onWheel${string}`]: Ev | Handler<V, PartEvents["onWheel"]>;
|
|
80
|
+
readonly [key: `onTouch${string}`]: Ev | Handler<V, PartEvents["onTouch"]>;
|
|
81
|
+
readonly [key: `onDrag${string}`]: Ev | Handler<V, PartEvents["onDrag"]>;
|
|
82
|
+
readonly [key: `onDrop${string}`]: Ev | Handler<V, PartEvents["onDrop"]>;
|
|
83
|
+
readonly [key: `onFocus${string}`]: Ev | Handler<V, PartEvents["onFocus"]>;
|
|
84
|
+
readonly [key: `onBlur${string}`]: Ev | Handler<V, PartEvents["onBlur"]>;
|
|
85
|
+
readonly [key: `onBeforeInput${string}`]: Ev | Handler<V, PartEvents["onBeforeInput"]>;
|
|
86
|
+
readonly [key: `onInput${string}`]: Ev | Handler<V, PartEvents["onInput"]>;
|
|
87
|
+
readonly [key: `onChange${string}`]: Ev | Handler<V, PartEvents["onChange"]>;
|
|
88
|
+
readonly [key: `onSubmit${string}`]: Ev | Handler<V, PartEvents["onSubmit"]>;
|
|
89
|
+
readonly [key: `onReset${string}`]: Ev | Handler<V, PartEvents["onReset"]>;
|
|
90
|
+
readonly [key: `onScroll${string}`]: Ev | Handler<V, PartEvents["onScroll"]>;
|
|
91
|
+
readonly [key: `onSelect${string}`]: Ev | Handler<V, PartEvents["onSelect"]>;
|
|
92
|
+
readonly [key: `onInvalid${string}`]: Ev | Handler<V, PartEvents["onInvalid"]>;
|
|
93
|
+
readonly [key: `onToggle${string}`]: Ev | Handler<V, PartEvents["onToggle"]>;
|
|
94
|
+
readonly [key: `onCopy${string}`]: Ev | Handler<V, PartEvents["onCopy"]>;
|
|
95
|
+
readonly [key: `onCut${string}`]: Ev | Handler<V, PartEvents["onCut"]>;
|
|
96
|
+
readonly [key: `onPaste${string}`]: Ev | Handler<V, PartEvents["onPaste"]>;
|
|
97
|
+
readonly [key: `onComposition${string}`]: Ev | Handler<V, PartEvents["onComposition"]>;
|
|
98
|
+
readonly [key: `onAnimation${string}`]: Ev | Handler<V, PartEvents["onAnimation"]>;
|
|
99
|
+
readonly [key: `onTransition${string}`]: Ev | Handler<V, PartEvents["onTransition"]>;
|
|
100
|
+
};
|
|
101
|
+
/** `parts`: one {@link PartConfig} per part name. */
|
|
102
|
+
type PartsConfig<V> = {
|
|
103
|
+
readonly [part: string]: PartConfig<V>;
|
|
104
|
+
};
|
|
105
|
+
/** The attributes every part gets (PLAN §4.4). */
|
|
106
|
+
interface PartBase {
|
|
107
|
+
readonly id: string;
|
|
108
|
+
readonly "data-scope": string;
|
|
109
|
+
readonly "data-part": string;
|
|
110
|
+
/** The active path: `"open"`, `"form.editing"`. */
|
|
111
|
+
readonly "data-state": string;
|
|
112
|
+
}
|
|
113
|
+
/** A part's props as the component spreads them: statics as written, functions' results, handlers. */
|
|
114
|
+
type PartProps<P> = PartBase & { readonly [K in keyof P]: K extends `on${string}` ? (event?: unknown) => void : P[K] extends ((...args: never) => infer R) ? R : P[K]; };
|
|
115
|
+
/**
|
|
116
|
+
* What `useMachine` returns: the state, `is`, `send`, `context`, each
|
|
117
|
+
* part's props, and what `expose` returns.
|
|
118
|
+
*/
|
|
119
|
+
type Api<M extends AnyMachine> = {
|
|
120
|
+
readonly state: TypesOf<M>["leaves"];
|
|
121
|
+
readonly is: (state: TypesOf<M>["paths"]) => boolean;
|
|
122
|
+
readonly send: Send<EventUnion<TypesOf<M>["events"]>>;
|
|
123
|
+
readonly context: Readonly<TypesOf<M>["context"]>;
|
|
124
|
+
readonly parts: { readonly [K in keyof TypesOf<M>["parts"]]: PartProps<TypesOf<M>["parts"][K]>; };
|
|
125
|
+
/** Calls `listener` after each visible change: what code outside a framework's rendering listens to. */
|
|
126
|
+
subscribe(listener: () => void): () => void;
|
|
127
|
+
/** New owner props, for code without a framework: a framework passes them on each render. */
|
|
128
|
+
setProps(props: UseProps<M>): void;
|
|
129
|
+
/**
|
|
130
|
+
* Stops the instance (its effects, timers and listeners), for code without
|
|
131
|
+
* a framework when the element goes: a framework's glue does it on unmount.
|
|
132
|
+
*/
|
|
133
|
+
stop(): void;
|
|
134
|
+
} & Readonly<TypesOf<M>["exposed"]>;
|
|
135
|
+
/**
|
|
136
|
+
* What a component passes: the owner props `start` takes, and an `id` for
|
|
137
|
+
* the parts' ids when the framework has none to give. Vue, Svelte and
|
|
138
|
+
* Solid also take a function returning them, read reactively.
|
|
139
|
+
*/
|
|
140
|
+
type UseProps<M extends AnyMachine> = TypesOf<M>["owner"] & {
|
|
141
|
+
readonly id?: string;
|
|
142
|
+
};
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/core/types.d.ts
|
|
145
|
+
declare const PAYLOAD: unique symbol;
|
|
146
|
+
declare const BOUND_VALUE: unique symbol;
|
|
147
|
+
declare const FEATURE: unique symbol;
|
|
148
|
+
declare const REGIONS: unique symbol;
|
|
149
|
+
/** An event's data, declared in `events` with `payload<T>()` (D7). */
|
|
150
|
+
interface Payload<T extends object> {
|
|
151
|
+
readonly [PAYLOAD]: T;
|
|
152
|
+
}
|
|
153
|
+
/** What a machine without `events` accepts: any event name, any data. */
|
|
154
|
+
type AnyEvents = {
|
|
155
|
+
readonly [type: string]: null;
|
|
156
|
+
};
|
|
157
|
+
/** The event `K` of `E`: `{ type: "ADD"; by: number }`. */
|
|
158
|
+
type EventOf<E, K extends keyof E> = K extends string ? Readonly<{
|
|
159
|
+
type: K;
|
|
160
|
+
} & (E[K] extends Payload<infer P> ? P : unknown)> : never;
|
|
161
|
+
/** Every event of `E`, as a discriminated union. */
|
|
162
|
+
type EventUnion<E> = { [K in keyof E & string]: EventOf<E, K>; }[keyof E & string];
|
|
163
|
+
/**
|
|
164
|
+
* The events the machine itself raises: entering the initial state, an
|
|
165
|
+
* `after` timer, and an owner changing a bound prop the machine follows.
|
|
166
|
+
*/
|
|
167
|
+
type MachineEvent = {
|
|
168
|
+
readonly type: "machine.start";
|
|
169
|
+
} | {
|
|
170
|
+
readonly type: "machine.after";
|
|
171
|
+
} | {
|
|
172
|
+
readonly type: "machine.props";
|
|
173
|
+
} | DoneEvent;
|
|
174
|
+
/** What a parallel state's `done` transition gets: every region reached a final state. */
|
|
175
|
+
interface DoneEvent {
|
|
176
|
+
readonly type: "machine.done";
|
|
177
|
+
}
|
|
178
|
+
/** Any event object: what `send` hands along unchanged. */
|
|
179
|
+
interface AnyEvent {
|
|
180
|
+
readonly type: string;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* A name `send` does not know meets this, beside the names it does: the
|
|
184
|
+
* message is the fix (an argument gets no "Did you mean" from TypeScript).
|
|
185
|
+
*/
|
|
186
|
+
type Unknown<K extends string> = `${K} is not an event of this machine: send one of its events, or declare it in \`events\``;
|
|
187
|
+
type EventName<K extends string, Types extends string> = K extends Types ? K : Types | Unknown<K>;
|
|
188
|
+
type PayloadOf<Ev> = Omit<Ev, "type">;
|
|
189
|
+
type SendArgs<All extends AnyEvent, K> = string extends All["type"] ? [payload?: object] : [keyof PayloadOf<Extract<All, {
|
|
190
|
+
type: K;
|
|
191
|
+
}>>] extends [never] ? [] : {} extends PayloadOf<Extract<All, {
|
|
192
|
+
type: K;
|
|
193
|
+
}>> ? [payload?: PayloadOf<Extract<All, {
|
|
194
|
+
type: K;
|
|
195
|
+
}>>] : [payload: PayloadOf<Extract<All, {
|
|
196
|
+
type: K;
|
|
197
|
+
}>>];
|
|
198
|
+
/**
|
|
199
|
+
* Both send forms, typed and equivalent (D7): `send("ADD", { by: 2 })` and
|
|
200
|
+
* `send({ type: "ADD", by: 2 })`. A send made while an event is being
|
|
201
|
+
* handled runs once that event is done, before the outer `send` returns.
|
|
202
|
+
*
|
|
203
|
+
* One signature, not two overloads: a wrong name then gets one error, not
|
|
204
|
+
* "No overload matches this call" with one per form. `string & {}` keeps
|
|
205
|
+
* the event names as completions while letting any name through to the
|
|
206
|
+
* message.
|
|
207
|
+
*/
|
|
208
|
+
interface Send<All extends AnyEvent> {
|
|
209
|
+
<const T extends All["type"] | (string & {}) | AnyEvent>(event: T extends string ? EventName<T, All["type"]> : T extends {
|
|
210
|
+
readonly type: infer K extends string;
|
|
211
|
+
} ? K extends All["type"] ? All : {
|
|
212
|
+
readonly type: EventName<K, All["type"]>;
|
|
213
|
+
} : All, ...payload: T extends string ? SendArgs<All, T> : []): void;
|
|
214
|
+
}
|
|
215
|
+
/** A controlled-or-uncontrolled prop declared with `bindable()` (D9). */
|
|
216
|
+
interface Bindable<V> {
|
|
217
|
+
readonly [BOUND_VALUE]?: V;
|
|
218
|
+
readonly value: V;
|
|
219
|
+
}
|
|
220
|
+
type BoundKeys<PC> = { [K in keyof PC]: PC[K] extends Bindable<unknown> ? K : never; }[keyof PC];
|
|
221
|
+
type ValueOf<T> = T extends Bindable<infer V> ? V : T;
|
|
222
|
+
/**
|
|
223
|
+
* Props as actions read them: plain props are read-only, a bindable one
|
|
224
|
+
* can be written (`props.value = next`), which asks the owner when it is
|
|
225
|
+
* controlled.
|
|
226
|
+
*/
|
|
227
|
+
type PropsOf<PC> = { -readonly [K in keyof PC as PC[K] extends Bindable<unknown> ? K : never]: ValueOf<PC[K]>; } & { readonly [K in keyof PC as PC[K] extends Bindable<unknown> ? never : K]: PC[K]; };
|
|
228
|
+
/**
|
|
229
|
+
* What the owner passes (`start(m, { props })`, `setProps`): every prop
|
|
230
|
+
* optional; for each bindable `x`, also `defaultX` and `onXChange`.
|
|
231
|
+
*/
|
|
232
|
+
type OwnerProps<PC> = { readonly [K in keyof PC]?: ValueOf<PC[K]>; } & { readonly [K in BoundKeys<PC> & string as `default${Capitalize<K>}`]?: ValueOf<PC[K]>; } & { readonly [K in BoundKeys<PC> & string as `on${Capitalize<K>}Change`]?: (value: ValueOf<PC[K]>) => void; };
|
|
233
|
+
/** What a derived function reads. */
|
|
234
|
+
interface DerivedView<C, P> {
|
|
235
|
+
readonly context: Readonly<C>;
|
|
236
|
+
readonly props: Readonly<P>;
|
|
237
|
+
readonly state: string;
|
|
238
|
+
is(state: string): boolean;
|
|
239
|
+
}
|
|
240
|
+
/** `derived`: a function per value. */
|
|
241
|
+
type DerivedConfig<C, P> = {
|
|
242
|
+
readonly [name: string]: (view: DerivedView<C, P>) => unknown;
|
|
243
|
+
};
|
|
244
|
+
/** The values `derived`'s functions return. */
|
|
245
|
+
type DerivedOf<DC> = { readonly [K in keyof DC]: DC[K] extends ((view: never) => infer R) ? R : never; };
|
|
246
|
+
/**
|
|
247
|
+
* What guards, actions and effects receive: the instance, read through
|
|
248
|
+
* destructuring (`({ context, event, send }) => …`). Inside the config,
|
|
249
|
+
* `state` and `is` take any string (checked in development): the state tree
|
|
250
|
+
* is inferred from the very object they sit in. A started instance types
|
|
251
|
+
* them to the machine's paths.
|
|
252
|
+
*/
|
|
253
|
+
interface View<C = {}, P = {}, D = {}, All extends AnyEvent = AnyEvent, Ev extends AnyEvent = All | MachineEvent, Path extends string = string, Leaf extends string = string, Part extends string = string> {
|
|
254
|
+
/** The machine's own data; actions write it directly (D5). */
|
|
255
|
+
readonly context: C;
|
|
256
|
+
readonly props: P;
|
|
257
|
+
readonly derived: D;
|
|
258
|
+
/** The event being handled, narrowed to the handler's own. */
|
|
259
|
+
readonly event: Ev;
|
|
260
|
+
/** The active path: `"open"`, `"form.editing"`. */
|
|
261
|
+
readonly state: Leaf;
|
|
262
|
+
readonly send: Send<All>;
|
|
263
|
+
/** True in `state` and in every state inside it. */
|
|
264
|
+
readonly is: (state: Path) => boolean;
|
|
265
|
+
/**
|
|
266
|
+
* Each part's id (`ids.content`), the same on the server and the client
|
|
267
|
+
* (PLAN §4.4). Only a component (`useMachine`) has parts: in an instance
|
|
268
|
+
* `start` or `testMachine` made, every id reads `undefined`.
|
|
269
|
+
*/
|
|
270
|
+
readonly ids: { readonly [K in Part]: string | undefined; };
|
|
271
|
+
/**
|
|
272
|
+
* Each part's element, found by its id when read (`el.content`): effects
|
|
273
|
+
* start once the DOM is committed (D13), so a part the component renders
|
|
274
|
+
* is there. A part it does not render reads `undefined`, as does every
|
|
275
|
+
* part on a server and in an instance `start` or `testMachine` made.
|
|
276
|
+
*/
|
|
277
|
+
readonly el: { readonly [K in Part]: DomElement | undefined; };
|
|
278
|
+
}
|
|
279
|
+
/** The DOM's `HTMLElement` where the DOM's types are loaded. */
|
|
280
|
+
type DomElement = typeof globalThis extends {
|
|
281
|
+
HTMLElement: {
|
|
282
|
+
prototype: infer E;
|
|
283
|
+
};
|
|
284
|
+
} ? E : object;
|
|
285
|
+
/**
|
|
286
|
+
* The view of everything but actions: guards, effects, attribute functions
|
|
287
|
+
* and `expose` read context and never write it (D5; development throws on a
|
|
288
|
+
* write), so their `context` is read-only.
|
|
289
|
+
*/
|
|
290
|
+
type Reading<V extends {
|
|
291
|
+
readonly context: unknown;
|
|
292
|
+
}> = Omit<V, "context"> & {
|
|
293
|
+
readonly context: Readonly<V["context"]>;
|
|
294
|
+
};
|
|
295
|
+
type Action<V> = (view: V) => void;
|
|
296
|
+
type Guard<V extends {
|
|
297
|
+
readonly context: unknown;
|
|
298
|
+
}> = (view: Reading<V>) => boolean;
|
|
299
|
+
/** What an effect may return: a cleanup, or nothing to clean up. */
|
|
300
|
+
type EffectResult = (() => void) | void | false | null | undefined;
|
|
301
|
+
type Effect<V extends {
|
|
302
|
+
readonly context: unknown;
|
|
303
|
+
}> = (view: Reading<V>) => EffectResult;
|
|
304
|
+
/**
|
|
305
|
+
* A list of transitions or implementations. Not `readonly T[]`: a union
|
|
306
|
+
* holding an array type keeps TypeScript from pointing an error inside a
|
|
307
|
+
* `{ to, if, do }` object at the key that is wrong.
|
|
308
|
+
*/
|
|
309
|
+
interface List<T> {
|
|
310
|
+
readonly [index: number]: T;
|
|
311
|
+
readonly length: number;
|
|
312
|
+
}
|
|
313
|
+
/** One implementation or several: a function, or the name of one in `actions` / `effects`. */
|
|
314
|
+
type Some<T> = T | List<T>;
|
|
315
|
+
/**
|
|
316
|
+
* A transition (D6): a target, an action that stays in the state, or
|
|
317
|
+
* `{ to, if, do }` with every key optional. `T` is every target the state
|
|
318
|
+
* may name: a sibling's name (or an ancestor's sibling's), a child
|
|
319
|
+
* (`".editing"`) or a path from the root (`"#form.editing"`). `A` and `G`
|
|
320
|
+
* name the machine's `actions` and `guards`.
|
|
321
|
+
*/
|
|
322
|
+
type Transition<T extends string, V extends {
|
|
323
|
+
readonly context: unknown;
|
|
324
|
+
}, A extends string = never, G extends string = never> = T | Action<V> | {
|
|
325
|
+
readonly to?: T;
|
|
326
|
+
readonly if?: G | Guard<V>;
|
|
327
|
+
readonly do?: Some<A | Action<V>>;
|
|
328
|
+
};
|
|
329
|
+
/** One transition, or several tried in order: the first whose `if` passes wins. */
|
|
330
|
+
type Transitions<T extends string, V extends {
|
|
331
|
+
readonly context: unknown;
|
|
332
|
+
}, A extends string = never, G extends string = never> = Transition<T, V, A, G> | List<Transition<T, V, A, G>>;
|
|
333
|
+
/**
|
|
334
|
+
* The states, as `defineMachine` infers them: each name maps to its
|
|
335
|
+
* children's tree, and a leaf to `unknown`. TypeScript reads a config that
|
|
336
|
+
* holds functions in two passes, and in the first one a subtree whose
|
|
337
|
+
* states hold only functions has no children yet: its tree comes from its
|
|
338
|
+
* `initial` alone, with `any` for the one child that names. Such a subtree
|
|
339
|
+
* may then be named any way (`string`), so the first pass never rejects
|
|
340
|
+
* what the second one checks; the second has the whole tree.
|
|
341
|
+
*/
|
|
342
|
+
type Loose<T> = unknown extends T ? never : 0 extends 1 & T[keyof T] ? string : { [K in keyof T & string]: K | `${K}.${Loose<T[K]>}`; }[keyof T & string];
|
|
343
|
+
/** The names of a tree's top states: the plain targets its children may use. */
|
|
344
|
+
type Names<T> = unknown extends T ? never : 0 extends 1 & T[keyof T] ? string : keyof T & string;
|
|
345
|
+
/**
|
|
346
|
+
* The paths of a tree: every state's, parents included (what `is()`
|
|
347
|
+
* takes), and the leaves' (what `state` is). Members of an interface, not
|
|
348
|
+
* aliases, so hovers and errors list the paths instead of naming the alias.
|
|
349
|
+
*/
|
|
350
|
+
interface Tree<T> {
|
|
351
|
+
readonly paths: { [K in keyof T & string]: K | (unknown extends T[K] ? never : `${K}.${Tree<T[K]>["paths"]}`); }[keyof T & string];
|
|
352
|
+
readonly leaves: { [K in keyof T & string]: unknown extends T[K] ? K : `${K}.${Tree<T[K]>["leaves"]}`; }[keyof T & string];
|
|
353
|
+
}
|
|
354
|
+
/** What `parallel` marks: a state whose children are regions. */
|
|
355
|
+
interface Regions {
|
|
356
|
+
readonly [REGIONS]: true;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Which states are parallel, read from the same `states` object as the tree
|
|
360
|
+
* but into a tree of its own, `Q` (PLAN §11, question 24): a parallel state
|
|
361
|
+
* is {@link Regions} there, any other its children's marks. The state tree
|
|
362
|
+
* cannot hold it: a state's children are inferred into one type, and a
|
|
363
|
+
* second source for that type replaces the children instead of joining them.
|
|
364
|
+
*/
|
|
365
|
+
type Marks<Q> = { readonly [K in keyof Q]?: Mark<Q[K]>; };
|
|
366
|
+
interface Mark<T> {
|
|
367
|
+
readonly type?: T extends Regions ? {
|
|
368
|
+
readonly [FEATURE]?: T;
|
|
369
|
+
} : unknown;
|
|
370
|
+
readonly states?: Marks<T>;
|
|
371
|
+
}
|
|
372
|
+
/** The paths of the history pseudo-states in a tree of {@link Marks}: never active, so neither `state` nor `is()` names them. */
|
|
373
|
+
type HistoryPaths<Q> = { [K in keyof Q & string]: Q[K] extends "history" ? K : `${K}.${HistoryPaths<Q[K]>}`; }[keyof Q & string];
|
|
374
|
+
/** Whether a tree of {@link Marks} holds a parallel state. */
|
|
375
|
+
type HasParallel<Q> = Q extends Regions ? true : Q extends object ? true extends { [K in keyof Q]: HasParallel<Q[K]>; }[keyof Q] ? true : false : false;
|
|
376
|
+
/**
|
|
377
|
+
* What `state` holds: one leaf's path, or, in a machine with parallel
|
|
378
|
+
* states, the active leaves joined by spaces in document order
|
|
379
|
+
* (`"editor.bold.on editor.save.dirty"`). The order of keys is not a type,
|
|
380
|
+
* so the leaves after the first are a `string`.
|
|
381
|
+
*/
|
|
382
|
+
type LeafOf<S, Q> = HasParallel<Q> extends true ? Leaves<S, Q> | `${Leaves<S, Q>} ${string}` : Leaves<S, Q>;
|
|
383
|
+
type Leaves<S, Q> = Exclude<Tree<S>["leaves"], HistoryPaths<Q>>;
|
|
384
|
+
/** What `is()` takes: every state's path but a history pseudo-state's. */
|
|
385
|
+
type PathOf<S, Q> = Exclude<Tree<S>["paths"], HistoryPaths<Q>>;
|
|
386
|
+
/** The views handlers get, built once per machine: one for every event, one per event, one for `done`. */
|
|
387
|
+
interface Handlers<C, P, D, E, Part extends string> {
|
|
388
|
+
readonly any: View<C, P, D, EventUnion<E>, EventUnion<E> | MachineEvent, string, string, Part>;
|
|
389
|
+
readonly done: View<C, P, D, EventUnion<E>, DoneEvent, string, string, Part>;
|
|
390
|
+
readonly on: { readonly [K in keyof E & string]: View<C, P, D, EventUnion<E>, string extends keyof E ? EventUnion<E> : EventOf<E, K>, string, string, Part>; };
|
|
391
|
+
}
|
|
392
|
+
/** What every state of one machine may refer to: its named implementations and its top targets. */
|
|
393
|
+
interface Refs {
|
|
394
|
+
readonly actions: string;
|
|
395
|
+
readonly guards: string;
|
|
396
|
+
readonly effects: string;
|
|
397
|
+
/** The top states' names, and `"#form.editing"` for every state. */
|
|
398
|
+
readonly root: string;
|
|
399
|
+
}
|
|
400
|
+
interface AnyView {
|
|
401
|
+
readonly context: unknown;
|
|
402
|
+
}
|
|
403
|
+
interface AnyHandlers {
|
|
404
|
+
readonly any: AnyView;
|
|
405
|
+
readonly done: AnyView;
|
|
406
|
+
readonly on: {
|
|
407
|
+
readonly [event: string]: AnyView;
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
type On<H extends AnyHandlers, N extends Refs, T extends string> = { readonly [K in keyof H["on"]]?: Transitions<T, H["on"][K], N["actions"], N["guards"]>; };
|
|
411
|
+
/** Each child of a state whose children tree is `U`; `Up` is what its ancestors let it name. */
|
|
412
|
+
type StatesOf<H extends AnyHandlers, N extends Refs, U, Up extends string> = { readonly [K in keyof U]: StateConfig<H, N, U[K], Up>; };
|
|
413
|
+
/**
|
|
414
|
+
* One state. `U` is its children's tree, which TypeScript infers through
|
|
415
|
+
* `states` and, in its first pass, `initial` (see `Loose`); every other use
|
|
416
|
+
* of it is `NoInfer`. Contextual types walk the generic mapped `states` key
|
|
417
|
+
* by key, so a handler's view never waits for the tree.
|
|
418
|
+
*/
|
|
419
|
+
interface StateConfig<H extends AnyHandlers, N extends Refs, U, Up extends string> {
|
|
420
|
+
readonly initial?: keyof U & string;
|
|
421
|
+
readonly states?: StatesOf<H, N, U, Up | NoInfer<Names<U>>>;
|
|
422
|
+
readonly on?: On<H, N, NoInfer<Up | N["root"] | `.${Loose<U>}`>>;
|
|
423
|
+
readonly entry?: Some<N["actions"] | Action<H["any"]>>;
|
|
424
|
+
readonly exit?: Some<N["actions"] | Action<H["any"]>>;
|
|
425
|
+
/** Runs while the state is active; a returned function cleans up on exit (D13). */
|
|
426
|
+
readonly effect?: Some<N["effects"] | Effect<H["any"]>>;
|
|
427
|
+
/** Timers set on entry and cleared on exit: `{ 10_000: "failure" }`. */
|
|
428
|
+
readonly after?: {
|
|
429
|
+
readonly [ms: number]: Transitions<NoInfer<Up | N["root"] | `.${Loose<U>}`>, H["any"], N["actions"], N["guards"]>;
|
|
430
|
+
};
|
|
431
|
+
/** Eventless transitions, checked after every event and on entry. */
|
|
432
|
+
readonly always?: Transitions<NoInfer<Up | N["root"] | `.${Loose<U>}`>, H["any"], N["actions"], N["guards"]>;
|
|
433
|
+
/**
|
|
434
|
+
* `"final"`: at the top level it ends the machine and resolves `done`; in
|
|
435
|
+
* a region of a parallel state it ends the region. `parallel`, imported
|
|
436
|
+
* from `@menapo/machine`: the children are regions, all active at once.
|
|
437
|
+
*/
|
|
438
|
+
readonly type?: "final" | Parallel | History;
|
|
439
|
+
/** A parallel state's: taken once every one of its regions is in a final state. */
|
|
440
|
+
readonly done?: Transitions<NoInfer<Up | N["root"] | `.${Loose<U>}`>, H["done"], N["actions"], N["guards"]>;
|
|
441
|
+
}
|
|
442
|
+
/** `type: parallel`: the state's children are regions, all active at once (PLAN §4.9). */
|
|
443
|
+
interface Parallel {
|
|
444
|
+
readonly [FEATURE]: Regions;
|
|
445
|
+
}
|
|
446
|
+
/** The `type` of a history pseudo-state. */
|
|
447
|
+
interface History {
|
|
448
|
+
readonly [FEATURE]: "history";
|
|
449
|
+
}
|
|
450
|
+
/** What `history()` returns: a pseudo-state that re-enters where its parent was when last exited. */
|
|
451
|
+
interface HistoryState {
|
|
452
|
+
readonly type: History;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* The default of `actions`, `guards` and `effects` before TypeScript has
|
|
456
|
+
* read them: any name. Once read, only theirs.
|
|
457
|
+
*/
|
|
458
|
+
interface Unnamed {
|
|
459
|
+
readonly [name: string]: unknown;
|
|
460
|
+
}
|
|
461
|
+
/** The names a map declares, or none when the machine has no such map. */
|
|
462
|
+
type NamesOf<T> = string extends keyof T ? never : keyof T & string;
|
|
463
|
+
type ViewIn<C, PC, E, DC, PN extends string> = View<C, PropsOf<PC>, DerivedOf<DC>, EventUnion<E>, EventUnion<E> | MachineEvent, string, string, PN>;
|
|
464
|
+
interface MachineConfig<C, PC, E, DC, A, G, F, S, PS = {}, X = {}, PN extends string = never, Q = unknown> {
|
|
465
|
+
/** Names the machine in dev errors; `"machine"` when left out. */
|
|
466
|
+
readonly name?: string;
|
|
467
|
+
readonly context?: C;
|
|
468
|
+
readonly props?: PC;
|
|
469
|
+
readonly events?: E;
|
|
470
|
+
/**
|
|
471
|
+
* Cached values of context, props and the state, recomputed when what
|
|
472
|
+
* they read changes. The intersection gives the functions their view
|
|
473
|
+
* while `DC` is inferred from them, so actions can read their results.
|
|
474
|
+
*/
|
|
475
|
+
readonly derived?: DC & DerivedConfig<NoInfer<C>, PropsOf<NoInfer<PC>>>;
|
|
476
|
+
/**
|
|
477
|
+
* Named implementations (D8): written once, used by name in any state
|
|
478
|
+
* (`do: "save"`, `if: "valid"`, `effect: "load"`), replaced with
|
|
479
|
+
* `provide`. They get every event, so they narrow `event.type` themselves.
|
|
480
|
+
*/
|
|
481
|
+
readonly actions?: A & {
|
|
482
|
+
readonly [name: string]: Action<ViewIn<NoInfer<C>, NoInfer<PC>, NoInfer<E>, NoInfer<DC>, NoInfer<PN>>>;
|
|
483
|
+
};
|
|
484
|
+
readonly guards?: G & {
|
|
485
|
+
readonly [name: string]: Guard<ViewIn<NoInfer<C>, NoInfer<PC>, NoInfer<E>, NoInfer<DC>, NoInfer<PN>>>;
|
|
486
|
+
};
|
|
487
|
+
readonly effects?: F & {
|
|
488
|
+
readonly [name: string]: Effect<ViewIn<NoInfer<C>, NoInfer<PC>, NoInfer<E>, NoInfer<DC>, NoInfer<PN>>>;
|
|
489
|
+
};
|
|
490
|
+
readonly initial: keyof S & string;
|
|
491
|
+
readonly states: Marks<Q> & StatesOf<Handlers<NoInfer<C>, PropsOf<NoInfer<PC>>, DerivedOf<NoInfer<DC>>, NoInfer<E>, NoInfer<PN>>, {
|
|
492
|
+
readonly actions: keyof NoInfer<A> & string;
|
|
493
|
+
readonly guards: keyof NoInfer<G> & string;
|
|
494
|
+
readonly effects: keyof NoInfer<F> & string;
|
|
495
|
+
readonly root: NoInfer<Names<S> | `#${Loose<S>}`>;
|
|
496
|
+
}, S, never>;
|
|
497
|
+
/** Transitions every state takes unless it handles the event itself. */
|
|
498
|
+
readonly on?: On<Handlers<NoInfer<C>, PropsOf<NoInfer<PC>>, DerivedOf<NoInfer<DC>>, NoInfer<E>, NoInfer<PN>>, {
|
|
499
|
+
readonly actions: keyof NoInfer<A> & string;
|
|
500
|
+
readonly guards: keyof NoInfer<G> & string;
|
|
501
|
+
readonly effects: keyof NoInfer<F> & string;
|
|
502
|
+
readonly root: never;
|
|
503
|
+
}, NoInfer<Names<S> | `#${Loose<S>}` | `.${Loose<S>}`>>;
|
|
504
|
+
/**
|
|
505
|
+
* The component's DOM attributes, per part (PLAN §4.4): static values,
|
|
506
|
+
* attribute functions re-evaluated when what they read changes, and
|
|
507
|
+
* `on*` handlers (an event name, or a function of the view and the DOM
|
|
508
|
+
* event), created once per instance.
|
|
509
|
+
*/
|
|
510
|
+
readonly parts?: PS & { readonly [K in PN]: PartConfig<Reading<ViewIn<NoInfer<C>, NoInfer<PC>, NoInfer<E>, NoInfer<DC>, NoInfer<PN>>>, keyof NoInfer<E> & string>; };
|
|
511
|
+
/** What `useMachine` returns beside `state`, `is`, `send`, `context` and `parts` (PLAN §4.6). */
|
|
512
|
+
readonly expose?: (view: Reading<ViewIn<NoInfer<C>, NoInfer<PC>, NoInfer<E>, NoInfer<DC>, NoInfer<PN>>>) => X;
|
|
513
|
+
}
|
|
514
|
+
/** The names of a machine's `actions`, `guards` and `effects`, for `provide`. */
|
|
515
|
+
interface Named {
|
|
516
|
+
readonly actions: string;
|
|
517
|
+
readonly guards: string;
|
|
518
|
+
readonly effects: string;
|
|
519
|
+
}
|
|
520
|
+
/** A machine `defineMachine` made, carrying its config's types to `start`. */
|
|
521
|
+
interface Machine<C = {}, PC = {}, E = AnyEvents, DC = {}, Path extends string = string, Leaf extends string = string, N extends Named = Named, PS = {}, X = {}> {
|
|
522
|
+
/** Never set: the types `start`, `provide` and the instance read. */
|
|
523
|
+
readonly types?: {
|
|
524
|
+
readonly context: C;
|
|
525
|
+
readonly props: PC;
|
|
526
|
+
readonly events: E;
|
|
527
|
+
readonly derived: DC;
|
|
528
|
+
readonly paths: Path;
|
|
529
|
+
readonly leaves: Leaf;
|
|
530
|
+
readonly named: N;
|
|
531
|
+
readonly owner: OwnerProps<PC>;
|
|
532
|
+
readonly parts: PS;
|
|
533
|
+
readonly exposed: X;
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
/** Any machine, for `start`'s constraint. */
|
|
537
|
+
type AnyMachine = Machine<unknown, unknown, unknown, unknown, string, string, Named, unknown, unknown>;
|
|
538
|
+
type TypesOf<M extends AnyMachine> = NonNullable<M["types"]>;
|
|
539
|
+
/**
|
|
540
|
+
* What guards, actions and effects of machine `M` receive, every event
|
|
541
|
+
* included: the type of a function `provide` supplies, or of one written
|
|
542
|
+
* outside the machine that its actions call.
|
|
543
|
+
*/
|
|
544
|
+
type ViewOf<M extends AnyMachine> = View<TypesOf<M>["context"], PropsOf<TypesOf<M>["props"]>, DerivedOf<TypesOf<M>["derived"]>, EventUnion<TypesOf<M>["events"]>, EventUnion<TypesOf<M>["events"]> | MachineEvent, string, string, keyof TypesOf<M>["parts"] & string>;
|
|
545
|
+
/** `provide(machine, implementations)`: any of the machine's named implementations. */
|
|
546
|
+
interface Implementations<M extends AnyMachine> {
|
|
547
|
+
readonly actions?: Replacing<TypesOf<M>["named"]["actions"], Action<ViewOf<M>>>;
|
|
548
|
+
readonly guards?: Replacing<TypesOf<M>["named"]["guards"], Guard<ViewOf<M>>>;
|
|
549
|
+
readonly effects?: Replacing<TypesOf<M>["named"]["effects"], Effect<ViewOf<M>>>;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Replacements for the names `N`: none when the machine declares no such
|
|
553
|
+
* map (an empty mapped type would take any key, unchecked).
|
|
554
|
+
*/
|
|
555
|
+
type Replacing<N extends string, F> = [N] extends [never] ? {
|
|
556
|
+
readonly [name: string]: never;
|
|
557
|
+
} : { readonly [K in N]?: F; };
|
|
558
|
+
/** `start(machine, options)`: initial context fields and the owner's props. */
|
|
559
|
+
interface StartOptions<M extends AnyMachine> {
|
|
560
|
+
readonly context?: Partial<TypesOf<M>["context"]>;
|
|
561
|
+
readonly props?: TypesOf<M>["owner"];
|
|
562
|
+
}
|
|
563
|
+
type Listener<I> = (instance: I) => void;
|
|
564
|
+
/** A started machine (PLAN §4.8): `state` and `is` take its paths. */
|
|
565
|
+
interface Instance<C = {}, PC = {}, E = AnyEvents, DC = {}, Path extends string = string, Leaf extends string = string> extends Omit<View<C, PropsOf<PC>, DerivedOf<DC>, EventUnion<E>, EventUnion<E> | MachineEvent, Path, Leaf>, "ids" | "el"> {
|
|
566
|
+
/** The context fields and props the last notification changed, by name. */
|
|
567
|
+
readonly changed: readonly string[];
|
|
568
|
+
/** Called once per event that changed something, with the instance. */
|
|
569
|
+
subscribe(listener: Listener<this>): () => void;
|
|
570
|
+
/** The owner's props, as a component passes them on each render. */
|
|
571
|
+
setProps(props: OwnerProps<PC>): void;
|
|
572
|
+
/** Resolves when the machine reaches a top-level final state. */
|
|
573
|
+
readonly done: Promise<void>;
|
|
574
|
+
/** Stops effects and timers; later sends are ignored. */
|
|
575
|
+
stop(): void;
|
|
576
|
+
}
|
|
577
|
+
/** The instance `start` returns for machine `M`. */
|
|
578
|
+
type InstanceOf<M extends AnyMachine> = M extends Machine<infer C, infer PC, infer E, infer DC, infer Path, infer Leaf> ? Instance<C, PC, E, DC, Path, Leaf> : never;
|
|
579
|
+
//#endregion
|
|
580
|
+
export { StateConfig as A, PartProps as B, OwnerProps as C, PropsOf as D, Payload as E, ViewOf as F, UseProps as H, Api as I, PartBase as L, Transitions as M, Unnamed as N, Send as O, View as P, PartConfig as R, NamesOf as S, PathOf as T, PartsConfig as V, InstanceOf as _, Bindable as a, MachineConfig as b, Effect as c, EventUnion as d, Guard as f, Instance as g, Implementations as h, AnyMachine as i, Transition as j, StartOptions as k, EffectResult as l, HistoryState as m, AnyEvent as n, DerivedView as o, History as p, AnyEvents as r, DoneEvent as s, Action as t, EventOf as u, LeafOf as v, Parallel as w, MachineEvent as x, Machine as y, PartEvent as z };
|
package/dist/vicss.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { i as AnyMachine } from "./types-Bk8svtO6.js";
|
|
2
|
+
//#region src/vicss.d.ts
|
|
3
|
+
/** A vicss preset: named conditions, as `defineConfig({ conditions })` takes them. */
|
|
4
|
+
export interface ConditionsPreset {
|
|
5
|
+
readonly conditions: Readonly<Record<string, string | string[]>>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The conditions of every state of `machines`, by name: the machine's
|
|
9
|
+
* `name` then the state's path, in camelCase (`dialogOpen`,
|
|
10
|
+
* `wizardFormEditing`). A state holds while it is active, as `is()` says: a
|
|
11
|
+
* leaf when `data-state` lists it, a parent while it lists a leaf inside it;
|
|
12
|
+
* in a machine with parallel states `data-state` lists one leaf per region,
|
|
13
|
+
* so every region's states hold at once. History states are never active
|
|
14
|
+
* and get none. Use a condition on a part's own element, the one its
|
|
15
|
+
* props are spread on, beside its vicss style (`{...api.parts.content}
|
|
16
|
+
* {...sx(s.content)}`).
|
|
17
|
+
*/
|
|
18
|
+
export declare function conditions(...machines: readonly AnyMachine[]): ConditionsPreset;
|
|
19
|
+
//#endregion
|
package/dist/vicss.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { r as fail } from "./dev-BAz6e3W-.js";
|
|
2
|
+
//#region src/vicss.ts
|
|
3
|
+
/**
|
|
4
|
+
* `@menapo/machine/vicss`: the vicss conditions preset generated from the
|
|
5
|
+
* machines' anatomies (PLAN D2). Every part of a machine carries
|
|
6
|
+
* `data-scope` (the machine's `name`) and `data-state` (the active leaves,
|
|
7
|
+
* PLAN §4.4), so each state is a condition on the part's element:
|
|
8
|
+
* `presets: [conditions(dialog)]` in `vicss.config.ts` gives `_dialogOpen`
|
|
9
|
+
* and `_dialogClosed`, and a recipe styles a part per state with no class
|
|
10
|
+
* toggled by hand and no state name written twice.
|
|
11
|
+
*
|
|
12
|
+
* Read from the machine's tables, runtime-normalised or compiled, when the
|
|
13
|
+
* config loads; nothing here ships to the browser, and it imports nothing
|
|
14
|
+
* from vicss: the preset is plain config data.
|
|
15
|
+
*/
|
|
16
|
+
const word = (text) => text.split(/[^A-Za-z0-9]+/).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join("");
|
|
17
|
+
/** A CSS attribute value inside double quotes. */
|
|
18
|
+
const quoted = (value) => `"${value.replace(/["\\]/g, "\\$&")}"`;
|
|
19
|
+
/**
|
|
20
|
+
* The conditions of every state of `machines`, by name: the machine's
|
|
21
|
+
* `name` then the state's path, in camelCase (`dialogOpen`,
|
|
22
|
+
* `wizardFormEditing`). A state holds while it is active, as `is()` says: a
|
|
23
|
+
* leaf when `data-state` lists it, a parent while it lists a leaf inside it;
|
|
24
|
+
* in a machine with parallel states `data-state` lists one leaf per region,
|
|
25
|
+
* so every region's states hold at once. History states are never active
|
|
26
|
+
* and get none. Use a condition on a part's own element, the one its
|
|
27
|
+
* props are spread on, beside its vicss style (`{...api.parts.content}
|
|
28
|
+
* {...sx(s.content)}`).
|
|
29
|
+
*/
|
|
30
|
+
function conditions(...machines) {
|
|
31
|
+
const out = {};
|
|
32
|
+
for (const machine of machines) {
|
|
33
|
+
const tables = machine;
|
|
34
|
+
const scope = tables.c.name ?? "machine";
|
|
35
|
+
const capitalised = word(scope);
|
|
36
|
+
const prefix = capitalised.charAt(0).toLowerCase() + capitalised.slice(1);
|
|
37
|
+
const element = `&[data-scope=${quoted(scope)}]`;
|
|
38
|
+
const parents = new Set(tables.ss.map((state) => state.up));
|
|
39
|
+
for (const state of tables.ss) {
|
|
40
|
+
if (state.h !== void 0) continue;
|
|
41
|
+
const name = prefix + state.path.split(".").map(word).join("");
|
|
42
|
+
const condition = parents.has(state) ? [`${element}[data-state^=${quoted(`${state.path}.`)}]`, `${element}[data-state*=${quoted(` ${state.path}.`)}]`] : `${element}[data-state~=${quoted(state.path)}]`;
|
|
43
|
+
const taken = out[name];
|
|
44
|
+
if (taken !== void 0 && String(taken) !== String(condition)) fail("CONDITION", scope, `the vicss condition "${name}" names state "${state.path}" here and a state of another machine; give the machines names that differ in more than punctuation or case.`);
|
|
45
|
+
out[name] = condition;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { conditions: out };
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { conditions };
|