@hypen-space/web 0.2.0
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/chunk-2s02mkzs.js +32 -0
- package/dist/chunk-2s02mkzs.js.map +9 -0
- package/dist/src/canvas/accessibility.js +152 -0
- package/dist/src/canvas/accessibility.js.map +10 -0
- package/dist/src/canvas/events.js +198 -0
- package/dist/src/canvas/events.js.map +10 -0
- package/dist/src/canvas/index.js +28 -0
- package/dist/src/canvas/index.js.map +9 -0
- package/dist/src/canvas/input.js +132 -0
- package/dist/src/canvas/input.js.map +10 -0
- package/dist/src/canvas/layout.js +309 -0
- package/dist/src/canvas/layout.js.map +10 -0
- package/dist/src/canvas/paint.js +878 -0
- package/dist/src/canvas/paint.js.map +10 -0
- package/dist/src/canvas/renderer.js +276 -0
- package/dist/src/canvas/renderer.js.map +10 -0
- package/dist/src/canvas/text.js +118 -0
- package/dist/src/canvas/text.js.map +10 -0
- package/dist/src/canvas/types.js +2 -0
- package/dist/src/canvas/types.js.map +9 -0
- package/dist/src/canvas/utils.js +139 -0
- package/dist/src/canvas/utils.js.map +10 -0
- package/dist/src/dom/applicators/advanced-layout.js +111 -0
- package/dist/src/dom/applicators/advanced-layout.js.map +10 -0
- package/dist/src/dom/applicators/background.js +54 -0
- package/dist/src/dom/applicators/background.js.map +10 -0
- package/dist/src/dom/applicators/border.js +33 -0
- package/dist/src/dom/applicators/border.js.map +10 -0
- package/dist/src/dom/applicators/color.js +36 -0
- package/dist/src/dom/applicators/color.js.map +10 -0
- package/dist/src/dom/applicators/display.js +57 -0
- package/dist/src/dom/applicators/display.js.map +10 -0
- package/dist/src/dom/applicators/effects.js +89 -0
- package/dist/src/dom/applicators/effects.js.map +10 -0
- package/dist/src/dom/applicators/events.js +518 -0
- package/dist/src/dom/applicators/events.js.map +10 -0
- package/dist/src/dom/applicators/font.js +39 -0
- package/dist/src/dom/applicators/font.js.map +10 -0
- package/dist/src/dom/applicators/index.js +296 -0
- package/dist/src/dom/applicators/index.js.map +10 -0
- package/dist/src/dom/applicators/layout.js +86 -0
- package/dist/src/dom/applicators/layout.js.map +10 -0
- package/dist/src/dom/applicators/margin.js +32 -0
- package/dist/src/dom/applicators/margin.js.map +10 -0
- package/dist/src/dom/applicators/padding.js +35 -0
- package/dist/src/dom/applicators/padding.js.map +10 -0
- package/dist/src/dom/applicators/size.js +42 -0
- package/dist/src/dom/applicators/size.js.map +10 -0
- package/dist/src/dom/applicators/transform.js +92 -0
- package/dist/src/dom/applicators/transform.js.map +10 -0
- package/dist/src/dom/applicators/transition.js +66 -0
- package/dist/src/dom/applicators/transition.js.map +10 -0
- package/dist/src/dom/applicators/typography.js +87 -0
- package/dist/src/dom/applicators/typography.js.map +10 -0
- package/dist/src/dom/canvas/index.js +50 -0
- package/dist/src/dom/canvas/index.js.map +10 -0
- package/dist/src/dom/components/audio.js +48 -0
- package/dist/src/dom/components/audio.js.map +10 -0
- package/dist/src/dom/components/avatar.js +58 -0
- package/dist/src/dom/components/avatar.js.map +10 -0
- package/dist/src/dom/components/badge.js +55 -0
- package/dist/src/dom/components/badge.js.map +10 -0
- package/dist/src/dom/components/button.js +29 -0
- package/dist/src/dom/components/button.js.map +10 -0
- package/dist/src/dom/components/card.js +33 -0
- package/dist/src/dom/components/card.js.map +10 -0
- package/dist/src/dom/components/center.js +32 -0
- package/dist/src/dom/components/center.js.map +10 -0
- package/dist/src/dom/components/checkbox.js +54 -0
- package/dist/src/dom/components/checkbox.js.map +10 -0
- package/dist/src/dom/components/column.js +31 -0
- package/dist/src/dom/components/column.js.map +10 -0
- package/dist/src/dom/components/container.js +29 -0
- package/dist/src/dom/components/container.js.map +10 -0
- package/dist/src/dom/components/divider.js +45 -0
- package/dist/src/dom/components/divider.js.map +10 -0
- package/dist/src/dom/components/grid.js +44 -0
- package/dist/src/dom/components/grid.js.map +10 -0
- package/dist/src/dom/components/heading.js +47 -0
- package/dist/src/dom/components/heading.js.map +10 -0
- package/dist/src/dom/components/image.js +39 -0
- package/dist/src/dom/components/image.js.map +10 -0
- package/dist/src/dom/components/index.js +217 -0
- package/dist/src/dom/components/index.js.map +10 -0
- package/dist/src/dom/components/input.js +41 -0
- package/dist/src/dom/components/input.js.map +10 -0
- package/dist/src/dom/components/link.js +42 -0
- package/dist/src/dom/components/link.js.map +10 -0
- package/dist/src/dom/components/list.js +42 -0
- package/dist/src/dom/components/list.js.map +10 -0
- package/dist/src/dom/components/paragraph.js +35 -0
- package/dist/src/dom/components/paragraph.js.map +10 -0
- package/dist/src/dom/components/progressbar.js +57 -0
- package/dist/src/dom/components/progressbar.js.map +10 -0
- package/dist/src/dom/components/route.js +44 -0
- package/dist/src/dom/components/route.js.map +10 -0
- package/dist/src/dom/components/router.js +33 -0
- package/dist/src/dom/components/router.js.map +10 -0
- package/dist/src/dom/components/row.js +31 -0
- package/dist/src/dom/components/row.js.map +10 -0
- package/dist/src/dom/components/select.js +57 -0
- package/dist/src/dom/components/select.js.map +10 -0
- package/dist/src/dom/components/slider.js +48 -0
- package/dist/src/dom/components/slider.js.map +10 -0
- package/dist/src/dom/components/spacer.js +30 -0
- package/dist/src/dom/components/spacer.js.map +10 -0
- package/dist/src/dom/components/spinner.js +65 -0
- package/dist/src/dom/components/spinner.js.map +10 -0
- package/dist/src/dom/components/stack.js +45 -0
- package/dist/src/dom/components/stack.js.map +10 -0
- package/dist/src/dom/components/switch.js +83 -0
- package/dist/src/dom/components/switch.js.map +10 -0
- package/dist/src/dom/components/text.js +37 -0
- package/dist/src/dom/components/text.js.map +10 -0
- package/dist/src/dom/components/textarea.js +51 -0
- package/dist/src/dom/components/textarea.js.map +10 -0
- package/dist/src/dom/components/video.js +51 -0
- package/dist/src/dom/components/video.js.map +10 -0
- package/dist/src/dom/debug.js +170 -0
- package/dist/src/dom/debug.js.map +10 -0
- package/dist/src/dom/events.js +112 -0
- package/dist/src/dom/events.js.map +10 -0
- package/dist/src/dom/index.js +73 -0
- package/dist/src/dom/index.js.map +9 -0
- package/dist/src/dom/renderer.js +277 -0
- package/dist/src/dom/renderer.js.map +10 -0
- package/dist/src/index.js +89 -0
- package/dist/src/index.js.map +9 -0
- package/package.json +84 -0
- package/src/canvas/QUICKSTART.md +421 -0
- package/src/canvas/README.md +376 -0
- package/src/canvas/accessibility.ts +218 -0
- package/src/canvas/events.ts +307 -0
- package/src/canvas/index.ts +35 -0
- package/src/canvas/input.ts +210 -0
- package/src/canvas/layout.ts +401 -0
- package/src/canvas/paint.ts +1321 -0
- package/src/canvas/renderer.ts +422 -0
- package/src/canvas/text.ts +182 -0
- package/src/canvas/types.ts +137 -0
- package/src/canvas/utils.ts +218 -0
- package/src/dom/README.md +265 -0
- package/src/dom/applicators/advanced-layout.ts +128 -0
- package/src/dom/applicators/background.ts +50 -0
- package/src/dom/applicators/border.ts +19 -0
- package/src/dom/applicators/color.ts +23 -0
- package/src/dom/applicators/display.ts +54 -0
- package/src/dom/applicators/effects.ts +97 -0
- package/src/dom/applicators/events.ts +689 -0
- package/src/dom/applicators/font.ts +27 -0
- package/src/dom/applicators/index.ts +354 -0
- package/src/dom/applicators/layout.ts +92 -0
- package/src/dom/applicators/margin.ts +18 -0
- package/src/dom/applicators/padding.ts +18 -0
- package/src/dom/applicators/size.ts +31 -0
- package/src/dom/applicators/transform.ts +93 -0
- package/src/dom/applicators/transition.ts +65 -0
- package/src/dom/applicators/typography.ts +91 -0
- package/src/dom/canvas/index.ts +60 -0
- package/src/dom/components/audio.ts +45 -0
- package/src/dom/components/avatar.ts +49 -0
- package/src/dom/components/badge.ts +45 -0
- package/src/dom/components/button.ts +13 -0
- package/src/dom/components/card.ts +19 -0
- package/src/dom/components/center.ts +16 -0
- package/src/dom/components/checkbox.ts +54 -0
- package/src/dom/components/column.ts +15 -0
- package/src/dom/components/container.ts +13 -0
- package/src/dom/components/divider.ts +37 -0
- package/src/dom/components/grid.ts +40 -0
- package/src/dom/components/heading.ts +41 -0
- package/src/dom/components/image.ts +27 -0
- package/src/dom/components/index.ts +115 -0
- package/src/dom/components/input.ts +29 -0
- package/src/dom/components/link.ts +35 -0
- package/src/dom/components/list.ts +30 -0
- package/src/dom/components/paragraph.ts +23 -0
- package/src/dom/components/progressbar.ts +51 -0
- package/src/dom/components/route.ts +37 -0
- package/src/dom/components/router.ts +22 -0
- package/src/dom/components/row.ts +15 -0
- package/src/dom/components/select.ts +56 -0
- package/src/dom/components/slider.ts +45 -0
- package/src/dom/components/spacer.ts +16 -0
- package/src/dom/components/spinner.ts +60 -0
- package/src/dom/components/stack.ts +34 -0
- package/src/dom/components/switch.ts +86 -0
- package/src/dom/components/text.ts +24 -0
- package/src/dom/components/textarea.ts +50 -0
- package/src/dom/components/video.ts +50 -0
- package/src/dom/debug.ts +247 -0
- package/src/dom/events.ts +168 -0
- package/src/dom/index.ts +11 -0
- package/src/dom/renderer.ts +327 -0
- package/src/index.ts +56 -0
package/src/dom/debug.ts
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug utilities for DOM rendering visualization
|
|
3
|
+
*
|
|
4
|
+
* Provides heatmap overlays to visualize re-renders
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface DebugConfig {
|
|
8
|
+
/** Enable debug mode */
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
/** Show heatmap overlays on re-renders */
|
|
11
|
+
showHeatmap: boolean;
|
|
12
|
+
/** Increment per re-render (default: 5%) */
|
|
13
|
+
heatmapIncrement: number;
|
|
14
|
+
/** Maximum opacity for heatmap (default: 0.8) */
|
|
15
|
+
maxOpacity: number;
|
|
16
|
+
/** Fade out duration in ms (0 to disable) */
|
|
17
|
+
fadeOutDuration: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const defaultDebugConfig: DebugConfig = {
|
|
21
|
+
enabled: false,
|
|
22
|
+
showHeatmap: true,
|
|
23
|
+
heatmapIncrement: 5,
|
|
24
|
+
maxOpacity: 0.8,
|
|
25
|
+
fadeOutDuration: 2000,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Tracks re-render counts for each element
|
|
30
|
+
*/
|
|
31
|
+
export class RerenderTracker {
|
|
32
|
+
private renderCounts = new Map<string, number>();
|
|
33
|
+
private overlays = new Map<string, HTMLDivElement>();
|
|
34
|
+
private config: DebugConfig;
|
|
35
|
+
|
|
36
|
+
constructor(config: DebugConfig = defaultDebugConfig) {
|
|
37
|
+
this.config = config;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Update the configuration
|
|
42
|
+
*/
|
|
43
|
+
setConfig(config: Partial<DebugConfig>): void {
|
|
44
|
+
this.config = { ...this.config, ...config };
|
|
45
|
+
|
|
46
|
+
// If debug mode is disabled, clean up all overlays
|
|
47
|
+
if (!this.config.enabled) {
|
|
48
|
+
this.cleanup();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Track a re-render for an element
|
|
54
|
+
*/
|
|
55
|
+
trackRerender(id: string, element: HTMLElement, patchType: string): void {
|
|
56
|
+
if (!this.config.enabled || !this.config.showHeatmap) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log(`๐ฅ [Debug] Tracking re-render: ${id} - ${patchType}`);
|
|
61
|
+
|
|
62
|
+
// Increment render count
|
|
63
|
+
const currentCount = this.renderCounts.get(id) || 0;
|
|
64
|
+
const newCount = currentCount + 1;
|
|
65
|
+
this.renderCounts.set(id, newCount);
|
|
66
|
+
|
|
67
|
+
// Create or update heatmap overlay
|
|
68
|
+
this.updateHeatmap(id, element, newCount, patchType);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Create or update the heatmap overlay for an element
|
|
73
|
+
*/
|
|
74
|
+
private updateHeatmap(id: string, element: HTMLElement, renderCount: number, patchType: string): void {
|
|
75
|
+
// Calculate opacity based on render count (increment by heatmapIncrement% each time)
|
|
76
|
+
const opacity = Math.min(
|
|
77
|
+
(renderCount * this.config.heatmapIncrement) / 100,
|
|
78
|
+
this.config.maxOpacity
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
console.log(`๐ฅ [Debug] Updating heatmap for ${id}, count: ${renderCount}, opacity: ${opacity}`);
|
|
82
|
+
|
|
83
|
+
// For inline elements or text, use a simpler approach: background color + outline
|
|
84
|
+
const isInline = window.getComputedStyle(element).display.includes('inline');
|
|
85
|
+
|
|
86
|
+
if (isInline || element.tagName === 'SPAN') {
|
|
87
|
+
// Store original styles if not already stored
|
|
88
|
+
if (!element.dataset.hypenDebugOriginalBg) {
|
|
89
|
+
element.dataset.hypenDebugOriginalBg = element.style.backgroundColor || '';
|
|
90
|
+
element.dataset.hypenDebugOriginalOutline = element.style.outline || '';
|
|
91
|
+
element.dataset.hypenDebugOriginalPosition = element.style.position || '';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Apply red background and outline directly to the element
|
|
95
|
+
element.style.backgroundColor = `rgba(255, 0, 0, ${Math.max(opacity, 0.15)})`;
|
|
96
|
+
element.style.outline = `2px solid rgba(255, 0, 0, ${Math.max(opacity + 0.2, 0.3)})`;
|
|
97
|
+
element.style.outlineOffset = '2px';
|
|
98
|
+
element.style.position = 'relative';
|
|
99
|
+
|
|
100
|
+
// Add count badge as ::before pseudo-element or data attribute
|
|
101
|
+
element.setAttribute('data-hypen-renders', `${renderCount}ร ${patchType}`);
|
|
102
|
+
|
|
103
|
+
// Add CSS for the badge if not already added
|
|
104
|
+
if (!document.getElementById('hypen-debug-styles')) {
|
|
105
|
+
const style = document.createElement('style');
|
|
106
|
+
style.id = 'hypen-debug-styles';
|
|
107
|
+
style.textContent = `
|
|
108
|
+
[data-hypen-renders]::before {
|
|
109
|
+
content: attr(data-hypen-renders);
|
|
110
|
+
position: absolute;
|
|
111
|
+
top: -18px;
|
|
112
|
+
left: 0;
|
|
113
|
+
background: rgba(255, 0, 0, 0.9);
|
|
114
|
+
color: white;
|
|
115
|
+
padding: 2px 6px;
|
|
116
|
+
font-size: 10px;
|
|
117
|
+
font-family: 'Courier New', monospace;
|
|
118
|
+
font-weight: bold;
|
|
119
|
+
border-radius: 3px;
|
|
120
|
+
z-index: 999999;
|
|
121
|
+
pointer-events: none;
|
|
122
|
+
white-space: nowrap;
|
|
123
|
+
text-shadow: none;
|
|
124
|
+
}
|
|
125
|
+
`;
|
|
126
|
+
document.head.appendChild(style);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Store in overlays map for cleanup
|
|
130
|
+
this.overlays.set(id, element as any);
|
|
131
|
+
|
|
132
|
+
// Fade out after duration if enabled
|
|
133
|
+
if (this.config.fadeOutDuration > 0) {
|
|
134
|
+
setTimeout(() => {
|
|
135
|
+
const originalBg = element.dataset.hypenDebugOriginalBg || '';
|
|
136
|
+
const originalOutline = element.dataset.hypenDebugOriginalOutline || '';
|
|
137
|
+
element.style.backgroundColor = originalBg;
|
|
138
|
+
element.style.outline = originalOutline;
|
|
139
|
+
element.style.opacity = '1';
|
|
140
|
+
}, this.config.fadeOutDuration);
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
// For block elements, use overlay approach
|
|
144
|
+
let overlay = this.overlays.get(id);
|
|
145
|
+
|
|
146
|
+
if (!overlay) {
|
|
147
|
+
overlay = document.createElement("div");
|
|
148
|
+
overlay.className = "hypen-debug-overlay";
|
|
149
|
+
overlay.style.cssText = `
|
|
150
|
+
position: absolute;
|
|
151
|
+
top: 0;
|
|
152
|
+
left: 0;
|
|
153
|
+
right: 0;
|
|
154
|
+
bottom: 0;
|
|
155
|
+
pointer-events: none;
|
|
156
|
+
z-index: 999999 !important;
|
|
157
|
+
transition: opacity ${this.config.fadeOutDuration}ms ease-out;
|
|
158
|
+
border: 2px solid rgba(255, 0, 0, 0.7) !important;
|
|
159
|
+
box-sizing: border-box;
|
|
160
|
+
font-size: 11px;
|
|
161
|
+
color: white;
|
|
162
|
+
text-shadow: 0 0 3px black, 0 0 5px black;
|
|
163
|
+
padding: 4px;
|
|
164
|
+
font-family: 'Courier New', monospace;
|
|
165
|
+
font-weight: bold;
|
|
166
|
+
display: block !important;
|
|
167
|
+
visibility: visible !important;
|
|
168
|
+
`;
|
|
169
|
+
|
|
170
|
+
const currentPosition = window.getComputedStyle(element).position;
|
|
171
|
+
if (currentPosition === 'static') {
|
|
172
|
+
element.style.position = 'relative';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
element.appendChild(overlay);
|
|
176
|
+
this.overlays.set(id, overlay);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
overlay.style.backgroundColor = `rgba(255, 0, 0, ${Math.max(opacity, 0.15)})`;
|
|
180
|
+
overlay.style.opacity = '1';
|
|
181
|
+
overlay.textContent = `${renderCount}ร ${patchType}`;
|
|
182
|
+
|
|
183
|
+
if (this.config.fadeOutDuration > 0) {
|
|
184
|
+
setTimeout(() => {
|
|
185
|
+
if (overlay) {
|
|
186
|
+
overlay.style.opacity = '0.2';
|
|
187
|
+
}
|
|
188
|
+
}, this.config.fadeOutDuration);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Reset tracking for a specific element
|
|
195
|
+
*/
|
|
196
|
+
reset(id: string): void {
|
|
197
|
+
this.renderCounts.delete(id);
|
|
198
|
+
const overlay = this.overlays.get(id);
|
|
199
|
+
if (overlay) {
|
|
200
|
+
overlay.remove();
|
|
201
|
+
this.overlays.delete(id);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Reset tracking for all elements
|
|
207
|
+
*/
|
|
208
|
+
resetAll(): void {
|
|
209
|
+
this.renderCounts.clear();
|
|
210
|
+
for (const overlay of this.overlays.values()) {
|
|
211
|
+
overlay.remove();
|
|
212
|
+
}
|
|
213
|
+
this.overlays.clear();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get render count for an element
|
|
218
|
+
*/
|
|
219
|
+
getRenderCount(id: string): number {
|
|
220
|
+
return this.renderCounts.get(id) || 0;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Clean up all overlays (called when debug mode is disabled)
|
|
225
|
+
*/
|
|
226
|
+
private cleanup(): void {
|
|
227
|
+
for (const overlay of this.overlays.values()) {
|
|
228
|
+
overlay.remove();
|
|
229
|
+
}
|
|
230
|
+
this.overlays.clear();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Get statistics about re-renders
|
|
235
|
+
*/
|
|
236
|
+
getStats(): { totalRerenders: number; elementCount: number; avgRerenders: number } {
|
|
237
|
+
const totalRerenders = Array.from(this.renderCounts.values()).reduce((sum, count) => sum + count, 0);
|
|
238
|
+
const elementCount = this.renderCounts.size;
|
|
239
|
+
const avgRerenders = elementCount > 0 ? totalRerenders / elementCount : 0;
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
totalRerenders,
|
|
243
|
+
elementCount,
|
|
244
|
+
avgRerenders: Math.round(avgRerenders * 100) / 100,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Management System
|
|
3
|
+
*
|
|
4
|
+
* Handles DOM event binding and action dispatching
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Interface for the engine that EventManager needs
|
|
8
|
+
interface IEngine {
|
|
9
|
+
dispatchAction(name: string, payload?: any): void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface EventBinding {
|
|
13
|
+
elementId: string;
|
|
14
|
+
eventName: string;
|
|
15
|
+
actionName: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class EventManager {
|
|
19
|
+
private engine: IEngine;
|
|
20
|
+
private bindings: Map<string, Map<string, EventListener>> = new Map();
|
|
21
|
+
|
|
22
|
+
constructor(engine: IEngine) {
|
|
23
|
+
this.engine = engine;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Attach an event listener to an element
|
|
28
|
+
*/
|
|
29
|
+
attach(elementId: string, element: HTMLElement, eventName: string, actionName: string): void {
|
|
30
|
+
// Convert onClick to click for DOM events
|
|
31
|
+
const domEventName = eventName === "onClick" ? "click" : eventName;
|
|
32
|
+
console.log(`[EventManager] Attaching ${eventName} (DOM: ${domEventName}) to element ${elementId}, action: ${actionName}`);
|
|
33
|
+
|
|
34
|
+
// Create event listener that dispatches action
|
|
35
|
+
const listener = (event: Event) => {
|
|
36
|
+
console.log(`๐ฅ [EventManager] Event fired: ${eventName} on ${elementId}, dispatching action: ${actionName}`);
|
|
37
|
+
console.log(`๐ฅ [EventManager] Event object:`, event);
|
|
38
|
+
console.log(`๐ฅ [EventManager] Element:`, element);
|
|
39
|
+
|
|
40
|
+
// Prevent default for certain events
|
|
41
|
+
if (eventName === "submit" || (eventName === "click" && element.tagName === "A")) {
|
|
42
|
+
event.preventDefault();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Extract event data
|
|
46
|
+
const payload = this.extractEventData(event, element);
|
|
47
|
+
console.log(`๐ฅ [EventManager] Event payload:`, payload);
|
|
48
|
+
|
|
49
|
+
// Dispatch action to engine
|
|
50
|
+
console.log(`๐ฅ [EventManager] Calling engine.dispatchAction(${actionName})`);
|
|
51
|
+
try {
|
|
52
|
+
this.engine.dispatchAction(actionName, payload);
|
|
53
|
+
console.log(`๐ฅ [EventManager] โ
dispatchAction succeeded`);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error(`๐ฅ [EventManager] โ dispatchAction failed:`, error);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Store listener reference
|
|
60
|
+
let elementBindings = this.bindings.get(elementId);
|
|
61
|
+
if (!elementBindings) {
|
|
62
|
+
elementBindings = new Map();
|
|
63
|
+
this.bindings.set(elementId, elementBindings);
|
|
64
|
+
}
|
|
65
|
+
elementBindings.set(eventName, listener);
|
|
66
|
+
|
|
67
|
+
// Attach to DOM
|
|
68
|
+
element.addEventListener(domEventName, listener);
|
|
69
|
+
console.log(`[EventManager] Listener attached to DOM for ${domEventName}`);
|
|
70
|
+
console.log(`[EventManager] Element details:`, {
|
|
71
|
+
tagName: element.tagName,
|
|
72
|
+
id: element.id,
|
|
73
|
+
dataset: element.dataset,
|
|
74
|
+
textContent: element.textContent?.substring(0, 50)
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Test: Add a simple click listener to verify DOM events work
|
|
78
|
+
if (domEventName === "click") {
|
|
79
|
+
element.addEventListener("click", (e) => {
|
|
80
|
+
console.log(`๐งช [TEST] Raw DOM click detected on ${element.tagName}`, e);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Detach an event listener from an element
|
|
87
|
+
*/
|
|
88
|
+
detach(elementId: string, element: HTMLElement, eventName: string): void {
|
|
89
|
+
const elementBindings = this.bindings.get(elementId);
|
|
90
|
+
if (!elementBindings) return;
|
|
91
|
+
|
|
92
|
+
const listener = elementBindings.get(eventName);
|
|
93
|
+
if (listener) {
|
|
94
|
+
element.removeEventListener(eventName, listener);
|
|
95
|
+
elementBindings.delete(eventName);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (elementBindings.size === 0) {
|
|
99
|
+
this.bindings.delete(elementId);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Extract relevant data from an event
|
|
105
|
+
*/
|
|
106
|
+
private extractEventData(event: Event, element: HTMLElement): any {
|
|
107
|
+
const data: any = {
|
|
108
|
+
type: event.type,
|
|
109
|
+
timestamp: Date.now(),
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Mouse events
|
|
113
|
+
if (event instanceof MouseEvent) {
|
|
114
|
+
data.clientX = event.clientX;
|
|
115
|
+
data.clientY = event.clientY;
|
|
116
|
+
data.button = event.button;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Keyboard events
|
|
120
|
+
if (event instanceof KeyboardEvent) {
|
|
121
|
+
data.key = event.key;
|
|
122
|
+
data.code = event.code;
|
|
123
|
+
data.ctrlKey = event.ctrlKey;
|
|
124
|
+
data.shiftKey = event.shiftKey;
|
|
125
|
+
data.altKey = event.altKey;
|
|
126
|
+
data.metaKey = event.metaKey;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Input events (for form elements)
|
|
130
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
|
|
131
|
+
data.value = element.value;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Select events
|
|
135
|
+
if (element instanceof HTMLSelectElement) {
|
|
136
|
+
data.value = element.value;
|
|
137
|
+
data.selectedIndex = element.selectedIndex;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Form events
|
|
141
|
+
if (event.type === "submit" && element instanceof HTMLFormElement) {
|
|
142
|
+
data.formData = new FormData(element);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return data;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Clear all event bindings for an element
|
|
150
|
+
*/
|
|
151
|
+
clearElement(elementId: string, element: HTMLElement): void {
|
|
152
|
+
const elementBindings = this.bindings.get(elementId);
|
|
153
|
+
if (!elementBindings) return;
|
|
154
|
+
|
|
155
|
+
for (const [eventName, listener] of elementBindings) {
|
|
156
|
+
element.removeEventListener(eventName, listener);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this.bindings.delete(elementId);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Clear all event bindings
|
|
164
|
+
*/
|
|
165
|
+
clearAll(): void {
|
|
166
|
+
this.bindings.clear();
|
|
167
|
+
}
|
|
168
|
+
}
|
package/src/dom/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM Renderer for Hypen
|
|
3
|
+
*
|
|
4
|
+
* Browser-only module for rendering Hypen UI to the DOM
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { DOMRenderer } from "./renderer.js";
|
|
8
|
+
export { ComponentRegistry } from "./components/index.js";
|
|
9
|
+
export { ApplicatorRegistry } from "./applicators/index.js";
|
|
10
|
+
export { EventManager } from "./events.js";
|
|
11
|
+
export { RerenderTracker, type DebugConfig, defaultDebugConfig } from "./debug.js";
|