@hypen-space/web 0.4.949 → 0.4.951
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 +13 -20
- package/dist/canvas/index.d.ts +1 -0
- package/dist/canvas/index.js +1 -0
- package/dist/canvas/index.js.map +1 -1
- package/dist/canvas/layout.js +24 -11
- package/dist/canvas/layout.js.map +1 -1
- package/dist/canvas/paint.js +3 -3
- package/dist/canvas/paint.js.map +1 -1
- package/dist/canvas/renderer.d.ts +3 -0
- package/dist/canvas/renderer.js +41 -3
- package/dist/canvas/renderer.js.map +1 -1
- package/dist/canvas/utils.d.ts +8 -0
- package/dist/canvas/utils.js +34 -3
- package/dist/canvas/utils.js.map +1 -1
- package/dist/client.d.ts +36 -0
- package/dist/client.js +47 -0
- package/dist/client.js.map +1 -0
- package/dist/dom/applicators/font.d.ts +6 -11
- package/dist/dom/applicators/font.js +27 -16
- package/dist/dom/applicators/font.js.map +1 -1
- package/dist/dom/applicators/index.js +34 -17
- package/dist/dom/applicators/index.js.map +1 -1
- package/dist/dom/canvas/index.js +2 -2
- package/dist/dom/canvas/index.js.map +1 -1
- package/dist/dom/components/app.js +2 -2
- package/dist/dom/components/app.js.map +1 -1
- package/dist/dom/components/audio.js +2 -2
- package/dist/dom/components/audio.js.map +1 -1
- package/dist/dom/components/avatar.js +4 -3
- package/dist/dom/components/avatar.js.map +1 -1
- package/dist/dom/components/badge.js +2 -2
- package/dist/dom/components/badge.js.map +1 -1
- package/dist/dom/components/button.js +2 -2
- package/dist/dom/components/button.js.map +1 -1
- package/dist/dom/components/card.js +2 -2
- package/dist/dom/components/card.js.map +1 -1
- package/dist/dom/components/center.js +2 -2
- package/dist/dom/components/center.js.map +1 -1
- package/dist/dom/components/checkbox.js +5 -4
- package/dist/dom/components/checkbox.js.map +1 -1
- package/dist/dom/components/column.js +2 -2
- package/dist/dom/components/column.js.map +1 -1
- package/dist/dom/components/container.js +2 -2
- package/dist/dom/components/container.js.map +1 -1
- package/dist/dom/components/divider.js +2 -2
- package/dist/dom/components/divider.js.map +1 -1
- package/dist/dom/components/grid.js +10 -10
- package/dist/dom/components/grid.js.map +1 -1
- package/dist/dom/components/heading.js +4 -3
- package/dist/dom/components/heading.js.map +1 -1
- package/dist/dom/components/hypenapp.js +2 -2
- package/dist/dom/components/hypenapp.js.map +1 -1
- package/dist/dom/components/icon.js +5 -4
- package/dist/dom/components/icon.js.map +1 -1
- package/dist/dom/components/image.js +2 -2
- package/dist/dom/components/image.js.map +1 -1
- package/dist/dom/components/index.d.ts +2 -2
- package/dist/dom/components/index.js +2 -2
- package/dist/dom/components/index.js.map +1 -1
- package/dist/dom/components/input.js +2 -2
- package/dist/dom/components/input.js.map +1 -1
- package/dist/dom/components/link.js +2 -2
- package/dist/dom/components/link.js.map +1 -1
- package/dist/dom/components/list.js +2 -2
- package/dist/dom/components/list.js.map +1 -1
- package/dist/dom/components/paragraph.js +2 -2
- package/dist/dom/components/paragraph.js.map +1 -1
- package/dist/dom/components/progressbar.js +3 -3
- package/dist/dom/components/progressbar.js.map +1 -1
- package/dist/dom/components/route.js +2 -2
- package/dist/dom/components/route.js.map +1 -1
- package/dist/dom/components/router.js +2 -2
- package/dist/dom/components/router.js.map +1 -1
- package/dist/dom/components/row.js +10 -10
- package/dist/dom/components/row.js.map +1 -1
- package/dist/dom/components/select.js +4 -3
- package/dist/dom/components/select.js.map +1 -1
- package/dist/dom/components/slider.js +2 -2
- package/dist/dom/components/slider.js.map +1 -1
- package/dist/dom/components/spacer.js +2 -2
- package/dist/dom/components/spacer.js.map +1 -1
- package/dist/dom/components/spinner.js +4 -4
- package/dist/dom/components/spinner.js.map +1 -1
- package/dist/dom/components/stack.js +10 -10
- package/dist/dom/components/stack.js.map +1 -1
- package/dist/dom/components/switch.js +6 -5
- package/dist/dom/components/switch.js.map +1 -1
- package/dist/dom/components/text.js +2 -2
- package/dist/dom/components/text.js.map +1 -1
- package/dist/dom/components/textarea.js +2 -2
- package/dist/dom/components/textarea.js.map +1 -1
- package/dist/dom/components/video.js +2 -2
- package/dist/dom/components/video.js.map +1 -1
- package/dist/dom/debug.js +8 -6
- package/dist/dom/debug.js.map +1 -1
- package/dist/dom/index.d.ts +1 -0
- package/dist/dom/index.js +1 -0
- package/dist/dom/index.js.map +1 -1
- package/dist/dom/renderer.js +3 -2
- package/dist/dom/renderer.js.map +1 -1
- package/package.json +2 -2
- package/src/canvas/QUICKSTART.md +7 -12
- package/src/canvas/README.md +5 -8
- package/src/canvas/index.ts +8 -0
- package/src/canvas/layout.ts +30 -11
- package/src/canvas/paint.ts +3 -3
- package/src/canvas/renderer.ts +44 -4
- package/src/canvas/utils.ts +37 -3
- package/src/client.ts +88 -0
- package/src/dom/README.md +7 -8
- package/src/dom/applicators/font.ts +29 -16
- package/src/dom/applicators/index.ts +35 -17
- package/src/dom/canvas/index.ts +2 -2
- package/src/dom/components/app.ts +2 -2
- package/src/dom/components/audio.ts +2 -2
- package/src/dom/components/avatar.ts +4 -3
- package/src/dom/components/badge.ts +2 -2
- package/src/dom/components/button.ts +2 -2
- package/src/dom/components/card.ts +2 -2
- package/src/dom/components/center.ts +2 -2
- package/src/dom/components/checkbox.ts +5 -4
- package/src/dom/components/column.ts +2 -2
- package/src/dom/components/container.ts +2 -2
- package/src/dom/components/divider.ts +2 -2
- package/src/dom/components/grid.ts +10 -10
- package/src/dom/components/heading.ts +4 -3
- package/src/dom/components/hypenapp.ts +2 -2
- package/src/dom/components/icon.ts +5 -4
- package/src/dom/components/image.ts +2 -2
- package/src/dom/components/index.ts +3 -3
- package/src/dom/components/input.ts +2 -2
- package/src/dom/components/link.ts +2 -2
- package/src/dom/components/list.ts +2 -2
- package/src/dom/components/paragraph.ts +2 -2
- package/src/dom/components/progressbar.ts +3 -3
- package/src/dom/components/route.ts +2 -2
- package/src/dom/components/router.ts +2 -2
- package/src/dom/components/row.ts +10 -10
- package/src/dom/components/select.ts +4 -3
- package/src/dom/components/slider.ts +2 -2
- package/src/dom/components/spacer.ts +2 -2
- package/src/dom/components/spinner.ts +4 -4
- package/src/dom/components/stack.ts +10 -10
- package/src/dom/components/switch.ts +6 -5
- package/src/dom/components/text.ts +2 -2
- package/src/dom/components/textarea.ts +2 -2
- package/src/dom/components/video.ts +2 -2
- package/src/dom/debug.ts +8 -6
- package/src/dom/index.ts +8 -0
- package/src/dom/renderer.ts +3 -2
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
import type { ComponentHandler } from "./index.js";
|
|
8
8
|
|
|
9
|
-
// Inject global styles for grid children
|
|
10
|
-
|
|
11
|
-
function ensureGridStyles(): void {
|
|
12
|
-
if (
|
|
13
|
-
|
|
9
|
+
// Inject global styles for grid children (per-document)
|
|
10
|
+
const injectedDocs = new WeakSet<Document>();
|
|
11
|
+
function ensureGridStyles(doc: Document): void {
|
|
12
|
+
if (injectedDocs.has(doc)) return;
|
|
13
|
+
injectedDocs.add(doc);
|
|
14
14
|
|
|
15
|
-
const style =
|
|
15
|
+
const style = doc.createElement("style");
|
|
16
16
|
style.id = "hypen-grid-styles";
|
|
17
17
|
style.textContent = `
|
|
18
18
|
/* Grid children stretch to fill cells by default (matches Android behavior) */
|
|
@@ -21,14 +21,14 @@ function ensureGridStyles(): void {
|
|
|
21
21
|
align-self: stretch;
|
|
22
22
|
}
|
|
23
23
|
`;
|
|
24
|
-
|
|
24
|
+
doc.head.appendChild(style);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export const gridHandler: ComponentHandler = {
|
|
28
|
-
create(): HTMLElement {
|
|
29
|
-
ensureGridStyles();
|
|
28
|
+
create(doc: Document): HTMLElement {
|
|
29
|
+
ensureGridStyles(doc);
|
|
30
30
|
|
|
31
|
-
const el =
|
|
31
|
+
const el = doc.createElement("div");
|
|
32
32
|
el.style.display = "grid";
|
|
33
33
|
el.dataset.hypenType = "grid";
|
|
34
34
|
return el;
|
|
@@ -5,17 +5,18 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const headingHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const el =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const el = doc.createElement("h2");
|
|
10
10
|
el.dataset.hypenType = "heading";
|
|
11
11
|
return el;
|
|
12
12
|
},
|
|
13
13
|
|
|
14
14
|
applyProps(el: HTMLElement, props: Record<string, any>): void {
|
|
15
|
+
const doc = el.ownerDocument as Document;
|
|
15
16
|
// Level property (1-6)
|
|
16
17
|
if (props.level !== undefined) {
|
|
17
18
|
const level = Math.max(1, Math.min(6, Number(props.level)));
|
|
18
|
-
const newEl =
|
|
19
|
+
const newEl = doc.createElement(`h${level}`);
|
|
19
20
|
newEl.dataset.hypenType = "heading";
|
|
20
21
|
|
|
21
22
|
// Copy content and attributes
|
|
@@ -29,8 +29,8 @@ const activeInstances = new WeakMap<
|
|
|
29
29
|
>();
|
|
30
30
|
|
|
31
31
|
export const hypenAppHandler: ComponentHandler = {
|
|
32
|
-
create(): HTMLElement {
|
|
33
|
-
const el =
|
|
32
|
+
create(doc: Document): HTMLElement {
|
|
33
|
+
const el = doc.createElement("div");
|
|
34
34
|
el.dataset.hypenType = "hypenapp";
|
|
35
35
|
el.style.display = "contents"; // Don't affect layout
|
|
36
36
|
return el;
|
|
@@ -11,8 +11,8 @@ import type { ComponentHandler } from "./index.js";
|
|
|
11
11
|
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
12
12
|
|
|
13
13
|
export const iconHandler: ComponentHandler = {
|
|
14
|
-
create(): HTMLElement {
|
|
15
|
-
const el =
|
|
14
|
+
create(doc: Document): HTMLElement {
|
|
15
|
+
const el = doc.createElement("span");
|
|
16
16
|
el.style.display = "inline-flex";
|
|
17
17
|
el.style.alignItems = "center";
|
|
18
18
|
el.style.justifyContent = "center";
|
|
@@ -22,6 +22,7 @@ export const iconHandler: ComponentHandler = {
|
|
|
22
22
|
},
|
|
23
23
|
|
|
24
24
|
applyProps(el: HTMLElement, props: Record<string, any>): void {
|
|
25
|
+
const doc = el.ownerDocument as Document;
|
|
25
26
|
// Server-resolved icon data (from engine's ResourceRegistry)
|
|
26
27
|
const paths: Array<{
|
|
27
28
|
d: string;
|
|
@@ -39,7 +40,7 @@ export const iconHandler: ComponentHandler = {
|
|
|
39
40
|
|
|
40
41
|
if (paths && Array.isArray(paths)) {
|
|
41
42
|
// Render from pre-resolved SVG path data
|
|
42
|
-
const svg =
|
|
43
|
+
const svg = doc.createElementNS(SVG_NS, "svg");
|
|
43
44
|
svg.setAttribute("xmlns", SVG_NS);
|
|
44
45
|
svg.setAttribute("width", String(size));
|
|
45
46
|
svg.setAttribute("height", String(size));
|
|
@@ -48,7 +49,7 @@ export const iconHandler: ComponentHandler = {
|
|
|
48
49
|
svg.style.display = "block";
|
|
49
50
|
|
|
50
51
|
for (const pathData of paths) {
|
|
51
|
-
const path =
|
|
52
|
+
const path = doc.createElementNS(SVG_NS, "path");
|
|
52
53
|
path.setAttribute("d", pathData.d);
|
|
53
54
|
path.setAttribute("fill", pathData.fill || "none");
|
|
54
55
|
path.setAttribute(
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const imageHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const el =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const el = doc.createElement("img");
|
|
10
10
|
el.dataset.hypenType = "image";
|
|
11
11
|
return el as any as HTMLElement;
|
|
12
12
|
},
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export interface ComponentHandler {
|
|
8
|
-
create(): HTMLElement;
|
|
8
|
+
create(doc: Document): HTMLElement;
|
|
9
9
|
applyProps?(element: HTMLElement, props: Record<string, any>): void;
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -34,12 +34,12 @@ export class ComponentRegistry {
|
|
|
34
34
|
/**
|
|
35
35
|
* Create element for a component type
|
|
36
36
|
*/
|
|
37
|
-
createElement(type: string, props: Record<string, any> = {}): HTMLElement | null {
|
|
37
|
+
createElement(type: string, props: Record<string, any> = {}, doc: Document = document): HTMLElement | null {
|
|
38
38
|
if (!type) return null;
|
|
39
39
|
const handler = this.get(type);
|
|
40
40
|
if (!handler) return null;
|
|
41
41
|
|
|
42
|
-
const element = handler.create();
|
|
42
|
+
const element = handler.create(doc);
|
|
43
43
|
if (handler.applyProps) {
|
|
44
44
|
handler.applyProps(element, props);
|
|
45
45
|
}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const inputHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const el =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const el = doc.createElement("input");
|
|
10
10
|
el.dataset.hypenType = "input";
|
|
11
11
|
// Reset the default user-agent chrome so `.tw("bg-gray-100 rounded-lg")`
|
|
12
12
|
// on an Input reads the same as any other styled element. Without
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const linkHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const el =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const el = doc.createElement("a");
|
|
10
10
|
el.dataset.hypenType = "link";
|
|
11
11
|
return el;
|
|
12
12
|
},
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const listHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const el =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const el = doc.createElement("div");
|
|
10
10
|
el.style.display = "flex";
|
|
11
11
|
el.style.flexDirection = "column"; // Default to vertical (like Android)
|
|
12
12
|
// Default to flex-start to match Android/iOS behavior
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const paragraphHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const el =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const el = doc.createElement("p");
|
|
10
10
|
el.dataset.hypenType = "paragraph";
|
|
11
11
|
return el;
|
|
12
12
|
},
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const progressBarHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const wrapper =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const wrapper = doc.createElement("div");
|
|
10
10
|
wrapper.dataset.hypenType = "progressbar";
|
|
11
11
|
wrapper.style.width = "100%";
|
|
12
12
|
wrapper.style.height = "8px";
|
|
@@ -14,7 +14,7 @@ export const progressBarHandler: ComponentHandler = {
|
|
|
14
14
|
wrapper.style.borderRadius = "4px";
|
|
15
15
|
wrapper.style.overflow = "hidden";
|
|
16
16
|
|
|
17
|
-
const bar =
|
|
17
|
+
const bar = doc.createElement("div");
|
|
18
18
|
bar.dataset.hypenBar = "true";
|
|
19
19
|
bar.style.height = "100%";
|
|
20
20
|
bar.style.backgroundColor = "#2196F3";
|
|
@@ -9,8 +9,8 @@ import { frameworkLoggers } from "@hypen-space/core/logger";
|
|
|
9
9
|
const log = frameworkLoggers.router;
|
|
10
10
|
|
|
11
11
|
export const routeHandler: ComponentHandler = {
|
|
12
|
-
create(): HTMLElement {
|
|
13
|
-
const el =
|
|
12
|
+
create(doc: Document): HTMLElement {
|
|
13
|
+
const el = doc.createElement("div");
|
|
14
14
|
el.style.display = "flex";
|
|
15
15
|
el.style.flexDirection = "column";
|
|
16
16
|
el.style.width = "100%";
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const routerHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const el =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const el = doc.createElement("div");
|
|
10
10
|
el.style.display = "flex";
|
|
11
11
|
el.style.flexDirection = "column";
|
|
12
12
|
el.style.width = "100%";
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
import type { ComponentHandler } from "./index.js";
|
|
8
8
|
|
|
9
|
-
// Inject global styles for Row with flex/weighted children
|
|
10
|
-
|
|
11
|
-
function ensureRowStyles(): void {
|
|
12
|
-
if (
|
|
13
|
-
|
|
9
|
+
// Inject global styles for Row with flex/weighted children (per-document)
|
|
10
|
+
const injectedDocs = new WeakSet<Document>();
|
|
11
|
+
function ensureRowStyles(doc: Document): void {
|
|
12
|
+
if (injectedDocs.has(doc)) return;
|
|
13
|
+
injectedDocs.add(doc);
|
|
14
14
|
|
|
15
|
-
const style =
|
|
15
|
+
const style = doc.createElement("style");
|
|
16
16
|
style.id = "hypen-row-styles";
|
|
17
17
|
style.textContent = `
|
|
18
18
|
/* Row expands to fill width when it has children with flex/weight */
|
|
@@ -21,14 +21,14 @@ function ensureRowStyles(): void {
|
|
|
21
21
|
width: 100%;
|
|
22
22
|
}
|
|
23
23
|
`;
|
|
24
|
-
|
|
24
|
+
doc.head.appendChild(style);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export const rowHandler: ComponentHandler = {
|
|
28
|
-
create(): HTMLElement {
|
|
29
|
-
ensureRowStyles();
|
|
28
|
+
create(doc: Document): HTMLElement {
|
|
29
|
+
ensureRowStyles(doc);
|
|
30
30
|
|
|
31
|
-
const el =
|
|
31
|
+
const el = doc.createElement("div");
|
|
32
32
|
el.style.display = "flex";
|
|
33
33
|
el.style.flexDirection = "row";
|
|
34
34
|
// Wrap to content by default (match iOS/Android behavior)
|
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const selectHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const el =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const el = doc.createElement("select");
|
|
10
10
|
el.dataset.hypenType = "select";
|
|
11
11
|
return el as any as HTMLElement;
|
|
12
12
|
},
|
|
13
13
|
|
|
14
14
|
applyProps(el: HTMLElement, props: Record<string, any>): void {
|
|
15
|
+
const doc = el.ownerDocument as Document;
|
|
15
16
|
const select = el as HTMLSelectElement;
|
|
16
17
|
|
|
17
18
|
// Value property
|
|
@@ -36,7 +37,7 @@ export const selectHandler: ComponentHandler = {
|
|
|
36
37
|
|
|
37
38
|
// Add new options
|
|
38
39
|
props.options.forEach((opt: any) => {
|
|
39
|
-
const option =
|
|
40
|
+
const option = doc.createElement("option");
|
|
40
41
|
|
|
41
42
|
if (typeof opt === "string") {
|
|
42
43
|
option.value = opt;
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const sliderHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const el =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const el = doc.createElement("input");
|
|
10
10
|
el.type = "range";
|
|
11
11
|
el.dataset.hypenType = "slider";
|
|
12
12
|
return el as any as HTMLElement;
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const spacerHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const el =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const el = doc.createElement("div");
|
|
10
10
|
el.style.flex = "1";
|
|
11
11
|
el.dataset.hypenType = "spacer";
|
|
12
12
|
return el;
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const spinnerHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const wrapper =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const wrapper = doc.createElement("div");
|
|
10
10
|
wrapper.dataset.hypenType = "spinner";
|
|
11
11
|
wrapper.style.display = "inline-block";
|
|
12
12
|
|
|
13
|
-
const spinner =
|
|
13
|
+
const spinner = doc.createElement("div");
|
|
14
14
|
spinner.style.width = "40px";
|
|
15
15
|
spinner.style.height = "40px";
|
|
16
16
|
spinner.style.border = "4px solid #f3f3f3";
|
|
@@ -19,7 +19,7 @@ export const spinnerHandler: ComponentHandler = {
|
|
|
19
19
|
spinner.style.animation = "spin 1s linear infinite";
|
|
20
20
|
|
|
21
21
|
// Add keyframe animation
|
|
22
|
-
const style =
|
|
22
|
+
const style = doc.createElement("style");
|
|
23
23
|
style.textContent = `
|
|
24
24
|
@keyframes spin {
|
|
25
25
|
0% { transform: rotate(0deg); }
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
|
-
// Inject global styles once
|
|
8
|
-
|
|
9
|
-
function ensureStackStyles(): void {
|
|
10
|
-
if (
|
|
11
|
-
|
|
7
|
+
// Inject global styles once per document
|
|
8
|
+
const injectedDocs = new WeakSet<Document>();
|
|
9
|
+
function ensureStackStyles(doc: Document): void {
|
|
10
|
+
if (injectedDocs.has(doc)) return;
|
|
11
|
+
injectedDocs.add(doc);
|
|
12
12
|
|
|
13
|
-
const style =
|
|
13
|
+
const style = doc.createElement("style");
|
|
14
14
|
style.id = "hypen-stack-styles";
|
|
15
15
|
style.textContent = `
|
|
16
16
|
[data-hypen-type="stack"] {
|
|
@@ -29,14 +29,14 @@ function ensureStackStyles(): void {
|
|
|
29
29
|
/* Don't set justify-self/align-self here - let parent's justify-items/align-items control */
|
|
30
30
|
}
|
|
31
31
|
`;
|
|
32
|
-
|
|
32
|
+
doc.head.appendChild(style);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export const stackHandler: ComponentHandler = {
|
|
36
|
-
create(): HTMLElement {
|
|
37
|
-
ensureStackStyles();
|
|
36
|
+
create(doc: Document): HTMLElement {
|
|
37
|
+
ensureStackStyles(doc);
|
|
38
38
|
|
|
39
|
-
const el =
|
|
39
|
+
const el = doc.createElement("div");
|
|
40
40
|
el.dataset.hypenType = "stack";
|
|
41
41
|
|
|
42
42
|
return el;
|
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const switchHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const wrapper =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const wrapper = doc.createElement("label");
|
|
10
10
|
wrapper.dataset.hypenType = "switch";
|
|
11
11
|
wrapper.style.display = "inline-flex";
|
|
12
12
|
wrapper.style.alignItems = "center";
|
|
13
13
|
wrapper.style.gap = "8px";
|
|
14
14
|
wrapper.style.cursor = "pointer";
|
|
15
15
|
|
|
16
|
-
const input =
|
|
16
|
+
const input = doc.createElement("input");
|
|
17
17
|
input.type = "checkbox";
|
|
18
18
|
input.dataset.hypenSwitch = "true";
|
|
19
19
|
|
|
@@ -28,7 +28,7 @@ export const switchHandler: ComponentHandler = {
|
|
|
28
28
|
input.style.transition = "background-color 0.2s";
|
|
29
29
|
|
|
30
30
|
// Add pseudo-element styling via CSS
|
|
31
|
-
const style =
|
|
31
|
+
const style = doc.createElement("style");
|
|
32
32
|
style.textContent = `
|
|
33
33
|
input[data-hypen-switch="true"]::before {
|
|
34
34
|
content: "";
|
|
@@ -55,6 +55,7 @@ export const switchHandler: ComponentHandler = {
|
|
|
55
55
|
},
|
|
56
56
|
|
|
57
57
|
applyProps(el: HTMLElement, props: Record<string, any>): void {
|
|
58
|
+
const doc = el.ownerDocument as Document;
|
|
58
59
|
const input = el.querySelector('input[type="checkbox"]') as HTMLInputElement;
|
|
59
60
|
if (!input) return;
|
|
60
61
|
|
|
@@ -78,7 +79,7 @@ export const switchHandler: ComponentHandler = {
|
|
|
78
79
|
textNodes.forEach(node => node.remove());
|
|
79
80
|
|
|
80
81
|
// Add new label text
|
|
81
|
-
el.appendChild(
|
|
82
|
+
el.appendChild(doc.createTextNode(String(label)));
|
|
82
83
|
}
|
|
83
84
|
},
|
|
84
85
|
};
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const textHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const el =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const el = doc.createElement("span");
|
|
10
10
|
// Use inline-block for proper flex child behavior
|
|
11
11
|
el.style.display = "inline-block";
|
|
12
12
|
// Tight line-height to match iOS/Android
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const textareaHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const el =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const el = doc.createElement("textarea");
|
|
10
10
|
el.dataset.hypenType = "textarea";
|
|
11
11
|
// Same user-agent reset as Input — otherwise `.tw(...)` styling draws
|
|
12
12
|
// over a native dark border and inset shadow.
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const videoHandler: ComponentHandler = {
|
|
8
|
-
create(): HTMLElement {
|
|
9
|
-
const el =
|
|
8
|
+
create(doc: Document): HTMLElement {
|
|
9
|
+
const el = doc.createElement("video");
|
|
10
10
|
el.dataset.hypenType = "video";
|
|
11
11
|
return el as any as HTMLElement;
|
|
12
12
|
},
|
package/src/dom/debug.ts
CHANGED
|
@@ -76,6 +76,8 @@ export class RerenderTracker {
|
|
|
76
76
|
* Create or update the heatmap overlay for an element
|
|
77
77
|
*/
|
|
78
78
|
private updateHeatmap(id: string, element: HTMLElement, renderCount: number, patchType: string): void {
|
|
79
|
+
const doc = element.ownerDocument as Document;
|
|
80
|
+
const win = doc.defaultView ?? window;
|
|
79
81
|
// Calculate opacity based on render count (increment by heatmapIncrement% each time)
|
|
80
82
|
const opacity = Math.min(
|
|
81
83
|
(renderCount * this.config.heatmapIncrement) / 100,
|
|
@@ -85,7 +87,7 @@ export class RerenderTracker {
|
|
|
85
87
|
log.debug(`Updating heatmap for ${id}, count: ${renderCount}, opacity: ${opacity}`);
|
|
86
88
|
|
|
87
89
|
// For inline elements or text, use a simpler approach: background color + outline
|
|
88
|
-
const isInline =
|
|
90
|
+
const isInline = win.getComputedStyle(element).display.includes('inline');
|
|
89
91
|
|
|
90
92
|
if (isInline || element.tagName === 'SPAN') {
|
|
91
93
|
// Store original styles if not already stored
|
|
@@ -105,8 +107,8 @@ export class RerenderTracker {
|
|
|
105
107
|
element.setAttribute('data-hypen-renders', `${renderCount}× ${patchType}`);
|
|
106
108
|
|
|
107
109
|
// Add CSS for the badge if not already added
|
|
108
|
-
if (!
|
|
109
|
-
const style =
|
|
110
|
+
if (!doc.getElementById('hypen-debug-styles')) {
|
|
111
|
+
const style = doc.createElement('style');
|
|
110
112
|
style.id = 'hypen-debug-styles';
|
|
111
113
|
style.textContent = `
|
|
112
114
|
[data-hypen-renders]::before {
|
|
@@ -127,7 +129,7 @@ export class RerenderTracker {
|
|
|
127
129
|
text-shadow: none;
|
|
128
130
|
}
|
|
129
131
|
`;
|
|
130
|
-
|
|
132
|
+
doc.head.appendChild(style);
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
// Store in overlays map for cleanup
|
|
@@ -148,7 +150,7 @@ export class RerenderTracker {
|
|
|
148
150
|
let overlay = this.overlays.get(id);
|
|
149
151
|
|
|
150
152
|
if (!overlay) {
|
|
151
|
-
overlay =
|
|
153
|
+
overlay = doc.createElement("div");
|
|
152
154
|
overlay.className = "hypen-debug-overlay";
|
|
153
155
|
overlay.style.cssText = `
|
|
154
156
|
position: absolute;
|
|
@@ -171,7 +173,7 @@ export class RerenderTracker {
|
|
|
171
173
|
visibility: visible !important;
|
|
172
174
|
`;
|
|
173
175
|
|
|
174
|
-
const currentPosition =
|
|
176
|
+
const currentPosition = win.getComputedStyle(element).position;
|
|
175
177
|
if (currentPosition === 'static') {
|
|
176
178
|
element.style.position = 'relative';
|
|
177
179
|
}
|
package/src/dom/index.ts
CHANGED
|
@@ -9,3 +9,11 @@ export { ComponentRegistry } from "./components/index.js";
|
|
|
9
9
|
export { ApplicatorRegistry } from "./applicators/index.js";
|
|
10
10
|
export { EventManager } from "./events.js";
|
|
11
11
|
export { RerenderTracker, type DebugConfig, defaultDebugConfig } from "./debug.js";
|
|
12
|
+
export {
|
|
13
|
+
createDomClient as createHypenClient,
|
|
14
|
+
createDomClient,
|
|
15
|
+
type DomClient as HypenClient,
|
|
16
|
+
type DomClient,
|
|
17
|
+
type DomClientOptions as HypenClientOptions,
|
|
18
|
+
type DomClientOptions,
|
|
19
|
+
} from "../client.js";
|
package/src/dom/renderer.ts
CHANGED
|
@@ -285,13 +285,14 @@ export class DOMRenderer {
|
|
|
285
285
|
private onCreate(id: string, elementType: string, props: Record<string, any> | Map<string, any>): void {
|
|
286
286
|
const propsObj = props instanceof Map ? Object.fromEntries(props) : props;
|
|
287
287
|
|
|
288
|
-
|
|
288
|
+
const doc = this.container.ownerDocument as Document;
|
|
289
|
+
let element = this.components.createElement(elementType, propsObj, doc);
|
|
289
290
|
|
|
290
291
|
if (!element) {
|
|
291
292
|
// For unknown component types, create a transparent container (div).
|
|
292
293
|
// This handles module wrappers (like "App") and custom components
|
|
293
294
|
// that aren't registered but should act as layout containers.
|
|
294
|
-
const fallback =
|
|
295
|
+
const fallback = doc.createElement("div");
|
|
295
296
|
fallback.dataset.hypenType = elementType.toLowerCase();
|
|
296
297
|
fallback.style.display = "contents"; // Make container transparent in layout
|
|
297
298
|
element = fallback;
|