@esportsplus/template 0.28.3 → 0.29.1

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.
Files changed (109) hide show
  1. package/build/attributes.d.ts +7 -1
  2. package/build/attributes.js +86 -33
  3. package/build/constants.d.ts +3 -11
  4. package/build/constants.js +4 -32
  5. package/build/event/constants.d.ts +3 -0
  6. package/build/event/constants.js +13 -0
  7. package/build/event/index.d.ts +9 -1
  8. package/build/event/index.js +29 -35
  9. package/build/event/ontick.js +6 -9
  10. package/build/html.d.ts +9 -0
  11. package/build/html.js +7 -0
  12. package/build/index.d.ts +8 -2
  13. package/build/index.js +8 -1
  14. package/build/render.d.ts +2 -2
  15. package/build/render.js +2 -3
  16. package/build/runtime.d.ts +1 -0
  17. package/build/runtime.js +5 -0
  18. package/build/slot/array.d.ts +3 -3
  19. package/build/slot/array.js +11 -14
  20. package/build/slot/cleanup.d.ts +1 -1
  21. package/build/slot/cleanup.js +1 -2
  22. package/build/slot/effect.js +5 -7
  23. package/build/slot/index.js +1 -7
  24. package/build/slot/render.js +6 -8
  25. package/build/svg.d.ts +1 -1
  26. package/build/svg.js +1 -1
  27. package/build/transformer/codegen.d.ts +18 -0
  28. package/build/transformer/codegen.js +316 -0
  29. package/build/transformer/index.d.ts +12 -0
  30. package/build/transformer/index.js +62 -0
  31. package/build/transformer/parser.d.ts +18 -0
  32. package/build/transformer/parser.js +166 -0
  33. package/build/transformer/plugins/esbuild.d.ts +5 -0
  34. package/build/transformer/plugins/esbuild.js +35 -0
  35. package/build/transformer/plugins/tsc.d.ts +3 -0
  36. package/build/transformer/plugins/tsc.js +4 -0
  37. package/build/transformer/plugins/vite.d.ts +5 -0
  38. package/build/transformer/plugins/vite.js +37 -0
  39. package/build/transformer/ts-parser.d.ts +21 -0
  40. package/build/transformer/ts-parser.js +72 -0
  41. package/build/transformer/type-analyzer.d.ts +7 -0
  42. package/build/transformer/type-analyzer.js +230 -0
  43. package/build/types.d.ts +2 -3
  44. package/build/utilities.d.ts +7 -0
  45. package/build/utilities.js +31 -0
  46. package/package.json +11 -4
  47. package/src/attributes.ts +115 -51
  48. package/src/constants.ts +6 -53
  49. package/src/event/constants.ts +16 -0
  50. package/src/event/index.ts +36 -42
  51. package/src/event/onconnect.ts +1 -1
  52. package/src/event/onresize.ts +1 -1
  53. package/src/event/ontick.ts +7 -11
  54. package/src/html.ts +18 -0
  55. package/src/index.ts +8 -2
  56. package/src/render.ts +6 -7
  57. package/src/runtime.ts +8 -0
  58. package/src/slot/array.ts +18 -24
  59. package/src/slot/cleanup.ts +3 -4
  60. package/src/slot/effect.ts +6 -8
  61. package/src/slot/index.ts +2 -8
  62. package/src/slot/render.ts +7 -9
  63. package/src/svg.ts +1 -1
  64. package/src/transformer/codegen.ts +518 -0
  65. package/src/transformer/index.ts +98 -0
  66. package/src/transformer/parser.ts +239 -0
  67. package/src/transformer/plugins/esbuild.ts +46 -0
  68. package/src/transformer/plugins/tsc.ts +7 -0
  69. package/src/transformer/plugins/vite.ts +49 -0
  70. package/src/transformer/ts-parser.ts +123 -0
  71. package/src/transformer/type-analyzer.ts +334 -0
  72. package/src/types.ts +3 -4
  73. package/src/utilities.ts +52 -0
  74. package/storage/rewrite-analysis-2026-01-04.md +439 -0
  75. package/test/constants.ts +69 -0
  76. package/test/effects.ts +237 -0
  77. package/test/events.ts +318 -0
  78. package/test/imported-values.ts +253 -0
  79. package/test/nested.ts +298 -0
  80. package/test/slots.ts +259 -0
  81. package/test/spread.ts +290 -0
  82. package/test/static.ts +118 -0
  83. package/test/templates.ts +473 -0
  84. package/test/tsconfig.json +17 -0
  85. package/test/vite.config.ts +50 -0
  86. package/build/html/index.d.ts +0 -9
  87. package/build/html/index.js +0 -29
  88. package/build/html/parser.d.ts +0 -5
  89. package/build/html/parser.js +0 -165
  90. package/build/utilities/element.d.ts +0 -11
  91. package/build/utilities/element.js +0 -9
  92. package/build/utilities/fragment.d.ts +0 -3
  93. package/build/utilities/fragment.js +0 -10
  94. package/build/utilities/marker.d.ts +0 -2
  95. package/build/utilities/marker.js +0 -4
  96. package/build/utilities/node.d.ts +0 -9
  97. package/build/utilities/node.js +0 -10
  98. package/build/utilities/raf.d.ts +0 -2
  99. package/build/utilities/raf.js +0 -1
  100. package/build/utilities/text.d.ts +0 -2
  101. package/build/utilities/text.js +0 -9
  102. package/src/html/index.ts +0 -48
  103. package/src/html/parser.ts +0 -235
  104. package/src/utilities/element.ts +0 -28
  105. package/src/utilities/fragment.ts +0 -19
  106. package/src/utilities/marker.ts +0 -6
  107. package/src/utilities/node.ts +0 -29
  108. package/src/utilities/raf.ts +0 -1
  109. package/src/utilities/text.ts +0 -15
