@esportsplus/template 0.28.3 → 0.29.2
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 +431 -0
- package/build/attributes.d.ts +7 -1
- package/build/attributes.js +86 -33
- package/build/constants.d.ts +3 -11
- package/build/constants.js +4 -32
- package/build/event/constants.d.ts +3 -0
- package/build/event/constants.js +13 -0
- package/build/event/index.d.ts +9 -1
- package/build/event/index.js +29 -35
- package/build/event/ontick.js +6 -9
- package/build/html.d.ts +9 -0
- package/build/html.js +7 -0
- package/build/index.d.ts +8 -2
- package/build/index.js +8 -1
- package/build/render.d.ts +2 -2
- package/build/render.js +2 -3
- package/build/runtime.d.ts +1 -0
- package/build/runtime.js +5 -0
- package/build/slot/array.d.ts +3 -3
- package/build/slot/array.js +11 -14
- package/build/slot/cleanup.d.ts +1 -1
- package/build/slot/cleanup.js +1 -2
- package/build/slot/effect.js +5 -7
- package/build/slot/index.js +1 -7
- package/build/slot/render.js +6 -8
- package/build/svg.d.ts +1 -1
- package/build/svg.js +1 -1
- package/build/transformer/codegen.d.ts +18 -0
- package/build/transformer/codegen.js +316 -0
- package/build/transformer/index.d.ts +12 -0
- package/build/transformer/index.js +62 -0
- package/build/transformer/parser.d.ts +18 -0
- package/build/transformer/parser.js +166 -0
- package/build/transformer/plugins/esbuild.d.ts +5 -0
- package/build/transformer/plugins/esbuild.js +35 -0
- package/build/transformer/plugins/tsc.d.ts +3 -0
- package/build/transformer/plugins/tsc.js +4 -0
- package/build/transformer/plugins/vite.d.ts +5 -0
- package/build/transformer/plugins/vite.js +37 -0
- package/build/transformer/ts-parser.d.ts +21 -0
- package/build/transformer/ts-parser.js +72 -0
- package/build/transformer/type-analyzer.d.ts +7 -0
- package/build/transformer/type-analyzer.js +230 -0
- package/build/types.d.ts +2 -3
- package/build/utilities.d.ts +7 -0
- package/build/utilities.js +31 -0
- package/package.json +33 -4
- package/src/attributes.ts +115 -51
- package/src/constants.ts +6 -53
- package/src/event/constants.ts +16 -0
- package/src/event/index.ts +36 -42
- package/src/event/onconnect.ts +1 -1
- package/src/event/onresize.ts +1 -1
- package/src/event/ontick.ts +7 -11
- package/src/html.ts +18 -0
- package/src/index.ts +8 -2
- package/src/render.ts +6 -7
- package/src/runtime.ts +8 -0
- package/src/slot/array.ts +18 -24
- package/src/slot/cleanup.ts +3 -4
- package/src/slot/effect.ts +6 -8
- package/src/slot/index.ts +2 -8
- package/src/slot/render.ts +7 -9
- package/src/svg.ts +1 -1
- package/src/transformer/codegen.ts +518 -0
- package/src/transformer/index.ts +98 -0
- package/src/transformer/parser.ts +239 -0
- package/src/transformer/plugins/esbuild.ts +46 -0
- package/src/transformer/plugins/tsc.ts +7 -0
- package/src/transformer/plugins/vite.ts +49 -0
- package/src/transformer/ts-parser.ts +123 -0
- package/src/transformer/type-analyzer.ts +334 -0
- package/src/types.ts +3 -4
- package/src/utilities.ts +52 -0
- package/storage/rewrite-analysis-2026-01-04.md +439 -0
- package/test/constants.ts +69 -0
- package/test/effects.ts +237 -0
- package/test/events.ts +318 -0
- package/test/imported-values.ts +253 -0
- package/test/nested.ts +298 -0
- package/test/slots.ts +259 -0
- package/test/spread.ts +290 -0
- package/test/static.ts +118 -0
- package/test/templates.ts +473 -0
- package/test/tsconfig.json +17 -0
- package/test/vite.config.ts +50 -0
- package/build/html/index.d.ts +0 -9
- package/build/html/index.js +0 -29
- package/build/html/parser.d.ts +0 -5
- package/build/html/parser.js +0 -165
- package/build/utilities/element.d.ts +0 -11
- package/build/utilities/element.js +0 -9
- package/build/utilities/fragment.d.ts +0 -3
- package/build/utilities/fragment.js +0 -10
- package/build/utilities/marker.d.ts +0 -2
- package/build/utilities/marker.js +0 -4
- package/build/utilities/node.d.ts +0 -9
- package/build/utilities/node.js +0 -10
- package/build/utilities/raf.d.ts +0 -2
- package/build/utilities/raf.js +0 -1
- package/build/utilities/text.d.ts +0 -2
- package/build/utilities/text.js +0 -9
- package/src/html/index.ts +0 -48
- package/src/html/parser.ts +0 -235
- package/src/utilities/element.ts +0 -28
- package/src/utilities/fragment.ts +0 -19
- package/src/utilities/marker.ts +0 -6
- package/src/utilities/node.ts +0 -29
- package/src/utilities/raf.ts +0 -1
- package/src/utilities/text.ts +0 -15
package/test/effects.ts
ADDED
|
@@ -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
|
+
`;
|