@hypen-space/web 0.3.9 → 0.3.11
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/dist/{src/canvas → canvas}/index.js +10 -6
- package/dist/{src/canvas → canvas}/index.js.map +4 -4
- package/dist/{src/canvas → canvas}/layout.js +4 -2
- package/dist/{src/canvas → canvas}/layout.js.map +3 -3
- package/dist/{src/canvas → canvas}/paint.js +4 -2
- package/dist/{src/canvas → canvas}/paint.js.map +3 -3
- package/dist/{src/canvas → canvas}/renderer.js +10 -6
- package/dist/{src/canvas → canvas}/renderer.js.map +4 -4
- package/dist/{src/canvas → canvas}/text.js +4 -2
- package/dist/{src/canvas → canvas}/text.js.map +3 -3
- package/dist/{src/dom → dom}/applicators/advanced-layout.js +16 -3
- package/dist/dom/applicators/advanced-layout.js.map +11 -0
- package/dist/{src/dom → dom}/applicators/events.js +6 -4
- package/dist/dom/applicators/events.js.map +11 -0
- package/dist/{src/dom → dom}/applicators/index.js +153 -24
- package/dist/dom/applicators/index.js.map +26 -0
- package/dist/{src/dom → dom}/applicators/layout.js +16 -3
- package/dist/dom/applicators/layout.js.map +10 -0
- package/dist/dom/applicators/margin.js +104 -0
- package/dist/dom/applicators/margin.js.map +10 -0
- package/dist/dom/applicators/padding.js +104 -0
- package/dist/dom/applicators/padding.js.map +10 -0
- package/dist/{src/dom → dom}/applicators/size.js +11 -3
- package/dist/{src/dom → dom}/applicators/size.js.map +3 -3
- package/dist/dom/components/button.js.map +10 -0
- package/dist/{src/dom → dom}/components/center.js +4 -1
- package/dist/dom/components/center.js.map +10 -0
- package/dist/dom/components/column.js.map +10 -0
- package/dist/{src/dom → dom}/components/container.js +1 -4
- package/dist/{src/dom → dom}/components/container.js.map +3 -3
- package/dist/{src/dom → dom}/components/grid.js +18 -2
- package/dist/dom/components/grid.js.map +10 -0
- package/dist/{src/dom → dom}/components/hypenapp.js +15 -13
- package/dist/dom/components/hypenapp.js.map +10 -0
- package/dist/{src/dom → dom}/components/index.js +64 -20
- package/dist/dom/components/index.js.map +41 -0
- package/dist/{src/dom → dom}/components/route.js +5 -3
- package/dist/dom/components/route.js.map +10 -0
- package/dist/{src/dom → dom}/components/row.js +18 -2
- package/dist/dom/components/row.js.map +10 -0
- package/dist/{src/dom → dom}/components/stack.js +5 -1
- package/dist/dom/components/stack.js.map +10 -0
- package/dist/{src/dom → dom}/components/text.js +5 -1
- package/dist/dom/components/text.js.map +10 -0
- package/dist/{src/dom → dom}/debug.js +5 -3
- package/dist/dom/debug.js.map +10 -0
- package/dist/{src/dom → dom}/events.js +15 -12
- package/dist/dom/events.js.map +10 -0
- package/dist/{src/dom/renderer.js → dom/index.js} +345 -55
- package/dist/dom/index.js.map +62 -0
- package/dist/{src/dom/index.js → dom/renderer.js} +233 -164
- package/dist/dom/renderer.js.map +61 -0
- package/dist/{src/hypen.js → hypen.js} +265 -118
- package/dist/hypen.js.map +62 -0
- package/dist/{src/index.js → index.js} +288 -134
- package/dist/index.js.map +72 -0
- package/package.json +22 -22
- package/src/canvas/renderer.ts +7 -4
- package/src/canvas/text.ts +4 -1
- package/src/dom/applicators/events.ts +6 -12
- package/src/dom/applicators/layout.ts +4 -0
- package/src/dom/applicators/size.ts +7 -2
- package/src/dom/components/center.ts +9 -0
- package/src/dom/components/column.ts +0 -4
- package/src/dom/components/grid.ts +22 -0
- package/src/dom/components/hypenapp.ts +14 -11
- package/src/dom/components/route.ts +4 -1
- package/src/dom/components/row.ts +20 -0
- package/src/dom/debug.ts +6 -2
- package/src/dom/events.ts +16 -12
- package/src/dom/renderer.ts +12 -9
- package/src/hypen.ts +32 -73
- package/dist/src/dom/applicators/advanced-layout.js.map +0 -11
- package/dist/src/dom/applicators/events.js.map +0 -11
- package/dist/src/dom/applicators/index.js.map +0 -26
- package/dist/src/dom/applicators/layout.js.map +0 -10
- package/dist/src/dom/applicators/margin.js +0 -51
- package/dist/src/dom/applicators/margin.js.map +0 -10
- package/dist/src/dom/applicators/padding.js +0 -51
- package/dist/src/dom/applicators/padding.js.map +0 -10
- package/dist/src/dom/components/button.js.map +0 -10
- package/dist/src/dom/components/center.js.map +0 -10
- package/dist/src/dom/components/column.js.map +0 -10
- package/dist/src/dom/components/grid.js.map +0 -10
- package/dist/src/dom/components/hypenapp.js.map +0 -10
- package/dist/src/dom/components/index.js.map +0 -41
- package/dist/src/dom/components/route.js.map +0 -10
- package/dist/src/dom/components/row.js.map +0 -10
- package/dist/src/dom/components/stack.js.map +0 -10
- package/dist/src/dom/components/text.js.map +0 -10
- package/dist/src/dom/debug.js.map +0 -10
- package/dist/src/dom/events.js.map +0 -10
- package/dist/src/dom/index.js.map +0 -62
- package/dist/src/dom/renderer.js.map +0 -61
- package/dist/src/hypen.js.map +0 -62
- package/dist/src/index.js.map +0 -72
- /package/dist/{src/canvas → canvas}/accessibility.js +0 -0
- /package/dist/{src/canvas → canvas}/accessibility.js.map +0 -0
- /package/dist/{src/canvas → canvas}/events.js +0 -0
- /package/dist/{src/canvas → canvas}/events.js.map +0 -0
- /package/dist/{src/canvas → canvas}/input.js +0 -0
- /package/dist/{src/canvas → canvas}/input.js.map +0 -0
- /package/dist/{src/canvas → canvas}/types.js +0 -0
- /package/dist/{src/canvas → canvas}/types.js.map +0 -0
- /package/dist/{src/canvas → canvas}/utils.js +0 -0
- /package/dist/{src/canvas → canvas}/utils.js.map +0 -0
- /package/dist/{src/dom → dom}/applicators/background.js +0 -0
- /package/dist/{src/dom → dom}/applicators/background.js.map +0 -0
- /package/dist/{src/dom → dom}/applicators/border.js +0 -0
- /package/dist/{src/dom → dom}/applicators/border.js.map +0 -0
- /package/dist/{src/dom → dom}/applicators/color.js +0 -0
- /package/dist/{src/dom → dom}/applicators/color.js.map +0 -0
- /package/dist/{src/dom → dom}/applicators/display.js +0 -0
- /package/dist/{src/dom → dom}/applicators/display.js.map +0 -0
- /package/dist/{src/dom → dom}/applicators/effects.js +0 -0
- /package/dist/{src/dom → dom}/applicators/effects.js.map +0 -0
- /package/dist/{src/dom → dom}/applicators/font.js +0 -0
- /package/dist/{src/dom → dom}/applicators/font.js.map +0 -0
- /package/dist/{src/dom → dom}/applicators/transform.js +0 -0
- /package/dist/{src/dom → dom}/applicators/transform.js.map +0 -0
- /package/dist/{src/dom → dom}/applicators/transition.js +0 -0
- /package/dist/{src/dom → dom}/applicators/transition.js.map +0 -0
- /package/dist/{src/dom → dom}/applicators/types.js +0 -0
- /package/dist/{src/dom → dom}/applicators/types.js.map +0 -0
- /package/dist/{src/dom → dom}/applicators/typography.js +0 -0
- /package/dist/{src/dom → dom}/applicators/typography.js.map +0 -0
- /package/dist/{src/dom → dom}/canvas/index.js +0 -0
- /package/dist/{src/dom → dom}/canvas/index.js.map +0 -0
- /package/dist/{src/dom → dom}/components/audio.js +0 -0
- /package/dist/{src/dom → dom}/components/audio.js.map +0 -0
- /package/dist/{src/dom → dom}/components/avatar.js +0 -0
- /package/dist/{src/dom → dom}/components/avatar.js.map +0 -0
- /package/dist/{src/dom → dom}/components/badge.js +0 -0
- /package/dist/{src/dom → dom}/components/badge.js.map +0 -0
- /package/dist/{src/dom → dom}/components/button.js +0 -0
- /package/dist/{src/dom → dom}/components/card.js +0 -0
- /package/dist/{src/dom → dom}/components/card.js.map +0 -0
- /package/dist/{src/dom → dom}/components/checkbox.js +0 -0
- /package/dist/{src/dom → dom}/components/checkbox.js.map +0 -0
- /package/dist/{src/dom → dom}/components/column.js +0 -0
- /package/dist/{src/dom → dom}/components/divider.js +0 -0
- /package/dist/{src/dom → dom}/components/divider.js.map +0 -0
- /package/dist/{src/dom → dom}/components/heading.js +0 -0
- /package/dist/{src/dom → dom}/components/heading.js.map +0 -0
- /package/dist/{src/dom → dom}/components/image.js +0 -0
- /package/dist/{src/dom → dom}/components/image.js.map +0 -0
- /package/dist/{src/dom → dom}/components/input.js +0 -0
- /package/dist/{src/dom → dom}/components/input.js.map +0 -0
- /package/dist/{src/dom → dom}/components/link.js +0 -0
- /package/dist/{src/dom → dom}/components/link.js.map +0 -0
- /package/dist/{src/dom → dom}/components/list.js +0 -0
- /package/dist/{src/dom → dom}/components/list.js.map +0 -0
- /package/dist/{src/dom → dom}/components/paragraph.js +0 -0
- /package/dist/{src/dom → dom}/components/paragraph.js.map +0 -0
- /package/dist/{src/dom → dom}/components/progressbar.js +0 -0
- /package/dist/{src/dom → dom}/components/progressbar.js.map +0 -0
- /package/dist/{src/dom → dom}/components/router.js +0 -0
- /package/dist/{src/dom → dom}/components/router.js.map +0 -0
- /package/dist/{src/dom → dom}/components/select.js +0 -0
- /package/dist/{src/dom → dom}/components/select.js.map +0 -0
- /package/dist/{src/dom → dom}/components/slider.js +0 -0
- /package/dist/{src/dom → dom}/components/slider.js.map +0 -0
- /package/dist/{src/dom → dom}/components/spacer.js +0 -0
- /package/dist/{src/dom → dom}/components/spacer.js.map +0 -0
- /package/dist/{src/dom → dom}/components/spinner.js +0 -0
- /package/dist/{src/dom → dom}/components/spinner.js.map +0 -0
- /package/dist/{src/dom → dom}/components/switch.js +0 -0
- /package/dist/{src/dom → dom}/components/switch.js.map +0 -0
- /package/dist/{src/dom → dom}/components/textarea.js +0 -0
- /package/dist/{src/dom → dom}/components/textarea.js.map +0 -0
- /package/dist/{src/dom → dom}/components/video.js +0 -0
- /package/dist/{src/dom → dom}/components/video.js.map +0 -0
- /package/dist/{src/dom → dom}/element-data.js +0 -0
- /package/dist/{src/dom → dom}/element-data.js.map +0 -0
package/package.json
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypen-space/web",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"description": "Hypen web renderers - DOM and Canvas rendering for browsers",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/
|
|
7
|
-
"module": "./dist/
|
|
8
|
-
"types": "./dist/
|
|
9
|
-
"browser": "./dist/
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"browser": "./dist/index.js",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
|
-
"types": "./dist/
|
|
13
|
-
"browser": "./dist/
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"browser": "./dist/index.js",
|
|
14
14
|
"bun": "./src/index.ts",
|
|
15
|
-
"import": "./dist/
|
|
16
|
-
"default": "./dist/
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
17
|
},
|
|
18
18
|
"./dom": {
|
|
19
|
-
"types": "./dist/
|
|
20
|
-
"browser": "./dist/
|
|
19
|
+
"types": "./dist/dom/index.d.ts",
|
|
20
|
+
"browser": "./dist/dom/index.js",
|
|
21
21
|
"bun": "./src/dom/index.ts",
|
|
22
|
-
"import": "./dist/
|
|
23
|
-
"default": "./dist/
|
|
22
|
+
"import": "./dist/dom/index.js",
|
|
23
|
+
"default": "./dist/dom/index.js"
|
|
24
24
|
},
|
|
25
25
|
"./canvas": {
|
|
26
|
-
"types": "./dist/
|
|
27
|
-
"browser": "./dist/
|
|
26
|
+
"types": "./dist/canvas/index.d.ts",
|
|
27
|
+
"browser": "./dist/canvas/index.js",
|
|
28
28
|
"bun": "./src/canvas/index.ts",
|
|
29
|
-
"import": "./dist/
|
|
30
|
-
"default": "./dist/
|
|
29
|
+
"import": "./dist/canvas/index.js",
|
|
30
|
+
"default": "./dist/canvas/index.js"
|
|
31
31
|
},
|
|
32
32
|
"./hypen": {
|
|
33
|
-
"types": "./dist/
|
|
34
|
-
"browser": "./dist/
|
|
33
|
+
"types": "./dist/hypen.d.ts",
|
|
34
|
+
"browser": "./dist/hypen.js",
|
|
35
35
|
"bun": "./src/hypen.ts",
|
|
36
|
-
"import": "./dist/
|
|
37
|
-
"default": "./dist/
|
|
36
|
+
"import": "./dist/hypen.js",
|
|
37
|
+
"default": "./dist/hypen.js"
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
"files": [
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"clean": "rm -rf dist"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@hypen-space/core": "^0.3.
|
|
51
|
+
"@hypen-space/core": "^0.3.10"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/bun": "latest",
|
package/src/canvas/renderer.ts
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { Renderer, Patch } from "@hypen-space/core";
|
|
8
|
+
import { frameworkLoggers } from "@hypen-space/core";
|
|
9
|
+
|
|
10
|
+
const log = frameworkLoggers.canvas;
|
|
8
11
|
|
|
9
12
|
// Interface for the engine that canvas renderer needs
|
|
10
13
|
interface IEngine {
|
|
@@ -130,7 +133,7 @@ export class CanvasRenderer implements Renderer {
|
|
|
130
133
|
private applyPatch(patch: Patch): void {
|
|
131
134
|
switch (patch.type) {
|
|
132
135
|
case "create":
|
|
133
|
-
this.onCreate(patch.id!,
|
|
136
|
+
this.onCreate(patch.id!, patch.elementType!, patch.props || {});
|
|
134
137
|
break;
|
|
135
138
|
|
|
136
139
|
case "setProp":
|
|
@@ -142,11 +145,11 @@ export class CanvasRenderer implements Renderer {
|
|
|
142
145
|
break;
|
|
143
146
|
|
|
144
147
|
case "insert":
|
|
145
|
-
this.onInsert(
|
|
148
|
+
this.onInsert(patch.parentId!, patch.id!, patch.beforeId);
|
|
146
149
|
break;
|
|
147
150
|
|
|
148
151
|
case "move":
|
|
149
|
-
this.onMove(
|
|
152
|
+
this.onMove(patch.parentId!, patch.id!, patch.beforeId);
|
|
150
153
|
break;
|
|
151
154
|
|
|
152
155
|
case "remove":
|
|
@@ -352,7 +355,7 @@ export class CanvasRenderer implements Renderer {
|
|
|
352
355
|
const elapsed = performance.now() - startTime;
|
|
353
356
|
this.frameCount++;
|
|
354
357
|
if (performance.now() - this.lastFrameTime > 1000) {
|
|
355
|
-
|
|
358
|
+
log.debug(`Canvas FPS: ${this.frameCount}, Last frame: ${elapsed.toFixed(2)}ms`);
|
|
356
359
|
this.frameCount = 0;
|
|
357
360
|
this.lastFrameTime = performance.now();
|
|
358
361
|
}
|
package/src/canvas/text.ts
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
import type { FontStyle, TextMetrics, TextStyle } from "./types.js";
|
|
8
8
|
import { createFontString } from "./utils.js";
|
|
9
|
+
import { frameworkLoggers } from "@hypen-space/core";
|
|
10
|
+
|
|
11
|
+
const log = frameworkLoggers.canvas;
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* Text metrics cache
|
|
@@ -166,7 +169,7 @@ export async function loadFont(fontFamily: string, fontWeight: string | number =
|
|
|
166
169
|
try {
|
|
167
170
|
await document.fonts.load(font);
|
|
168
171
|
} catch (error) {
|
|
169
|
-
|
|
172
|
+
log.warn(`Failed to load font: ${font}`, error);
|
|
170
173
|
}
|
|
171
174
|
}
|
|
172
175
|
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
disposableTimeout,
|
|
13
13
|
type Disposable,
|
|
14
14
|
} from "@hypen-space/core/disposable";
|
|
15
|
+
import { frameworkLoggers } from "@hypen-space/core";
|
|
15
16
|
import {
|
|
16
17
|
type IEngine,
|
|
17
18
|
getEngine,
|
|
@@ -22,6 +23,8 @@ import {
|
|
|
22
23
|
setKeyTarget,
|
|
23
24
|
} from "../element-data.js";
|
|
24
25
|
|
|
26
|
+
const log = frameworkLoggers.events;
|
|
27
|
+
|
|
25
28
|
// ============================================================================
|
|
26
29
|
// Types
|
|
27
30
|
// ============================================================================
|
|
@@ -198,10 +201,7 @@ function createEventHandler(
|
|
|
198
201
|
const { actionName, payload: customPayload } = extractActionDetails(value);
|
|
199
202
|
|
|
200
203
|
if (!actionName) {
|
|
201
|
-
|
|
202
|
-
`[EventApplicator] ${eventType} requires an action reference starting with @, got:`,
|
|
203
|
-
value
|
|
204
|
-
);
|
|
204
|
+
log.warn(`${eventType} requires an action reference starting with @, got:`, value);
|
|
205
205
|
return;
|
|
206
206
|
}
|
|
207
207
|
|
|
@@ -278,10 +278,7 @@ function createKeyHandler(defaultKey: string = "Enter"): ApplicatorHandler {
|
|
|
278
278
|
const { actionName, payload: customPayload } = extractActionDetails(value);
|
|
279
279
|
|
|
280
280
|
if (!actionName) {
|
|
281
|
-
|
|
282
|
-
`[EventApplicator] onKey requires an action reference starting with @, got:`,
|
|
283
|
-
value
|
|
284
|
-
);
|
|
281
|
+
log.warn(`onKey requires an action reference starting with @, got:`, value);
|
|
285
282
|
return;
|
|
286
283
|
}
|
|
287
284
|
|
|
@@ -343,10 +340,7 @@ function createLongClickHandler(thresholdMs: number = 500): ApplicatorHandler {
|
|
|
343
340
|
const { actionName, payload: customPayload } = extractActionDetails(value);
|
|
344
341
|
|
|
345
342
|
if (!actionName) {
|
|
346
|
-
|
|
347
|
-
`[EventApplicator] onLongClick requires an action reference starting with @, got:`,
|
|
348
|
-
value
|
|
349
|
-
);
|
|
343
|
+
log.warn(`onLongClick requires an action reference starting with @, got:`, value);
|
|
350
344
|
return;
|
|
351
345
|
}
|
|
352
346
|
|
|
@@ -104,11 +104,15 @@ export const layoutHandlers: Record<string, ApplicatorHandler> = {
|
|
|
104
104
|
// Use .weight(1) to make element take remaining space in Row/Column
|
|
105
105
|
weight: (el, value) => {
|
|
106
106
|
el.style.flex = String(value);
|
|
107
|
+
// Mark element as having flex for CSS parent detection
|
|
108
|
+
el.dataset.hypenFlex = "true";
|
|
107
109
|
},
|
|
108
110
|
|
|
109
111
|
// flex: CSS flex shorthand (kept for CSS compatibility)
|
|
110
112
|
flex: (el, value) => {
|
|
111
113
|
el.style.flex = String(value);
|
|
114
|
+
// Mark element as having flex for CSS parent detection
|
|
115
|
+
el.dataset.hypenFlex = "true";
|
|
112
116
|
},
|
|
113
117
|
|
|
114
118
|
flexGrow: (el, value) => {
|
|
@@ -136,12 +136,13 @@ export const sizeHandlers: Record<string, ApplicatorHandler> = {
|
|
|
136
136
|
},
|
|
137
137
|
|
|
138
138
|
// Fill max width - stretch to fill parent width
|
|
139
|
+
// Note: This only stretches within parent's current width.
|
|
140
|
+
// For full-width behavior, parent Columns also need fillMaxWidth(true).
|
|
139
141
|
fillMaxWidth: (el, value) => {
|
|
140
142
|
if (value === false) return;
|
|
141
143
|
const fraction = typeof value === "number" ? value : 1;
|
|
142
144
|
if (fraction === 1) {
|
|
143
|
-
//
|
|
144
|
-
// and width 100% as fallback for Row and other contexts
|
|
145
|
+
// Use align-self stretch to fill cross-axis in flex containers
|
|
145
146
|
el.style.alignSelf = "stretch";
|
|
146
147
|
el.style.width = "100%";
|
|
147
148
|
el.style.minWidth = "0"; // Prevent flex item from overflowing
|
|
@@ -168,5 +169,9 @@ export const sizeHandlers: Record<string, ApplicatorHandler> = {
|
|
|
168
169
|
const fraction = typeof value === "number" ? value : 1;
|
|
169
170
|
el.style.width = `${fraction * 100}%`;
|
|
170
171
|
el.style.height = `${fraction * 100}%`;
|
|
172
|
+
// Use align-self stretch to fill cross-axis in flex containers
|
|
173
|
+
// This is needed because parent might have alignItems: flex-start (wrap behavior)
|
|
174
|
+
el.style.alignSelf = "stretch";
|
|
175
|
+
el.style.minWidth = "0"; // Prevent flex item from overflowing
|
|
171
176
|
},
|
|
172
177
|
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Center Component - Centers content
|
|
3
|
+
*
|
|
4
|
+
* Center expands to fill available space by default (matching iOS/Android behavior).
|
|
5
|
+
* This is because a Center that wraps to content can't meaningfully center anything.
|
|
6
|
+
* The expansion is constrained by parent's layout rules.
|
|
3
7
|
*/
|
|
4
8
|
|
|
5
9
|
import type { ComponentHandler } from "./index.js";
|
|
@@ -10,6 +14,11 @@ export const centerHandler: ComponentHandler = {
|
|
|
10
14
|
el.style.display = "flex";
|
|
11
15
|
el.style.alignItems = "center";
|
|
12
16
|
el.style.justifyContent = "center";
|
|
17
|
+
// Center expands to fill available space by default
|
|
18
|
+
// This matches iOS which has .frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
19
|
+
el.style.width = "100%";
|
|
20
|
+
el.style.height = "100%";
|
|
21
|
+
el.style.alignSelf = "stretch"; // Needed for flex containers with alignItems: flex-start
|
|
13
22
|
el.dataset.hypenType = "center";
|
|
14
23
|
return el;
|
|
15
24
|
},
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Column Component - Vertical Stack
|
|
3
3
|
*
|
|
4
4
|
* Children wrap to content by default (matching Android/iOS behavior).
|
|
5
|
-
* Column stretches to fill parent's width so children with fillMaxWidth work.
|
|
6
5
|
* Use .fillMaxWidth() on children or .horizontalAlignment("stretch") to stretch.
|
|
7
6
|
*/
|
|
8
7
|
|
|
@@ -15,9 +14,6 @@ export const columnHandler: ComponentHandler = {
|
|
|
15
14
|
el.style.flexDirection = "column";
|
|
16
15
|
// Wrap children to content by default (match iOS/Android behavior)
|
|
17
16
|
el.style.alignItems = "flex-start";
|
|
18
|
-
// Stretch to fill parent's width so children with fillMaxWidth can reference it
|
|
19
|
-
// This matches iOS/Android where Column expands to accommodate children that want to stretch
|
|
20
|
-
el.style.alignSelf = "stretch";
|
|
21
17
|
el.dataset.hypenType = "column";
|
|
22
18
|
return el;
|
|
23
19
|
},
|
|
@@ -1,11 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Grid Component - CSS Grid Layout
|
|
3
|
+
*
|
|
4
|
+
* Grid items stretch to fill their cells by default (matching Android/iOS behavior).
|
|
3
5
|
*/
|
|
4
6
|
|
|
5
7
|
import type { ComponentHandler } from "./index.js";
|
|
6
8
|
|
|
9
|
+
// Inject global styles for grid children
|
|
10
|
+
let gridStylesInjected = false;
|
|
11
|
+
function ensureGridStyles(): void {
|
|
12
|
+
if (gridStylesInjected) return;
|
|
13
|
+
gridStylesInjected = true;
|
|
14
|
+
|
|
15
|
+
const style = document.createElement("style");
|
|
16
|
+
style.id = "hypen-grid-styles";
|
|
17
|
+
style.textContent = `
|
|
18
|
+
/* Grid children stretch to fill cells by default (matches Android behavior) */
|
|
19
|
+
[data-hypen-type="grid"] > * {
|
|
20
|
+
justify-self: stretch;
|
|
21
|
+
align-self: stretch;
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
document.head.appendChild(style);
|
|
25
|
+
}
|
|
26
|
+
|
|
7
27
|
export const gridHandler: ComponentHandler = {
|
|
8
28
|
create(): HTMLElement {
|
|
29
|
+
ensureGridStyles();
|
|
30
|
+
|
|
9
31
|
const el = document.createElement("div");
|
|
10
32
|
el.style.display = "grid";
|
|
11
33
|
el.dataset.hypenType = "grid";
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
import type { ComponentHandler } from "./index.js";
|
|
16
16
|
import { RemoteEngine } from "@hypen-space/core/remote/client";
|
|
17
17
|
import type { Patch } from "@hypen-space/core/remote";
|
|
18
|
+
import { frameworkLoggers } from "@hypen-space/core";
|
|
19
|
+
|
|
20
|
+
const log = frameworkLoggers.remote;
|
|
18
21
|
|
|
19
22
|
// Store active HypenApp instances for cleanup
|
|
20
23
|
const activeInstances = new WeakMap<
|
|
@@ -38,7 +41,7 @@ export const hypenAppHandler: ComponentHandler = {
|
|
|
38
41
|
const url = props["0"] || props.url;
|
|
39
42
|
|
|
40
43
|
if (!url || typeof url !== "string") {
|
|
41
|
-
|
|
44
|
+
log.error("HypenApp: URL is required");
|
|
42
45
|
element.innerHTML = '<div style="color: red;">HypenApp: URL required</div>';
|
|
43
46
|
return;
|
|
44
47
|
}
|
|
@@ -80,20 +83,20 @@ export const hypenAppHandler: ComponentHandler = {
|
|
|
80
83
|
.then(() => {
|
|
81
84
|
// Clear loading state - patches will populate content
|
|
82
85
|
element.innerHTML = "";
|
|
83
|
-
|
|
86
|
+
log.debug(`HypenApp connected to ${url}`);
|
|
84
87
|
})
|
|
85
88
|
.catch((error) => {
|
|
86
89
|
element.innerHTML = `<div style="color: red;">HypenApp: Connection failed - ${error.message}</div>`;
|
|
87
|
-
|
|
90
|
+
log.error("HypenApp connection failed:", error);
|
|
88
91
|
});
|
|
89
92
|
|
|
90
93
|
// Handle disconnection
|
|
91
94
|
engine.onDisconnect(() => {
|
|
92
|
-
|
|
95
|
+
log.debug("HypenApp disconnected");
|
|
93
96
|
});
|
|
94
97
|
|
|
95
98
|
engine.onError((error) => {
|
|
96
|
-
|
|
99
|
+
log.error("HypenApp error:", error);
|
|
97
100
|
});
|
|
98
101
|
|
|
99
102
|
// Cleanup on element removal
|
|
@@ -104,7 +107,7 @@ export const hypenAppHandler: ComponentHandler = {
|
|
|
104
107
|
engine.disconnect();
|
|
105
108
|
activeInstances.delete(element);
|
|
106
109
|
observer.disconnect();
|
|
107
|
-
|
|
110
|
+
log.debug("HypenApp cleaned up");
|
|
108
111
|
return;
|
|
109
112
|
}
|
|
110
113
|
}
|
|
@@ -131,7 +134,7 @@ function applyPatches(
|
|
|
131
134
|
for (const patch of patches) {
|
|
132
135
|
switch (patch.type) {
|
|
133
136
|
case "create": {
|
|
134
|
-
const el = createElement(
|
|
137
|
+
const el = createElement(patch.elementType!, patch.props || {});
|
|
135
138
|
el.dataset.hypenId = patch.id!;
|
|
136
139
|
(el as any).__hypenEngine = engine;
|
|
137
140
|
nodes.set(patch.id!, el);
|
|
@@ -155,10 +158,10 @@ function applyPatches(
|
|
|
155
158
|
}
|
|
156
159
|
|
|
157
160
|
case "insert": {
|
|
158
|
-
const parentId =
|
|
161
|
+
const parentId = patch.parentId;
|
|
159
162
|
const parent = parentId === "root" ? container : nodes.get(parentId);
|
|
160
163
|
const child = nodes.get(patch.id!);
|
|
161
|
-
const beforeId =
|
|
164
|
+
const beforeId = patch.beforeId;
|
|
162
165
|
|
|
163
166
|
if (parent && child) {
|
|
164
167
|
if (parentId === "root") {
|
|
@@ -180,10 +183,10 @@ function applyPatches(
|
|
|
180
183
|
}
|
|
181
184
|
|
|
182
185
|
case "move": {
|
|
183
|
-
const parentId =
|
|
186
|
+
const parentId = patch.parentId;
|
|
184
187
|
const parent = parentId === "root" ? container : nodes.get(parentId);
|
|
185
188
|
const child = nodes.get(patch.id!);
|
|
186
|
-
const beforeId =
|
|
189
|
+
const beforeId = patch.beforeId;
|
|
187
190
|
|
|
188
191
|
if (parent && child) {
|
|
189
192
|
if (beforeId) {
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { ComponentHandler } from "./index.js";
|
|
7
|
+
import { frameworkLoggers } from "@hypen-space/core";
|
|
8
|
+
|
|
9
|
+
const log = frameworkLoggers.router;
|
|
7
10
|
|
|
8
11
|
export const routeHandler: ComponentHandler = {
|
|
9
12
|
create(): HTMLElement {
|
|
@@ -31,7 +34,7 @@ export const routeHandler: ComponentHandler = {
|
|
|
31
34
|
el.dataset.routeComponent = String(componentName);
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
log.debug(`Route created: path="${path}", lazy=${isLazy}, component="${el.dataset.routeComponent || 'none'}"`);
|
|
35
38
|
},
|
|
36
39
|
};
|
|
37
40
|
|
|
@@ -6,8 +6,28 @@
|
|
|
6
6
|
|
|
7
7
|
import type { ComponentHandler } from "./index.js";
|
|
8
8
|
|
|
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
|
+
|
|
15
|
+
const style = document.createElement("style");
|
|
16
|
+
style.id = "hypen-row-styles";
|
|
17
|
+
style.textContent = `
|
|
18
|
+
/* Row expands to fill width when it has children with flex/weight */
|
|
19
|
+
/* This matches iOS/Android behavior where weighted children cause parent to expand */
|
|
20
|
+
[data-hypen-type="row"]:has(> [data-hypen-flex]) {
|
|
21
|
+
width: 100%;
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
document.head.appendChild(style);
|
|
25
|
+
}
|
|
26
|
+
|
|
9
27
|
export const rowHandler: ComponentHandler = {
|
|
10
28
|
create(): HTMLElement {
|
|
29
|
+
ensureRowStyles();
|
|
30
|
+
|
|
11
31
|
const el = document.createElement("div");
|
|
12
32
|
el.style.display = "flex";
|
|
13
33
|
el.style.flexDirection = "row";
|
package/src/dom/debug.ts
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* Provides heatmap overlays to visualize re-renders
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { frameworkLoggers } from "@hypen-space/core";
|
|
8
|
+
|
|
9
|
+
const log = frameworkLoggers.debug;
|
|
10
|
+
|
|
7
11
|
export interface DebugConfig {
|
|
8
12
|
/** Enable debug mode */
|
|
9
13
|
enabled: boolean;
|
|
@@ -57,7 +61,7 @@ export class RerenderTracker {
|
|
|
57
61
|
return;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
|
|
64
|
+
log.debug(`Tracking re-render: ${id} - ${patchType}`);
|
|
61
65
|
|
|
62
66
|
// Increment render count
|
|
63
67
|
const currentCount = this.renderCounts.get(id) || 0;
|
|
@@ -78,7 +82,7 @@ export class RerenderTracker {
|
|
|
78
82
|
this.config.maxOpacity
|
|
79
83
|
);
|
|
80
84
|
|
|
81
|
-
|
|
85
|
+
log.debug(`Updating heatmap for ${id}, count: ${renderCount}, opacity: ${opacity}`);
|
|
82
86
|
|
|
83
87
|
// For inline elements or text, use a simpler approach: background color + outline
|
|
84
88
|
const isInline = window.getComputedStyle(element).display.includes('inline');
|
package/src/dom/events.ts
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* Handles DOM event binding and action dispatching
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { frameworkLoggers } from "@hypen-space/core";
|
|
8
|
+
|
|
9
|
+
const log = frameworkLoggers.events;
|
|
10
|
+
|
|
7
11
|
// Interface for the engine that EventManager needs
|
|
8
12
|
interface IEngine {
|
|
9
13
|
dispatchAction(name: string, payload?: any): void;
|
|
@@ -29,13 +33,13 @@ export class EventManager {
|
|
|
29
33
|
attach(elementId: string, element: HTMLElement, eventName: string, actionName: string): void {
|
|
30
34
|
// Convert onClick to click for DOM events
|
|
31
35
|
const domEventName = eventName === "onClick" ? "click" : eventName;
|
|
32
|
-
|
|
36
|
+
log.debug(`Attaching ${eventName} (DOM: ${domEventName}) to element ${elementId}, action: ${actionName}`);
|
|
33
37
|
|
|
34
38
|
// Create event listener that dispatches action
|
|
35
39
|
const listener = (event: Event) => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
log.debug(`Event fired: ${eventName} on ${elementId}, dispatching action: ${actionName}`);
|
|
41
|
+
log.debug(`Event object:`, event);
|
|
42
|
+
log.debug(`Element:`, element);
|
|
39
43
|
|
|
40
44
|
// Prevent default for certain events
|
|
41
45
|
if (eventName === "submit" || (eventName === "click" && element.tagName === "A")) {
|
|
@@ -44,15 +48,15 @@ export class EventManager {
|
|
|
44
48
|
|
|
45
49
|
// Extract event data
|
|
46
50
|
const payload = this.extractEventData(event, element);
|
|
47
|
-
|
|
51
|
+
log.debug(`Event payload:`, payload);
|
|
48
52
|
|
|
49
53
|
// Dispatch action to engine
|
|
50
|
-
|
|
54
|
+
log.debug(`Calling engine.dispatchAction(${actionName})`);
|
|
51
55
|
try {
|
|
52
56
|
this.engine.dispatchAction(actionName, payload);
|
|
53
|
-
|
|
57
|
+
log.debug(`dispatchAction succeeded`);
|
|
54
58
|
} catch (error) {
|
|
55
|
-
|
|
59
|
+
log.error(`dispatchAction failed:`, error);
|
|
56
60
|
}
|
|
57
61
|
};
|
|
58
62
|
|
|
@@ -66,18 +70,18 @@ export class EventManager {
|
|
|
66
70
|
|
|
67
71
|
// Attach to DOM
|
|
68
72
|
element.addEventListener(domEventName, listener);
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
log.debug(`Listener attached to DOM for ${domEventName}`);
|
|
74
|
+
log.debug(`Element details:`, {
|
|
71
75
|
tagName: element.tagName,
|
|
72
76
|
id: element.id,
|
|
73
77
|
dataset: element.dataset,
|
|
74
78
|
textContent: element.textContent?.substring(0, 50)
|
|
75
79
|
});
|
|
76
|
-
|
|
80
|
+
|
|
77
81
|
// Test: Add a simple click listener to verify DOM events work
|
|
78
82
|
if (domEventName === "click") {
|
|
79
83
|
element.addEventListener("click", (e) => {
|
|
80
|
-
|
|
84
|
+
log.debug(`Raw DOM click detected on ${element.tagName}`, e);
|
|
81
85
|
});
|
|
82
86
|
}
|
|
83
87
|
}
|
package/src/dom/renderer.ts
CHANGED
|
@@ -8,6 +8,9 @@ import type { Patch } from "@hypen-space/core";
|
|
|
8
8
|
import type { HypenModuleInstance, RouterContext } from "@hypen-space/core";
|
|
9
9
|
import type { HypenRouter } from "@hypen-space/core";
|
|
10
10
|
import type { HypenGlobalContext } from "@hypen-space/core";
|
|
11
|
+
import { frameworkLoggers } from "@hypen-space/core";
|
|
12
|
+
|
|
13
|
+
const log = frameworkLoggers.renderer;
|
|
11
14
|
import { ComponentRegistry } from "./components/index.js";
|
|
12
15
|
import { ApplicatorRegistry } from "./applicators/index.js";
|
|
13
16
|
import { canvasHandler, canvasApplicators } from "./canvas/index.js";
|
|
@@ -67,7 +70,7 @@ export class DOMRenderer {
|
|
|
67
70
|
* Update state and interpolate text content
|
|
68
71
|
*/
|
|
69
72
|
updateState(state: Record<string, any>): void {
|
|
70
|
-
|
|
73
|
+
log.debug("Updating state:", state);
|
|
71
74
|
this.currentState = state;
|
|
72
75
|
this.interpolateAllText();
|
|
73
76
|
}
|
|
@@ -77,7 +80,7 @@ export class DOMRenderer {
|
|
|
77
80
|
*/
|
|
78
81
|
private mergeComponentState(componentState: Record<string, any>): void {
|
|
79
82
|
this.currentState = { ...this.currentState, ...componentState };
|
|
80
|
-
|
|
83
|
+
log.debug("Merged state:", this.currentState);
|
|
81
84
|
this.interpolateAllText();
|
|
82
85
|
}
|
|
83
86
|
|
|
@@ -124,7 +127,7 @@ export class DOMRenderer {
|
|
|
124
127
|
private applyPatch(patch: Patch): void {
|
|
125
128
|
switch (patch.type) {
|
|
126
129
|
case "create":
|
|
127
|
-
this.onCreate(patch.id!,
|
|
130
|
+
this.onCreate(patch.id!, patch.elementType!, patch.props || {});
|
|
128
131
|
break;
|
|
129
132
|
case "setProp":
|
|
130
133
|
this.onSetProp(patch.id!, patch.name!, patch.value);
|
|
@@ -133,10 +136,10 @@ export class DOMRenderer {
|
|
|
133
136
|
this.onSetText(patch.id!, patch.text!);
|
|
134
137
|
break;
|
|
135
138
|
case "insert":
|
|
136
|
-
this.onInsert(
|
|
139
|
+
this.onInsert(patch.parentId!, patch.id!, patch.beforeId);
|
|
137
140
|
break;
|
|
138
141
|
case "move":
|
|
139
|
-
this.onMove(
|
|
142
|
+
this.onMove(patch.parentId!, patch.id!, patch.beforeId);
|
|
140
143
|
break;
|
|
141
144
|
case "remove":
|
|
142
145
|
this.onRemove(patch.id!);
|
|
@@ -160,7 +163,7 @@ export class DOMRenderer {
|
|
|
160
163
|
fallback.dataset.hypenType = elementType.toLowerCase();
|
|
161
164
|
fallback.style.display = "contents"; // Make container transparent in layout
|
|
162
165
|
element = fallback;
|
|
163
|
-
|
|
166
|
+
log.debug(`Unknown component "${elementType}" - using transparent container`);
|
|
164
167
|
}
|
|
165
168
|
|
|
166
169
|
element.dataset.hypenType = elementType.toLowerCase();
|
|
@@ -194,7 +197,7 @@ export class DOMRenderer {
|
|
|
194
197
|
if (elementType === "input") {
|
|
195
198
|
const inputEl = element as HTMLInputElement;
|
|
196
199
|
inputEl.value = String(value);
|
|
197
|
-
|
|
200
|
+
log.debug(`Updated input value: "${value}"`);
|
|
198
201
|
return;
|
|
199
202
|
}
|
|
200
203
|
|
|
@@ -217,7 +220,7 @@ export class DOMRenderer {
|
|
|
217
220
|
// If current template isn't a template, keep it in sync.
|
|
218
221
|
element.dataset.textTemplate = nextText;
|
|
219
222
|
}
|
|
220
|
-
|
|
223
|
+
log.debug(`Updated text content: "${value}"`);
|
|
221
224
|
return;
|
|
222
225
|
}
|
|
223
226
|
|
|
@@ -242,7 +245,7 @@ export class DOMRenderer {
|
|
|
242
245
|
const parent = parentId === "root" ? this.container : this.nodes.get(parentId);
|
|
243
246
|
const child = this.nodes.get(id);
|
|
244
247
|
|
|
245
|
-
|
|
248
|
+
log.debug(`Inserting ${id} into ${parentId}`, {
|
|
246
249
|
parent: parent ? `${parent.tagName}#${parent.id || 'no-id'}` : 'null',
|
|
247
250
|
child: child ? `${child.tagName}#${child.id || 'no-id'}` : 'null',
|
|
248
251
|
childText: child?.textContent?.substring(0, 20)
|