@@ -0,0 +1,237 @@
1
+ // Effect Templates - Reactive bindings and slot type detection
2
+ // Tests EffectSlot detection for arrow functions and reactive patterns
3
+
4
+
5
+ import { html } from '../src';
6
+
7
+
8
+ // =============================================================================
9
+ // ARROW FUNCTION EFFECTS (should detect as EffectSlot)
10
+ // =============================================================================
11
+
12
+ // Simple arrow function text
13
+ export const effectTextArrow = (getValue: () => string) =>
14
+ html`<div>${getValue}</div>`;
15
+
16
+ // Arrow function class
17
+ export const effectClassArrow = (getClass: () => string) =>
18
+ html`<div class="${getClass}"></div>`;
19
+
20
+ // Arrow function style
21
+ export const effectStyleArrow = (getStyle: () => string) =>
22
+ html`<div style="${getStyle}"></div>`;
23
+
24
+ // Arrow function in nested element
25
+ export const effectNestedArrow = (getValue: () => string) =>
26
+ html`<div><span><strong>${getValue}</strong></span></div>`;
27
+
28
+ // Multiple effect slots
29
+ export const effectMultiple = (getA: () => string, getB: () => string) =>
30
+ html`<div><span>${getA}</span><span>${getB}</span></div>`;
31
+
32
+
33
+ // =============================================================================
34
+ // FUNCTION EXPRESSION EFFECTS
35
+ // =============================================================================
36
+
37
+ // Function returning string
38
+ export const effectFunctionString = (fn: () => string) =>
39
+ html`<div>${fn}</div>`;
40
+
41
+ // Function returning number
42
+ export const effectFunctionNumber = (fn: () => number) =>
43
+ html`<div>${fn}</div>`;
44
+
45
+ // Function returning boolean (conditional display)
46
+ export const effectFunctionBool = (fn: () => boolean) =>
47
+ html`<div>${fn}</div>`;
48
+
49
+
50
+ // =============================================================================
51
+ // INLINE ARROW FUNCTIONS
52
+ // =============================================================================
53
+
54
+ // Inline arrow for text
55
+ export const effectInlineArrow = (state: { count: number }) =>
56
+ html`<div>${() => state.count}</div>`;
57
+
58
+ // Inline arrow for computed class
59
+ export const effectInlineClass = (state: { active: boolean }) =>
60
+ html`<div class="${() => state.active ? 'active' : 'inactive'}"></div>`;
61
+
62
+ // Inline arrow for computed style
63
+ export const effectInlineStyle = (state: { width: number }) =>
64
+ html`<div style="${() => `width: ${state.width}px`}"></div>`;
65
+
66
+ // Multiple inline arrows
67
+ export const effectMultipleInline = (state: { x: number; y: number }) =>
68
+ html`<div data-x="${() => state.x}" data-y="${() => state.y}">
69
+ ${() => `Position: ${state.x}, ${state.y}`}
70
+ </div>`;
71
+
72
+
73
+ // =============================================================================
74
+ // MIXED STATIC AND EFFECT
75
+ // =============================================================================
76
+
77
+ // Static class, effect text
78
+ export const mixedStaticEffect = (getValue: () => string) =>
79
+ html`<div class="static-class">${getValue}</div>`;
80
+
81
+ // Effect class, static text
82
+ export const mixedEffectStatic = (getClass: () => string) =>
83
+ html`<div class="${getClass}">Static text</div>`;
84
+
85
+ // Effect class with static prefix
86
+ export const mixedEffectClassPrefix = (getDynamic: () => string) =>
87
+ html`<div class="base-class ${getDynamic}">Content</div>`;
88
+
89
+ // Multiple mixed
90
+ export const mixedMultiple = (
91
+ staticValue: string,
92
+ getEffectValue: () => string,
93
+ getEffectClass: () => string
94
+ ) => html`
95
+ <div class="${getEffectClass}">
96
+ <span>${staticValue}</span>
97
+ <span>${getEffectValue}</span>
98
+ </div>
99
+ `;
100
+
101
+
102
+ // =============================================================================
103
+ // EFFECTS IN NESTED TEMPLATES
104
+ // =============================================================================
105
+
106
+ // Effect inside map
107
+ export const effectInMap = (items: { getValue: () => string }[]) =>
108
+ html`<ul>${items.map(item => html`<li>${item.getValue}</li>`)}</ul>`;
109
+
110
+ // Effect class inside map
111
+ export const effectClassInMap = (items: { getClass: () => string; text: string }[]) =>
112
+ html`<ul>${items.map(item =>
113
+ html`<li class="${item.getClass}">${item.text}</li>`
114
+ )}</ul>`;
115
+
116
+ // Nested with multiple effects
117
+ export const effectNestedMultiple = (sections: {
118
+ getTitle: () => string;
119
+ items: { getValue: () => string }[];
120
+ }[]) => html`<div>${sections.map(section => html`
121
+ <section>
122
+ <h2>${section.getTitle}</h2>
123
+ <ul>${section.items.map(item => html`<li>${item.getValue}</li>`)}</ul>
124
+ </section>
125
+ `)}</div>`;
126
+
127
+
128
+ // =============================================================================
129
+ // CONDITIONAL EFFECTS
130
+ // =============================================================================
131
+
132
+ // Ternary with effects
133
+ export const effectTernary = (condition: () => boolean, a: () => string, b: () => string) =>
134
+ html`<div>${() => condition() ? a() : b()}</div>`;
135
+
136
+ // Effect returning conditional class
137
+ export const effectConditionalClass = (getActive: () => boolean) =>
138
+ html`<div class="${() => getActive() ? 'visible' : 'hidden'}"></div>`;
139
+
140
+
141
+ // =============================================================================
142
+ // STATIC VALUE DETECTION (should use textContent, not EffectSlot)
143
+ // =============================================================================
144
+
145
+ // String literal
146
+ export const staticString = () => html`<div>${'static text'}</div>`;
147
+
148
+ // Number literal
149
+ export const staticNumber = () => html`<div>${42}</div>`;
150
+
151
+ // Template literal (no substitutions)
152
+ export const staticTemplate = () => html`<div>${`static template`}</div>`;
153
+
154
+
155
+ // =============================================================================
156
+ // PRIMITIVE DETECTION (should use runtime slot)
157
+ // =============================================================================
158
+
159
+ // Variable string
160
+ export const primitiveString = (text: string) => html`<div>${text}</div>`;
161
+
162
+ // Variable number
163
+ export const primitiveNumber = (num: number) => html`<div>${num}</div>`;
164
+
165
+ // Template literal with substitution
166
+ export const primitiveTemplate = (name: string) =>
167
+ html`<div>${`Hello, ${name}!`}</div>`;
168
+
169
+
170
+ // =============================================================================
171
+ // DOCUMENT FRAGMENT DETECTION (nested html)
172
+ // =============================================================================
173
+
174
+ // Nested html template
175
+ export const fragmentNested = (content: string) =>
176
+ html`<div>${html`<span>${content}</span>`}</div>`;
177
+
178
+ // Conditional nested html
179
+ export const fragmentConditional = (show: boolean, content: string) =>
180
+ html`<div>${show ? html`<span>${content}</span>` : html`<span>Hidden</span>`}</div>`;
181
+
182
+
183
+ // =============================================================================
184
+ // COMPLEX REACTIVE PATTERNS
185
+ // =============================================================================
186
+
187
+ // Counter component pattern
188
+ export const counterPattern = (state: { count: number; increment: () => void }) =>
189
+ html`<div class="counter">
190
+ <span>${() => state.count}</span>
191
+ <button onclick="${state.increment}">+</button>
192
+ </div>`;
193
+
194
+ // Toggle component pattern
195
+ export const togglePattern = (state: { active: boolean; toggle: () => void }) =>
196
+ html`<div class="${() => state.active ? 'on' : 'off'}">
197
+ <button onclick="${state.toggle}">Toggle</button>
198
+ </div>`;
199
+
200
+ // Form field pattern
201
+ export const formFieldPattern = (field: {
202
+ value: () => string;
203
+ error: () => string | null;
204
+ onInput: (e: Event) => void;
205
+ }) => html`
206
+ <div class="field">
207
+ <input type="text" value="${field.value}" oninput="${field.onInput}">
208
+ <span class="${() => field.error() ? 'error visible' : 'error'}">${field.error}</span>
209
+ </div>
210
+ `;
211
+
212
+ // Dynamic list pattern
213
+ export const dynamicListPattern = (list: {
214
+ items: () => string[];
215
+ addItem: () => void;
216
+ }) => html`
217
+ <div class="dynamic-list">
218
+ <ul>${() => list.items().map(item => html`<li>${item}</li>`)}</ul>
219
+ <button onclick="${list.addItem}">Add Item</button>
220
+ </div>
221
+ `;
222
+
223
+
224
+ // =============================================================================
225
+ // STRESS TESTS
226
+ // =============================================================================
227
+
228
+ // Many effect slots
229
+ export const stressEffects = (getters: (() => string)[]) =>
230
+ html`<div>
231
+ ${getters[0]}${getters[1]}${getters[2]}${getters[3]}${getters[4]}
232
+ ${getters[5]}${getters[6]}${getters[7]}${getters[8]}${getters[9]}
233
+ </div>`;
234
+
235
+ // Deep effect nesting
236
+ export const stressDeepEffect = (getValue: () => string) =>
237
+ html`<div><div><div><div><div>${getValue}</div></div></div></div></div>`;
package/test/events.ts ADDED
@@ -0,0 +1,318 @@
1
+ // Event Templates - Event handler routing
2
+ // Tests delegate, direct, and lifecycle event detection
3
+
4
+
5
+ import { html } from '../src';
6
+
7
+
8
+ // =============================================================================
9
+ // DELEGATE EVENTS (most common - bubbles up)
10
+ // =============================================================================
11
+
12
+ // Click event
13
+ export const eventClick = (handler: () => void) =>
14
+ html`<button onclick="${handler}">Click me</button>`;
15
+
16
+ // Double click
17
+ export const eventDblClick = (handler: () => void) =>
18
+ html`<div ondblclick="${handler}">Double click</div>`;
19
+
20
+ // Context menu
21
+ export const eventContextMenu = (handler: (e: MouseEvent) => void) =>
22
+ html`<div oncontextmenu="${handler}">Right click</div>`;
23
+
24
+ // Keyboard events
25
+ export const eventKeydown = (handler: (e: KeyboardEvent) => void) =>
26
+ html`<input type="text" onkeydown="${handler}">`;
27
+
28
+ export const eventKeyup = (handler: (e: KeyboardEvent) => void) =>
29
+ html`<input type="text" onkeyup="${handler}">`;
30
+
31
+ export const eventKeypress = (handler: (e: KeyboardEvent) => void) =>
32
+ html`<input type="text" onkeypress="${handler}">`;
33
+
34
+ // Input events
35
+ export const eventInput = (handler: (e: Event) => void) =>
36
+ html`<input type="text" oninput="${handler}">`;
37
+
38
+ export const eventChange = (handler: (e: Event) => void) =>
39
+ html`<select onchange="${handler}"><option>A</option><option>B</option></select>`;
40
+
41
+ // Mouse button events
42
+ export const eventMousedown = (handler: (e: MouseEvent) => void) =>
43
+ html`<div onmousedown="${handler}">Mouse down</div>`;
44
+
45
+ export const eventMouseup = (handler: (e: MouseEvent) => void) =>
46
+ html`<div onmouseup="${handler}">Mouse up</div>`;
47
+
48
+
49
+ // =============================================================================
50
+ // DIRECT ATTACH EVENTS (don't bubble, need direct listener)
51
+ // =============================================================================
52
+
53
+ // Focus events
54
+ export const eventFocus = (handler: (e: FocusEvent) => void) =>
55
+ html`<input type="text" onfocus="${handler}">`;
56
+
57
+ export const eventBlur = (handler: (e: FocusEvent) => void) =>
58
+ html`<input type="text" onblur="${handler}">`;
59
+
60
+ export const eventFocusin = (handler: (e: FocusEvent) => void) =>
61
+ html`<div onfocusin="${handler}"><input type="text"></div>`;
62
+
63
+ export const eventFocusout = (handler: (e: FocusEvent) => void) =>
64
+ html`<div onfocusout="${handler}"><input type="text"></div>`;
65
+
66
+ // Media events
67
+ export const eventPlay = (handler: () => void) =>
68
+ html`<video onplay="${handler}"><source src="video.mp4"></video>`;
69
+
70
+ export const eventPause = (handler: () => void) =>
71
+ html`<video onpause="${handler}"><source src="video.mp4"></video>`;
72
+
73
+ export const eventEnded = (handler: () => void) =>
74
+ html`<video onended="${handler}"><source src="video.mp4"></video>`;
75
+
76
+ export const eventTimeupdate = (handler: (e: Event) => void) =>
77
+ html`<video ontimeupdate="${handler}"><source src="video.mp4"></video>`;
78
+
79
+ // Form events
80
+ export const eventSubmit = (handler: (e: Event) => void) =>
81
+ html`<form onsubmit="${handler}"><button type="submit">Submit</button></form>`;
82
+
83
+ export const eventReset = (handler: () => void) =>
84
+ html`<form onreset="${handler}"><button type="reset">Reset</button></form>`;
85
+
86
+ // Resource events
87
+ export const eventLoad = (handler: () => void) =>
88
+ html`<img onload="${handler}" src="image.png">`;
89
+
90
+ export const eventError = (handler: () => void) =>
91
+ html`<img onerror="${handler}" src="missing.png">`;
92
+
93
+ // Scroll event
94
+ export const eventScroll = (handler: (e: Event) => void) =>
95
+ html`<div onscroll="${handler}" style="overflow: auto; height: 100px">Content</div>`;
96
+
97
+
98
+ // =============================================================================
99
+ // LIFECYCLE EVENTS (custom template events)
100
+ // =============================================================================
101
+
102
+ // onconnect - element added to DOM
103
+ export const eventConnect = (handler: (el: HTMLElement) => void) =>
104
+ html`<div onconnect="${handler}">Connected</div>`;
105
+
106
+ // ondisconnect - element removed from DOM
107
+ export const eventDisconnect = (handler: (el: HTMLElement) => void) =>
108
+ html`<div ondisconnect="${handler}">Will disconnect</div>`;
109
+
110
+ // onrender - after template renders
111
+ export const eventRender = (handler: (el: HTMLElement) => void) =>
112
+ html`<div onrender="${handler}">Rendered</div>`;
113
+
114
+ // onresize - element resized
115
+ export const eventResize = (handler: (el: HTMLElement) => void) =>
116
+ html`<div onresize="${handler}">Resizable</div>`;
117
+
118
+ // ontick - animation frame tick
119
+ export const eventTick = (handler: (dispose: () => void, el: HTMLElement) => void) =>
120
+ html`<div ontick="${handler}">Animating</div>`;
121
+
122
+
123
+ // =============================================================================
124
+ // MULTIPLE EVENTS - SAME ELEMENT
125
+ // =============================================================================
126
+
127
+ // Mouse enter/leave
128
+ export const eventMouseEnterLeave = (
129
+ enter: (e: MouseEvent) => void,
130
+ leave: (e: MouseEvent) => void
131
+ ) => html`<div onmouseenter="${enter}" onmouseleave="${leave}">Hover me</div>`;
132
+
133
+ // Click and context menu
134
+ export const eventClickContext = (
135
+ click: () => void,
136
+ context: (e: MouseEvent) => void
137
+ ) => html`<div onclick="${click}" oncontextmenu="${context}">Click or right-click</div>`;
138
+
139
+ // Input handlers
140
+ export const eventInputFull = (
141
+ input: (e: Event) => void,
142
+ focus: () => void,
143
+ blur: () => void
144
+ ) => html`<input type="text" oninput="${input}" onfocus="${focus}" onblur="${blur}">`;
145
+
146
+ // Lifecycle combo
147
+ export const eventLifecycleFull = (
148
+ connect: (el: HTMLElement) => void,
149
+ disconnect: (el: HTMLElement) => void,
150
+ render: (el: HTMLElement) => void
151
+ ) => html`<div
152
+ onconnect="${connect}"
153
+ ondisconnect="${disconnect}"
154
+ onrender="${render}"
155
+ >Lifecycle element</div>`;
156
+
157
+
158
+ // =============================================================================
159
+ // EVENTS WITH ATTRIBUTES
160
+ // =============================================================================
161
+
162
+ // Event with class
163
+ export const eventWithClass = (cls: string, handler: () => void) =>
164
+ html`<button class="${cls}" onclick="${handler}">Styled button</button>`;
165
+
166
+ // Event with multiple attributes
167
+ export const eventWithMultiAttr = (
168
+ id: string,
169
+ cls: string,
170
+ handler: () => void
171
+ ) => html`<button id="${id}" class="${cls}" onclick="${handler}">Action</button>`;
172
+
173
+ // Event with data attributes
174
+ export const eventWithData = (
175
+ itemId: string,
176
+ handler: (e: MouseEvent) => void
177
+ ) => html`<button data-item-id="${itemId}" onclick="${handler}">Item action</button>`;
178
+
179
+
180
+ // =============================================================================
181
+ // EVENTS IN NESTED TEMPLATES
182
+ // =============================================================================
183
+
184
+ // Click handler in map
185
+ export const eventInMap = (items: { text: string; onClick: () => void }[]) =>
186
+ html`<ul>${items.map(item =>
187
+ html`<li onclick="${item.onClick}">${item.text}</li>`
188
+ )}</ul>`;
189
+
190
+ // Multiple events in map
191
+ export const eventMultipleInMap = (items: {
192
+ text: string;
193
+ onClick: () => void;
194
+ onMouseEnter: () => void;
195
+ }[]) => html`<ul>${items.map(item =>
196
+ html`<li onclick="${item.onClick}" onmouseenter="${item.onMouseEnter}">${item.text}</li>`
197
+ )}</ul>`;
198
+
199
+ // Lifecycle in map
200
+ export const eventLifecycleInMap = (items: {
201
+ text: string;
202
+ onConnect: (el: HTMLElement) => void;
203
+ }[]) => html`<ul>${items.map(item =>
204
+ html`<li onconnect="${item.onConnect}">${item.text}</li>`
205
+ )}</ul>`;
206
+
207
+
208
+ // =============================================================================
209
+ // EVENT HANDLER PATTERNS
210
+ // =============================================================================
211
+
212
+ // Inline arrow handler
213
+ export const eventInlineArrow = (state: { count: number }) =>
214
+ html`<button onclick="${() => state.count++}">Increment</button>`;
215
+
216
+ // Method reference
217
+ export const eventMethodRef = (controller: { handleClick: () => void }) =>
218
+ html`<button onclick="${controller.handleClick}">Controller action</button>`;
219
+
220
+ // Bound method
221
+ export const eventBoundMethod = (obj: { value: number; increment: () => void }) =>
222
+ html`<button onclick="${obj.increment.bind(obj)}">Bound increment</button>`;
223
+
224
+
225
+ // =============================================================================
226
+ // COMPLEX EVENT PATTERNS
227
+ // =============================================================================
228
+
229
+ // Interactive card
230
+ export const eventInteractiveCard = (card: {
231
+ title: string;
232
+ onClick: () => void;
233
+ onMouseEnter: () => void;
234
+ onMouseLeave: () => void;
235
+ }) => html`
236
+ <div class="card"
237
+ onclick="${card.onClick}"
238
+ onmouseenter="${card.onMouseEnter}"
239
+ onmouseleave="${card.onMouseLeave}"
240
+ >
241
+ <h3>${card.title}</h3>
242
+ </div>
243
+ `;
244
+
245
+ // Form with events
246
+ export const eventForm = (form: {
247
+ onSubmit: (e: Event) => void;
248
+ onReset: () => void;
249
+ onInputChange: (e: Event) => void;
250
+ }) => html`
251
+ <form onsubmit="${form.onSubmit}" onreset="${form.onReset}">
252
+ <input type="text" oninput="${form.onInputChange}">
253
+ <button type="submit">Submit</button>
254
+ <button type="reset">Reset</button>
255
+ </form>
256
+ `;
257
+
258
+ // Draggable element
259
+ export const eventDraggable = (handlers: {
260
+ onDragStart: (e: DragEvent) => void;
261
+ onDrag: (e: DragEvent) => void;
262
+ onDragEnd: (e: DragEvent) => void;
263
+ }) => html`
264
+ <div draggable="true"
265
+ ondragstart="${handlers.onDragStart}"
266
+ ondrag="${handlers.onDrag}"
267
+ ondragend="${handlers.onDragEnd}"
268
+ >Drag me</div>
269
+ `;
270
+
271
+ // Drop zone
272
+ export const eventDropZone = (handlers: {
273
+ onDragOver: (e: DragEvent) => void;
274
+ onDragEnter: (e: DragEvent) => void;
275
+ onDragLeave: (e: DragEvent) => void;
276
+ onDrop: (e: DragEvent) => void;
277
+ }) => html`
278
+ <div class="drop-zone"
279
+ ondragover="${handlers.onDragOver}"
280
+ ondragenter="${handlers.onDragEnter}"
281
+ ondragleave="${handlers.onDragLeave}"
282
+ ondrop="${handlers.onDrop}"
283
+ >Drop here</div>
284
+ `;
285
+
286
+
287
+ // =============================================================================
288
+ // TOUCH EVENTS (for mobile)
289
+ // =============================================================================
290
+
291
+ export const eventTouch = (handlers: {
292
+ onTouchStart: (e: TouchEvent) => void;
293
+ onTouchMove: (e: TouchEvent) => void;
294
+ onTouchEnd: (e: TouchEvent) => void;
295
+ }) => html`
296
+ <div
297
+ ontouchstart="${handlers.onTouchStart}"
298
+ ontouchmove="${handlers.onTouchMove}"
299
+ ontouchend="${handlers.onTouchEnd}"
300
+ >Touch area</div>
301
+ `;
302
+
303
+
304
+ // =============================================================================
305
+ // POINTER EVENTS
306
+ // =============================================================================
307
+
308
+ export const eventPointer = (handlers: {
309
+ onPointerDown: (e: PointerEvent) => void;
310
+ onPointerMove: (e: PointerEvent) => void;
311
+ onPointerUp: (e: PointerEvent) => void;
312
+ }) => html`
313
+ <div
314
+ onpointerdown="${handlers.onPointerDown}"
315
+ onpointermove="${handlers.onPointerMove}"
316
+ onpointerup="${handlers.onPointerUp}"
317
+ >Pointer area</div>
318
+ `;