@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
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Focus Manager
|
|
3
|
+
*
|
|
4
|
+
* Real DOM focus on accessibility-mirror elements (canvas fallback content)
|
|
5
|
+
* is the single source of truth for canvas focus. Tab/Shift+Tab traverse
|
|
6
|
+
* the mirror natively; `focusin`/`focusout` translate DOM focus into
|
|
7
|
+
* `node.focused` + repaint; the pointer path (canvas hit-test) funnels into
|
|
8
|
+
* the same place by focusing the node's mirror element.
|
|
9
|
+
*
|
|
10
|
+
* Loop-free by construction: DOM focus events only ever *read* into node
|
|
11
|
+
* state — nothing in the sync path calls `.focus()` back.
|
|
12
|
+
*
|
|
13
|
+
* The mirror also carries keyboard/AT interaction back to the engine:
|
|
14
|
+
* Enter/Space on a mirror `<button>` produces a native `click` (dispatched
|
|
15
|
+
* to the node's action), and keydown/keyup on the focused element dispatch
|
|
16
|
+
* the node's `onKeyDown`/`onKeyUp` applicators — which the old
|
|
17
|
+
* canvas-attached listeners never could (a canvas without tabindex never
|
|
18
|
+
* receives key events).
|
|
19
|
+
*/
|
|
20
|
+
import { dispatchNodeEvent } from "./dispatch.js";
|
|
21
|
+
export class FocusManager {
|
|
22
|
+
mirror;
|
|
23
|
+
engine;
|
|
24
|
+
hooks;
|
|
25
|
+
focusedNode = null;
|
|
26
|
+
boundFocusIn = (e) => this.onFocusIn(e);
|
|
27
|
+
boundFocusOut = (e) => this.onFocusOut(e);
|
|
28
|
+
boundClick = (e) => this.onMirrorClick(e);
|
|
29
|
+
boundKeyDown = (e) => this.onMirrorKey("keydown", e);
|
|
30
|
+
boundKeyUp = (e) => this.onMirrorKey("keyup", e);
|
|
31
|
+
constructor(mirror, engine, hooks) {
|
|
32
|
+
this.mirror = mirror;
|
|
33
|
+
this.engine = engine;
|
|
34
|
+
this.hooks = hooks;
|
|
35
|
+
const root = mirror.getRoot();
|
|
36
|
+
if (root && typeof root.addEventListener === "function") {
|
|
37
|
+
root.addEventListener("focusin", this.boundFocusIn);
|
|
38
|
+
root.addEventListener("focusout", this.boundFocusOut);
|
|
39
|
+
root.addEventListener("click", this.boundClick);
|
|
40
|
+
root.addEventListener("keydown", this.boundKeyDown);
|
|
41
|
+
root.addEventListener("keyup", this.boundKeyUp);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// -------------------------------------------------------------------------
|
|
45
|
+
// DOM → node state (the only direction focus state flows)
|
|
46
|
+
// -------------------------------------------------------------------------
|
|
47
|
+
/** Climb from an event target to the mirror element's virtual node. */
|
|
48
|
+
resolveNode(target) {
|
|
49
|
+
let el = target;
|
|
50
|
+
const root = this.mirror.getRoot();
|
|
51
|
+
while (el && el !== root) {
|
|
52
|
+
const id = el.getAttribute?.("data-hypen-id");
|
|
53
|
+
if (id) {
|
|
54
|
+
return this.hooks.getNode(id) ?? null;
|
|
55
|
+
}
|
|
56
|
+
el = el.parentNode ?? null;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
onFocusIn(e) {
|
|
61
|
+
this.syncFocus(this.resolveNode(e.target));
|
|
62
|
+
}
|
|
63
|
+
onFocusOut(e) {
|
|
64
|
+
// If focus moves to another mirror element, its focusin handles the
|
|
65
|
+
// transition; a handoff to the editor's IME proxy keeps the node
|
|
66
|
+
// focused; only a departure from both clears focus.
|
|
67
|
+
const next = e.relatedTarget;
|
|
68
|
+
if (next && this.hooks.isAuxFocusTarget?.(next))
|
|
69
|
+
return;
|
|
70
|
+
const root = this.mirror.getRoot();
|
|
71
|
+
if (!next || !root || !(root.contains?.(next) ?? false)) {
|
|
72
|
+
this.syncFocus(null);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** Update node.focused flags from the new DOM focus target. Idempotent. */
|
|
76
|
+
syncFocus(node) {
|
|
77
|
+
if (node === this.focusedNode)
|
|
78
|
+
return;
|
|
79
|
+
const prev = this.focusedNode;
|
|
80
|
+
if (prev) {
|
|
81
|
+
prev.focused = false;
|
|
82
|
+
dispatchNodeEvent(this.engine, prev, "blur", {});
|
|
83
|
+
}
|
|
84
|
+
this.focusedNode = node;
|
|
85
|
+
if (node) {
|
|
86
|
+
node.focused = true;
|
|
87
|
+
dispatchNodeEvent(this.engine, node, "focus", {});
|
|
88
|
+
}
|
|
89
|
+
this.hooks.onFocusChange(node, prev);
|
|
90
|
+
}
|
|
91
|
+
// -------------------------------------------------------------------------
|
|
92
|
+
// Pointer path — canvas hit-test funnels into DOM focus
|
|
93
|
+
// -------------------------------------------------------------------------
|
|
94
|
+
/**
|
|
95
|
+
* Focus a node from the canvas pointer path. Focuses the mirror element
|
|
96
|
+
* (so `document.activeElement` agrees) and syncs node state directly —
|
|
97
|
+
* in real browsers the element's focusin already did that synchronously
|
|
98
|
+
* and the second sync is a no-op; in environments without focus events
|
|
99
|
+
* the direct sync keeps behavior identical.
|
|
100
|
+
*/
|
|
101
|
+
requestFocus(node) {
|
|
102
|
+
if (!node) {
|
|
103
|
+
this.clearFocus();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const el = this.mirror.getElement(node.id);
|
|
107
|
+
el?.focus?.({ preventScroll: true });
|
|
108
|
+
this.syncFocus(node);
|
|
109
|
+
}
|
|
110
|
+
/** Pointer landed on nothing focusable: blur any mirror focus and clear. */
|
|
111
|
+
clearFocus() {
|
|
112
|
+
if (typeof document !== "undefined") {
|
|
113
|
+
const active = document.activeElement;
|
|
114
|
+
const root = this.mirror.getRoot();
|
|
115
|
+
if (active &&
|
|
116
|
+
((root && (root.contains?.(active) ?? false)) ||
|
|
117
|
+
this.hooks.isAuxFocusTarget?.(active))) {
|
|
118
|
+
active.blur?.();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
this.syncFocus(null);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Clear focus if the focused node is `node` or one of its descendants —
|
|
125
|
+
* called before a subtree is removed/detached (the mirror element may
|
|
126
|
+
* leave the DOM without a browser blur when focus sits on the IME proxy).
|
|
127
|
+
*/
|
|
128
|
+
clearIfWithin(node) {
|
|
129
|
+
let current = this.focusedNode;
|
|
130
|
+
while (current) {
|
|
131
|
+
if (current === node) {
|
|
132
|
+
this.syncFocus(null);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
current = current.parent;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
getFocusedNode() {
|
|
139
|
+
return this.focusedNode;
|
|
140
|
+
}
|
|
141
|
+
// -------------------------------------------------------------------------
|
|
142
|
+
// Mirror interaction → engine actions
|
|
143
|
+
// -------------------------------------------------------------------------
|
|
144
|
+
/**
|
|
145
|
+
* Native click on a mirror element — keyboard activation (Enter/Space on
|
|
146
|
+
* a real `<button>`) or an AT-initiated action. Pointer clicks never land
|
|
147
|
+
* here (fallback content has no rendered boxes), so this can't double-fire
|
|
148
|
+
* with the canvas hit-test click path.
|
|
149
|
+
*/
|
|
150
|
+
onMirrorClick(e) {
|
|
151
|
+
const node = this.resolveNode(e.target);
|
|
152
|
+
if (!node)
|
|
153
|
+
return;
|
|
154
|
+
if (!node.clickable)
|
|
155
|
+
return;
|
|
156
|
+
dispatchNodeEvent(this.engine, node, "click", {});
|
|
157
|
+
}
|
|
158
|
+
onMirrorKey(type, e) {
|
|
159
|
+
const node = this.resolveNode(e.target);
|
|
160
|
+
if (!node)
|
|
161
|
+
return;
|
|
162
|
+
dispatchNodeEvent(this.engine, node, type, {
|
|
163
|
+
key: e.key,
|
|
164
|
+
code: e.code,
|
|
165
|
+
ctrlKey: e.ctrlKey,
|
|
166
|
+
shiftKey: e.shiftKey,
|
|
167
|
+
altKey: e.altKey,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
destroy() {
|
|
171
|
+
const root = this.mirror.getRoot();
|
|
172
|
+
if (root && typeof root.removeEventListener === "function") {
|
|
173
|
+
root.removeEventListener("focusin", this.boundFocusIn);
|
|
174
|
+
root.removeEventListener("focusout", this.boundFocusOut);
|
|
175
|
+
root.removeEventListener("click", this.boundClick);
|
|
176
|
+
root.removeEventListener("keydown", this.boundKeyDown);
|
|
177
|
+
root.removeEventListener("keyup", this.boundKeyUp);
|
|
178
|
+
}
|
|
179
|
+
this.focusedNode = null;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=focus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focus.js","sourceRoot":"","sources":["../../src/canvas/focus.ts"],"names":[],"mappings":"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"}
|
package/dist/canvas/index.d.ts
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
* Browser-only module for rendering Hypen UI to Canvas
|
|
5
5
|
*/
|
|
6
6
|
export { CanvasRenderer } from "./renderer.js";
|
|
7
|
-
export { createCanvasClient as createHypenClient, createCanvasClient, type CanvasClient as HypenClient, type CanvasClient, type CanvasClientOptions as HypenClientOptions, type CanvasClientOptions, } from "../client.js";
|
|
8
7
|
export { registerPainter } from "./paint.js";
|
|
9
8
|
export { CanvasEventManager } from "./events.js";
|
|
10
|
-
export { InputOverlay } from "./input.js";
|
|
11
9
|
export { AccessibilityLayer } from "./accessibility.js";
|
|
10
|
+
export { FocusManager } from "./focus.js";
|
|
11
|
+
export { TextEditController } from "./editing.js";
|
|
12
12
|
export { initTaffyLayout } from "./layout.js";
|
|
13
13
|
export { ScrollManager } from "./scroll.js";
|
|
14
14
|
export { DirtyRectTracker } from "./dirty.js";
|
|
15
15
|
export { SelectionManager } from "./selection.js";
|
|
16
|
+
export { pointToOffset, offsetToCaretRect, rangeToRects, type TextGeometry, } from "./text-geometry.js";
|
|
16
17
|
export type { VirtualNode, Layout, Rectangle, Point, FontStyle, TextStyle, TextMetrics, ScrollState, CanvasRendererOptions, PainterFunction, LayoutFunction, } from "./types.js";
|
package/dist/canvas/index.js
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
* Browser-only module for rendering Hypen UI to Canvas
|
|
5
5
|
*/
|
|
6
6
|
export { CanvasRenderer } from "./renderer.js";
|
|
7
|
-
export { createCanvasClient as createHypenClient, createCanvasClient, } from "../client.js";
|
|
8
7
|
export { registerPainter } from "./paint.js";
|
|
9
8
|
export { CanvasEventManager } from "./events.js";
|
|
10
|
-
export { InputOverlay } from "./input.js";
|
|
11
9
|
export { AccessibilityLayer } from "./accessibility.js";
|
|
10
|
+
export { FocusManager } from "./focus.js";
|
|
11
|
+
export { TextEditController } from "./editing.js";
|
|
12
12
|
export { initTaffyLayout } from "./layout.js";
|
|
13
13
|
export { ScrollManager } from "./scroll.js";
|
|
14
14
|
export { DirtyRectTracker } from "./dirty.js";
|
|
15
15
|
export { SelectionManager } from "./selection.js";
|
|
16
|
+
export { pointToOffset, offsetToCaretRect, rangeToRects, } from "./text-geometry.js";
|
|
16
17
|
//# sourceMappingURL=index.js.map
|
package/dist/canvas/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/canvas/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/canvas/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,YAAY,GAEb,MAAM,oBAAoB,CAAC"}
|
package/dist/canvas/layout.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* layout. Falls back to a basic flexbox implementation in environments
|
|
6
6
|
* without WASM support (e.g. test runners).
|
|
7
7
|
*/
|
|
8
|
-
import { parseSpacing, parseSize, cssLengthToPx, cssLengthToDimension
|
|
8
|
+
import { parseSpacing, parseSize, cssLengthToPx, cssLengthToDimension } from "./utils.js";
|
|
9
9
|
import { measureText } from "./text.js";
|
|
10
10
|
import { getImageNaturalAspect } from "./paint.js";
|
|
11
11
|
/**
|
|
@@ -43,6 +43,18 @@ export async function initTaffyLayout() {
|
|
|
43
43
|
taffyInitPromise = (async () => {
|
|
44
44
|
try {
|
|
45
45
|
taffy = await import("taffy-layout");
|
|
46
|
+
// In a server runtime (Bun/Node — including test runs where a JSDOM
|
|
47
|
+
// `window` may be globally registered by another test file), load the
|
|
48
|
+
// WASM from node_modules via fs. This path is deterministic; the
|
|
49
|
+
// fetch-based branch below depends on a reachable origin/CDN and must
|
|
50
|
+
// never be selected just because a test polyfilled `window`.
|
|
51
|
+
const isServerRuntime = typeof process !== "undefined" &&
|
|
52
|
+
!!(process.versions?.bun || process.versions?.node);
|
|
53
|
+
if (isServerRuntime) {
|
|
54
|
+
await taffy.loadTaffy();
|
|
55
|
+
taffyReady = true;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
46
58
|
// Bundlers (esp. Bun's browser bundler) sometimes inline a file:// URL
|
|
47
59
|
// for import.meta.url inside taffy_wasm.js, which the browser blocks
|
|
48
60
|
// ("Not allowed to load local resource"). The package's own `loadTaffy`
|
|
@@ -50,28 +62,42 @@ export async function initTaffyLayout() {
|
|
|
50
62
|
// raw `__wbg_init` (default export of the `taffy-layout/wasm` subpath)
|
|
51
63
|
// directly with an explicit Response.
|
|
52
64
|
let usedExplicit = false;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
// Only take the fetch path in a real browser. Test environments (and
|
|
66
|
+
// anything else that installs a fake `window` global) must fall through
|
|
67
|
+
// to `loadTaffy()`, which resolves the WASM from the package on disk —
|
|
68
|
+
// a fake window without `location.origin` used to throw here and
|
|
69
|
+
// silently disable Taffy for the rest of the process.
|
|
70
|
+
const isRealBrowser = typeof window !== "undefined" &&
|
|
71
|
+
typeof fetch === "function" &&
|
|
72
|
+
typeof window.location?.origin === "string" &&
|
|
73
|
+
window.document?.defaultView === window;
|
|
74
|
+
if (isRealBrowser) {
|
|
75
|
+
try {
|
|
76
|
+
const candidates = [
|
|
77
|
+
new URL("/taffy_wasm_bg.wasm", window.location.origin),
|
|
78
|
+
new URL("https://cdn.jsdelivr.net/npm/taffy-layout@2.0.3/pkg/taffy_wasm_bg.wasm"),
|
|
79
|
+
new URL("https://unpkg.com/taffy-layout@2.0.3/pkg/taffy_wasm_bg.wasm"),
|
|
80
|
+
];
|
|
81
|
+
const rawWasm = await import(
|
|
82
|
+
/* @vite-ignore */ "taffy-layout/wasm");
|
|
83
|
+
const wbgInit = rawWasm.default;
|
|
84
|
+
for (const url of candidates) {
|
|
85
|
+
try {
|
|
86
|
+
const res = await fetch(url);
|
|
87
|
+
if (!res.ok)
|
|
88
|
+
continue;
|
|
89
|
+
await wbgInit({ module_or_path: res });
|
|
90
|
+
usedExplicit = true;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Try next candidate
|
|
95
|
+
}
|
|
73
96
|
}
|
|
74
97
|
}
|
|
98
|
+
catch {
|
|
99
|
+
// Fall through to loadTaffy()
|
|
100
|
+
}
|
|
75
101
|
}
|
|
76
102
|
if (!usedExplicit) {
|
|
77
103
|
await taffy.loadTaffy();
|
|
@@ -166,21 +192,9 @@ function buildTaffyStyle(node, parent = null) {
|
|
|
166
192
|
// `List` is the DSL's vertical-stack iterator (see dom/components/list.ts
|
|
167
193
|
// — `flex-direction: column` is its default). Match that here so feeds
|
|
168
194
|
// like Notifications stack their rows vertically instead of flowing
|
|
169
|
-
// sideways. The
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
// vertically because the List default won the tie-break).
|
|
173
|
-
const isListColumn = type === "list" &&
|
|
174
|
-
props.direction !== "horizontal" &&
|
|
175
|
-
props.flexDirection !== "row";
|
|
176
|
-
// Button defaults to `flex-direction: column` to match the DOM renderer
|
|
177
|
-
// (see dom/components/button.ts). With Taffy's default `align-items:
|
|
178
|
-
// stretch`, treating Button as a row caused inner `Column { Icon, Text }`
|
|
179
|
-
// children to collapse onto each other (icon overlapping label). Only
|
|
180
|
-
// apply the column default when no explicit `flexDirection` is set so a
|
|
181
|
-
// Tailwind `flex-row` on a button still wins.
|
|
182
|
-
const isButtonColumn = type === "button" && props.flexDirection === undefined;
|
|
183
|
-
const isColumn = !isStack && (type === "column" || isListColumn || isButtonColumn || props.flexDirection === "column");
|
|
195
|
+
// sideways. The `direction` prop can still flip it to row.
|
|
196
|
+
const isListColumn = type === "list" && props.direction !== "horizontal";
|
|
197
|
+
const isColumn = !isStack && (type === "column" || isListColumn || props.flexDirection === "column");
|
|
184
198
|
const isGrid = !isStack && (type === "grid" || props.display === "grid");
|
|
185
199
|
if (isStack) {
|
|
186
200
|
style.display = T.Display.Grid;
|
|
@@ -375,7 +389,7 @@ function buildTaffyStyle(node, parent = null) {
|
|
|
375
389
|
// Taffy defaults to `box-sizing: border-box`, so we add the
|
|
376
390
|
// padding/border ourselves into `size.height`.
|
|
377
391
|
const fontSize = cssLengthToPx(props.fontSize) ?? 16;
|
|
378
|
-
const lineHeight =
|
|
392
|
+
const lineHeight = cssLengthToPx(props.lineHeight) ?? fontSize * 1.5;
|
|
379
393
|
const minRows = type === "textarea" ? Math.max(1, Number(props.rows) || 3) : 1;
|
|
380
394
|
const padTop = cssLengthToPx(props.paddingTop ?? props.padding) ?? 0;
|
|
381
395
|
const padBottom = cssLengthToPx(props.paddingBottom ?? props.padding) ?? 0;
|
|
@@ -397,32 +411,7 @@ function buildTaffyStyle(node, parent = null) {
|
|
|
397
411
|
(parent.type.toLowerCase() === "grid" || parent.props.display === "grid");
|
|
398
412
|
const widthIn = parentIsGridLayout && props.width === "100%" ? undefined : props.width;
|
|
399
413
|
const heightIn = parentIsGridLayout && props.height === "100%" ? undefined : props.height;
|
|
400
|
-
|
|
401
|
-
// { width: 100% }` (and the symmetric column case). When a flex
|
|
402
|
-
// container has no explicit main-axis size and any child opts into
|
|
403
|
-
// weighted growth (`flex` / `flexGrow > 0`), Taffy would otherwise
|
|
404
|
-
// size the container to its min-content — so a Row of `flex-1`
|
|
405
|
-
// segmented-control buttons stayed pill-sized instead of stretching
|
|
406
|
-
// across the parent. Only do this when at least one child explicitly
|
|
407
|
-
// asks for flex, matching the DOM `:has(> [data-hypen-flex])` guard.
|
|
408
|
-
const isRowContainer = !isStack && !isGrid && !isColumn;
|
|
409
|
-
const isColumnContainer = !isStack && !isGrid && isColumn;
|
|
410
|
-
const childAsksFlex = node.children.some((c) => {
|
|
411
|
-
const fg = parseFloat(c.props.flexGrow);
|
|
412
|
-
if (Number.isFinite(fg) && fg > 0)
|
|
413
|
-
return true;
|
|
414
|
-
const f = parseFloat(c.props.flex);
|
|
415
|
-
return Number.isFinite(f) && f > 0;
|
|
416
|
-
});
|
|
417
|
-
let resolvedWidth = toDimension(widthIn);
|
|
418
|
-
let resolvedHeight = toDimension(heightIn);
|
|
419
|
-
if (childAsksFlex) {
|
|
420
|
-
if (isRowContainer && widthIn === undefined)
|
|
421
|
-
resolvedWidth = "100%";
|
|
422
|
-
if (isColumnContainer && heightIn === undefined)
|
|
423
|
-
resolvedHeight = "100%";
|
|
424
|
-
}
|
|
425
|
-
style.size = { width: resolvedWidth, height: resolvedHeight };
|
|
414
|
+
style.size = { width: toDimension(widthIn), height: toDimension(heightIn) };
|
|
426
415
|
}
|
|
427
416
|
// --- Min / Max constraints -------------------------------------------------
|
|
428
417
|
// Default `min-width: 0` for children of a Grid container. CSS makes
|
|
@@ -763,17 +752,12 @@ function buildTree(tree, node, parent = null) {
|
|
|
763
752
|
// container with size 0×0, and downstream siblings were placed on top of
|
|
764
753
|
// each other ("1,431 likes" overlapping the caption row was the visible
|
|
765
754
|
// symptom).
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
// those cases skip the measurement leaf and inherit a 0-height container,
|
|
769
|
-
// which made siblings stack on top of each other (a bound `0` overlapping
|
|
770
|
-
// the next line was the visible symptom in calorie-counter's hero).
|
|
771
|
-
if (node.type.toLowerCase() === "text" && props[0] != null && node.children.length === 0) {
|
|
772
|
-
const text = String(props[0]);
|
|
755
|
+
if (node.type.toLowerCase() === "text" && props[0] && node.children.length === 0) {
|
|
756
|
+
const text = String(props[0] || "");
|
|
773
757
|
const fontSize = cssLengthToPx(props.fontSize) ?? 16;
|
|
774
758
|
const fontWeight = props.fontWeight || "normal";
|
|
775
759
|
const fontFamily = props.fontFamily || "system-ui, sans-serif";
|
|
776
|
-
const lineHeight =
|
|
760
|
+
const lineHeight = cssLengthToPx(props.lineHeight) ?? fontSize * 1.2;
|
|
777
761
|
const p = parseSpacing(props.padding || 0);
|
|
778
762
|
if (props.paddingTop !== undefined)
|
|
779
763
|
p.top = cssLengthToPx(props.paddingTop) ?? 0;
|
|
@@ -1041,32 +1025,19 @@ function computeLayoutTaffy(ctx, node, availableWidth, availableHeight, x, y) {
|
|
|
1041
1025
|
* layout. Falls back to a basic JS flexbox implementation otherwise.
|
|
1042
1026
|
*/
|
|
1043
1027
|
export function computeLayout(ctx, node, availableWidth, availableHeight, x = 0, y = 0) {
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
try {
|
|
1053
|
-
if (taffyReady && taffy) {
|
|
1028
|
+
if (taffyReady && taffy) {
|
|
1029
|
+
computeLayoutTaffy(ctx, node, availableWidth, availableHeight, x, y);
|
|
1030
|
+
// After the first pass, Taffy's grid auto-row sizing for aspect-ratio
|
|
1031
|
+
// image leaves can collapse rows (the leaf's max-content height is 0
|
|
1032
|
+
// before column widths are known). Detect that pattern and re-run
|
|
1033
|
+
// with explicit `gridAutoRows` derived from the now-known column
|
|
1034
|
+
// width — see `annotateCollapsedAspectGrids`.
|
|
1035
|
+
if (annotateCollapsedAspectGrids(node)) {
|
|
1054
1036
|
computeLayoutTaffy(ctx, node, availableWidth, availableHeight, x, y);
|
|
1055
|
-
// After the first pass, Taffy's grid auto-row sizing for aspect-ratio
|
|
1056
|
-
// image leaves can collapse rows (the leaf's max-content height is 0
|
|
1057
|
-
// before column widths are known). Detect that pattern and re-run
|
|
1058
|
-
// with explicit `gridAutoRows` derived from the now-known column
|
|
1059
|
-
// width — see `annotateCollapsedAspectGrids`.
|
|
1060
|
-
if (annotateCollapsedAspectGrids(node)) {
|
|
1061
|
-
computeLayoutTaffy(ctx, node, availableWidth, availableHeight, x, y);
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
else {
|
|
1065
|
-
computeLayoutFallback(ctx, node, availableWidth, availableHeight, x, y);
|
|
1066
1037
|
}
|
|
1067
1038
|
}
|
|
1068
|
-
|
|
1069
|
-
|
|
1039
|
+
else {
|
|
1040
|
+
computeLayoutFallback(ctx, node, availableWidth, availableHeight, x, y);
|
|
1070
1041
|
}
|
|
1071
1042
|
}
|
|
1072
1043
|
// ---------------------------------------------------------------------------
|
|
@@ -1182,13 +1153,12 @@ function computeLayoutFallback(ctx, node, availableWidth, availableHeight, x = 0
|
|
|
1182
1153
|
if (height === null)
|
|
1183
1154
|
height = size;
|
|
1184
1155
|
}
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
const text = String(node.props[0]);
|
|
1156
|
+
if (node.type.toLowerCase() === "text" && node.props[0]) {
|
|
1157
|
+
const text = String(node.props[0] || "");
|
|
1188
1158
|
const fontSize = cssLengthToPx(props.fontSize) ?? 16;
|
|
1189
1159
|
const fontWeight = props.fontWeight || "normal";
|
|
1190
1160
|
const fontFamily = props.fontFamily || "system-ui, sans-serif";
|
|
1191
|
-
const lineHeight =
|
|
1161
|
+
const lineHeight = cssLengthToPx(props.lineHeight) ?? fontSize * 1.2;
|
|
1192
1162
|
const maxWidth = width || availableAfterMargin.width - padding.left - padding.right;
|
|
1193
1163
|
const maxLinesRaw = props.maxLines;
|
|
1194
1164
|
const maxLines = typeof maxLinesRaw === "number"
|