@hypen-space/web 0.4.980 → 0.5.3
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 +20 -13
- package/dist/canvas/accessibility.d.ts +118 -11
- package/dist/canvas/accessibility.js +470 -108
- package/dist/canvas/accessibility.js.map +1 -1
- package/dist/canvas/dispatch.d.ts +24 -0
- package/dist/canvas/dispatch.js +62 -0
- package/dist/canvas/dispatch.js.map +1 -0
- package/dist/canvas/editing.d.ts +178 -0
- package/dist/canvas/editing.js +590 -0
- package/dist/canvas/editing.js.map +1 -0
- package/dist/canvas/events.d.ts +28 -23
- package/dist/canvas/events.js +90 -103
- package/dist/canvas/events.js.map +1 -1
- package/dist/canvas/focus.d.ts +78 -0
- package/dist/canvas/focus.js +182 -0
- package/dist/canvas/focus.js.map +1 -0
- package/dist/canvas/index.d.ts +3 -2
- package/dist/canvas/index.js +3 -2
- package/dist/canvas/index.js.map +1 -1
- package/dist/canvas/layout.js +68 -98
- package/dist/canvas/layout.js.map +1 -1
- package/dist/canvas/paint.d.ts +16 -3
- package/dist/canvas/paint.js +449 -110
- package/dist/canvas/paint.js.map +1 -1
- package/dist/canvas/renderer.d.ts +28 -4
- package/dist/canvas/renderer.js +201 -81
- package/dist/canvas/renderer.js.map +1 -1
- package/dist/canvas/selection.js +31 -177
- package/dist/canvas/selection.js.map +1 -1
- package/dist/canvas/text-geometry.d.ts +72 -0
- package/dist/canvas/text-geometry.js +244 -0
- package/dist/canvas/text-geometry.js.map +1 -0
- package/dist/canvas/text.js +44 -18
- package/dist/canvas/text.js.map +1 -1
- package/dist/canvas/types.d.ts +10 -1
- package/dist/canvas/utils.d.ts +0 -23
- package/dist/canvas/utils.js +3 -67
- package/dist/canvas/utils.js.map +1 -1
- package/dist/canvas/variants.d.ts +52 -0
- package/dist/canvas/variants.js +156 -0
- package/dist/canvas/variants.js.map +1 -0
- package/dist/dom/a11y-styles.d.ts +20 -0
- package/dist/dom/a11y-styles.js +61 -0
- package/dist/dom/a11y-styles.js.map +1 -0
- package/dist/dom/applicators/aria.d.ts +19 -0
- package/dist/dom/applicators/aria.js +36 -0
- package/dist/dom/applicators/aria.js.map +1 -0
- package/dist/dom/applicators/events.d.ts +7 -0
- package/dist/dom/applicators/events.js +36 -8
- package/dist/dom/applicators/events.js.map +1 -1
- package/dist/dom/applicators/font.d.ts +11 -6
- package/dist/dom/applicators/font.js +16 -27
- package/dist/dom/applicators/font.js.map +1 -1
- package/dist/dom/applicators/index.js +51 -45
- 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 +3 -4
- package/dist/dom/components/avatar.js.map +1 -1
- package/dist/dom/components/badge.js +3 -4
- 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 +4 -5
- 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 -6
- package/dist/dom/components/heading.js.map +1 -1
- package/dist/dom/components/hypenapp.d.ts +25 -1
- package/dist/dom/components/hypenapp.js +215 -247
- package/dist/dom/components/hypenapp.js.map +1 -1
- package/dist/dom/components/icon.js +4 -5
- 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 +9 -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 +3 -4
- 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 +3 -4
- 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 +27 -13
- 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 +5 -6
- package/dist/dom/components/switch.js.map +1 -1
- package/dist/dom/components/tabs.d.ts +27 -0
- package/dist/dom/components/tabs.js +69 -0
- package/dist/dom/components/tabs.js.map +1 -0
- package/dist/dom/components/text.js +4 -7
- 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/components/visuallyhidden.d.ts +9 -0
- package/dist/dom/components/visuallyhidden.js +26 -0
- package/dist/dom/components/visuallyhidden.js.map +1 -0
- package/dist/dom/debug.js +6 -8
- package/dist/dom/debug.js.map +1 -1
- package/dist/dom/index.d.ts +0 -1
- package/dist/dom/index.js +0 -1
- package/dist/dom/index.js.map +1 -1
- package/dist/dom/operability.d.ts +100 -0
- package/dist/dom/operability.js +298 -0
- package/dist/dom/operability.js.map +1 -0
- package/dist/dom/renderer.d.ts +98 -5
- package/dist/dom/renderer.js +400 -66
- package/dist/dom/renderer.js.map +1 -1
- package/dist/dom/route-focus.d.ts +42 -0
- package/dist/dom/route-focus.js +88 -0
- package/dist/dom/route-focus.js.map +1 -0
- package/dist/dom/semantics.d.ts +35 -0
- package/dist/dom/semantics.js +184 -0
- package/dist/dom/semantics.js.map +1 -0
- package/dist/variants.d.ts +90 -0
- package/dist/variants.js +206 -0
- package/dist/variants.js.map +1 -0
- package/package.json +2 -2
- package/src/canvas/QUICKSTART.md +16 -11
- package/src/canvas/README.md +63 -34
- package/src/canvas/accessibility.ts +507 -115
- package/src/canvas/dispatch.ts +78 -0
- package/src/canvas/editing.ts +697 -0
- package/src/canvas/events.ts +104 -115
- package/src/canvas/focus.ts +216 -0
- package/src/canvas/index.ts +8 -9
- package/src/canvas/layout.ts +68 -103
- package/src/canvas/paint.ts +509 -109
- package/src/canvas/renderer.ts +239 -99
- package/src/canvas/selection.ts +52 -210
- package/src/canvas/text-geometry.ts +311 -0
- package/src/canvas/text.ts +45 -18
- package/src/canvas/types.ts +28 -1
- package/src/canvas/utils.ts +3 -69
- package/src/canvas/variants.ts +172 -0
- package/src/dom/README.md +8 -7
- package/src/dom/a11y-styles.ts +65 -0
- package/src/dom/applicators/aria.ts +41 -0
- package/src/dom/applicators/events.ts +39 -7
- package/src/dom/applicators/font.ts +16 -29
- package/src/dom/applicators/index.ts +61 -46
- 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 +3 -4
- package/src/dom/components/badge.ts +3 -4
- 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 +4 -5
- 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 -6
- package/src/dom/components/hypenapp.ts +246 -274
- package/src/dom/components/icon.ts +4 -5
- package/src/dom/components/image.ts +2 -2
- package/src/dom/components/index.ts +10 -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 +3 -4
- 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 +3 -4
- package/src/dom/components/slider.ts +2 -2
- package/src/dom/components/spacer.ts +2 -2
- package/src/dom/components/spinner.ts +34 -16
- package/src/dom/components/stack.ts +10 -10
- package/src/dom/components/switch.ts +5 -6
- package/src/dom/components/tabs.ts +76 -0
- package/src/dom/components/text.ts +4 -7
- package/src/dom/components/textarea.ts +2 -2
- package/src/dom/components/video.ts +2 -2
- package/src/dom/components/visuallyhidden.ts +30 -0
- package/src/dom/debug.ts +6 -8
- package/src/dom/index.ts +0 -8
- package/src/dom/operability.ts +312 -0
- package/src/dom/renderer.ts +457 -66
- package/src/dom/route-focus.ts +98 -0
- package/src/dom/semantics.ts +199 -0
- package/src/variants.ts +249 -0
- package/dist/canvas/input.d.ts +0 -76
- package/dist/canvas/input.js +0 -207
- package/dist/canvas/input.js.map +0 -1
- package/dist/client.d.ts +0 -36
- package/dist/client.js +0 -47
- package/dist/client.js.map +0 -1
- package/src/canvas/input.ts +0 -251
- package/src/client.ts +0 -88
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const inputHandler: ComponentHandler = {
|
|
8
|
-
create(
|
|
9
|
-
const el =
|
|
8
|
+
create(): HTMLElement {
|
|
9
|
+
const el = document.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(
|
|
9
|
-
const el =
|
|
8
|
+
create(): HTMLElement {
|
|
9
|
+
const el = document.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(
|
|
9
|
-
const el =
|
|
8
|
+
create(): HTMLElement {
|
|
9
|
+
const el = document.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,16 +5,15 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const paragraphHandler: ComponentHandler = {
|
|
8
|
-
create(
|
|
9
|
-
const el =
|
|
8
|
+
create(): HTMLElement {
|
|
9
|
+
const el = document.createElement("p");
|
|
10
10
|
el.dataset.hypenType = "paragraph";
|
|
11
11
|
return el;
|
|
12
12
|
},
|
|
13
13
|
|
|
14
14
|
applyProps(el: HTMLElement, props: Record<string, any>): void {
|
|
15
15
|
// Text content
|
|
16
|
-
|
|
17
|
-
const text = props["0"] ?? props.text;
|
|
16
|
+
const text = props["0"] || props.text;
|
|
18
17
|
if (text !== undefined) {
|
|
19
18
|
el.textContent = String(text);
|
|
20
19
|
}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const progressBarHandler: ComponentHandler = {
|
|
8
|
-
create(
|
|
9
|
-
const wrapper =
|
|
8
|
+
create(): HTMLElement {
|
|
9
|
+
const wrapper = document.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 = document.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(
|
|
13
|
-
const el =
|
|
12
|
+
create(): HTMLElement {
|
|
13
|
+
const el = document.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(
|
|
9
|
-
const el =
|
|
8
|
+
create(): HTMLElement {
|
|
9
|
+
const el = document.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(
|
|
12
|
-
if (
|
|
13
|
-
|
|
9
|
+
// Inject global styles for Row with flex/weighted children
|
|
10
|
+
let rowStylesInjected = false;
|
|
11
|
+
function ensureRowStyles(): void {
|
|
12
|
+
if (rowStylesInjected) return;
|
|
13
|
+
rowStylesInjected = true;
|
|
14
14
|
|
|
15
|
-
const style =
|
|
15
|
+
const style = document.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(doc: Document): void {
|
|
|
21
21
|
width: 100%;
|
|
22
22
|
}
|
|
23
23
|
`;
|
|
24
|
-
|
|
24
|
+
document.head.appendChild(style);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export const rowHandler: ComponentHandler = {
|
|
28
|
-
create(
|
|
29
|
-
ensureRowStyles(
|
|
28
|
+
create(): HTMLElement {
|
|
29
|
+
ensureRowStyles();
|
|
30
30
|
|
|
31
|
-
const el =
|
|
31
|
+
const el = document.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,14 +5,13 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const selectHandler: ComponentHandler = {
|
|
8
|
-
create(
|
|
9
|
-
const el =
|
|
8
|
+
create(): HTMLElement {
|
|
9
|
+
const el = document.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;
|
|
16
15
|
const select = el as HTMLSelectElement;
|
|
17
16
|
|
|
18
17
|
// Value property
|
|
@@ -37,7 +36,7 @@ export const selectHandler: ComponentHandler = {
|
|
|
37
36
|
|
|
38
37
|
// Add new options
|
|
39
38
|
props.options.forEach((opt: any) => {
|
|
40
|
-
const option =
|
|
39
|
+
const option = document.createElement("option");
|
|
41
40
|
|
|
42
41
|
if (typeof opt === "string") {
|
|
43
42
|
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(
|
|
9
|
-
const el =
|
|
8
|
+
create(): HTMLElement {
|
|
9
|
+
const el = document.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(
|
|
9
|
-
const el =
|
|
8
|
+
create(): HTMLElement {
|
|
9
|
+
const el = document.createElement("div");
|
|
10
10
|
el.style.flex = "1";
|
|
11
11
|
el.dataset.hypenType = "spacer";
|
|
12
12
|
return el;
|
|
@@ -4,32 +4,50 @@
|
|
|
4
4
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
|
+
const STYLE_ID = "hypen-spinner-styles";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Inject the shared spin keyframes once (same singleton pattern as
|
|
11
|
+
* ensureA11yStyles). Safe to call repeatedly and in non-DOM environments.
|
|
12
|
+
*/
|
|
13
|
+
function ensureSpinnerStyles(): void {
|
|
14
|
+
if (typeof document === "undefined" || !document.head) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (typeof document.getElementById === "function" && document.getElementById(STYLE_ID)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const style = document.createElement("style");
|
|
23
|
+
style.id = STYLE_ID;
|
|
24
|
+
style.textContent = `
|
|
25
|
+
@keyframes hypen-spin {
|
|
26
|
+
0% { transform: rotate(0deg); }
|
|
27
|
+
100% { transform: rotate(360deg); }
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
document.head.appendChild(style);
|
|
31
|
+
}
|
|
32
|
+
|
|
7
33
|
export const spinnerHandler: ComponentHandler = {
|
|
8
|
-
create(
|
|
9
|
-
|
|
34
|
+
create(): HTMLElement {
|
|
35
|
+
ensureSpinnerStyles();
|
|
36
|
+
|
|
37
|
+
const wrapper = document.createElement("div");
|
|
10
38
|
wrapper.dataset.hypenType = "spinner";
|
|
11
39
|
wrapper.style.display = "inline-block";
|
|
12
40
|
|
|
13
|
-
const spinner =
|
|
41
|
+
const spinner = document.createElement("div");
|
|
14
42
|
spinner.style.width = "40px";
|
|
15
43
|
spinner.style.height = "40px";
|
|
16
44
|
spinner.style.border = "4px solid #f3f3f3";
|
|
17
45
|
spinner.style.borderTop = "4px solid #3498db";
|
|
18
46
|
spinner.style.borderRadius = "50%";
|
|
19
|
-
spinner.style.animation = "spin 1s linear infinite";
|
|
20
|
-
|
|
21
|
-
// Add keyframe animation
|
|
22
|
-
const style = doc.createElement("style");
|
|
23
|
-
style.textContent = `
|
|
24
|
-
@keyframes spin {
|
|
25
|
-
0% { transform: rotate(0deg); }
|
|
26
|
-
100% { transform: rotate(360deg); }
|
|
27
|
-
}
|
|
28
|
-
`;
|
|
29
|
-
|
|
30
|
-
wrapper.appendChild(style);
|
|
47
|
+
spinner.style.animation = "hypen-spin 1s linear infinite";
|
|
48
|
+
|
|
31
49
|
wrapper.appendChild(spinner);
|
|
32
|
-
|
|
50
|
+
|
|
33
51
|
return wrapper;
|
|
34
52
|
},
|
|
35
53
|
|
|
@@ -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(
|
|
10
|
-
if (
|
|
11
|
-
|
|
7
|
+
// Inject global styles once
|
|
8
|
+
let stackStylesInjected = false;
|
|
9
|
+
function ensureStackStyles(): void {
|
|
10
|
+
if (stackStylesInjected) return;
|
|
11
|
+
stackStylesInjected = true;
|
|
12
12
|
|
|
13
|
-
const style =
|
|
13
|
+
const style = document.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(doc: Document): 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
|
+
document.head.appendChild(style);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export const stackHandler: ComponentHandler = {
|
|
36
|
-
create(
|
|
37
|
-
ensureStackStyles(
|
|
36
|
+
create(): HTMLElement {
|
|
37
|
+
ensureStackStyles();
|
|
38
38
|
|
|
39
|
-
const el =
|
|
39
|
+
const el = document.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(
|
|
9
|
-
const wrapper =
|
|
8
|
+
create(): HTMLElement {
|
|
9
|
+
const wrapper = document.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 = document.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 = document.createElement("style");
|
|
32
32
|
style.textContent = `
|
|
33
33
|
input[data-hypen-switch="true"]::before {
|
|
34
34
|
content: "";
|
|
@@ -55,7 +55,6 @@ 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;
|
|
59
58
|
const input = el.querySelector('input[type="checkbox"]') as HTMLInputElement;
|
|
60
59
|
if (!input) return;
|
|
61
60
|
|
|
@@ -79,7 +78,7 @@ export const switchHandler: ComponentHandler = {
|
|
|
79
78
|
textNodes.forEach(node => node.remove());
|
|
80
79
|
|
|
81
80
|
// Add new label text
|
|
82
|
-
el.appendChild(
|
|
81
|
+
el.appendChild(document.createTextNode(String(label)));
|
|
83
82
|
}
|
|
84
83
|
},
|
|
85
84
|
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composite-widget hosts: Tabs / Tab / TabPanel / Option.
|
|
3
|
+
*
|
|
4
|
+
* These exist so the composite element types render *focusable, styleable*
|
|
5
|
+
* hosts instead of falling through to the unknown-component fallback (a
|
|
6
|
+
* `display: contents` div, which cannot reliably hold keyboard focus). The
|
|
7
|
+
* accessibility roles come from the engine-derived Semantics block
|
|
8
|
+
* (tablist/tab/tabpanel/option), not from these handlers; keyboard roving is
|
|
9
|
+
* wired by the renderer when it sees `role="tablist"` (see operability.ts).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ComponentHandler } from "./index.js";
|
|
13
|
+
|
|
14
|
+
/** Tabs → a plain row container; role="tablist" arrives via semantics. */
|
|
15
|
+
export const tabsHandler: ComponentHandler = {
|
|
16
|
+
create(): HTMLElement {
|
|
17
|
+
const el = document.createElement("div");
|
|
18
|
+
el.dataset.hypenType = "tabs";
|
|
19
|
+
el.style.display = "flex";
|
|
20
|
+
el.style.flexDirection = "row";
|
|
21
|
+
return el;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Tab → a native `<button>` so focus and Enter/Space activation come free;
|
|
27
|
+
* the engine's `role="tab"` overrides the implicit button role (see the
|
|
28
|
+
* IMPLICIT_ROLE override logic in semantics.ts).
|
|
29
|
+
*/
|
|
30
|
+
export const tabHandler: ComponentHandler = {
|
|
31
|
+
create(): HTMLElement {
|
|
32
|
+
const el = document.createElement("button");
|
|
33
|
+
el.setAttribute("type", "button");
|
|
34
|
+
el.dataset.hypenType = "tab";
|
|
35
|
+
return el;
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
applyProps(el: HTMLElement, props: Record<string, any>): void {
|
|
39
|
+
const text = props["0"] ?? props.text;
|
|
40
|
+
if (text !== undefined) {
|
|
41
|
+
el.dataset.textTemplate = String(text);
|
|
42
|
+
el.textContent = String(text);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* TabPanel → a focusable region (`tabindex=0`) so a Tab keypress from the
|
|
49
|
+
* tablist lands inside the panel even when it has no focusable content
|
|
50
|
+
* (WAI-ARIA APG recommendation).
|
|
51
|
+
*/
|
|
52
|
+
export const tabPanelHandler: ComponentHandler = {
|
|
53
|
+
create(): HTMLElement {
|
|
54
|
+
const el = document.createElement("div");
|
|
55
|
+
el.dataset.hypenType = "tabpanel";
|
|
56
|
+
el.tabIndex = 0;
|
|
57
|
+
return el;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** Option → a plain div host; role="option" arrives via semantics. */
|
|
62
|
+
export const optionHandler: ComponentHandler = {
|
|
63
|
+
create(): HTMLElement {
|
|
64
|
+
const el = document.createElement("div");
|
|
65
|
+
el.dataset.hypenType = "option";
|
|
66
|
+
return el;
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
applyProps(el: HTMLElement, props: Record<string, any>): void {
|
|
70
|
+
const text = props["0"] ?? props.text;
|
|
71
|
+
if (text !== undefined) {
|
|
72
|
+
el.dataset.textTemplate = String(text);
|
|
73
|
+
el.textContent = String(text);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
};
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const textHandler: ComponentHandler = {
|
|
8
|
-
create(
|
|
9
|
-
const el =
|
|
8
|
+
create(): HTMLElement {
|
|
9
|
+
const el = document.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
|
|
@@ -20,11 +20,8 @@ export const textHandler: ComponentHandler = {
|
|
|
20
20
|
},
|
|
21
21
|
|
|
22
22
|
applyProps(el: HTMLElement, props: Record<string, any>): void {
|
|
23
|
-
// Text content from first positional arg or "text" prop
|
|
24
|
-
|
|
25
|
-
// render — `||` falls through on falsy values and a bound `0` was
|
|
26
|
-
// disappearing entirely on web while iOS rendered it correctly.
|
|
27
|
-
const text = props["0"] ?? props.text;
|
|
23
|
+
// Text content from first positional arg or "text" prop
|
|
24
|
+
const text = props["0"] || props.text;
|
|
28
25
|
if (text !== undefined) {
|
|
29
26
|
// Store the original text template for state interpolation
|
|
30
27
|
el.dataset.textTemplate = String(text);
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import type { ComponentHandler } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export const textareaHandler: ComponentHandler = {
|
|
8
|
-
create(
|
|
9
|
-
const el =
|
|
8
|
+
create(): HTMLElement {
|
|
9
|
+
const el = document.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(
|
|
9
|
-
const el =
|
|
8
|
+
create(): HTMLElement {
|
|
9
|
+
const el = document.createElement("video");
|
|
10
10
|
el.dataset.hypenType = "video";
|
|
11
11
|
return el as any as HTMLElement;
|
|
12
12
|
},
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VisuallyHidden Component
|
|
3
|
+
*
|
|
4
|
+
* Renders content that is invisible on screen but remains in the accessibility
|
|
5
|
+
* tree for screen readers — the standard "sr-only" pattern. Useful for giving
|
|
6
|
+
* an icon-only control readable text, announcing context, etc.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ComponentHandler } from "./index.js";
|
|
10
|
+
|
|
11
|
+
export const visuallyHiddenHandler: ComponentHandler = {
|
|
12
|
+
create(): HTMLElement {
|
|
13
|
+
const el = document.createElement("span");
|
|
14
|
+
el.dataset.hypenType = "visuallyhidden";
|
|
15
|
+
|
|
16
|
+
// Standard visually-hidden (sr-only) styles: removed from the visual
|
|
17
|
+
// layout but still read by assistive technology.
|
|
18
|
+
el.style.position = "absolute";
|
|
19
|
+
el.style.width = "1px";
|
|
20
|
+
el.style.height = "1px";
|
|
21
|
+
el.style.padding = "0";
|
|
22
|
+
el.style.margin = "-1px";
|
|
23
|
+
el.style.overflow = "hidden";
|
|
24
|
+
el.style.clip = "rect(0, 0, 0, 0)";
|
|
25
|
+
el.style.whiteSpace = "nowrap";
|
|
26
|
+
el.style.border = "0";
|
|
27
|
+
|
|
28
|
+
return el;
|
|
29
|
+
},
|
|
30
|
+
};
|
package/src/dom/debug.ts
CHANGED
|
@@ -76,8 +76,6 @@ 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;
|
|
81
79
|
// Calculate opacity based on render count (increment by heatmapIncrement% each time)
|
|
82
80
|
const opacity = Math.min(
|
|
83
81
|
(renderCount * this.config.heatmapIncrement) / 100,
|
|
@@ -87,7 +85,7 @@ export class RerenderTracker {
|
|
|
87
85
|
log.debug(`Updating heatmap for ${id}, count: ${renderCount}, opacity: ${opacity}`);
|
|
88
86
|
|
|
89
87
|
// For inline elements or text, use a simpler approach: background color + outline
|
|
90
|
-
const isInline =
|
|
88
|
+
const isInline = window.getComputedStyle(element).display.includes('inline');
|
|
91
89
|
|
|
92
90
|
if (isInline || element.tagName === 'SPAN') {
|
|
93
91
|
// Store original styles if not already stored
|
|
@@ -107,8 +105,8 @@ export class RerenderTracker {
|
|
|
107
105
|
element.setAttribute('data-hypen-renders', `${renderCount}× ${patchType}`);
|
|
108
106
|
|
|
109
107
|
// Add CSS for the badge if not already added
|
|
110
|
-
if (!
|
|
111
|
-
const style =
|
|
108
|
+
if (!document.getElementById('hypen-debug-styles')) {
|
|
109
|
+
const style = document.createElement('style');
|
|
112
110
|
style.id = 'hypen-debug-styles';
|
|
113
111
|
style.textContent = `
|
|
114
112
|
[data-hypen-renders]::before {
|
|
@@ -129,7 +127,7 @@ export class RerenderTracker {
|
|
|
129
127
|
text-shadow: none;
|
|
130
128
|
}
|
|
131
129
|
`;
|
|
132
|
-
|
|
130
|
+
document.head.appendChild(style);
|
|
133
131
|
}
|
|
134
132
|
|
|
135
133
|
// Store in overlays map for cleanup
|
|
@@ -150,7 +148,7 @@ export class RerenderTracker {
|
|
|
150
148
|
let overlay = this.overlays.get(id);
|
|
151
149
|
|
|
152
150
|
if (!overlay) {
|
|
153
|
-
overlay =
|
|
151
|
+
overlay = document.createElement("div");
|
|
154
152
|
overlay.className = "hypen-debug-overlay";
|
|
155
153
|
overlay.style.cssText = `
|
|
156
154
|
position: absolute;
|
|
@@ -173,7 +171,7 @@ export class RerenderTracker {
|
|
|
173
171
|
visibility: visible !important;
|
|
174
172
|
`;
|
|
175
173
|
|
|
176
|
-
const currentPosition =
|
|
174
|
+
const currentPosition = window.getComputedStyle(element).position;
|
|
177
175
|
if (currentPosition === 'static') {
|
|
178
176
|
element.style.position = 'relative';
|
|
179
177
|
}
|
package/src/dom/index.ts
CHANGED
|
@@ -9,11 +9,3 @@ 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";
|