@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.
- 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 +11 -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
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// Imported Values Templates - Using constants from external modules
|
|
2
|
+
// Tests that imported values are preserved correctly during compilation
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import { html } from '../src';
|
|
6
|
+
import {
|
|
7
|
+
ACTIVE_CLASS,
|
|
8
|
+
APP_NAME,
|
|
9
|
+
BUTTON_ATTRS,
|
|
10
|
+
COLUMNS,
|
|
11
|
+
createCardAttrs,
|
|
12
|
+
DEFAULT_ATTRS,
|
|
13
|
+
DEFAULT_CLASS,
|
|
14
|
+
DEFAULT_TIMEOUT,
|
|
15
|
+
FLEX_CENTER,
|
|
16
|
+
HIDDEN_STYLE,
|
|
17
|
+
INACTIVE_CLASS,
|
|
18
|
+
MAX_ITEMS,
|
|
19
|
+
NAV_ITEMS,
|
|
20
|
+
noop,
|
|
21
|
+
preventDefault,
|
|
22
|
+
PRIORITIES,
|
|
23
|
+
STATUS,
|
|
24
|
+
stopPropagation,
|
|
25
|
+
THEME,
|
|
26
|
+
VERSION
|
|
27
|
+
} from './constants';
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
// =============================================================================
|
|
31
|
+
// IMPORTED STRING CONSTANTS
|
|
32
|
+
// =============================================================================
|
|
33
|
+
|
|
34
|
+
// Using imported string in text slot
|
|
35
|
+
export const importedTextSlot = () =>
|
|
36
|
+
html`<h1>${APP_NAME}</h1>`;
|
|
37
|
+
|
|
38
|
+
// Using imported string in attribute
|
|
39
|
+
export const importedClassAttr = () =>
|
|
40
|
+
html`<div class="${DEFAULT_CLASS}">Content</div>`;
|
|
41
|
+
|
|
42
|
+
// Using imported style string
|
|
43
|
+
export const importedStyleAttr = () =>
|
|
44
|
+
html`<div style="${FLEX_CENTER}">Centered</div>`;
|
|
45
|
+
|
|
46
|
+
// Multiple imported strings
|
|
47
|
+
export const importedMultipleStrings = () =>
|
|
48
|
+
html`<div class="${DEFAULT_CLASS}" style="${FLEX_CENTER}">
|
|
49
|
+
<span>${APP_NAME}</span>
|
|
50
|
+
<span>${VERSION}</span>
|
|
51
|
+
</div>`;
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
// =============================================================================
|
|
55
|
+
// IMPORTED NUMBER CONSTANTS
|
|
56
|
+
// =============================================================================
|
|
57
|
+
|
|
58
|
+
// Number in text slot
|
|
59
|
+
export const importedNumberText = () =>
|
|
60
|
+
html`<span>Max items: ${MAX_ITEMS}</span>`;
|
|
61
|
+
|
|
62
|
+
// Number in data attribute
|
|
63
|
+
export const importedNumberData = () =>
|
|
64
|
+
html`<div data-timeout="${DEFAULT_TIMEOUT}">Timeout element</div>`;
|
|
65
|
+
|
|
66
|
+
// Number in style (grid columns)
|
|
67
|
+
export const importedNumberStyle = () =>
|
|
68
|
+
html`<div style="grid-template-columns: repeat(${COLUMNS}, 1fr)">Grid</div>`;
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
// =============================================================================
|
|
72
|
+
// IMPORTED OBJECT CONSTANTS
|
|
73
|
+
// =============================================================================
|
|
74
|
+
|
|
75
|
+
// Spread with imported object
|
|
76
|
+
export const importedSpreadObject = () =>
|
|
77
|
+
html`<div ${DEFAULT_ATTRS}>Default element</div>`;
|
|
78
|
+
|
|
79
|
+
// Spread with imported button attrs
|
|
80
|
+
export const importedSpreadButton = () =>
|
|
81
|
+
html`<button ${BUTTON_ATTRS}>Action</button>`;
|
|
82
|
+
|
|
83
|
+
// Accessing object properties
|
|
84
|
+
export const importedObjectProperty = () =>
|
|
85
|
+
html`<div class="${STATUS.ACTIVE}">Active status</div>`;
|
|
86
|
+
|
|
87
|
+
// Using enum-like constant
|
|
88
|
+
export const importedThemeClass = () =>
|
|
89
|
+
html`<div class="${THEME.DARK}">Dark themed</div>`;
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
// =============================================================================
|
|
93
|
+
// IMPORTED ARRAY CONSTANTS
|
|
94
|
+
// =============================================================================
|
|
95
|
+
|
|
96
|
+
// Mapping over imported array
|
|
97
|
+
export const importedArrayMap = () =>
|
|
98
|
+
html`<nav>
|
|
99
|
+
<ul>${NAV_ITEMS.map(item => html`<li>${item}</li>`)}</ul>
|
|
100
|
+
</nav>`;
|
|
101
|
+
|
|
102
|
+
// Using array with index
|
|
103
|
+
export const importedArrayIndex = () =>
|
|
104
|
+
html`<ul>${NAV_ITEMS.map((item, i) =>
|
|
105
|
+
html`<li data-index="${i}">${item}</li>`
|
|
106
|
+
)}</ul>`;
|
|
107
|
+
|
|
108
|
+
// Priorities list
|
|
109
|
+
export const importedPriorities = () =>
|
|
110
|
+
html`<select>
|
|
111
|
+
${PRIORITIES.map(p => html`<option value="${p}">${p}</option>`)}
|
|
112
|
+
</select>`;
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
// =============================================================================
|
|
116
|
+
// IMPORTED FUNCTION CONSTANTS
|
|
117
|
+
// =============================================================================
|
|
118
|
+
|
|
119
|
+
// Using noop handler
|
|
120
|
+
export const importedNoopHandler = () =>
|
|
121
|
+
html`<button onclick="${noop}">No-op button</button>`;
|
|
122
|
+
|
|
123
|
+
// Using preventDefault handler
|
|
124
|
+
export const importedPreventDefault = () =>
|
|
125
|
+
html`<form onsubmit="${preventDefault}">
|
|
126
|
+
<button type="submit">Submit</button>
|
|
127
|
+
</form>`;
|
|
128
|
+
|
|
129
|
+
// Using stopPropagation handler
|
|
130
|
+
export const importedStopPropagation = () =>
|
|
131
|
+
html`<div onclick="${stopPropagation}">
|
|
132
|
+
<button>Contained click</button>
|
|
133
|
+
</div>`;
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
// =============================================================================
|
|
137
|
+
// IMPORTED FUNCTION THAT RETURNS OBJECT (Factory)
|
|
138
|
+
// =============================================================================
|
|
139
|
+
|
|
140
|
+
// Using factory function for spread
|
|
141
|
+
export const importedFactory = () =>
|
|
142
|
+
html`<div ${createCardAttrs(123, 'user')}>Card 123</div>`;
|
|
143
|
+
|
|
144
|
+
// Factory in list
|
|
145
|
+
export const importedFactoryInList = (ids: number[]) =>
|
|
146
|
+
html`<div>${ids.map(id =>
|
|
147
|
+
html`<div ${createCardAttrs(id, 'item')}>Item ${id}</div>`
|
|
148
|
+
)}</div>`;
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
// =============================================================================
|
|
152
|
+
// CONDITIONAL USING IMPORTED VALUES
|
|
153
|
+
// =============================================================================
|
|
154
|
+
|
|
155
|
+
// Conditional class with imported constants
|
|
156
|
+
export const importedConditionalClass = (isActive: boolean) =>
|
|
157
|
+
html`<div class="${isActive ? ACTIVE_CLASS : INACTIVE_CLASS}">Status</div>`;
|
|
158
|
+
|
|
159
|
+
// Conditional style with imported constants
|
|
160
|
+
export const importedConditionalStyle = (isHidden: boolean) =>
|
|
161
|
+
html`<div style="${isHidden ? HIDDEN_STYLE : FLEX_CENTER}">Visibility</div>`;
|
|
162
|
+
|
|
163
|
+
// Conditional with STATUS enum
|
|
164
|
+
export const importedConditionalEnum = (status: string) =>
|
|
165
|
+
html`<div class="${
|
|
166
|
+
status === STATUS.ACTIVE ? 'active' :
|
|
167
|
+
status === STATUS.PENDING ? 'pending' :
|
|
168
|
+
status === STATUS.COMPLETED ? 'completed' :
|
|
169
|
+
'cancelled'
|
|
170
|
+
}">${status}</div>`;
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
// =============================================================================
|
|
174
|
+
// TEMPLATE LITERALS WITH IMPORTED VALUES
|
|
175
|
+
// =============================================================================
|
|
176
|
+
|
|
177
|
+
// Interpolated in template literal text
|
|
178
|
+
export const importedInterpolatedText = () =>
|
|
179
|
+
html`<p>${`Welcome to ${APP_NAME} v${VERSION}`}</p>`;
|
|
180
|
+
|
|
181
|
+
// Interpolated in class
|
|
182
|
+
export const importedInterpolatedClass = (variant: string) =>
|
|
183
|
+
html`<button class="${`btn ${variant} ${DEFAULT_CLASS}`}">Button</button>`;
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
// =============================================================================
|
|
187
|
+
// COMPLEX COMBINATIONS
|
|
188
|
+
// =============================================================================
|
|
189
|
+
|
|
190
|
+
// Full component using multiple imports
|
|
191
|
+
export const importedFullComponent = (isActive: boolean) =>
|
|
192
|
+
html`<div class="${isActive ? ACTIVE_CLASS : INACTIVE_CLASS}" style="${FLEX_CENTER}">
|
|
193
|
+
<h1>${APP_NAME}</h1>
|
|
194
|
+
<nav>
|
|
195
|
+
<ul>${NAV_ITEMS.map(item => html`<li>${item}</li>`)}</ul>
|
|
196
|
+
</nav>
|
|
197
|
+
<span>Version: ${VERSION}</span>
|
|
198
|
+
</div>`;
|
|
199
|
+
|
|
200
|
+
// Card with imported factory and constants
|
|
201
|
+
export const importedCard = (id: number, type: string, title: string) =>
|
|
202
|
+
html`<article ${createCardAttrs(id, type)}>
|
|
203
|
+
<header class="${DEFAULT_CLASS}">
|
|
204
|
+
<h2>${title}</h2>
|
|
205
|
+
<span class="${THEME.LIGHT}">${type}</span>
|
|
206
|
+
</header>
|
|
207
|
+
<footer onclick="${noop}">
|
|
208
|
+
<span>${APP_NAME}</span>
|
|
209
|
+
</footer>
|
|
210
|
+
</article>`;
|
|
211
|
+
|
|
212
|
+
// Priority list with theme
|
|
213
|
+
export const importedPriorityList = (currentPriority: string) =>
|
|
214
|
+
html`<div class="${THEME.SYSTEM}">
|
|
215
|
+
<h3>Select Priority</h3>
|
|
216
|
+
<ul>${PRIORITIES.map(p =>
|
|
217
|
+
html`<li class="${p === currentPriority ? ACTIVE_CLASS : INACTIVE_CLASS}">${p}</li>`
|
|
218
|
+
)}</ul>
|
|
219
|
+
</div>`;
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
// =============================================================================
|
|
223
|
+
// STRESS TESTS
|
|
224
|
+
// =============================================================================
|
|
225
|
+
|
|
226
|
+
// Many imported values
|
|
227
|
+
export const importedStress = () =>
|
|
228
|
+
html`<div
|
|
229
|
+
class="${DEFAULT_CLASS}"
|
|
230
|
+
style="${FLEX_CENTER}"
|
|
231
|
+
data-app="${APP_NAME}"
|
|
232
|
+
data-version="${VERSION}"
|
|
233
|
+
data-max="${MAX_ITEMS}"
|
|
234
|
+
data-timeout="${DEFAULT_TIMEOUT}"
|
|
235
|
+
onclick="${noop}"
|
|
236
|
+
>
|
|
237
|
+
${NAV_ITEMS.map(item => html`<span>${item}</span>`)}
|
|
238
|
+
</div>`;
|
|
239
|
+
|
|
240
|
+
// Nested with many imports
|
|
241
|
+
export const importedNestedStress = () =>
|
|
242
|
+
html`<div class="${THEME.DARK}">
|
|
243
|
+
${PRIORITIES.map(priority =>
|
|
244
|
+
html`<section class="${priority === 'critical' ? ACTIVE_CLASS : INACTIVE_CLASS}">
|
|
245
|
+
<h2>${priority}</h2>
|
|
246
|
+
${NAV_ITEMS.map(item =>
|
|
247
|
+
html`<div ${DEFAULT_ATTRS} onclick="${stopPropagation}">
|
|
248
|
+
${item} - ${priority}
|
|
249
|
+
</div>`
|
|
250
|
+
)}
|
|
251
|
+
</section>`
|
|
252
|
+
)}
|
|
253
|
+
</div>`;
|
package/test/nested.ts
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// Nested Templates - Template hoisting and depth sorting
|
|
2
|
+
// Tests nested html`` calls, array mapping, and conditional templates
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import { html } from '../src';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// SIMPLE NESTED TEMPLATES
|
|
10
|
+
// =============================================================================
|
|
11
|
+
|
|
12
|
+
// Basic nested template in slot
|
|
13
|
+
export const nestedSimple = (items: string[]) =>
|
|
14
|
+
html`<ul>${items.map(item => html`<li>${item}</li>`)}</ul>`;
|
|
15
|
+
|
|
16
|
+
// Nested with static content
|
|
17
|
+
export const nestedStatic = (items: string[]) =>
|
|
18
|
+
html`<ul class="list">${items.map(item => html`<li class="item">${item}</li>`)}</ul>`;
|
|
19
|
+
|
|
20
|
+
// Nested with index
|
|
21
|
+
export const nestedWithIndex = (items: string[]) =>
|
|
22
|
+
html`<ul>${items.map((item, i) => html`<li data-index="${i}">${item}</li>`)}</ul>`;
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// NESTED WITH ATTRIBUTES
|
|
27
|
+
// =============================================================================
|
|
28
|
+
|
|
29
|
+
// Nested template with dynamic class
|
|
30
|
+
export const nestedAttrClass = (items: { cls: string; text: string }[]) =>
|
|
31
|
+
html`<ul>${items.map(item => html`<li class="${item.cls}">${item.text}</li>`)}</ul>`;
|
|
32
|
+
|
|
33
|
+
// Nested template with multiple attributes
|
|
34
|
+
export const nestedAttrMultiple = (items: { id: string; cls: string; text: string }[]) =>
|
|
35
|
+
html`<ul>${items.map(item =>
|
|
36
|
+
html`<li id="${item.id}" class="${item.cls}">${item.text}</li>`
|
|
37
|
+
)}</ul>`;
|
|
38
|
+
|
|
39
|
+
// Nested with data attributes
|
|
40
|
+
export const nestedAttrData = (items: { id: number; value: string }[]) =>
|
|
41
|
+
html`<div>${items.map(item =>
|
|
42
|
+
html`<span data-id="${item.id}" data-value="${item.value}">${item.value}</span>`
|
|
43
|
+
)}</div>`;
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
// =============================================================================
|
|
47
|
+
// DEEPLY NESTED TEMPLATES (2+ levels)
|
|
48
|
+
// =============================================================================
|
|
49
|
+
|
|
50
|
+
// Two levels of nesting
|
|
51
|
+
export const nestedTwoLevel = (sections: { title: string; items: string[] }[]) =>
|
|
52
|
+
html`<div>${sections.map(section => html`
|
|
53
|
+
<section>
|
|
54
|
+
<h2>${section.title}</h2>
|
|
55
|
+
<ul>${section.items.map(item => html`<li>${item}</li>`)}</ul>
|
|
56
|
+
</section>
|
|
57
|
+
`)}</div>`;
|
|
58
|
+
|
|
59
|
+
// Three levels of nesting
|
|
60
|
+
export const nestedThreeLevel = (groups: {
|
|
61
|
+
name: string;
|
|
62
|
+
sections: { title: string; items: string[] }[];
|
|
63
|
+
}[]) => html`<div>${groups.map(group => html`
|
|
64
|
+
<div class="group">
|
|
65
|
+
<h1>${group.name}</h1>
|
|
66
|
+
${group.sections.map(section => html`
|
|
67
|
+
<section>
|
|
68
|
+
<h2>${section.title}</h2>
|
|
69
|
+
<ul>${section.items.map(item => html`<li>${item}</li>`)}</ul>
|
|
70
|
+
</section>
|
|
71
|
+
`)}
|
|
72
|
+
</div>
|
|
73
|
+
`)}</div>`;
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
// =============================================================================
|
|
77
|
+
// TABLE STRUCTURES
|
|
78
|
+
// =============================================================================
|
|
79
|
+
|
|
80
|
+
// Simple table rows
|
|
81
|
+
export const tableRows = (rows: { id: number; name: string }[]) =>
|
|
82
|
+
html`<table>
|
|
83
|
+
<thead><tr><th>ID</th><th>Name</th></tr></thead>
|
|
84
|
+
<tbody>${rows.map(row => html`<tr><td>${row.id}</td><td>${row.name}</td></tr>`)}</tbody>
|
|
85
|
+
</table>`;
|
|
86
|
+
|
|
87
|
+
// Table with multiple columns
|
|
88
|
+
export const tableMultiColumn = (rows: { id: number; name: string; email: string; role: string }[]) =>
|
|
89
|
+
html`<table>
|
|
90
|
+
<thead><tr><th>ID</th><th>Name</th><th>Email</th><th>Role</th></tr></thead>
|
|
91
|
+
<tbody>${rows.map(row => html`
|
|
92
|
+
<tr>
|
|
93
|
+
<td>${row.id}</td>
|
|
94
|
+
<td>${row.name}</td>
|
|
95
|
+
<td>${row.email}</td>
|
|
96
|
+
<td>${row.role}</td>
|
|
97
|
+
</tr>
|
|
98
|
+
`)}</tbody>
|
|
99
|
+
</table>`;
|
|
100
|
+
|
|
101
|
+
// Grouped table
|
|
102
|
+
export const tableGrouped = (groups: { category: string; items: { name: string; value: number }[] }[]) =>
|
|
103
|
+
html`<table>
|
|
104
|
+
<tbody>${groups.map(group => html`
|
|
105
|
+
<tr class="group-header"><th colspan="2">${group.category}</th></tr>
|
|
106
|
+
${group.items.map(item => html`<tr><td>${item.name}</td><td>${item.value}</td></tr>`)}
|
|
107
|
+
`)}</tbody>
|
|
108
|
+
</table>`;
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
// =============================================================================
|
|
112
|
+
// LIST STRUCTURES
|
|
113
|
+
// =============================================================================
|
|
114
|
+
|
|
115
|
+
// Definition list
|
|
116
|
+
export const definitionList = (items: { term: string; definition: string }[]) =>
|
|
117
|
+
html`<dl>${items.map(item => html`<dt>${item.term}</dt><dd>${item.definition}</dd>`)}</dl>`;
|
|
118
|
+
|
|
119
|
+
// Nested list (ul > li > ul)
|
|
120
|
+
export const nestedList = (items: { text: string; children: string[] }[]) =>
|
|
121
|
+
html`<ul>${items.map(item => html`
|
|
122
|
+
<li>
|
|
123
|
+
${item.text}
|
|
124
|
+
${item.children.length > 0 ? html`<ul>${item.children.map(c => html`<li>${c}</li>`)}</ul>` : ''}
|
|
125
|
+
</li>
|
|
126
|
+
`)}</ul>`;
|
|
127
|
+
|
|
128
|
+
// Ordered list with nesting
|
|
129
|
+
export const orderedNestedList = (items: { text: string; subItems: string[] }[]) =>
|
|
130
|
+
html`<ol>${items.map(item => html`
|
|
131
|
+
<li>
|
|
132
|
+
<span>${item.text}</span>
|
|
133
|
+
<ol>${item.subItems.map(sub => html`<li>${sub}</li>`)}</ol>
|
|
134
|
+
</li>
|
|
135
|
+
`)}</ol>`;
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
// =============================================================================
|
|
139
|
+
// CARD/GRID LAYOUTS
|
|
140
|
+
// =============================================================================
|
|
141
|
+
|
|
142
|
+
// Card grid
|
|
143
|
+
export const cardGrid = (cards: { title: string; body: string; footer: string }[]) =>
|
|
144
|
+
html`<div class="grid">${cards.map(card => html`
|
|
145
|
+
<div class="card">
|
|
146
|
+
<div class="card-header">${card.title}</div>
|
|
147
|
+
<div class="card-body">${card.body}</div>
|
|
148
|
+
<div class="card-footer">${card.footer}</div>
|
|
149
|
+
</div>
|
|
150
|
+
`)}</div>`;
|
|
151
|
+
|
|
152
|
+
// Product grid
|
|
153
|
+
export const productGrid = (products: {
|
|
154
|
+
id: number;
|
|
155
|
+
name: string;
|
|
156
|
+
price: number;
|
|
157
|
+
image: string;
|
|
158
|
+
}[]) => html`<div class="products">${products.map(p => html`
|
|
159
|
+
<div class="product" data-id="${p.id}">
|
|
160
|
+
<img src="${p.image}" alt="${p.name}">
|
|
161
|
+
<h3>${p.name}</h3>
|
|
162
|
+
<span class="price">$${p.price}</span>
|
|
163
|
+
</div>
|
|
164
|
+
`)}</div>`;
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
// =============================================================================
|
|
168
|
+
// CONDITIONAL NESTED TEMPLATES
|
|
169
|
+
// =============================================================================
|
|
170
|
+
|
|
171
|
+
// Conditional inner template
|
|
172
|
+
export const conditionalNested = (items: string[] | null) =>
|
|
173
|
+
html`<div>
|
|
174
|
+
${items
|
|
175
|
+
? html`<ul>${items.map(i => html`<li>${i}</li>`)}</ul>`
|
|
176
|
+
: html`<p>No items</p>`
|
|
177
|
+
}
|
|
178
|
+
</div>`;
|
|
179
|
+
|
|
180
|
+
// Conditional based on length
|
|
181
|
+
export const conditionalLength = (items: string[]) =>
|
|
182
|
+
html`<div>
|
|
183
|
+
${items.length > 0
|
|
184
|
+
? html`<ul>${items.map(i => html`<li>${i}</li>`)}</ul>`
|
|
185
|
+
: html`<p class="empty">Empty list</p>`
|
|
186
|
+
}
|
|
187
|
+
</div>`;
|
|
188
|
+
|
|
189
|
+
// Conditional class in nested
|
|
190
|
+
export const conditionalClassNested = (items: { text: string; active: boolean }[]) =>
|
|
191
|
+
html`<ul>${items.map(item =>
|
|
192
|
+
html`<li class="${item.active ? 'active' : 'inactive'}">${item.text}</li>`
|
|
193
|
+
)}</ul>`;
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
// =============================================================================
|
|
197
|
+
// COMPLEX REAL-WORLD PATTERNS
|
|
198
|
+
// =============================================================================
|
|
199
|
+
|
|
200
|
+
// Navigation menu
|
|
201
|
+
export const navMenu = (items: { href: string; text: string; active: boolean }[]) =>
|
|
202
|
+
html`<nav class="nav">
|
|
203
|
+
<ul class="nav-list">${items.map(item => html`
|
|
204
|
+
<li class="${item.active ? 'active' : ''}">
|
|
205
|
+
<a href="${item.href}">${item.text}</a>
|
|
206
|
+
</li>
|
|
207
|
+
`)}</ul>
|
|
208
|
+
</nav>`;
|
|
209
|
+
|
|
210
|
+
// Blog post card
|
|
211
|
+
export const blogCard = (post: {
|
|
212
|
+
title: string;
|
|
213
|
+
author: string;
|
|
214
|
+
date: string;
|
|
215
|
+
excerpt: string;
|
|
216
|
+
tags: string[];
|
|
217
|
+
}) => html`
|
|
218
|
+
<article class="blog-card">
|
|
219
|
+
<h2>${post.title}</h2>
|
|
220
|
+
<p class="meta">By ${post.author} on ${post.date}</p>
|
|
221
|
+
<p>${post.excerpt}</p>
|
|
222
|
+
<div class="tags">${post.tags.map(tag => html`<span class="tag">${tag}</span>`)}</div>
|
|
223
|
+
</article>
|
|
224
|
+
`;
|
|
225
|
+
|
|
226
|
+
// Comment thread
|
|
227
|
+
export const commentThread = (comments: {
|
|
228
|
+
author: string;
|
|
229
|
+
text: string;
|
|
230
|
+
replies: { author: string; text: string }[];
|
|
231
|
+
}[]) => html`
|
|
232
|
+
<div class="comments">${comments.map(comment => html`
|
|
233
|
+
<div class="comment">
|
|
234
|
+
<p class="author">${comment.author}</p>
|
|
235
|
+
<p class="text">${comment.text}</p>
|
|
236
|
+
${comment.replies.length > 0 ? html`
|
|
237
|
+
<div class="replies">${comment.replies.map(reply => html`
|
|
238
|
+
<div class="reply">
|
|
239
|
+
<p class="author">${reply.author}</p>
|
|
240
|
+
<p class="text">${reply.text}</p>
|
|
241
|
+
</div>
|
|
242
|
+
`)}</div>
|
|
243
|
+
` : ''}
|
|
244
|
+
</div>
|
|
245
|
+
`)}</div>
|
|
246
|
+
`;
|
|
247
|
+
|
|
248
|
+
// Dashboard widget
|
|
249
|
+
export const dashboardWidget = (widget: {
|
|
250
|
+
title: string;
|
|
251
|
+
value: number;
|
|
252
|
+
chartData: number[];
|
|
253
|
+
}) => html`
|
|
254
|
+
<div class="widget">
|
|
255
|
+
<h3>${widget.title}</h3>
|
|
256
|
+
<p class="value">${widget.value}</p>
|
|
257
|
+
<div class="chart">${widget.chartData.map(point =>
|
|
258
|
+
html`<div class="bar" style="height: ${point}%"></div>`
|
|
259
|
+
)}</div>
|
|
260
|
+
</div>
|
|
261
|
+
`;
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
// =============================================================================
|
|
265
|
+
// STRESS TESTS
|
|
266
|
+
// =============================================================================
|
|
267
|
+
|
|
268
|
+
// 50 item list
|
|
269
|
+
export const stressList50 = (items: string[]) =>
|
|
270
|
+
html`<ul>${items.slice(0, 50).map(item => html`<li>${item}</li>`)}</ul>`;
|
|
271
|
+
|
|
272
|
+
// 100 item list
|
|
273
|
+
export const stressList100 = (items: string[]) =>
|
|
274
|
+
html`<ul>${items.slice(0, 100).map(item => html`<li>${item}</li>`)}</ul>`;
|
|
275
|
+
|
|
276
|
+
// Complex nested structure
|
|
277
|
+
export const stressComplex = (data: {
|
|
278
|
+
header: { title: string; subtitle: string };
|
|
279
|
+
sections: { title: string; items: { name: string; value: string }[] }[];
|
|
280
|
+
footer: string;
|
|
281
|
+
}) => html`
|
|
282
|
+
<div class="complex">
|
|
283
|
+
<header>
|
|
284
|
+
<h1>${data.header.title}</h1>
|
|
285
|
+
<p>${data.header.subtitle}</p>
|
|
286
|
+
</header>
|
|
287
|
+
<main>${data.sections.map(section => html`
|
|
288
|
+
<section>
|
|
289
|
+
<h2>${section.title}</h2>
|
|
290
|
+
<dl>${section.items.map(item => html`
|
|
291
|
+
<dt>${item.name}</dt>
|
|
292
|
+
<dd>${item.value}</dd>
|
|
293
|
+
`)}</dl>
|
|
294
|
+
</section>
|
|
295
|
+
`)}</main>
|
|
296
|
+
<footer>${data.footer}</footer>
|
|
297
|
+
</div>
|
|
298
|
+
`;
|