@streetui/dom 1.0.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.
@@ -0,0 +1,215 @@
1
+ /**
2
+ * DOMAdapter — framework-owned abstraction over DOM operations.
3
+ *
4
+ * The renderer depends on this interface, never on raw browser globals,
5
+ * which makes the renderer testable and portable.
6
+ */
7
+ interface DOMAdapter {
8
+ createElement(tag: string, ns?: string): Element;
9
+ createTextNode(data: string): Text;
10
+ createComment(data: string): Comment;
11
+ createFragment(): DocumentFragment;
12
+ appendChild(parent: Node, child: Node): void;
13
+ insertBefore(parent: Node, child: Node, reference: Node | null): void;
14
+ removeChild(parent: Node, child: Node): void;
15
+ replaceChild(parent: Node, newChild: Node, oldChild: Node): void;
16
+ setAttribute(element: Element, name: string, value: string): void;
17
+ removeAttribute(element: Element, name: string): void;
18
+ getAttribute(element: Element, name: string): string | null;
19
+ setProperty(element: Element, name: string, value: unknown): void;
20
+ setTextContent(node: Node, text: string): void;
21
+ getTextContent(node: Node): string | null;
22
+ addEventListener(target: EventTarget, type: string, handler: EventListener, options?: AddEventListenerOptions): void;
23
+ removeEventListener(target: EventTarget, type: string, handler: EventListener, options?: EventListenerOptions): void;
24
+ querySelector(root: Element | Document, selector: string): Element | null;
25
+ querySelectorAll(root: Element | Document, selector: string): NodeListOf<Element>;
26
+ getElementById(id: string): Element | null;
27
+ /**
28
+ * Move focus to an element. On the server (or when the element cannot receive
29
+ * focus) this is a safe no-op, keeping focus management SSR-compatible.
30
+ */
31
+ focus(element: Element): void;
32
+ isElement(node: Node): node is Element;
33
+ isTextNode(node: Node): node is Text;
34
+ /** Lower-cased tag name of an element (e.g. "div", "h1"). */
35
+ tagName(element: Element): string;
36
+ parentNode(node: Node): Node | null;
37
+ nextSibling(node: Node): Node | null;
38
+ /** First child node (element, text, or otherwise), or null. */
39
+ firstChild(node: Node): Node | null;
40
+ /** All child nodes of an element in order (empty for leaf/text nodes). */
41
+ childNodes(node: Node): Node[];
42
+ }
43
+
44
+ /**
45
+ * Browser implementation of DOMAdapter — delegates directly to browser APIs.
46
+ */
47
+
48
+ declare class BrowserDOMAdapter implements DOMAdapter {
49
+ createElement(tag: string, ns?: string): Element;
50
+ createTextNode(data: string): Text;
51
+ createComment(data: string): Comment;
52
+ createFragment(): DocumentFragment;
53
+ appendChild(parent: Node, child: Node): void;
54
+ insertBefore(parent: Node, child: Node, reference: Node | null): void;
55
+ removeChild(parent: Node, child: Node): void;
56
+ replaceChild(parent: Node, newChild: Node, oldChild: Node): void;
57
+ setAttribute(element: Element, name: string, value: string): void;
58
+ removeAttribute(element: Element, name: string): void;
59
+ getAttribute(element: Element, name: string): string | null;
60
+ setProperty(element: Element, name: string, value: unknown): void;
61
+ setTextContent(node: Node, text: string): void;
62
+ getTextContent(node: Node): string | null;
63
+ addEventListener(target: EventTarget, type: string, handler: EventListener, options?: AddEventListenerOptions): void;
64
+ removeEventListener(target: EventTarget, type: string, handler: EventListener, options?: EventListenerOptions): void;
65
+ querySelector(root: Element | Document, selector: string): Element | null;
66
+ querySelectorAll(root: Element | Document, selector: string): NodeListOf<Element>;
67
+ getElementById(id: string): Element | null;
68
+ focus(element: Element): void;
69
+ isElement(node: Node): node is Element;
70
+ isTextNode(node: Node): node is Text;
71
+ tagName(element: Element): string;
72
+ parentNode(node: Node): Node | null;
73
+ nextSibling(node: Node): Node | null;
74
+ firstChild(node: Node): Node | null;
75
+ childNodes(node: Node): Node[];
76
+ }
77
+ declare const browserDOMAdapter: BrowserDOMAdapter;
78
+
79
+ /**
80
+ * Server-side DOM node model.
81
+ *
82
+ * A tiny, dependency-free tree of plain objects that mirrors just enough of the
83
+ * browser DOM for StreetUI's renderer to build a tree on the server and
84
+ * serialize it to an HTML string. There is NO browser global here — these are
85
+ * ordinary classes usable in any JavaScript environment (Node, workers, tests).
86
+ *
87
+ * The renderer never touches these types directly; it goes through the
88
+ * `DOMAdapter` interface, and `ServerDOMAdapter` translates adapter calls into
89
+ * operations on this model.
90
+ */
91
+ type ServerNodeKind = 'element' | 'text' | 'comment' | 'fragment';
92
+ interface ServerNode {
93
+ readonly kind: ServerNodeKind;
94
+ parent: ServerParent | null;
95
+ }
96
+ type ServerParent = ServerElement | ServerFragment;
97
+ /** A minimal inline-style holder mirroring `element.style.setProperty`. */
98
+ declare class ServerStyle {
99
+ readonly declarations: Map<string, string>;
100
+ setProperty(name: string, value: string): void;
101
+ get isEmpty(): boolean;
102
+ toCss(): string;
103
+ }
104
+ declare class ServerText implements ServerNode {
105
+ readonly kind: "text";
106
+ parent: ServerParent | null;
107
+ data: string;
108
+ constructor(data: string);
109
+ }
110
+ declare class ServerComment implements ServerNode {
111
+ readonly kind: "comment";
112
+ parent: ServerParent | null;
113
+ data: string;
114
+ constructor(data: string);
115
+ }
116
+ declare class ServerFragment implements ServerNode {
117
+ readonly kind: "fragment";
118
+ parent: ServerParent | null;
119
+ readonly children: ServerNode[];
120
+ }
121
+ declare class ServerElement implements ServerNode {
122
+ readonly kind: "element";
123
+ parent: ServerParent | null;
124
+ readonly tagName: string;
125
+ readonly attributes: Map<string, string>;
126
+ /** JS properties set via `setProperty` (e.g. input `value`, `checked`). */
127
+ readonly properties: Map<string, unknown>;
128
+ readonly children: ServerNode[];
129
+ readonly style: ServerStyle;
130
+ constructor(tagName: string);
131
+ }
132
+ /** Escape text node content. */
133
+ declare function escapeHtmlText(value: string): string;
134
+ /** Escape a double-quoted attribute value. */
135
+ declare function escapeHtmlAttr(value: string): string;
136
+ /** Serialize a single server node (element/text/comment/fragment) to HTML. */
137
+ declare function serializeServerNode(node: ServerNode): string;
138
+ /** Serialize the children of an element or fragment (its "inner HTML"). */
139
+ declare function serializeChildren(node: ServerElement | ServerFragment): string;
140
+
141
+ /**
142
+ * Server implementation of `DOMAdapter`.
143
+ *
144
+ * Builds a lightweight in-memory tree (see `server-node.ts`) instead of touching
145
+ * a real browser DOM, then lets the caller serialize it to an HTML string. It is
146
+ * completely free of browser globals, so the exact same renderer that runs in
147
+ * the browser can produce HTML on the server.
148
+ *
149
+ * The `DOMAdapter` interface is typed against the lib DOM types (`Element`,
150
+ * `Node`, `Text`, …). Our server nodes structurally stand in for those at
151
+ * runtime, so the boundary uses `as unknown as` casts in one place. Everything
152
+ * inside operates on the real server-node model.
153
+ */
154
+
155
+ declare class ServerDOMAdapter implements DOMAdapter {
156
+ createElement(tag: string, _ns?: string): Element;
157
+ createTextNode(data: string): Text;
158
+ createComment(data: string): Comment;
159
+ createFragment(): DocumentFragment;
160
+ appendChild(parent: Node, child: Node): void;
161
+ insertBefore(parent: Node, child: Node, reference: Node | null): void;
162
+ removeChild(parent: Node, child: Node): void;
163
+ replaceChild(parent: Node, newChild: Node, oldChild: Node): void;
164
+ private _detach;
165
+ setAttribute(element: Element, name: string, value: string): void;
166
+ removeAttribute(element: Element, name: string): void;
167
+ getAttribute(element: Element, name: string): string | null;
168
+ setProperty(element: Element, name: string, value: unknown): void;
169
+ setTextContent(node: Node, text: string): void;
170
+ getTextContent(node: Node): string | null;
171
+ addEventListener(): void;
172
+ removeEventListener(): void;
173
+ querySelector(): Element | null;
174
+ querySelectorAll(): NodeListOf<Element>;
175
+ getElementById(): Element | null;
176
+ focus(): void;
177
+ isElement(node: Node): node is Element;
178
+ isTextNode(node: Node): node is Text;
179
+ tagName(element: Element): string;
180
+ parentNode(node: Node): Node | null;
181
+ nextSibling(node: Node): Node | null;
182
+ firstChild(node: Node): Node | null;
183
+ childNodes(node: Node): Node[];
184
+ /** Serialize a node's children ("inner HTML") to an HTML string. */
185
+ serializeInner(node: Node): string;
186
+ /** Serialize a node (including itself) to an HTML string. */
187
+ serializeOuter(node: Node): string;
188
+ }
189
+ declare const serverDOMAdapter: ServerDOMAdapter;
190
+
191
+ /**
192
+ * Focus helpers built on the {@link DOMAdapter} abstraction.
193
+ *
194
+ * These are the minimal, genuinely-useful focus operations an app needs:
195
+ * focus a specific element (e.g. the first field when a route or modal opens)
196
+ * or focus the first focusable element inside a container (e.g. move focus
197
+ * into a dialog). Both go through the adapter, so they are no-ops on the server
198
+ * (`ServerDOMAdapter.querySelector` returns null / `focus` does nothing) and
199
+ * therefore safe to call from universal code.
200
+ */
201
+
202
+ /** Default selector for natively focusable / tabbable elements. */
203
+ declare const FOCUSABLE_SELECTOR = "a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex=\"-1\"])";
204
+ /**
205
+ * Focus the element with the given id, scoped to `root`.
206
+ * Returns true if an element was found and focused.
207
+ */
208
+ declare function focusById(dom: DOMAdapter, root: Element | Document, id: string): boolean;
209
+ /**
210
+ * Focus the first focusable element inside `container`.
211
+ * Returns true if a focusable element was found and focused.
212
+ */
213
+ declare function focusFirst(dom: DOMAdapter, container: Element | Document, selector?: string): boolean;
214
+
215
+ export { BrowserDOMAdapter, type DOMAdapter, FOCUSABLE_SELECTOR, ServerComment, ServerDOMAdapter, ServerElement, ServerFragment, type ServerNode, type ServerNodeKind, type ServerParent, ServerStyle, ServerText, browserDOMAdapter, escapeHtmlAttr, escapeHtmlText, focusById, focusFirst, serializeChildren, serializeServerNode, serverDOMAdapter };
@@ -0,0 +1,215 @@
1
+ /**
2
+ * DOMAdapter — framework-owned abstraction over DOM operations.
3
+ *
4
+ * The renderer depends on this interface, never on raw browser globals,
5
+ * which makes the renderer testable and portable.
6
+ */
7
+ interface DOMAdapter {
8
+ createElement(tag: string, ns?: string): Element;
9
+ createTextNode(data: string): Text;
10
+ createComment(data: string): Comment;
11
+ createFragment(): DocumentFragment;
12
+ appendChild(parent: Node, child: Node): void;
13
+ insertBefore(parent: Node, child: Node, reference: Node | null): void;
14
+ removeChild(parent: Node, child: Node): void;
15
+ replaceChild(parent: Node, newChild: Node, oldChild: Node): void;
16
+ setAttribute(element: Element, name: string, value: string): void;
17
+ removeAttribute(element: Element, name: string): void;
18
+ getAttribute(element: Element, name: string): string | null;
19
+ setProperty(element: Element, name: string, value: unknown): void;
20
+ setTextContent(node: Node, text: string): void;
21
+ getTextContent(node: Node): string | null;
22
+ addEventListener(target: EventTarget, type: string, handler: EventListener, options?: AddEventListenerOptions): void;
23
+ removeEventListener(target: EventTarget, type: string, handler: EventListener, options?: EventListenerOptions): void;
24
+ querySelector(root: Element | Document, selector: string): Element | null;
25
+ querySelectorAll(root: Element | Document, selector: string): NodeListOf<Element>;
26
+ getElementById(id: string): Element | null;
27
+ /**
28
+ * Move focus to an element. On the server (or when the element cannot receive
29
+ * focus) this is a safe no-op, keeping focus management SSR-compatible.
30
+ */
31
+ focus(element: Element): void;
32
+ isElement(node: Node): node is Element;
33
+ isTextNode(node: Node): node is Text;
34
+ /** Lower-cased tag name of an element (e.g. "div", "h1"). */
35
+ tagName(element: Element): string;
36
+ parentNode(node: Node): Node | null;
37
+ nextSibling(node: Node): Node | null;
38
+ /** First child node (element, text, or otherwise), or null. */
39
+ firstChild(node: Node): Node | null;
40
+ /** All child nodes of an element in order (empty for leaf/text nodes). */
41
+ childNodes(node: Node): Node[];
42
+ }
43
+
44
+ /**
45
+ * Browser implementation of DOMAdapter — delegates directly to browser APIs.
46
+ */
47
+
48
+ declare class BrowserDOMAdapter implements DOMAdapter {
49
+ createElement(tag: string, ns?: string): Element;
50
+ createTextNode(data: string): Text;
51
+ createComment(data: string): Comment;
52
+ createFragment(): DocumentFragment;
53
+ appendChild(parent: Node, child: Node): void;
54
+ insertBefore(parent: Node, child: Node, reference: Node | null): void;
55
+ removeChild(parent: Node, child: Node): void;
56
+ replaceChild(parent: Node, newChild: Node, oldChild: Node): void;
57
+ setAttribute(element: Element, name: string, value: string): void;
58
+ removeAttribute(element: Element, name: string): void;
59
+ getAttribute(element: Element, name: string): string | null;
60
+ setProperty(element: Element, name: string, value: unknown): void;
61
+ setTextContent(node: Node, text: string): void;
62
+ getTextContent(node: Node): string | null;
63
+ addEventListener(target: EventTarget, type: string, handler: EventListener, options?: AddEventListenerOptions): void;
64
+ removeEventListener(target: EventTarget, type: string, handler: EventListener, options?: EventListenerOptions): void;
65
+ querySelector(root: Element | Document, selector: string): Element | null;
66
+ querySelectorAll(root: Element | Document, selector: string): NodeListOf<Element>;
67
+ getElementById(id: string): Element | null;
68
+ focus(element: Element): void;
69
+ isElement(node: Node): node is Element;
70
+ isTextNode(node: Node): node is Text;
71
+ tagName(element: Element): string;
72
+ parentNode(node: Node): Node | null;
73
+ nextSibling(node: Node): Node | null;
74
+ firstChild(node: Node): Node | null;
75
+ childNodes(node: Node): Node[];
76
+ }
77
+ declare const browserDOMAdapter: BrowserDOMAdapter;
78
+
79
+ /**
80
+ * Server-side DOM node model.
81
+ *
82
+ * A tiny, dependency-free tree of plain objects that mirrors just enough of the
83
+ * browser DOM for StreetUI's renderer to build a tree on the server and
84
+ * serialize it to an HTML string. There is NO browser global here — these are
85
+ * ordinary classes usable in any JavaScript environment (Node, workers, tests).
86
+ *
87
+ * The renderer never touches these types directly; it goes through the
88
+ * `DOMAdapter` interface, and `ServerDOMAdapter` translates adapter calls into
89
+ * operations on this model.
90
+ */
91
+ type ServerNodeKind = 'element' | 'text' | 'comment' | 'fragment';
92
+ interface ServerNode {
93
+ readonly kind: ServerNodeKind;
94
+ parent: ServerParent | null;
95
+ }
96
+ type ServerParent = ServerElement | ServerFragment;
97
+ /** A minimal inline-style holder mirroring `element.style.setProperty`. */
98
+ declare class ServerStyle {
99
+ readonly declarations: Map<string, string>;
100
+ setProperty(name: string, value: string): void;
101
+ get isEmpty(): boolean;
102
+ toCss(): string;
103
+ }
104
+ declare class ServerText implements ServerNode {
105
+ readonly kind: "text";
106
+ parent: ServerParent | null;
107
+ data: string;
108
+ constructor(data: string);
109
+ }
110
+ declare class ServerComment implements ServerNode {
111
+ readonly kind: "comment";
112
+ parent: ServerParent | null;
113
+ data: string;
114
+ constructor(data: string);
115
+ }
116
+ declare class ServerFragment implements ServerNode {
117
+ readonly kind: "fragment";
118
+ parent: ServerParent | null;
119
+ readonly children: ServerNode[];
120
+ }
121
+ declare class ServerElement implements ServerNode {
122
+ readonly kind: "element";
123
+ parent: ServerParent | null;
124
+ readonly tagName: string;
125
+ readonly attributes: Map<string, string>;
126
+ /** JS properties set via `setProperty` (e.g. input `value`, `checked`). */
127
+ readonly properties: Map<string, unknown>;
128
+ readonly children: ServerNode[];
129
+ readonly style: ServerStyle;
130
+ constructor(tagName: string);
131
+ }
132
+ /** Escape text node content. */
133
+ declare function escapeHtmlText(value: string): string;
134
+ /** Escape a double-quoted attribute value. */
135
+ declare function escapeHtmlAttr(value: string): string;
136
+ /** Serialize a single server node (element/text/comment/fragment) to HTML. */
137
+ declare function serializeServerNode(node: ServerNode): string;
138
+ /** Serialize the children of an element or fragment (its "inner HTML"). */
139
+ declare function serializeChildren(node: ServerElement | ServerFragment): string;
140
+
141
+ /**
142
+ * Server implementation of `DOMAdapter`.
143
+ *
144
+ * Builds a lightweight in-memory tree (see `server-node.ts`) instead of touching
145
+ * a real browser DOM, then lets the caller serialize it to an HTML string. It is
146
+ * completely free of browser globals, so the exact same renderer that runs in
147
+ * the browser can produce HTML on the server.
148
+ *
149
+ * The `DOMAdapter` interface is typed against the lib DOM types (`Element`,
150
+ * `Node`, `Text`, …). Our server nodes structurally stand in for those at
151
+ * runtime, so the boundary uses `as unknown as` casts in one place. Everything
152
+ * inside operates on the real server-node model.
153
+ */
154
+
155
+ declare class ServerDOMAdapter implements DOMAdapter {
156
+ createElement(tag: string, _ns?: string): Element;
157
+ createTextNode(data: string): Text;
158
+ createComment(data: string): Comment;
159
+ createFragment(): DocumentFragment;
160
+ appendChild(parent: Node, child: Node): void;
161
+ insertBefore(parent: Node, child: Node, reference: Node | null): void;
162
+ removeChild(parent: Node, child: Node): void;
163
+ replaceChild(parent: Node, newChild: Node, oldChild: Node): void;
164
+ private _detach;
165
+ setAttribute(element: Element, name: string, value: string): void;
166
+ removeAttribute(element: Element, name: string): void;
167
+ getAttribute(element: Element, name: string): string | null;
168
+ setProperty(element: Element, name: string, value: unknown): void;
169
+ setTextContent(node: Node, text: string): void;
170
+ getTextContent(node: Node): string | null;
171
+ addEventListener(): void;
172
+ removeEventListener(): void;
173
+ querySelector(): Element | null;
174
+ querySelectorAll(): NodeListOf<Element>;
175
+ getElementById(): Element | null;
176
+ focus(): void;
177
+ isElement(node: Node): node is Element;
178
+ isTextNode(node: Node): node is Text;
179
+ tagName(element: Element): string;
180
+ parentNode(node: Node): Node | null;
181
+ nextSibling(node: Node): Node | null;
182
+ firstChild(node: Node): Node | null;
183
+ childNodes(node: Node): Node[];
184
+ /** Serialize a node's children ("inner HTML") to an HTML string. */
185
+ serializeInner(node: Node): string;
186
+ /** Serialize a node (including itself) to an HTML string. */
187
+ serializeOuter(node: Node): string;
188
+ }
189
+ declare const serverDOMAdapter: ServerDOMAdapter;
190
+
191
+ /**
192
+ * Focus helpers built on the {@link DOMAdapter} abstraction.
193
+ *
194
+ * These are the minimal, genuinely-useful focus operations an app needs:
195
+ * focus a specific element (e.g. the first field when a route or modal opens)
196
+ * or focus the first focusable element inside a container (e.g. move focus
197
+ * into a dialog). Both go through the adapter, so they are no-ops on the server
198
+ * (`ServerDOMAdapter.querySelector` returns null / `focus` does nothing) and
199
+ * therefore safe to call from universal code.
200
+ */
201
+
202
+ /** Default selector for natively focusable / tabbable elements. */
203
+ declare const FOCUSABLE_SELECTOR = "a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex=\"-1\"])";
204
+ /**
205
+ * Focus the element with the given id, scoped to `root`.
206
+ * Returns true if an element was found and focused.
207
+ */
208
+ declare function focusById(dom: DOMAdapter, root: Element | Document, id: string): boolean;
209
+ /**
210
+ * Focus the first focusable element inside `container`.
211
+ * Returns true if a focusable element was found and focused.
212
+ */
213
+ declare function focusFirst(dom: DOMAdapter, container: Element | Document, selector?: string): boolean;
214
+
215
+ export { BrowserDOMAdapter, type DOMAdapter, FOCUSABLE_SELECTOR, ServerComment, ServerDOMAdapter, ServerElement, ServerFragment, type ServerNode, type ServerNodeKind, type ServerParent, ServerStyle, ServerText, browserDOMAdapter, escapeHtmlAttr, escapeHtmlText, focusById, focusFirst, serializeChildren, serializeServerNode, serverDOMAdapter };