@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
package/dist/variants.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared variant (responsive breakpoint + interaction state) support.
|
|
3
|
+
*
|
|
4
|
+
* The engine emits applicator props using the canonical key format:
|
|
5
|
+
*
|
|
6
|
+
* <camelBase><variant?><argSuffix>
|
|
7
|
+
*
|
|
8
|
+
* - camelBase: camelCase applicator name, e.g. "padding", "backgroundColor"
|
|
9
|
+
* - variant?: "@<bp>" and/or ":<state>", in that order if combined.
|
|
10
|
+
* bp in { sm, md, lg, xl, 2xl }
|
|
11
|
+
* state in { hover, focus, active, disabled,
|
|
12
|
+
* focus-visible, focus-within }
|
|
13
|
+
* combined example: "backgroundColor@md:hover" (BOTH md AND hover)
|
|
14
|
+
* - argSuffix: ".<index>" (almost always ".0") or ".<name>" for named args.
|
|
15
|
+
*
|
|
16
|
+
* Examples emitted by the engine:
|
|
17
|
+
* "padding.0", "padding@md.0", "backgroundColor:hover.0",
|
|
18
|
+
* "backgroundColor@md:hover.0".
|
|
19
|
+
*
|
|
20
|
+
* IMPORTANT: the variant marker sits BETWEEN the base name and the ".0" arg
|
|
21
|
+
* suffix. A lookup that forgets the ".0" (e.g. building just "padding@md")
|
|
22
|
+
* MISSES the real key. Always parse the variant out of the full key.
|
|
23
|
+
*
|
|
24
|
+
* Both the DOM applicator path and the Canvas renderer import this module so
|
|
25
|
+
* breakpoint pixels, state names, and precedence stay byte-compatible across
|
|
26
|
+
* renderers. The DOM renderer leans on the CSS engine for actual resolution
|
|
27
|
+
* (media queries + pseudo-classes), so it only needs PARSE_* here; Canvas does
|
|
28
|
+
* its own paint/layout and therefore also uses the runtime resolver.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Tailwind breakpoint min-widths (px). Order matters: ascending min-width.
|
|
32
|
+
* Mirrors the DOM `BREAKPOINTS` table and the iOS reference.
|
|
33
|
+
*/
|
|
34
|
+
export const BREAKPOINTS = {
|
|
35
|
+
sm: 640,
|
|
36
|
+
md: 768,
|
|
37
|
+
lg: 1024,
|
|
38
|
+
xl: 1280,
|
|
39
|
+
"2xl": 1536,
|
|
40
|
+
};
|
|
41
|
+
/** Breakpoint names in ascending min-width order (sm < md < lg < xl < 2xl). */
|
|
42
|
+
export const BREAKPOINT_ORDER = ["sm", "md", "lg", "xl", "2xl"];
|
|
43
|
+
/** Interaction-state names recognised in variant keys (same set as the DOM). */
|
|
44
|
+
export const VALID_STATES = [
|
|
45
|
+
"hover",
|
|
46
|
+
"focus",
|
|
47
|
+
"active",
|
|
48
|
+
"disabled",
|
|
49
|
+
"focus-visible",
|
|
50
|
+
"focus-within",
|
|
51
|
+
];
|
|
52
|
+
const VALID_STATE_SET = new Set(VALID_STATES);
|
|
53
|
+
/**
|
|
54
|
+
* Parse a prop key into `{ base, breakpoint, state, arg }`.
|
|
55
|
+
*
|
|
56
|
+
* Splits the arg suffix first (the LAST `.<token>`), then peels the variant
|
|
57
|
+
* markers (`@bp` and/or `:state`) off the remainder. An unknown breakpoint or
|
|
58
|
+
* state is left as part of the base name (caller decides what to do); this
|
|
59
|
+
* matches the DOM behaviour where an invalid `@invalid` / `:invalid` simply
|
|
60
|
+
* never matches a real variant.
|
|
61
|
+
*
|
|
62
|
+
* Examples:
|
|
63
|
+
* "padding.0" -> { base:"padding", breakpoint:null, state:null, arg:"0" }
|
|
64
|
+
* "padding@md.0" -> { base:"padding", breakpoint:"md", state:null, arg:"0" }
|
|
65
|
+
* "backgroundColor:hover.0" -> { base:"backgroundColor", breakpoint:null, state:"hover", arg:"0" }
|
|
66
|
+
* "backgroundColor@md:hover.0" -> { base:"backgroundColor", breakpoint:"md", state:"hover", arg:"0" }
|
|
67
|
+
* "backgroundColor@md:hover" -> { base:"backgroundColor", breakpoint:"md", state:"hover", arg:null }
|
|
68
|
+
*/
|
|
69
|
+
export function parseVariantKey(key) {
|
|
70
|
+
let work = key;
|
|
71
|
+
let arg = null;
|
|
72
|
+
// Peel the arg suffix: the segment after the LAST dot, but only if that dot
|
|
73
|
+
// comes AFTER any variant markers (so we never split inside "focus-within"
|
|
74
|
+
// — that has no dot — and never treat "padding" alone as having an arg).
|
|
75
|
+
const lastDot = work.lastIndexOf(".");
|
|
76
|
+
if (lastDot > 0) {
|
|
77
|
+
arg = work.slice(lastDot + 1);
|
|
78
|
+
work = work.slice(0, lastDot);
|
|
79
|
+
}
|
|
80
|
+
// Peel the state marker (":state") off the end of the remainder.
|
|
81
|
+
let state = null;
|
|
82
|
+
const colon = work.indexOf(":");
|
|
83
|
+
if (colon !== -1) {
|
|
84
|
+
const candidate = work.slice(colon + 1);
|
|
85
|
+
if (VALID_STATE_SET.has(candidate)) {
|
|
86
|
+
state = candidate;
|
|
87
|
+
work = work.slice(0, colon);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Peel the breakpoint marker ("@bp").
|
|
91
|
+
let breakpoint = null;
|
|
92
|
+
const at = work.indexOf("@");
|
|
93
|
+
if (at !== -1) {
|
|
94
|
+
const candidate = work.slice(at + 1);
|
|
95
|
+
if (candidate in BREAKPOINTS) {
|
|
96
|
+
breakpoint = candidate;
|
|
97
|
+
work = work.slice(0, at);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { base: work, breakpoint, state, arg };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Precedence rank for a parsed variant (higher wins). Combines the breakpoint
|
|
104
|
+
* tier and the state tier into a single sortable number.
|
|
105
|
+
*
|
|
106
|
+
* Precedence (low -> high), later overrides earlier:
|
|
107
|
+
* base
|
|
108
|
+
* < breakpoints ascending (sm < md < lg < xl < 2xl)
|
|
109
|
+
* < disabled < hover < focus < active
|
|
110
|
+
*
|
|
111
|
+
* State always outranks any breakpoint (a `:hover` override beats a `@2xl`
|
|
112
|
+
* sizing override on the same prop), matching the iOS reference where state
|
|
113
|
+
* overrides are merged AFTER the responsive base. Within the same state tier,
|
|
114
|
+
* a higher breakpoint still breaks the tie so `@md:hover` beats `:hover`.
|
|
115
|
+
*/
|
|
116
|
+
const STATE_RANK = {
|
|
117
|
+
disabled: 1,
|
|
118
|
+
hover: 2,
|
|
119
|
+
// focus-visible / focus-within are focus-flavoured states: they share the
|
|
120
|
+
// `focus` precedence slot (NOT base/0) so they outrank hover and lose to
|
|
121
|
+
// active, matching the engine's `state_rank` in portable/variant.rs.
|
|
122
|
+
focus: 3,
|
|
123
|
+
"focus-visible": 3,
|
|
124
|
+
"focus-within": 3,
|
|
125
|
+
active: 4,
|
|
126
|
+
};
|
|
127
|
+
// State tier must be weighted by a value strictly greater than the largest
|
|
128
|
+
// possible breakpoint tier so the bp tiebreak can never leak across state
|
|
129
|
+
// bands (mirrors the engine's STATE_STEP > max-breakpoint invariant). Derived
|
|
130
|
+
// from BREAKPOINT_ORDER.length so adding breakpoints can't silently break it.
|
|
131
|
+
const STATE_WEIGHT = BREAKPOINT_ORDER.length + 1;
|
|
132
|
+
function variantRank(breakpoint, state) {
|
|
133
|
+
const bpTier = breakpoint ? BREAKPOINT_ORDER.indexOf(breakpoint) + 1 : 0; // 0..len
|
|
134
|
+
const stateTier = state ? STATE_RANK[state] ?? 0 : 0; // 0..4
|
|
135
|
+
// State tier dominates; breakpoint tier breaks ties within a state tier.
|
|
136
|
+
return stateTier * STATE_WEIGHT + bpTier;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Is a parsed variant currently applicable, given the viewport width and the
|
|
140
|
+
* active interaction states? A combined key requires BOTH conditions.
|
|
141
|
+
*/
|
|
142
|
+
function variantApplies(parsed, width, states) {
|
|
143
|
+
if (parsed.breakpoint) {
|
|
144
|
+
const minWidth = BREAKPOINTS[parsed.breakpoint];
|
|
145
|
+
if (minWidth === undefined || width < minWidth)
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
if (parsed.state) {
|
|
149
|
+
if (!states[parsed.state])
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Resolve variant-bearing props against the current viewport width and active
|
|
156
|
+
* interaction states, producing a flat map of `base -> winning value`.
|
|
157
|
+
*
|
|
158
|
+
* Input keys are the already-normalised flat applicator keys (variant markers
|
|
159
|
+
* present, arg suffix already stripped by the applicator-grouping pass), e.g.
|
|
160
|
+
* `{ padding: 8, "padding@md": 16, "backgroundColor:hover": "red" }`.
|
|
161
|
+
*
|
|
162
|
+
* Only bases that actually have at least one applicable variant appear in the
|
|
163
|
+
* result with an overridden value; a base with only its plain entry resolves to
|
|
164
|
+
* that plain value. The caller merges the result over the node's base props.
|
|
165
|
+
*/
|
|
166
|
+
export function resolveVariantProps(props, width, states) {
|
|
167
|
+
// base -> { value, rank }
|
|
168
|
+
const winners = new Map();
|
|
169
|
+
for (const key of Object.keys(props)) {
|
|
170
|
+
const parsed = parseVariantKey(key);
|
|
171
|
+
// Only consider entries that carry a variant marker. Plain keys (no bp/state)
|
|
172
|
+
// are the base and are handled implicitly via rank 0 below so a base with a
|
|
173
|
+
// single applicable variant still falls back to base when the variant is off.
|
|
174
|
+
if (!parsed.breakpoint && !parsed.state) {
|
|
175
|
+
// Seed the base value at rank 0 so variant lookups have a fallback and so
|
|
176
|
+
// a base that ALSO has variants resolves correctly when none apply.
|
|
177
|
+
const existing = winners.get(parsed.base);
|
|
178
|
+
if (!existing || existing.rank === 0) {
|
|
179
|
+
winners.set(parsed.base, { value: props[key], rank: 0 });
|
|
180
|
+
}
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (!variantApplies(parsed, width, states))
|
|
184
|
+
continue;
|
|
185
|
+
const rank = variantRank(parsed.breakpoint, parsed.state);
|
|
186
|
+
const existing = winners.get(parsed.base);
|
|
187
|
+
if (!existing || rank >= existing.rank) {
|
|
188
|
+
winners.set(parsed.base, { value: props[key], rank });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const out = {};
|
|
192
|
+
for (const [base, win] of winners) {
|
|
193
|
+
out[base] = win.value;
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
/** True if any key in `props` carries a variant marker (`@bp` or `:state`). */
|
|
198
|
+
export function hasVariantProps(props) {
|
|
199
|
+
for (const key of Object.keys(props)) {
|
|
200
|
+
const parsed = parseVariantKey(key);
|
|
201
|
+
if (parsed.breakpoint || parsed.state)
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=variants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variants.js","sourceRoot":"","sources":["../src/variants.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypen-space/web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "Hypen web renderers - DOM and Canvas rendering for browsers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@chenglou/pretext": "^0.0.3",
|
|
46
|
-
"@hypen-space/core": "^0.
|
|
46
|
+
"@hypen-space/core": "^0.5.3",
|
|
47
47
|
"taffy-layout": "^2.0.3"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
package/src/canvas/QUICKSTART.md
CHANGED
|
@@ -30,7 +30,7 @@ bun add @hypen-space/core @hypen-space/web @hypen-space/web-engine
|
|
|
30
30
|
```typescript
|
|
31
31
|
import { app, HypenModuleInstance } from "@hypen-space/core";
|
|
32
32
|
import { Engine } from "@hypen-space/web-engine";
|
|
33
|
-
import {
|
|
33
|
+
import { CanvasRenderer } from "@hypen-space/web/canvas";
|
|
34
34
|
|
|
35
35
|
// Define state
|
|
36
36
|
type AppState = {
|
|
@@ -54,12 +54,17 @@ async function main() {
|
|
|
54
54
|
const engine = new Engine();
|
|
55
55
|
await engine.init();
|
|
56
56
|
|
|
57
|
-
// Create renderer
|
|
58
|
-
|
|
57
|
+
// Create renderer
|
|
58
|
+
const renderer = new CanvasRenderer(canvas, engine, {
|
|
59
59
|
devicePixelRatio: window.devicePixelRatio,
|
|
60
60
|
backgroundColor: "#ffffff",
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
+
// Connect renderer
|
|
64
|
+
engine.setRenderCallback((patches) => {
|
|
65
|
+
renderer.applyPatches(patches);
|
|
66
|
+
});
|
|
67
|
+
|
|
63
68
|
// Create module
|
|
64
69
|
const instance = new HypenModuleInstance(engine, appModule);
|
|
65
70
|
|
|
@@ -244,15 +249,14 @@ Container {
|
|
|
244
249
|
## Configuration Options
|
|
245
250
|
|
|
246
251
|
```typescript
|
|
247
|
-
const
|
|
252
|
+
const renderer = new CanvasRenderer(canvas, engine, {
|
|
248
253
|
// Display
|
|
249
254
|
devicePixelRatio: window.devicePixelRatio, // HiDPI support
|
|
250
255
|
backgroundColor: "#ffffff", // Canvas background
|
|
251
256
|
|
|
252
257
|
// Features
|
|
253
|
-
enableAccessibility: true, // Screen
|
|
258
|
+
enableAccessibility: true, // Screen readers + Tab focus + text editing
|
|
254
259
|
enableHitTesting: true, // Mouse events
|
|
255
|
-
enableInputOverlay: true, // Text input support
|
|
256
260
|
|
|
257
261
|
// Debug
|
|
258
262
|
showLayoutBounds: false, // Show red boxes around elements
|
|
@@ -349,7 +353,7 @@ let timeoutId: number;
|
|
|
349
353
|
### 3. Measure Performance
|
|
350
354
|
|
|
351
355
|
```typescript
|
|
352
|
-
const
|
|
356
|
+
const renderer = new CanvasRenderer(canvas, engine, {
|
|
353
357
|
logPerformance: true, // Logs FPS and frame times
|
|
354
358
|
});
|
|
355
359
|
```
|
|
@@ -361,18 +365,19 @@ const { renderer } = createHypenClient(canvas, engine, {
|
|
|
361
365
|
Make sure to set `devicePixelRatio`:
|
|
362
366
|
|
|
363
367
|
```typescript
|
|
364
|
-
const
|
|
368
|
+
const renderer = new CanvasRenderer(canvas, engine, {
|
|
365
369
|
devicePixelRatio: window.devicePixelRatio,
|
|
366
370
|
});
|
|
367
371
|
```
|
|
368
372
|
|
|
369
373
|
### Text Input Not Working
|
|
370
374
|
|
|
371
|
-
|
|
375
|
+
Text editing runs through the accessibility mirror (canvas fallback
|
|
376
|
+
content), so `enableAccessibility` must be true (the default):
|
|
372
377
|
|
|
373
378
|
```typescript
|
|
374
|
-
const
|
|
375
|
-
|
|
379
|
+
const renderer = new CanvasRenderer(canvas, engine, {
|
|
380
|
+
enableAccessibility: true,
|
|
376
381
|
});
|
|
377
382
|
```
|
|
378
383
|
|
package/src/canvas/README.md
CHANGED
|
@@ -13,12 +13,10 @@ The Canvas Renderer is a complete alternative to the DOM renderer that draws all
|
|
|
13
13
|
|
|
14
14
|
## Quick Start
|
|
15
15
|
|
|
16
|
-
`createHypenClient` is the recommended one-call wiring (constructs the renderer and subscribes to engine patches). `CanvasRenderer` is still exported directly for advanced setups.
|
|
17
|
-
|
|
18
16
|
```typescript
|
|
19
17
|
import { app } from "@hypen-space/core";
|
|
20
18
|
import { Engine } from "@hypen-space/web-engine";
|
|
21
|
-
import {
|
|
19
|
+
import { CanvasRenderer } from "@hypen-space/web/canvas";
|
|
22
20
|
|
|
23
21
|
// Setup canvas
|
|
24
22
|
const canvas = document.getElementById("app") as HTMLCanvasElement;
|
|
@@ -27,8 +25,13 @@ const canvas = document.getElementById("app") as HTMLCanvasElement;
|
|
|
27
25
|
const engine = new Engine();
|
|
28
26
|
await engine.init();
|
|
29
27
|
|
|
30
|
-
// Create canvas renderer
|
|
31
|
-
|
|
28
|
+
// Create canvas renderer
|
|
29
|
+
const renderer = new CanvasRenderer(canvas, engine);
|
|
30
|
+
|
|
31
|
+
// Set render callback
|
|
32
|
+
engine.setRenderCallback((patches) => {
|
|
33
|
+
renderer.applyPatches(patches);
|
|
34
|
+
});
|
|
32
35
|
|
|
33
36
|
// Render UI
|
|
34
37
|
await engine.renderSource(`
|
|
@@ -141,35 +144,61 @@ Handles text measurement and rendering:
|
|
|
141
144
|
- **Text Alignment**: left, center, right, top, middle, bottom
|
|
142
145
|
- **Caching**: Text metrics are cached for performance
|
|
143
146
|
|
|
144
|
-
### 5.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
147
|
+
### 5. Native Text Editing (`editing.ts`)
|
|
148
|
+
|
|
149
|
+
Text, caret, and selection inside `Input`/`Textarea` are painted **on the
|
|
150
|
+
canvas** — there is no visible DOM overlay. The browser does the actual
|
|
151
|
+
editing (typing, caret movement, word-jumps, select-all, clipboard, undo,
|
|
152
|
+
IME composition) in a single hidden **proxy textarea** positioned at the
|
|
153
|
+
caret (the Monaco/xterm.js approach); the `TextEditController` reads
|
|
154
|
+
`value`/`selectionStart`/`selectionEnd` after each event and the canvas
|
|
155
|
+
paints the result. Focus and accessibility stay on the node's mirror
|
|
156
|
+
element (below) — browsers won't run text editing on unrendered fallback
|
|
157
|
+
content, which is why the keystroke stream needs the proxy. Positioning the
|
|
158
|
+
proxy at the caret also puts the IME candidate window next to the painted
|
|
159
|
+
text.
|
|
160
|
+
|
|
161
|
+
**Editing visuals (painted in `paint.ts#paintInput`):**
|
|
162
|
+
- Selection highlight and blinking caret via shared `text-geometry.ts` math
|
|
163
|
+
- IME composition range underlined (dashed)
|
|
164
|
+
- Single-line inputs pan horizontally (`scrollX`) to keep the caret visible
|
|
165
|
+
- Click/drag on the canvas maps point → character offset → `setSelectionRange`
|
|
166
|
+
|
|
167
|
+
Two-way binding dispatches `__hypen_bind {path, value}` on input (suppressed
|
|
168
|
+
during IME composition; flushed at `compositionend`).
|
|
169
|
+
|
|
170
|
+
### 6. Accessibility & Focus (`accessibility.ts`, `focus.ts`)
|
|
171
|
+
|
|
172
|
+
A live DOM mirror of the virtual tree is rendered as a **transparent
|
|
173
|
+
positioned overlay** above the canvas (the Flutter-web approach), with every
|
|
174
|
+
element absolutely positioned at its painted bounds and `pointer-events:
|
|
175
|
+
none` so the canvas keeps all pointer interaction.
|
|
176
|
+
|
|
177
|
+
Canvas *fallback content* was evaluated first and rejected on evidence:
|
|
178
|
+
Chromium exposes fallback elements to the accessibility tree (names, roles,
|
|
179
|
+
focus, Tab order all work) but gives them **zero geometry** — and screen
|
|
180
|
+
reader browse modes (VoiceOver cursor, rotor, touch exploration) are
|
|
181
|
+
geometry-driven, so they skip boundless elements entirely.
|
|
164
182
|
|
|
165
183
|
**Strategy:**
|
|
166
|
-
1.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
184
|
+
1. Mirror elements use semantic HTML (button, input, h1-h6, ...) driven by
|
|
185
|
+
the engine-derived `Semantics` block, invisibly rendered (transparent
|
|
186
|
+
text/background; placeholders and selection suppressed via a shared
|
|
187
|
+
stylesheet) so AT gets real boxes and native focus rings land exactly
|
|
188
|
+
over the painted controls
|
|
189
|
+
2. The mirror is synced **incrementally** from the patch stream
|
|
190
|
+
(create/insert/move/remove/detach/attach) — element identity survives
|
|
191
|
+
re-renders and router navigation, so AT focus/virtual-cursor position
|
|
192
|
+
is never destroyed by an update; element positions are refreshed after
|
|
193
|
+
every canvas render
|
|
194
|
+
3. Real DOM focus on mirror elements is the **single source of truth** for
|
|
195
|
+
focus: Tab/Shift+Tab work natively, `focusin`/`focusout` drive
|
|
196
|
+
`node.focused` and the painted focus ring, and the canvas hit-test path
|
|
197
|
+
funnels into the same place by focusing the node's mirror element
|
|
198
|
+
4. Enter/Space on a mirror `<button>` (or AT activation) dispatches the
|
|
199
|
+
node's action — identical payload to a canvas click
|
|
200
|
+
5. The `<canvas>` itself is `aria-hidden` — the overlay carries all
|
|
201
|
+
semantics
|
|
173
202
|
|
|
174
203
|
## Supported Components
|
|
175
204
|
|
|
@@ -200,9 +229,9 @@ const renderer = new CanvasRenderer(canvas, engine, {
|
|
|
200
229
|
backgroundColor: "#ffffff", // Canvas background
|
|
201
230
|
|
|
202
231
|
// Features
|
|
203
|
-
enableAccessibility: true, //
|
|
232
|
+
enableAccessibility: true, // Fallback-content mirror: screen readers,
|
|
233
|
+
// Tab focus, and text-input editing
|
|
204
234
|
enableHitTesting: true, // Mouse event handling
|
|
205
|
-
enableInputOverlay: true, // DOM overlays for text input
|
|
206
235
|
|
|
207
236
|
// Performance (future)
|
|
208
237
|
enableDirtyRects: false, // Only redraw changed regions
|