@zenithbuild/core 0.1.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/.eslintignore +15 -0
- package/.gitattributes +2 -0
- package/.github/ISSUE_TEMPLATE/compiler-errors-for-invalid-state-declarations.md +25 -0
- package/.github/ISSUE_TEMPLATE/new_ticket.yaml +34 -0
- package/.github/pull_request_template.md +15 -0
- package/.github/workflows/discord-changelog.yml +141 -0
- package/.github/workflows/discord-notify.yml +242 -0
- package/.github/workflows/discord-version.yml +195 -0
- package/.prettierignore +13 -0
- package/.prettierrc +21 -0
- package/.zen.d.ts +15 -0
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/app/components/Button.zen +46 -0
- package/app/components/Link.zen +11 -0
- package/app/favicon.ico +0 -0
- package/app/layouts/Main.zen +59 -0
- package/app/pages/about.zen +23 -0
- package/app/pages/blog/[id].zen +53 -0
- package/app/pages/blog/index.zen +32 -0
- package/app/pages/dynamic-dx.zen +712 -0
- package/app/pages/dynamic-primitives.zen +453 -0
- package/app/pages/index.zen +154 -0
- package/app/pages/navigation-demo.zen +229 -0
- package/app/pages/posts/[...slug].zen +61 -0
- package/app/pages/primitives-demo.zen +273 -0
- package/assets/logos/0E3B5DDD-605C-4839-BB2E-DFCA8ADC9604.PNG +0 -0
- package/assets/logos/760971E5-79A1-44F9-90B9-925DF30F4278.PNG +0 -0
- package/assets/logos/8A06ED80-9ED2-4689-BCBD-13B2E95EE8E4.JPG +0 -0
- package/assets/logos/C691FF58-ED13-4E8D-B6A3-02E835849340.PNG +0 -0
- package/assets/logos/C691FF58-ED13-4E8D-B6A3-02E835849340.svg +601 -0
- package/assets/logos/README.md +54 -0
- package/assets/logos/zen.icns +0 -0
- package/bun.lock +39 -0
- package/compiler/README.md +380 -0
- package/compiler/errors/compilerError.ts +24 -0
- package/compiler/finalize/finalizeOutput.ts +163 -0
- package/compiler/finalize/generateFinalBundle.ts +82 -0
- package/compiler/index.ts +44 -0
- package/compiler/ir/types.ts +83 -0
- package/compiler/legacy/binding.ts +254 -0
- package/compiler/legacy/bindings.ts +338 -0
- package/compiler/legacy/component-process.ts +1208 -0
- package/compiler/legacy/component.ts +301 -0
- package/compiler/legacy/event.ts +50 -0
- package/compiler/legacy/expression.ts +1149 -0
- package/compiler/legacy/mutation.ts +280 -0
- package/compiler/legacy/parse.ts +299 -0
- package/compiler/legacy/split.ts +608 -0
- package/compiler/legacy/types.ts +32 -0
- package/compiler/output/types.ts +34 -0
- package/compiler/parse/detectMapExpressions.ts +102 -0
- package/compiler/parse/parseScript.ts +22 -0
- package/compiler/parse/parseTemplate.ts +425 -0
- package/compiler/parse/parseZenFile.ts +66 -0
- package/compiler/parse/trackLoopContext.ts +82 -0
- package/compiler/runtime/dataExposure.ts +291 -0
- package/compiler/runtime/generateDOM.ts +144 -0
- package/compiler/runtime/generateHydrationBundle.ts +383 -0
- package/compiler/runtime/hydration.ts +309 -0
- package/compiler/runtime/navigation.ts +432 -0
- package/compiler/runtime/thinRuntime.ts +160 -0
- package/compiler/runtime/transformIR.ts +256 -0
- package/compiler/runtime/wrapExpression.ts +84 -0
- package/compiler/runtime/wrapExpressionWithLoop.ts +77 -0
- package/compiler/spa-build.ts +1000 -0
- package/compiler/test/validate-test.ts +104 -0
- package/compiler/transform/generateBindings.ts +47 -0
- package/compiler/transform/generateHTML.ts +28 -0
- package/compiler/transform/transformNode.ts +126 -0
- package/compiler/transform/transformTemplate.ts +38 -0
- package/compiler/validate/validateExpressions.ts +168 -0
- package/core/index.ts +135 -0
- package/core/lifecycle/index.ts +49 -0
- package/core/lifecycle/zen-mount.ts +182 -0
- package/core/lifecycle/zen-unmount.ts +88 -0
- package/core/reactivity/index.ts +54 -0
- package/core/reactivity/tracking.ts +167 -0
- package/core/reactivity/zen-batch.ts +57 -0
- package/core/reactivity/zen-effect.ts +139 -0
- package/core/reactivity/zen-memo.ts +146 -0
- package/core/reactivity/zen-ref.ts +52 -0
- package/core/reactivity/zen-signal.ts +121 -0
- package/core/reactivity/zen-state.ts +180 -0
- package/core/reactivity/zen-untrack.ts +44 -0
- package/docs/COMMENTS.md +111 -0
- package/docs/COMMITS.md +36 -0
- package/docs/CONTRIBUTING.md +116 -0
- package/docs/STYLEGUIDE.md +62 -0
- package/package.json +44 -0
- package/router/index.ts +76 -0
- package/router/manifest.ts +314 -0
- package/router/navigation/ZenLink.zen +231 -0
- package/router/navigation/index.ts +78 -0
- package/router/navigation/zen-link.ts +584 -0
- package/router/runtime.ts +458 -0
- package/router/types.ts +168 -0
- package/runtime/build.ts +17 -0
- package/runtime/serve.ts +93 -0
- package/scripts/webhook-proxy.ts +213 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// ============================================
|
|
3
|
+
// Dynamic HTML - Clean DX Demo
|
|
4
|
+
// ============================================
|
|
5
|
+
|
|
6
|
+
// User authentication state
|
|
7
|
+
state user = null
|
|
8
|
+
state isAuthenticated = false
|
|
9
|
+
|
|
10
|
+
// Data
|
|
11
|
+
state notifications = [
|
|
12
|
+
{ id: 1, type: "info", message: "Welcome to Zenith!", read: false },
|
|
13
|
+
{ id: 2, type: "success", message: "Profile updated", read: true },
|
|
14
|
+
{ id: 3, type: "warning", message: "Storage almost full", read: false }
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
state todoItems = [
|
|
18
|
+
{ id: 1, text: "Learn Zenith basics", completed: true },
|
|
19
|
+
{ id: 2, text: "Build a component", completed: true },
|
|
20
|
+
{ id: 3, text: "Master dynamic HTML", completed: false },
|
|
21
|
+
{ id: 4, text: "Deploy to production", completed: false }
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
state todoCount = 4
|
|
25
|
+
|
|
26
|
+
// Auth toggle - used in onclick handler
|
|
27
|
+
function toggleAuth() {
|
|
28
|
+
if (isAuthenticated) {
|
|
29
|
+
user = null
|
|
30
|
+
isAuthenticated = false
|
|
31
|
+
} else {
|
|
32
|
+
user = { name: "Alex Developer", email: "alex@zenith.dev", avatar: "đ¨âđģ" }
|
|
33
|
+
isAuthenticated = true
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Mark all notifications as read - used in onclick handler
|
|
38
|
+
function markAllRead() {
|
|
39
|
+
notifications = notifications.map(n => ({ ...n, read: true }))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Clear all notifications - used in onclick handler
|
|
43
|
+
function clearNotifications() {
|
|
44
|
+
notifications = []
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Toggle the first incomplete todo - used in onclick handler
|
|
48
|
+
function toggleNextTodo() {
|
|
49
|
+
const firstIncomplete = todoItems.find(t => !t.completed)
|
|
50
|
+
if (firstIncomplete) {
|
|
51
|
+
todoItems = todoItems.map(item =>
|
|
52
|
+
item.id === firstIncomplete.id ? { ...item, completed: true } : item
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Add a demo todo - used in onclick handler
|
|
58
|
+
function addTodo() {
|
|
59
|
+
todoCount = todoCount + 1
|
|
60
|
+
todoItems = [...todoItems, { id: todoCount, text: "New task #" + todoCount, completed: false }]
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<style>
|
|
65
|
+
/* Reset and full-page container */
|
|
66
|
+
.dx-page {
|
|
67
|
+
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
|
68
|
+
background: #0f172a;
|
|
69
|
+
color: #f1f5f9;
|
|
70
|
+
min-height: 100vh;
|
|
71
|
+
margin: 0;
|
|
72
|
+
padding: 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Header */
|
|
76
|
+
.dx-header {
|
|
77
|
+
background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 50%, #ec4899 100%);
|
|
78
|
+
padding: 60px 32px;
|
|
79
|
+
text-align: center;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.dx-header h1 {
|
|
83
|
+
margin: 0 0 16px 0;
|
|
84
|
+
font-size: 3rem;
|
|
85
|
+
font-weight: 800;
|
|
86
|
+
color: white;
|
|
87
|
+
letter-spacing: -0.02em;
|
|
88
|
+
text-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.dx-header .subtitle {
|
|
92
|
+
font-size: 1.25rem;
|
|
93
|
+
color: rgba(255, 255, 255, 0.9);
|
|
94
|
+
font-weight: 500;
|
|
95
|
+
letter-spacing: 0.1em;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Main content */
|
|
99
|
+
.dx-main {
|
|
100
|
+
max-width: 1000px;
|
|
101
|
+
margin: 0 auto;
|
|
102
|
+
padding: 48px 24px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Feature Section */
|
|
106
|
+
.feature-section {
|
|
107
|
+
background: #1e293b;
|
|
108
|
+
border: 1px solid #334155;
|
|
109
|
+
border-radius: 20px;
|
|
110
|
+
padding: 32px;
|
|
111
|
+
margin-bottom: 32px;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.feature-header {
|
|
115
|
+
margin-bottom: 24px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.feature-title {
|
|
119
|
+
font-size: 1.5rem;
|
|
120
|
+
margin: 0 0 8px 0;
|
|
121
|
+
color: #f1f5f9;
|
|
122
|
+
display: flex;
|
|
123
|
+
align-items: center;
|
|
124
|
+
gap: 12px;
|
|
125
|
+
font-weight: 700;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.feature-description {
|
|
129
|
+
color: #94a3b8;
|
|
130
|
+
font-size: 1rem;
|
|
131
|
+
line-height: 1.6;
|
|
132
|
+
margin: 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.demo-card {
|
|
136
|
+
background: #0f172a;
|
|
137
|
+
border: 1px solid #334155;
|
|
138
|
+
border-radius: 16px;
|
|
139
|
+
padding: 24px;
|
|
140
|
+
margin-bottom: 24px;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.code-example {
|
|
144
|
+
background: #0a0e1a;
|
|
145
|
+
border: 1px solid #1e293b;
|
|
146
|
+
border-radius: 12px;
|
|
147
|
+
padding: 20px;
|
|
148
|
+
margin-top: 20px;
|
|
149
|
+
font-family: 'Fira Code', 'Consolas', monospace;
|
|
150
|
+
font-size: 13px;
|
|
151
|
+
line-height: 1.8;
|
|
152
|
+
overflow-x: auto;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.code-keyword {
|
|
156
|
+
color: #c084fc;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.code-string {
|
|
160
|
+
color: #34d399;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.code-comment {
|
|
164
|
+
color: #64748b;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* Auth Card */
|
|
168
|
+
.auth-card {
|
|
169
|
+
background: #0f172a;
|
|
170
|
+
border: 1px solid #334155;
|
|
171
|
+
border-radius: 16px;
|
|
172
|
+
padding: 24px;
|
|
173
|
+
margin-bottom: 24px;
|
|
174
|
+
display: flex;
|
|
175
|
+
justify-content: space-between;
|
|
176
|
+
align-items: center;
|
|
177
|
+
flex-wrap: wrap;
|
|
178
|
+
gap: 20px;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.auth-info {
|
|
182
|
+
display: flex;
|
|
183
|
+
align-items: center;
|
|
184
|
+
gap: 20px;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.user-card, .guest-card {
|
|
188
|
+
display: flex;
|
|
189
|
+
align-items: center;
|
|
190
|
+
padding: 16px 20px;
|
|
191
|
+
border-radius: 12px;
|
|
192
|
+
gap: 16px;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.user-card {
|
|
196
|
+
background: rgba(34, 197, 94, 0.1);
|
|
197
|
+
border: 1px solid rgba(34, 197, 94, 0.25);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.user-card .avatar {
|
|
201
|
+
background: linear-gradient(135deg, #22c55e, #16a34a);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.guest-card {
|
|
205
|
+
background: rgba(251, 191, 36, 0.1);
|
|
206
|
+
border: 1px solid rgba(251, 191, 36, 0.25);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.guest-card .avatar {
|
|
210
|
+
background: linear-gradient(135deg, #fbbf24, #f59e0b);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.avatar {
|
|
214
|
+
width: 48px;
|
|
215
|
+
height: 48px;
|
|
216
|
+
border-radius: 50%;
|
|
217
|
+
display: flex;
|
|
218
|
+
align-items: center;
|
|
219
|
+
justify-content: center;
|
|
220
|
+
font-size: 24px;
|
|
221
|
+
flex-shrink: 0;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.auth-text {
|
|
225
|
+
font-size: 1rem;
|
|
226
|
+
font-weight: 600;
|
|
227
|
+
color: #e2e8f0;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.status-active-text {
|
|
231
|
+
color: #22c55e;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.status-inactive-text {
|
|
235
|
+
color: #fbbf24;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* Buttons */
|
|
239
|
+
.btn {
|
|
240
|
+
background: #3b82f6;
|
|
241
|
+
color: white;
|
|
242
|
+
border: none;
|
|
243
|
+
border-radius: 12px;
|
|
244
|
+
padding: 14px 28px;
|
|
245
|
+
cursor: pointer;
|
|
246
|
+
font-size: 15px;
|
|
247
|
+
font-weight: 600;
|
|
248
|
+
font-family: inherit;
|
|
249
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
250
|
+
display: inline-flex;
|
|
251
|
+
align-items: center;
|
|
252
|
+
gap: 8px;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.btn:hover {
|
|
256
|
+
background: #2563eb;
|
|
257
|
+
transform: translateY(-2px);
|
|
258
|
+
box-shadow: 0 8px 24px rgba(59, 130, 246, 0.35);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.btn-outline {
|
|
262
|
+
background: transparent;
|
|
263
|
+
border: 2px solid #475569;
|
|
264
|
+
color: #e2e8f0;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.btn-outline:hover {
|
|
268
|
+
background: #334155;
|
|
269
|
+
border-color: #3b82f6;
|
|
270
|
+
box-shadow: none;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.btn-sm {
|
|
274
|
+
padding: 10px 20px;
|
|
275
|
+
font-size: 13px;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/* Notification Item */
|
|
279
|
+
.notification-list {
|
|
280
|
+
display: flex;
|
|
281
|
+
flex-direction: column;
|
|
282
|
+
gap: 12px;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.notification-item {
|
|
286
|
+
background: #0f172a;
|
|
287
|
+
border: 1px solid #334155;
|
|
288
|
+
border-radius: 12px;
|
|
289
|
+
padding: 16px 20px;
|
|
290
|
+
display: flex;
|
|
291
|
+
align-items: center;
|
|
292
|
+
justify-content: space-between;
|
|
293
|
+
gap: 16px;
|
|
294
|
+
transition: all 0.2s ease;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.notification-item:hover {
|
|
298
|
+
border-color: #3b82f6;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.notification-content {
|
|
302
|
+
display: flex;
|
|
303
|
+
align-items: center;
|
|
304
|
+
gap: 16px;
|
|
305
|
+
flex: 1;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.notification-icon {
|
|
309
|
+
width: 40px;
|
|
310
|
+
height: 40px;
|
|
311
|
+
border-radius: 10px;
|
|
312
|
+
display: flex;
|
|
313
|
+
align-items: center;
|
|
314
|
+
justify-content: center;
|
|
315
|
+
font-size: 18px;
|
|
316
|
+
background: rgba(59, 130, 246, 0.15);
|
|
317
|
+
flex-shrink: 0;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.notification-text {
|
|
321
|
+
color: #e2e8f0;
|
|
322
|
+
font-size: 0.95rem;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.notification-unread {
|
|
326
|
+
width: 10px;
|
|
327
|
+
height: 10px;
|
|
328
|
+
border-radius: 50%;
|
|
329
|
+
background: #3b82f6;
|
|
330
|
+
flex-shrink: 0;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.empty-state {
|
|
334
|
+
text-align: center;
|
|
335
|
+
padding: 40px 20px;
|
|
336
|
+
color: #64748b;
|
|
337
|
+
font-size: 0.95rem;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.notification-header {
|
|
341
|
+
display: flex;
|
|
342
|
+
justify-content: space-between;
|
|
343
|
+
align-items: center;
|
|
344
|
+
margin-bottom: 20px;
|
|
345
|
+
flex-wrap: wrap;
|
|
346
|
+
gap: 12px;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.dismiss-btn {
|
|
350
|
+
background: rgba(239, 68, 68, 0.1);
|
|
351
|
+
border: none;
|
|
352
|
+
color: #f87171;
|
|
353
|
+
cursor: pointer;
|
|
354
|
+
padding: 10px 16px;
|
|
355
|
+
border-radius: 8px;
|
|
356
|
+
font-size: 13px;
|
|
357
|
+
font-weight: 600;
|
|
358
|
+
transition: all 0.2s ease;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.dismiss-btn:hover {
|
|
362
|
+
background: rgba(239, 68, 68, 0.2);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/* Todo section */
|
|
366
|
+
.todo-actions {
|
|
367
|
+
display: flex;
|
|
368
|
+
gap: 12px;
|
|
369
|
+
margin-bottom: 24px;
|
|
370
|
+
flex-wrap: wrap;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.todo-list {
|
|
374
|
+
display: flex;
|
|
375
|
+
flex-direction: column;
|
|
376
|
+
gap: 12px;
|
|
377
|
+
margin-bottom: 24px;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.todo-item {
|
|
381
|
+
display: flex;
|
|
382
|
+
align-items: center;
|
|
383
|
+
gap: 16px;
|
|
384
|
+
padding: 16px 20px;
|
|
385
|
+
background: #0f172a;
|
|
386
|
+
border-radius: 12px;
|
|
387
|
+
border: 1px solid #334155;
|
|
388
|
+
transition: all 0.2s ease;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.todo-item:hover {
|
|
392
|
+
border-color: #3b82f6;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.todo-completed {
|
|
396
|
+
opacity: 0.6;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.todo-checkbox {
|
|
400
|
+
width: 24px;
|
|
401
|
+
height: 24px;
|
|
402
|
+
border-radius: 6px;
|
|
403
|
+
border: 2px solid #475569;
|
|
404
|
+
display: flex;
|
|
405
|
+
align-items: center;
|
|
406
|
+
justify-content: center;
|
|
407
|
+
font-size: 14px;
|
|
408
|
+
color: #22c55e;
|
|
409
|
+
flex-shrink: 0;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.todo-text {
|
|
413
|
+
color: #e2e8f0;
|
|
414
|
+
font-size: 0.95rem;
|
|
415
|
+
font-weight: 500;
|
|
416
|
+
flex: 1;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.todo-stats {
|
|
420
|
+
display: flex;
|
|
421
|
+
gap: 32px;
|
|
422
|
+
padding-top: 20px;
|
|
423
|
+
border-top: 1px solid #334155;
|
|
424
|
+
color: #94a3b8;
|
|
425
|
+
font-size: 0.9rem;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.stat-value {
|
|
429
|
+
color: #3b82f6;
|
|
430
|
+
font-weight: 700;
|
|
431
|
+
font-size: 1.1rem;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/* Navigation */
|
|
435
|
+
.nav-section {
|
|
436
|
+
display: flex;
|
|
437
|
+
gap: 16px;
|
|
438
|
+
flex-wrap: wrap;
|
|
439
|
+
justify-content: center;
|
|
440
|
+
padding-top: 16px;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.nav-link {
|
|
444
|
+
display: inline-flex;
|
|
445
|
+
align-items: center;
|
|
446
|
+
gap: 10px;
|
|
447
|
+
padding: 16px 32px;
|
|
448
|
+
background: #334155;
|
|
449
|
+
color: white;
|
|
450
|
+
text-decoration: none;
|
|
451
|
+
border-radius: 14px;
|
|
452
|
+
font-weight: 600;
|
|
453
|
+
font-size: 15px;
|
|
454
|
+
transition: all 0.2s ease;
|
|
455
|
+
border: 1px solid #475569;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.nav-link:hover {
|
|
459
|
+
background: #475569;
|
|
460
|
+
border-color: #3b82f6;
|
|
461
|
+
transform: translateY(-2px);
|
|
462
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
|
|
463
|
+
}
|
|
464
|
+
</style>
|
|
465
|
+
|
|
466
|
+
<Main title="Dynamic HTML - DX Demo">
|
|
467
|
+
<div class="dx-page">
|
|
468
|
+
<header class="dx-header">
|
|
469
|
+
<h1>⨠Clean DX Dynamic HTML</h1>
|
|
470
|
+
<p class="subtitle">DECLARATIVE âĸ EXPRESSIVE âĸ INTUITIVE</p>
|
|
471
|
+
</header>
|
|
472
|
+
|
|
473
|
+
<main class="dx-main">
|
|
474
|
+
<!-- Conditional Rendering -->
|
|
475
|
+
<section class="feature-section">
|
|
476
|
+
<div class="feature-header">
|
|
477
|
+
<h2 class="feature-title">đ Conditional Rendering</h2>
|
|
478
|
+
<p class="feature-description">
|
|
479
|
+
Use <code style="background: rgba(59, 130, 246, 0.1); padding: 2px 6px; border-radius: 4px;">condition && element</code> to show or hide content based on state.
|
|
480
|
+
Perfect for authentication states, feature flags, and dynamic UI elements.
|
|
481
|
+
</p>
|
|
482
|
+
</div>
|
|
483
|
+
|
|
484
|
+
<div class="demo-card">
|
|
485
|
+
<div class="auth-card">
|
|
486
|
+
<div class="auth-info">
|
|
487
|
+
{isAuthenticated && user && (
|
|
488
|
+
<div class="user-card">
|
|
489
|
+
<div class="avatar">{user.avatar}</div>
|
|
490
|
+
<span class="auth-text">Welcome back, {user.name}!</span>
|
|
491
|
+
</div>
|
|
492
|
+
)}
|
|
493
|
+
{!isAuthenticated && (
|
|
494
|
+
<div class="guest-card">
|
|
495
|
+
<div class="avatar">đ¤</div>
|
|
496
|
+
<span class="auth-text">Welcome, Guest</span>
|
|
497
|
+
</div>
|
|
498
|
+
)}
|
|
499
|
+
</div>
|
|
500
|
+
|
|
501
|
+
<button class="btn" onclick="toggleAuth">
|
|
502
|
+
{isAuthenticated ? "đĒ Logout" : "đ Login"}
|
|
503
|
+
</button>
|
|
504
|
+
</div>
|
|
505
|
+
</div>
|
|
506
|
+
|
|
507
|
+
<div class="code-example">
|
|
508
|
+
<span class="code-comment">// Conditional rendering with && operator</span>
|
|
509
|
+
<span class="code-keyword">state</span> isAuthenticated = <span class="code-keyword">false</span>
|
|
510
|
+
<span class="code-keyword">state</span> user = <span class="code-keyword">null</span>
|
|
511
|
+
|
|
512
|
+
<span class="code-comment">// Show element only when condition is true</span>
|
|
513
|
+
{isAuthenticated && user && (
|
|
514
|
+
<div class="user-card">
|
|
515
|
+
Welcome back, {user.name}!
|
|
516
|
+
</div>
|
|
517
|
+
)}
|
|
518
|
+
|
|
519
|
+
<span class="code-comment">// Negated condition for alternative content</span>
|
|
520
|
+
{!isAuthenticated && (
|
|
521
|
+
<div class="guest-card">
|
|
522
|
+
Welcome, Guest
|
|
523
|
+
</div>
|
|
524
|
+
)}
|
|
525
|
+
</div>
|
|
526
|
+
</section>
|
|
527
|
+
|
|
528
|
+
<!-- Ternary Expressions -->
|
|
529
|
+
<section class="feature-section">
|
|
530
|
+
<div class="feature-header">
|
|
531
|
+
<h2 class="feature-title">â Inline Ternary Expressions</h2>
|
|
532
|
+
<p class="feature-description">
|
|
533
|
+
Use <code style="background: rgba(59, 130, 246, 0.1); padding: 2px 6px; border-radius: 4px;">condition ? A : B</code> for concise inline conditional content.
|
|
534
|
+
Great for dynamic button text, status indicators, and toggleable values.
|
|
535
|
+
</p>
|
|
536
|
+
</div>
|
|
537
|
+
|
|
538
|
+
<div class="demo-card">
|
|
539
|
+
<div class="auth-card">
|
|
540
|
+
<div class="auth-info">
|
|
541
|
+
<span class="auth-text">Status:</span>
|
|
542
|
+
<span class="auth-text" :class="isAuthenticated ? 'status-active-text' : 'status-inactive-text'">
|
|
543
|
+
{isAuthenticated ? "â Authenticated" : "â Not Authenticated"}
|
|
544
|
+
</span>
|
|
545
|
+
</div>
|
|
546
|
+
|
|
547
|
+
<button class="btn" onclick="toggleAuth">
|
|
548
|
+
{isAuthenticated ? "đĒ Logout" : "đ Login"}
|
|
549
|
+
</button>
|
|
550
|
+
</div>
|
|
551
|
+
</div>
|
|
552
|
+
|
|
553
|
+
<div class="code-example">
|
|
554
|
+
<span class="code-comment">// Ternary expression for dynamic content</span>
|
|
555
|
+
<span class="code-keyword">state</span> isAuthenticated = <span class="code-keyword">false</span>
|
|
556
|
+
|
|
557
|
+
<button>
|
|
558
|
+
{isAuthenticated ? <span class="code-string">"Logout"</span> : <span class="code-string">"Login"</span>}
|
|
559
|
+
</button>
|
|
560
|
+
|
|
561
|
+
<span class="code-comment">// Ternary in attributes using :class binding</span>
|
|
562
|
+
<span :class="isAuthenticated ? 'status-active' : 'status-inactive'">
|
|
563
|
+
{isAuthenticated ? <span class="code-string">"Authenticated"</span> : <span class="code-string">"Not Authenticated"</span>}
|
|
564
|
+
</span>
|
|
565
|
+
</div>
|
|
566
|
+
</section>
|
|
567
|
+
|
|
568
|
+
<!-- Array Mapping -->
|
|
569
|
+
<section class="feature-section">
|
|
570
|
+
<div class="feature-header">
|
|
571
|
+
<h2 class="feature-title">đ Array Iteration (map)</h2>
|
|
572
|
+
<p class="feature-description">
|
|
573
|
+
Use <code style="background: rgba(59, 130, 246, 0.1); padding: 2px 6px; border-radius: 4px;">array.map(item => <element key={item.id}>...</element>)</code> to render lists.
|
|
574
|
+
The <code style="background: rgba(59, 130, 246, 0.1); padding: 2px 6px; border-radius: 4px;">key</code> prop is required for stable DOM updates.
|
|
575
|
+
</p>
|
|
576
|
+
</div>
|
|
577
|
+
|
|
578
|
+
<div class="demo-card">
|
|
579
|
+
<div class="notification-header">
|
|
580
|
+
<h3 style="margin: 0; color: #e2e8f0; font-size: 1.1rem;">đ Notifications ({notifications.length})</h3>
|
|
581
|
+
<div style="display: flex; gap: 12px;">
|
|
582
|
+
<button class="btn btn-sm btn-outline" onclick="markAllRead">
|
|
583
|
+
â Mark all read
|
|
584
|
+
</button>
|
|
585
|
+
<button class="btn btn-sm dismiss-btn" onclick="clearNotifications">
|
|
586
|
+
â Clear All
|
|
587
|
+
</button>
|
|
588
|
+
</div>
|
|
589
|
+
</div>
|
|
590
|
+
|
|
591
|
+
<div class="notification-list">
|
|
592
|
+
{notifications.length === 0 && (
|
|
593
|
+
<div class="empty-state">No notifications</div>
|
|
594
|
+
)}
|
|
595
|
+
{notifications.map(n => (
|
|
596
|
+
<div key={n.id} class="notification-item">
|
|
597
|
+
<div class="notification-content">
|
|
598
|
+
<div class="notification-icon">
|
|
599
|
+
{n.type === 'info' ? 'âšī¸' : n.type === 'success' ? 'â
' : 'â ī¸'}
|
|
600
|
+
</div>
|
|
601
|
+
<span class="notification-text">{n.message}</span>
|
|
602
|
+
</div>
|
|
603
|
+
{!n.read && <div class="notification-unread"></div>}
|
|
604
|
+
</div>
|
|
605
|
+
))}
|
|
606
|
+
</div>
|
|
607
|
+
</div>
|
|
608
|
+
|
|
609
|
+
<div class="code-example">
|
|
610
|
+
<span class="code-comment">// Array state</span>
|
|
611
|
+
<span class="code-keyword">state</span> notifications = [
|
|
612
|
+
{ id: 1, type: <span class="code-string">"info"</span>, message: <span class="code-string">"Welcome!"</span>, read: <span class="code-keyword">false</span> },
|
|
613
|
+
{ id: 2, type: <span class="code-string">"success"</span>, message: <span class="code-string">"Updated"</span>, read: <span class="code-keyword">true</span> }
|
|
614
|
+
]
|
|
615
|
+
|
|
616
|
+
<span class="code-comment">// Map over array - key prop is required!</span>
|
|
617
|
+
<div>
|
|
618
|
+
{notifications.map(n => (
|
|
619
|
+
<div key={n.id} class="notification-item">
|
|
620
|
+
<span>{n.message}</span>
|
|
621
|
+
{!n.read && <div class="unread"></div>}
|
|
622
|
+
</div>
|
|
623
|
+
))}
|
|
624
|
+
</div>
|
|
625
|
+
|
|
626
|
+
<span class="code-comment">// Empty state with conditional</span>
|
|
627
|
+
{notifications.length === 0 && (
|
|
628
|
+
<div class="empty-state">No notifications</div>
|
|
629
|
+
)}
|
|
630
|
+
</div>
|
|
631
|
+
</section>
|
|
632
|
+
|
|
633
|
+
<!-- Nested Composition -->
|
|
634
|
+
<section class="feature-section">
|
|
635
|
+
<div class="feature-header">
|
|
636
|
+
<h2 class="feature-title">đ¯ Nested Composition</h2>
|
|
637
|
+
<p class="feature-description">
|
|
638
|
+
Combine conditionals, ternaries, and maps for complex rendering logic.
|
|
639
|
+
Ternaries inside maps, conditionals inside ternaries, maps inside conditionals - it all works seamlessly!
|
|
640
|
+
</p>
|
|
641
|
+
</div>
|
|
642
|
+
|
|
643
|
+
<div class="demo-card">
|
|
644
|
+
<div class="todo-actions">
|
|
645
|
+
<button class="btn" onclick="addTodo">+ Add Task</button>
|
|
646
|
+
<button class="btn btn-outline" onclick="toggleNextTodo">â Complete Next</button>
|
|
647
|
+
</div>
|
|
648
|
+
|
|
649
|
+
<div class="todo-list">
|
|
650
|
+
{todoItems.length === 0 && (
|
|
651
|
+
<div class="empty-state">No tasks yet! Add one to get started.</div>
|
|
652
|
+
)}
|
|
653
|
+
{todoItems.map(todo => (
|
|
654
|
+
<div key={todo.id} class="todo-item" :class="todo.completed ? 'todo-completed' : ''">
|
|
655
|
+
<div class="todo-checkbox">{todo.completed && 'â'}</div>
|
|
656
|
+
<span class="todo-text">{todo.text}</span>
|
|
657
|
+
</div>
|
|
658
|
+
))}
|
|
659
|
+
</div>
|
|
660
|
+
|
|
661
|
+
<div class="todo-stats">
|
|
662
|
+
<span>
|
|
663
|
+
<span class="stat-value">{todoItems.filter(t => t.completed).length}</span> completed
|
|
664
|
+
</span>
|
|
665
|
+
<span>
|
|
666
|
+
<span class="stat-value">{todoItems.filter(t => !t.completed).length}</span> remaining
|
|
667
|
+
</span>
|
|
668
|
+
<span>
|
|
669
|
+
<span class="stat-value">{todoItems.length}</span> total
|
|
670
|
+
</span>
|
|
671
|
+
</div>
|
|
672
|
+
</div>
|
|
673
|
+
|
|
674
|
+
<div class="code-example">
|
|
675
|
+
<span class="code-comment">// Complex nested composition</span>
|
|
676
|
+
<span class="code-keyword">state</span> todoItems = [
|
|
677
|
+
{ id: 1, text: <span class="code-string">"Learn"</span>, completed: <span class="code-keyword">true</span> },
|
|
678
|
+
{ id: 2, text: <span class="code-string">"Build"</span>, completed: <span class="code-keyword">false</span> }
|
|
679
|
+
]
|
|
680
|
+
|
|
681
|
+
<span class="code-comment">// Map with nested conditional and ternary</span>
|
|
682
|
+
<div>
|
|
683
|
+
{todoItems.length === 0 && (
|
|
684
|
+
<div class="empty-state">No tasks</div>
|
|
685
|
+
)}
|
|
686
|
+
{todoItems.map(todo => (
|
|
687
|
+
<div key={todo.id} :class="todo.completed ? 'todo-completed' : ''">
|
|
688
|
+
{todo.completed && <span>â</span>}
|
|
689
|
+
<span>{todo.text}</span>
|
|
690
|
+
</div>
|
|
691
|
+
))}
|
|
692
|
+
</div>
|
|
693
|
+
|
|
694
|
+
<span class="code-comment">// Ternary with array methods</span>
|
|
695
|
+
<span>
|
|
696
|
+
{todoItems.filter(t => t.completed).length} completed
|
|
697
|
+
</span>
|
|
698
|
+
</div>
|
|
699
|
+
</section>
|
|
700
|
+
|
|
701
|
+
<!-- Navigation -->
|
|
702
|
+
<section class="feature-section">
|
|
703
|
+
<h2 class="feature-title">đ Navigation</h2>
|
|
704
|
+
<nav class="nav-section">
|
|
705
|
+
<ZenLink href="/" class="nav-link">â Home</ZenLink>
|
|
706
|
+
<ZenLink href="/dynamic-primitives" class="nav-link">Primitives Demo â</ZenLink>
|
|
707
|
+
<ZenLink href="/primitives-demo" class="nav-link">Zen* API Demo</ZenLink>
|
|
708
|
+
</nav>
|
|
709
|
+
</section>
|
|
710
|
+
</main>
|
|
711
|
+
</div>
|
|
712
|
+
</Main>
|