@rohal12/spindle 0.59.11 → 0.59.13

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.
@@ -0,0 +1,48 @@
1
+ import { getByPath } from './object-path';
2
+
3
+ /**
4
+ * Records the edits a reader made through a control bound to a story
5
+ * variable (`{textbox}`, `{checkbox}`, ...). {for} tells these apart from a
6
+ * list that is replaced: typing into a control inside an iteration changes
7
+ * its item, but must not remount the iteration and drop the control's focus.
8
+ * An edit is attributed to the variable path it wrote, so a replacement made
9
+ * elsewhere (a watcher reacting to the edit) still remounts.
10
+ */
11
+ let version = 0;
12
+ const MAX_LOG = 256;
13
+ const log: { version: number; segments: readonly string[] }[] = [];
14
+
15
+ export function noteControlEdit(segments: readonly string[]): void {
16
+ log.push({ version: ++version, segments });
17
+ if (log.length > MAX_LOG) log.shift();
18
+ }
19
+
20
+ export function controlEditVersion(): number {
21
+ return version;
22
+ }
23
+
24
+ /**
25
+ * Whether an edit since `since` wrote inside `item`, element `index` of
26
+ * `list`: the path leads through the item (compared by reference in
27
+ * `variables`), or, for a primitive item, ends at its slot in the list.
28
+ */
29
+ export function editedItem(
30
+ since: number,
31
+ variables: object,
32
+ list: readonly unknown[],
33
+ item: unknown,
34
+ index: number,
35
+ ): boolean {
36
+ return log.some(({ version: v, segments }) => {
37
+ if (v <= since) return false;
38
+ if (typeof item === 'object' && item !== null) {
39
+ return segments.some(
40
+ (_, n) => getByPath(variables, segments.slice(0, n + 1)) === item,
41
+ );
42
+ }
43
+ return (
44
+ segments[segments.length - 1] === String(index) &&
45
+ getByPath(variables, segments.slice(0, -1)) === list
46
+ );
47
+ });
48
+ }
@@ -0,0 +1,44 @@
1
+ import { registerWidget } from './widget-registry';
2
+ import { astContainsChildren } from './ast-scanner';
3
+ import { registerBlockMacro, type ASTNode } from '../markup/ast';
4
+ import { errorMessage } from '../utils/error-message';
5
+ import { parseWidgetDef, type WidgetDef } from './widget-def';
6
+
7
+ /**
8
+ * Register the widget a definition declares, with its body. Widgets whose
9
+ * body renders {@children} take a closing tag, so they are registered as
10
+ * block macros too: passages parsed later nest their content. A parameter
11
+ * that no namespace can hold throws (see registerWidget), registering
12
+ * nothing.
13
+ */
14
+ export function registerWidgetDef(
15
+ { name, params }: WidgetDef,
16
+ body: ASTNode[],
17
+ ): void {
18
+ const isBlock = astContainsChildren(body);
19
+ registerWidget(name, body, params, isBlock);
20
+ if (isBlock) registerBlockMacro(name);
21
+ }
22
+
23
+ /**
24
+ * Register the widgets `nodes` (a passage's markup) define at its top level,
25
+ * as the {widget} macro does when it renders. A widget that fails to register
26
+ * is reported; the others still register.
27
+ */
28
+ export function registerWidgetDefinitions(
29
+ nodes: ASTNode[],
30
+ passageName: string,
31
+ ): void {
32
+ for (const node of nodes) {
33
+ if (node.type === 'macro' && node.name === 'widget' && node.rawArgs) {
34
+ const def = parseWidgetDef(node.rawArgs);
35
+ try {
36
+ registerWidgetDef(def, node.children as ASTNode[]);
37
+ } catch (err) {
38
+ console.error(
39
+ `spindle: widget "${def.name}" in passage "${passageName}" was not registered: ${errorMessage(err)}`,
40
+ );
41
+ }
42
+ }
43
+ }
44
+ }
@@ -100,7 +100,13 @@ export declare function defineMacro(config: MacroDefinition): void;
100
100
  export declare function getMacroRegistry(): MacroMetadata[];
101
101
 
102
102
  /** Variable type inferred from a StoryVariables/StoryTransients default value. */
103
- export type VarType = 'number' | 'string' | 'boolean' | 'array' | 'object';
103
+ export type VarType =
104
+ | 'number'
105
+ | 'string'
106
+ | 'boolean'
107
+ | 'array'
108
+ | 'object'
109
+ | 'null';
104
110
 
105
111
  /** Inferred shape of a declared variable (or one of its object fields). */
106
112
  export interface FieldSchema {