frontend-hamroun 1.2.83 → 1.2.84
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/bin/cli.js +57 -869
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.client.cjs +1 -1
- package/dist/index.client.cjs.map +1 -1
- package/dist/index.client.js +2 -2
- package/dist/index.client.js.map +1 -1
- package/dist/index.js +116 -136
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.cjs.map +1 -1
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/renderer-DaVfBeVi.cjs +2 -0
- package/dist/renderer-DaVfBeVi.cjs.map +1 -0
- package/dist/renderer-nfT7XSpo.js +61 -0
- package/dist/renderer-nfT7XSpo.js.map +1 -0
- package/dist/server-renderer-B5b0Q0ck.cjs +2 -0
- package/dist/server-renderer-B5b0Q0ck.cjs.map +1 -0
- package/dist/{server-renderer-C1WXH-zV.js → server-renderer-C4MB-jAp.js} +6 -39
- package/dist/server-renderer-C4MB-jAp.js.map +1 -0
- package/dist/server-renderer.cjs +1 -1
- package/dist/server-renderer.js +1 -1
- package/package.json +1 -1
- package/templates/basic-app/src/App.tsx +397 -19
- package/templates/ssr-template/src/App.tsx +43 -18
- package/dist/renderer-BL3gq8cW.cjs +0 -2
- package/dist/renderer-BL3gq8cW.cjs.map +0 -1
- package/dist/renderer-Dyy-o05F.js +0 -52
- package/dist/renderer-Dyy-o05F.js.map +0 -1
- package/dist/server-renderer-C1WXH-zV.js.map +0 -1
- package/dist/server-renderer-Chs-nmJm.cjs +0 -2
- package/dist/server-renderer-Chs-nmJm.cjs.map +0 -1
@@ -1,26 +1,404 @@
|
|
1
|
-
import {
|
1
|
+
import {
|
2
|
+
jsx,
|
3
|
+
useState,
|
4
|
+
useEffect,
|
5
|
+
useMemo,
|
6
|
+
useErrorBoundary,
|
7
|
+
useForm
|
8
|
+
} from 'frontend-hamroun';
|
2
9
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
10
|
+
// Todo item interface
|
11
|
+
interface Todo {
|
12
|
+
id: number;
|
13
|
+
text: string;
|
14
|
+
completed: boolean;
|
15
|
+
priority: 'high' | 'medium' | 'low';
|
16
|
+
}
|
17
|
+
|
18
|
+
// Component prop interfaces
|
19
|
+
interface TodoItemProps {
|
20
|
+
todo: Todo;
|
21
|
+
onToggle: (id: number) => void;
|
22
|
+
onDelete: (id: number) => void;
|
23
|
+
}
|
24
|
+
|
25
|
+
interface ThemeToggleButtonProps {
|
26
|
+
theme: string;
|
27
|
+
onToggle: () => void;
|
28
|
+
}
|
29
|
+
|
30
|
+
interface AppFooterProps {
|
31
|
+
theme: string;
|
32
|
+
}
|
33
|
+
|
34
|
+
// Todo Item Component
|
35
|
+
function TodoItem({ todo, onToggle, onDelete }: TodoItemProps) {
|
7
36
|
return (
|
8
|
-
<div>
|
9
|
-
<
|
10
|
-
|
37
|
+
<div className={`todo-item ${todo.completed ? 'completed' : ''}`}>
|
38
|
+
<input
|
39
|
+
type="checkbox"
|
40
|
+
checked={todo.completed}
|
41
|
+
onChange={() => onToggle(todo.id)}
|
42
|
+
className="todo-checkbox"
|
43
|
+
/>
|
44
|
+
<span className="todo-text">{todo.text}</span>
|
45
|
+
<span className={`todo-priority priority-${todo.priority}`}>
|
46
|
+
{todo.priority === 'high' && '🔴'}
|
47
|
+
{todo.priority === 'medium' && '🟡'}
|
48
|
+
{todo.priority === 'low' && '🟢'}
|
49
|
+
{todo.priority}
|
50
|
+
</span>
|
51
|
+
<div className="todo-actions">
|
11
52
|
<button
|
12
|
-
onClick={() =>
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
53
|
+
onClick={() => onDelete(todo.id)}
|
54
|
+
className="btn btn-sm btn-danger"
|
55
|
+
title="Delete todo"
|
56
|
+
>
|
57
|
+
🗑️
|
58
|
+
</button>
|
59
|
+
</div>
|
60
|
+
</div>
|
61
|
+
);
|
62
|
+
}
|
63
|
+
|
64
|
+
// Theme Toggle Button Component
|
65
|
+
function ThemeToggleButton({ theme, onToggle }: ThemeToggleButtonProps) {
|
66
|
+
return (
|
67
|
+
<button
|
68
|
+
onClick={onToggle}
|
69
|
+
className="btn btn-secondary theme-toggle"
|
70
|
+
title="Toggle theme"
|
71
|
+
>
|
72
|
+
{theme === 'light' ? '🌙' : '☀️'}
|
73
|
+
</button>
|
74
|
+
);
|
75
|
+
}
|
76
|
+
|
77
|
+
// App Footer Component
|
78
|
+
function AppFooter({ theme }: AppFooterProps) {
|
79
|
+
return (
|
80
|
+
<footer className="app-footer">
|
81
|
+
<div className="container">
|
82
|
+
<p>Built with Frontend Hamroun • Hooks: useState, useEffect, useMemo, useErrorBoundary, useForm</p>
|
83
|
+
<p>Theme: <strong>{theme}</strong> • Environment: <strong>Client</strong></p>
|
84
|
+
</div>
|
85
|
+
</footer>
|
86
|
+
);
|
87
|
+
}
|
88
|
+
|
89
|
+
export function App() {
|
90
|
+
// State hooks with proper typing
|
91
|
+
const [todos, setTodos] = useState<Todo[]>([
|
92
|
+
{ id: 1, text: 'Learn Frontend Hamroun hooks', completed: false, priority: 'high' },
|
93
|
+
{ id: 2, text: 'Build a todo app', completed: false, priority: 'medium' },
|
94
|
+
{ id: 3, text: 'Master client-side concepts', completed: true, priority: 'low' }
|
95
|
+
]);
|
96
|
+
const [theme, setTheme] = useState<string>('light');
|
97
|
+
const [taskFilter, setTaskFilter] = useState<string>('all');
|
98
|
+
|
99
|
+
// Error boundary hook
|
100
|
+
const [error, resetError] = useErrorBoundary();
|
101
|
+
|
102
|
+
// Form hook for adding new todos
|
103
|
+
const addTodoForm = useForm({
|
104
|
+
initialValues: {
|
105
|
+
text: '',
|
106
|
+
priority: 'medium' as 'high' | 'medium' | 'low'
|
107
|
+
},
|
108
|
+
validate: (values) => {
|
109
|
+
const errors: Record<string, string> = {};
|
110
|
+
if (!values.text.trim()) {
|
111
|
+
errors.text = 'Todo text is required';
|
112
|
+
}
|
113
|
+
return errors;
|
114
|
+
},
|
115
|
+
onSubmit: (values) => {
|
116
|
+
const newTodo: Todo = {
|
117
|
+
id: Date.now(),
|
118
|
+
text: values.text.trim(),
|
119
|
+
completed: false,
|
120
|
+
priority: values.priority
|
121
|
+
};
|
122
|
+
setTodos(prev => [...prev, newTodo]);
|
123
|
+
addTodoForm.resetForm();
|
124
|
+
}
|
125
|
+
});
|
126
|
+
|
127
|
+
// Mount effect
|
128
|
+
useEffect(() => {
|
129
|
+
console.log('Todo App mounted');
|
130
|
+
|
131
|
+
// Load saved todos from localStorage
|
132
|
+
const savedTodos = localStorage.getItem('todos');
|
133
|
+
const savedTheme = localStorage.getItem('theme');
|
134
|
+
|
135
|
+
if (savedTodos) {
|
136
|
+
try {
|
137
|
+
setTodos(JSON.parse(savedTodos));
|
138
|
+
} catch (e) {
|
139
|
+
console.error('Failed to load saved todos');
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
if (savedTheme) {
|
144
|
+
setTheme(savedTheme);
|
145
|
+
}
|
146
|
+
|
147
|
+
return () => {
|
148
|
+
console.log('Todo App unmounting');
|
149
|
+
};
|
150
|
+
}, []);
|
151
|
+
|
152
|
+
// Theme effect
|
153
|
+
useEffect(() => {
|
154
|
+
document.body.className = `theme-${theme}`;
|
155
|
+
document.documentElement.setAttribute('data-theme', theme);
|
156
|
+
localStorage.setItem('theme', theme);
|
157
|
+
}, [theme]);
|
158
|
+
|
159
|
+
// Save todos effect
|
160
|
+
useEffect(() => {
|
161
|
+
if (todos.length === 0) return;
|
162
|
+
localStorage.setItem('todos', JSON.stringify(todos));
|
163
|
+
}, [todos]);
|
164
|
+
|
165
|
+
// Memoized filtered todos
|
166
|
+
const filteredTodos = useMemo(() => {
|
167
|
+
console.log('Filtering todos - memoized computation');
|
168
|
+
return todos.filter(todo => {
|
169
|
+
if (taskFilter === 'completed') return todo.completed;
|
170
|
+
if (taskFilter === 'active') return !todo.completed;
|
171
|
+
if (taskFilter === 'high') return todo.priority === 'high';
|
172
|
+
if (taskFilter === 'medium') return todo.priority === 'medium';
|
173
|
+
if (taskFilter === 'low') return todo.priority === 'low';
|
174
|
+
return true;
|
175
|
+
});
|
176
|
+
}, [todos, taskFilter]);
|
177
|
+
|
178
|
+
// Memoized todo stats
|
179
|
+
const todoStats = useMemo(() => {
|
180
|
+
const total = todos.length;
|
181
|
+
const completed = todos.filter(t => t.completed).length;
|
182
|
+
const active = total - completed;
|
183
|
+
const highPriority = todos.filter(t => t.priority === 'high' && !t.completed).length;
|
184
|
+
|
185
|
+
return { total, completed, active, highPriority };
|
186
|
+
}, [todos]);
|
187
|
+
|
188
|
+
// Todo action handlers
|
189
|
+
const handleToggleTodo = (id: number) => {
|
190
|
+
console.log('Toggling todo:', id);
|
191
|
+
setTodos(prev => prev.map(todo =>
|
192
|
+
todo.id === id ? { ...todo, completed: !todo.completed } : todo
|
193
|
+
));
|
194
|
+
};
|
195
|
+
|
196
|
+
const handleDeleteTodo = (id: number) => {
|
197
|
+
console.log('Deleting todo:', id);
|
198
|
+
setTodos(prev => prev.filter(todo => todo.id !== id));
|
199
|
+
};
|
200
|
+
|
201
|
+
// Event handlers
|
202
|
+
const clearCompleted = () => {
|
203
|
+
setTodos(prev => prev.filter(todo => !todo.completed));
|
204
|
+
};
|
205
|
+
|
206
|
+
const markAllComplete = () => {
|
207
|
+
setTodos(prev => prev.map(todo => ({ ...todo, completed: true })));
|
208
|
+
};
|
209
|
+
|
210
|
+
const toggleTheme = () => {
|
211
|
+
setTheme(prev => prev === 'light' ? 'dark' : 'light');
|
212
|
+
};
|
213
|
+
|
214
|
+
const simulateError = () => {
|
215
|
+
throw new Error('Simulated error for testing error boundary');
|
216
|
+
};
|
217
|
+
|
218
|
+
if (error) {
|
219
|
+
return (
|
220
|
+
<div className="error-container">
|
221
|
+
<h2>Something went wrong!</h2>
|
222
|
+
<p>{(error as Error).message}</p>
|
223
|
+
<button onClick={resetError} className="btn btn-primary">
|
224
|
+
Try Again
|
225
|
+
</button>
|
20
226
|
</div>
|
21
|
-
|
22
|
-
|
23
|
-
|
227
|
+
);
|
228
|
+
}
|
229
|
+
|
230
|
+
return (
|
231
|
+
<div className={`app theme-${theme}`}>
|
232
|
+
|
233
|
+
{/* Header */}
|
234
|
+
<header className="app-header">
|
235
|
+
<div className="container">
|
236
|
+
<h1 className="app-title">
|
237
|
+
📝 Todo App
|
238
|
+
<span className="subtitle">Built with Frontend Hamroun</span>
|
239
|
+
</h1>
|
240
|
+
|
241
|
+
<div className="header-controls">
|
242
|
+
<ThemeToggleButton theme={theme} onToggle={toggleTheme} />
|
243
|
+
<div className="stats">
|
244
|
+
<span className="stat">
|
245
|
+
Total: <strong>{todoStats.total}</strong>
|
246
|
+
</span>
|
247
|
+
<span className="stat">
|
248
|
+
Active: <strong>{todoStats.active}</strong>
|
249
|
+
</span>
|
250
|
+
<span className="stat">
|
251
|
+
Done: <strong>{todoStats.completed}</strong>
|
252
|
+
</span>
|
253
|
+
</div>
|
254
|
+
</div>
|
255
|
+
</div>
|
256
|
+
</header>
|
257
|
+
|
258
|
+
<main className="main-content">
|
259
|
+
<div className="container">
|
260
|
+
|
261
|
+
{/* Add Todo Section */}
|
262
|
+
<section className="card add-todo-section">
|
263
|
+
<h2>➕ Add New Todo</h2>
|
264
|
+
|
265
|
+
<form onSubmit={addTodoForm.handleSubmit} className="add-todo-form">
|
266
|
+
<input
|
267
|
+
type="text"
|
268
|
+
name="text"
|
269
|
+
value={addTodoForm.values.text}
|
270
|
+
onChange={addTodoForm.handleChange}
|
271
|
+
onBlur={addTodoForm.handleBlur}
|
272
|
+
placeholder="What needs to be done?"
|
273
|
+
className={`input todo-input ${addTodoForm.errors.text && addTodoForm.touched.text ? 'error' : ''}`}
|
274
|
+
/>
|
275
|
+
|
276
|
+
<select
|
277
|
+
name="priority"
|
278
|
+
value={addTodoForm.values.priority}
|
279
|
+
onChange={addTodoForm.handleChange}
|
280
|
+
className="select priority-select"
|
281
|
+
>
|
282
|
+
<option value="low">🟢 Low Priority</option>
|
283
|
+
<option value="medium">🟡 Medium Priority</option>
|
284
|
+
<option value="high">🔴 High Priority</option>
|
285
|
+
</select>
|
286
|
+
|
287
|
+
<button
|
288
|
+
type="submit"
|
289
|
+
className={`btn btn-primary add-btn ${addTodoForm.isSubmitting ? 'loading' : ''}`}
|
290
|
+
disabled={addTodoForm.isSubmitting || !addTodoForm.values.text.trim()}
|
291
|
+
>
|
292
|
+
{addTodoForm.isSubmitting ? '⏳ Adding...' : '➕ Add Todo'}
|
293
|
+
</button>
|
294
|
+
</form>
|
295
|
+
|
296
|
+
{addTodoForm.errors.text && addTodoForm.touched.text && (
|
297
|
+
<div className="error-message">{addTodoForm.errors.text}</div>
|
298
|
+
)}
|
299
|
+
</section>
|
300
|
+
|
301
|
+
{/* Filters Section */}
|
302
|
+
<section className="card filters-section">
|
303
|
+
<h2>🔍 Filter Todos</h2>
|
304
|
+
|
305
|
+
<div className="filters">
|
306
|
+
{(['all', 'active', 'completed', 'high', 'medium', 'low'] as const).map(filterType => (
|
307
|
+
<button
|
308
|
+
key={filterType}
|
309
|
+
onClick={() => setTaskFilter(filterType)}
|
310
|
+
className={`btn btn-sm filter-btn ${taskFilter === filterType ? 'btn-primary' : 'btn-outline'}`}
|
311
|
+
>
|
312
|
+
{filterType === 'all' && '📋 All'}
|
313
|
+
{filterType === 'active' && '⏳ Active'}
|
314
|
+
{filterType === 'completed' && '✅ Completed'}
|
315
|
+
{filterType === 'high' && '🔴 High Priority'}
|
316
|
+
{filterType === 'medium' && '🟡 Medium Priority'}
|
317
|
+
{filterType === 'low' && '🟢 Low Priority'}
|
318
|
+
</button>
|
319
|
+
))}
|
320
|
+
</div>
|
321
|
+
|
322
|
+
<div className="bulk-actions">
|
323
|
+
<button
|
324
|
+
onClick={markAllComplete}
|
325
|
+
className="btn btn-success btn-sm"
|
326
|
+
disabled={todoStats.active === 0}
|
327
|
+
>
|
328
|
+
✅ Mark All Complete
|
329
|
+
</button>
|
330
|
+
<button
|
331
|
+
onClick={clearCompleted}
|
332
|
+
className="btn btn-warning btn-sm"
|
333
|
+
disabled={todoStats.completed === 0}
|
334
|
+
>
|
335
|
+
🗑️ Clear Completed
|
336
|
+
</button>
|
337
|
+
</div>
|
338
|
+
</section>
|
339
|
+
|
340
|
+
{/* Todos List Section */}
|
341
|
+
<section className="card todos-section">
|
342
|
+
<div className="section-header">
|
343
|
+
<h2>📋 Todo List</h2>
|
344
|
+
<div className="filter-info">
|
345
|
+
Showing <strong>{filteredTodos.length}</strong> of <strong>{todoStats.total}</strong> todos
|
346
|
+
{taskFilter !== 'all' && <span className="filter-badge">{taskFilter}</span>}
|
347
|
+
</div>
|
348
|
+
</div>
|
349
|
+
|
350
|
+
<div className="todos-list">
|
351
|
+
{filteredTodos.length > 0 ? (
|
352
|
+
filteredTodos.map(todo => (
|
353
|
+
<TodoItem
|
354
|
+
key={todo.id}
|
355
|
+
todo={todo}
|
356
|
+
onToggle={handleToggleTodo}
|
357
|
+
onDelete={handleDeleteTodo}
|
358
|
+
/>
|
359
|
+
))
|
360
|
+
) : (
|
361
|
+
<div className="empty-state">
|
362
|
+
<p>
|
363
|
+
{taskFilter === 'all' ? '🎉 No todos yet. Add one above!' :
|
364
|
+
taskFilter === 'completed' ? '📝 No completed todos yet.' :
|
365
|
+
taskFilter === 'active' ? '🎯 No active todos. Great job!' :
|
366
|
+
`🔍 No ${taskFilter} priority todos found.`}
|
367
|
+
</p>
|
368
|
+
</div>
|
369
|
+
)}
|
370
|
+
</div>
|
371
|
+
</section>
|
372
|
+
|
373
|
+
{/* Actions Section */}
|
374
|
+
<section className="card actions-section">
|
375
|
+
<h2>⚙️ Actions</h2>
|
376
|
+
<div className="action-buttons">
|
377
|
+
<button onClick={simulateError} className="btn btn-danger">
|
378
|
+
💥 Test Error Boundary
|
379
|
+
</button>
|
380
|
+
</div>
|
381
|
+
</section>
|
382
|
+
|
383
|
+
</div>
|
384
|
+
</main>
|
385
|
+
|
386
|
+
{/* Footer */}
|
387
|
+
<AppFooter theme={theme} />
|
388
|
+
|
389
|
+
{/* Styles */}
|
390
|
+
<style>{`
|
391
|
+
/* ...existing styles... */
|
392
|
+
.error-message {
|
393
|
+
color: #e74c3c;
|
394
|
+
font-size: 0.875rem;
|
395
|
+
margin-top: 0.5rem;
|
396
|
+
}
|
397
|
+
|
398
|
+
.input.error {
|
399
|
+
border-color: #e74c3c;
|
400
|
+
}
|
401
|
+
`}</style>
|
24
402
|
</div>
|
25
403
|
);
|
26
404
|
}
|
@@ -6,8 +6,33 @@ import {
|
|
6
6
|
useErrorBoundary
|
7
7
|
} from 'frontend-hamroun';
|
8
8
|
|
9
|
+
// Todo item interface
|
10
|
+
interface Todo {
|
11
|
+
id: number;
|
12
|
+
text: string;
|
13
|
+
completed: boolean;
|
14
|
+
priority: 'high' | 'medium' | 'low';
|
15
|
+
}
|
16
|
+
|
17
|
+
// Component prop interfaces
|
18
|
+
interface TodoItemProps {
|
19
|
+
todo: Todo;
|
20
|
+
onToggle: (id: number) => void;
|
21
|
+
onDelete: (id: number) => void;
|
22
|
+
}
|
23
|
+
|
24
|
+
interface ThemeToggleButtonProps {
|
25
|
+
theme: string;
|
26
|
+
onToggle: () => void;
|
27
|
+
}
|
28
|
+
|
29
|
+
interface AppFooterProps {
|
30
|
+
theme: string;
|
31
|
+
isBrowser: boolean;
|
32
|
+
}
|
33
|
+
|
9
34
|
// Todo Item Component - simple props-based component
|
10
|
-
function TodoItem({ todo, onToggle, onDelete }) {
|
35
|
+
function TodoItem({ todo, onToggle, onDelete }: TodoItemProps) {
|
11
36
|
return (
|
12
37
|
<div className={`todo-item ${todo.completed ? 'completed' : ''}`}>
|
13
38
|
<input
|
@@ -37,7 +62,7 @@ function TodoItem({ todo, onToggle, onDelete }) {
|
|
37
62
|
}
|
38
63
|
|
39
64
|
// Theme Toggle Button Component
|
40
|
-
function ThemeToggleButton({ theme, onToggle }) {
|
65
|
+
function ThemeToggleButton({ theme, onToggle }: ThemeToggleButtonProps) {
|
41
66
|
return (
|
42
67
|
<button
|
43
68
|
onClick={onToggle}
|
@@ -50,7 +75,7 @@ function ThemeToggleButton({ theme, onToggle }) {
|
|
50
75
|
}
|
51
76
|
|
52
77
|
// App Footer Component
|
53
|
-
function AppFooter({ theme, isBrowser }) {
|
78
|
+
function AppFooter({ theme, isBrowser }: AppFooterProps) {
|
54
79
|
return (
|
55
80
|
<footer className="app-footer">
|
56
81
|
<div className="container">
|
@@ -62,17 +87,17 @@ function AppFooter({ theme, isBrowser }) {
|
|
62
87
|
}
|
63
88
|
|
64
89
|
export function App() {
|
65
|
-
// State hooks
|
66
|
-
const [todos, setTodos] = useState([
|
90
|
+
// State hooks with proper typing
|
91
|
+
const [todos, setTodos] = useState<Todo[]>([
|
67
92
|
{ id: 1, text: 'Learn Frontend Hamroun hooks', completed: false, priority: 'high' },
|
68
93
|
{ id: 2, text: 'Build a todo app', completed: false, priority: 'medium' },
|
69
94
|
{ id: 3, text: 'Master SSR concepts', completed: true, priority: 'low' }
|
70
95
|
]);
|
71
|
-
const [newTask, setNewTask] = useState('');
|
72
|
-
const [taskFilter, setTaskFilter] = useState('all');
|
73
|
-
const [taskPriority, setTaskPriority] = useState('medium');
|
74
|
-
const [theme, setTheme] = useState('light');
|
75
|
-
const [isLoading, setIsLoading] = useState(false);
|
96
|
+
const [newTask, setNewTask] = useState<string>('');
|
97
|
+
const [taskFilter, setTaskFilter] = useState<string>('all');
|
98
|
+
const [taskPriority, setTaskPriority] = useState<'high' | 'medium' | 'low'>('medium');
|
99
|
+
const [theme, setTheme] = useState<string>('light');
|
100
|
+
const [isLoading, setIsLoading] = useState<boolean>(false);
|
76
101
|
|
77
102
|
// Error boundary hook
|
78
103
|
const [error, resetError] = useErrorBoundary();
|
@@ -147,22 +172,22 @@ export function App() {
|
|
147
172
|
}, [todos]);
|
148
173
|
|
149
174
|
// Todo action handlers
|
150
|
-
const handleToggleTodo = (id) => {
|
175
|
+
const handleToggleTodo = (id: number) => {
|
151
176
|
console.log('Toggling todo:', id);
|
152
177
|
setTodos(prev => prev.map(todo =>
|
153
178
|
todo.id === id ? { ...todo, completed: !todo.completed } : todo
|
154
179
|
));
|
155
180
|
};
|
156
181
|
|
157
|
-
const handleDeleteTodo = (id) => {
|
182
|
+
const handleDeleteTodo = (id: number) => {
|
158
183
|
console.log('Deleting todo:', id);
|
159
184
|
setTodos(prev => prev.filter(todo => todo.id !== id));
|
160
185
|
};
|
161
186
|
|
162
|
-
const handleAddTodo = (text, priority) => {
|
187
|
+
const handleAddTodo = (text: string, priority: 'high' | 'medium' | 'low') => {
|
163
188
|
if (!text || !text.trim()) return;
|
164
189
|
|
165
|
-
const newTodo = {
|
190
|
+
const newTodo: Todo = {
|
166
191
|
id: Date.now(),
|
167
192
|
text: text.trim(),
|
168
193
|
completed: false,
|
@@ -185,7 +210,7 @@ export function App() {
|
|
185
210
|
}
|
186
211
|
};
|
187
212
|
|
188
|
-
const handleKeyPress = (e) => {
|
213
|
+
const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
189
214
|
if (e.key === 'Enter') {
|
190
215
|
addTask();
|
191
216
|
}
|
@@ -211,7 +236,7 @@ export function App() {
|
|
211
236
|
return (
|
212
237
|
<div className="error-container">
|
213
238
|
<h2>Something went wrong!</h2>
|
214
|
-
<p>{error.message}</p>
|
239
|
+
<p>{(error as Error).message}</p>
|
215
240
|
<button onClick={resetError} className="btn btn-primary">
|
216
241
|
Try Again
|
217
242
|
</button>
|
@@ -267,7 +292,7 @@ export function App() {
|
|
267
292
|
|
268
293
|
<select
|
269
294
|
value={taskPriority}
|
270
|
-
onChange={(e) => setTaskPriority(e.target.value)}
|
295
|
+
onChange={(e) => setTaskPriority(e.target.value as 'high' | 'medium' | 'low')}
|
271
296
|
className="select priority-select"
|
272
297
|
disabled={isLoading}
|
273
298
|
>
|
@@ -291,7 +316,7 @@ export function App() {
|
|
291
316
|
<h2>🔍 Filter Todos</h2>
|
292
317
|
|
293
318
|
<div className="filters">
|
294
|
-
{['all', 'active', 'completed', 'high', 'medium', 'low'].map(filterType => (
|
319
|
+
{(['all', 'active', 'completed', 'high', 'medium', 'low'] as const).map(filterType => (
|
295
320
|
<button
|
296
321
|
key={filterType}
|
297
322
|
onClick={() => setTaskFilter(filterType)}
|
@@ -1,2 +0,0 @@
|
|
1
|
-
"use strict";const e=require("./jsx-runtime.cjs"),r=require("./server-renderer-Chs-nmJm.cjs");let n=!1;async function t(i,s){console.log("Rendering to:",s.id),r.batchUpdates((async()=>{r.prepareRender();try{r.setRenderCallback(t,i,s);const c=await e.createElement(i);n||(s.innerHTML=""),s.appendChild(c)}finally{r.finishRender()}}))}exports.createContext=function(e){return{Provider:({value:e,children:r})=>r,Consumer:({children:r})=>r(e),_id:Symbol(),useSelector:r=>r(e)}},exports.hydrate=async function(e,r){n=!0;try{await t(e,r)}finally{n=!1}},exports.render=t,exports.useContext=function(e){return e};
|
2
|
-
//# sourceMappingURL=renderer-BL3gq8cW.cjs.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"renderer-BL3gq8cW.cjs","sources":["../src/renderer.ts","../src/context.ts"],"sourcesContent":["import { createElement } from './jsx-runtime.js';\r\nimport { prepareRender, finishRender, setRenderCallback } from './hooks.js';\r\nimport { batchUpdates } from './batch.js';\r\n\r\nlet isHydrating = false;\r\n\r\nexport async function hydrate(element: any, container: HTMLElement) {\r\n isHydrating = true;\r\n try {\r\n await render(element, container);\r\n } finally {\r\n isHydrating = false;\r\n }\r\n}\r\n\r\nexport async function render(element: any, container: HTMLElement) {\r\n console.log('Rendering to:', container.id);\r\n \r\n batchUpdates(async () => {\r\n const rendererId = prepareRender();\r\n try {\r\n setRenderCallback(render, element, container);\r\n const domNode = await createElement(element);\r\n \r\n if (!isHydrating) {\r\n container.innerHTML = '';\r\n }\r\n container.appendChild(domNode);\r\n \r\n } finally {\r\n finishRender();\r\n }\r\n });\r\n}\r\n","\r\n\r\nconst contexts = new Map<symbol, any>();\r\nlet currentRender: Function | null = null;\r\n\r\nexport interface Context<T> {\r\n Provider: (props: { value: T; children?: any }) => any;\r\n Consumer: (props: { children: (value: T) => any }) => any;\r\n _id: symbol;\r\n useSelector: <S>(selector: (state: T) => S) => S;\r\n}\r\n\r\nexport function createContext<T>(defaultValue: T): Context<T> {\r\n const context = {\r\n Provider: ({ value, children }: { value: T, children?: any }) => {\r\n return children;\r\n },\r\n Consumer: ({ children }: { children: (value: T) => any }) => {\r\n return children(defaultValue);\r\n },\r\n _id: Symbol(),\r\n useSelector: <S>(selector: (state: T) => S) => {\r\n return selector(defaultValue);\r\n }\r\n };\r\n\r\n return context;\r\n}\r\n\r\nexport function useContext<T>(context: any): T {\r\n return context;\r\n}\r\n"],"names":["isHydrating","async","render","element","container","console","log","id","batchUpdates","prepareRender","domNode","createElement","innerHTML","appendChild","finishRender","defaultValue","Provider","value","children","Consumer","_id","Symbol","useSelector","selector","context"],"mappings":"8FAIA,IAAIA,GAAc,EAWIC,eAAAC,EAAOC,EAAcC,GACjCC,QAAAC,IAAI,gBAAiBF,EAAUG,IAEvCC,EAAAA,cAAaP,UACQQ,EAAAA,gBACf,wBACgBP,EAAQC,EAASC,GAC7B,MAAAM,QAAgBC,EAAAA,cAAcR,GAE/BH,IACHI,EAAUQ,UAAY,IAExBR,EAAUS,YAAYH,EAAO,CAE7B,QACaI,gBAAA,IAGnB,uBCrBO,SAA0BC,GAcxB,MAbS,CACdC,SAAU,EAAGC,QAAOC,cACXA,EAETC,SAAU,EAAGD,cACJA,EAASH,GAElBK,IAAKC,SACLC,YAAiBC,GACRA,EAASR,GAKtB,kBDrBsBd,eAAQE,EAAcC,GAC5BJ,GAAA,EACV,UACIE,EAAOC,EAASC,EAAS,CAC/B,QACcJ,GAAA,CAAA,CAElB,sCCgBO,SAAuBwB,GACrB,OAAAA,CACT"}
|
@@ -1,52 +0,0 @@
|
|
1
|
-
import { createElement } from "./jsx-runtime.js";
|
2
|
-
import { b as batchUpdates, p as prepareRender, s as setRenderCallback, f as finishRender } from "./server-renderer-C1WXH-zV.js";
|
3
|
-
function createContext(defaultValue) {
|
4
|
-
const context = {
|
5
|
-
Provider: ({ value, children }) => {
|
6
|
-
return children;
|
7
|
-
},
|
8
|
-
Consumer: ({ children }) => {
|
9
|
-
return children(defaultValue);
|
10
|
-
},
|
11
|
-
_id: Symbol(),
|
12
|
-
useSelector: (selector) => {
|
13
|
-
return selector(defaultValue);
|
14
|
-
}
|
15
|
-
};
|
16
|
-
return context;
|
17
|
-
}
|
18
|
-
function useContext(context) {
|
19
|
-
return context;
|
20
|
-
}
|
21
|
-
let isHydrating = false;
|
22
|
-
async function hydrate(element, container) {
|
23
|
-
isHydrating = true;
|
24
|
-
try {
|
25
|
-
await render(element, container);
|
26
|
-
} finally {
|
27
|
-
isHydrating = false;
|
28
|
-
}
|
29
|
-
}
|
30
|
-
async function render(element, container) {
|
31
|
-
console.log("Rendering to:", container.id);
|
32
|
-
batchUpdates(async () => {
|
33
|
-
const rendererId = prepareRender();
|
34
|
-
try {
|
35
|
-
setRenderCallback(render, element, container);
|
36
|
-
const domNode = await createElement(element);
|
37
|
-
if (!isHydrating) {
|
38
|
-
container.innerHTML = "";
|
39
|
-
}
|
40
|
-
container.appendChild(domNode);
|
41
|
-
} finally {
|
42
|
-
finishRender();
|
43
|
-
}
|
44
|
-
});
|
45
|
-
}
|
46
|
-
export {
|
47
|
-
createContext as c,
|
48
|
-
hydrate as h,
|
49
|
-
render as r,
|
50
|
-
useContext as u
|
51
|
-
};
|
52
|
-
//# sourceMappingURL=renderer-Dyy-o05F.js.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"renderer-Dyy-o05F.js","sources":["../src/context.ts","../src/renderer.ts"],"sourcesContent":["\r\n\r\nconst contexts = new Map<symbol, any>();\r\nlet currentRender: Function | null = null;\r\n\r\nexport interface Context<T> {\r\n Provider: (props: { value: T; children?: any }) => any;\r\n Consumer: (props: { children: (value: T) => any }) => any;\r\n _id: symbol;\r\n useSelector: <S>(selector: (state: T) => S) => S;\r\n}\r\n\r\nexport function createContext<T>(defaultValue: T): Context<T> {\r\n const context = {\r\n Provider: ({ value, children }: { value: T, children?: any }) => {\r\n return children;\r\n },\r\n Consumer: ({ children }: { children: (value: T) => any }) => {\r\n return children(defaultValue);\r\n },\r\n _id: Symbol(),\r\n useSelector: <S>(selector: (state: T) => S) => {\r\n return selector(defaultValue);\r\n }\r\n };\r\n\r\n return context;\r\n}\r\n\r\nexport function useContext<T>(context: any): T {\r\n return context;\r\n}\r\n","import { createElement } from './jsx-runtime.js';\r\nimport { prepareRender, finishRender, setRenderCallback } from './hooks.js';\r\nimport { batchUpdates } from './batch.js';\r\n\r\nlet isHydrating = false;\r\n\r\nexport async function hydrate(element: any, container: HTMLElement) {\r\n isHydrating = true;\r\n try {\r\n await render(element, container);\r\n } finally {\r\n isHydrating = false;\r\n }\r\n}\r\n\r\nexport async function render(element: any, container: HTMLElement) {\r\n console.log('Rendering to:', container.id);\r\n \r\n batchUpdates(async () => {\r\n const rendererId = prepareRender();\r\n try {\r\n setRenderCallback(render, element, container);\r\n const domNode = await createElement(element);\r\n \r\n if (!isHydrating) {\r\n container.innerHTML = '';\r\n }\r\n container.appendChild(domNode);\r\n \r\n } finally {\r\n finishRender();\r\n }\r\n });\r\n}\r\n"],"names":[],"mappings":";;AAYO,SAAS,cAAiB,cAA6B;AAC5D,QAAM,UAAU;AAAA,IACd,UAAU,CAAC,EAAE,OAAO,eAA6C;AACxD,aAAA;AAAA,IACT;AAAA,IACA,UAAU,CAAC,EAAE,eAAgD;AAC3D,aAAO,SAAS,YAAY;AAAA,IAC9B;AAAA,IACA,KAAK,OAAO;AAAA,IACZ,aAAa,CAAI,aAA8B;AAC7C,aAAO,SAAS,YAAY;AAAA,IAAA;AAAA,EAEhC;AAEO,SAAA;AACT;AAEO,SAAS,WAAc,SAAiB;AACtC,SAAA;AACT;AC3BA,IAAI,cAAc;AAEI,eAAA,QAAQ,SAAc,WAAwB;AACpD,gBAAA;AACV,MAAA;AACI,UAAA,OAAO,SAAS,SAAS;AAAA,EAAA,UAC/B;AACc,kBAAA;AAAA,EAAA;AAElB;AAEsB,eAAA,OAAO,SAAc,WAAwB;AACzD,UAAA,IAAI,iBAAiB,UAAU,EAAE;AAEzC,eAAa,YAAY;AACvB,UAAM,aAAa,cAAc;AAC7B,QAAA;AACgB,wBAAA,QAAQ,SAAS,SAAS;AACtC,YAAA,UAAU,MAAM,cAAc,OAAO;AAE3C,UAAI,CAAC,aAAa;AAChB,kBAAU,YAAY;AAAA,MAAA;AAExB,gBAAU,YAAY,OAAO;AAAA,IAAA,UAE7B;AACa,mBAAA;AAAA,IAAA;AAAA,EACf,CACD;AACH;"}
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"server-renderer-C1WXH-zV.js","sources":["../src/batch.ts","../src/hooks.ts","../src/server-renderer.ts"],"sourcesContent":["export let isBatching = false;\r\nconst queue: Function[] = [];\r\n\r\nexport function batchUpdates(fn: Function) {\r\n if (isBatching) {\r\n queue.push(fn);\r\n return;\r\n }\r\n\r\n isBatching = true;\r\n try {\r\n fn();\r\n while (queue.length > 0) {\r\n const nextFn = queue.shift();\r\n nextFn?.();\r\n }\r\n } finally {\r\n isBatching = false;\r\n }\r\n}\r\n\r\nexport function getIsBatching() {\r\n return isBatching;\r\n}\r\n","import { createElement } from './jsx-runtime.js';\r\nimport { batchUpdates, isBatching } from './batch.js';\r\nimport { diff } from './vdom.js';\r\nimport { createContext, useContext } from './context.js';\r\n\r\n// Current render ID counter\r\nlet currentRender = 0;\r\nlet isServerRender = false;\r\n\r\n// State storage\r\nconst states = new Map<number, any[]>();\r\nconst stateIndices = new Map<number, number>();\r\nconst effects = new Map<number, any[]>();\r\nconst memos = new Map<number, any[]>();\r\nconst refs = new Map<number, any[]>();\r\n\r\n// Server-side rendering detection\r\nconst isServer = typeof window === 'undefined';\r\nconst serverStates = new Map<number, Map<number, any>>();\r\n\r\n// Rendering callbacks\r\nlet globalRenderCallback: any = null;\r\nlet globalContainer: any = null;\r\nlet currentElement: any = null;\r\n\r\nexport function setRenderCallback(callback: any, element: any, container: any): void {\r\n globalRenderCallback = callback;\r\n globalContainer = container;\r\n currentElement = element;\r\n}\r\n\r\nexport function prepareRender(isSSR: boolean = false): number {\r\n currentRender++;\r\n isServerRender = isSSR;\r\n stateIndices.set(currentRender, 0);\r\n return currentRender;\r\n}\r\n\r\nexport function finishRender(): void {\r\n if (isServer || isServerRender) {\r\n serverStates.delete(currentRender);\r\n }\r\n isServerRender = false;\r\n currentRender = 0;\r\n}\r\n\r\nexport function useState<T>(initial: T): [T, (newValue: T | ((prev: T) => T)) => void] {\r\n if (!currentRender && !isServerRender) {\r\n throw new Error(\"useState must be called within a render\");\r\n }\r\n\r\n // Handle server-side rendering separately\r\n if (isServer || isServerRender) {\r\n if (!serverStates.has(currentRender)) {\r\n serverStates.set(currentRender, new Map());\r\n }\r\n const componentState = serverStates.get(currentRender)!;\r\n const index = stateIndices.get(currentRender) || 0;\r\n \r\n if (!componentState.has(index)) {\r\n componentState.set(index, initial);\r\n }\r\n \r\n const state = componentState.get(index);\r\n // In SSR, setState is a no-op\r\n const setState = (_newValue: T | ((prev: T) => T)) => {};\r\n \r\n stateIndices.set(currentRender, index + 1);\r\n return [state, setState];\r\n }\r\n\r\n // Client-side implementation\r\n if (!states.has(currentRender)) {\r\n states.set(currentRender, []);\r\n }\r\n \r\n const componentStates = states.get(currentRender)!;\r\n const index = stateIndices.get(currentRender) || 0;\r\n \r\n if (index >= componentStates.length) {\r\n componentStates.push(initial);\r\n }\r\n \r\n const state = componentStates[index];\r\n \r\n const setState = (newValue: T | ((prev: T) => T)) => {\r\n const nextValue = typeof newValue === 'function'\r\n ? (newValue as ((prev: T) => T))(componentStates[index])\r\n : newValue;\r\n \r\n if (componentStates[index] === nextValue) return;\r\n \r\n componentStates[index] = nextValue;\r\n \r\n if (isBatching) {\r\n batchUpdates(() => rerender(currentRender));\r\n } else {\r\n rerender(currentRender);\r\n }\r\n };\r\n \r\n stateIndices.set(currentRender, index + 1);\r\n return [state, setState];\r\n}\r\n\r\nexport function useEffect(callback: () => void | (() => void), deps?: any[]): void {\r\n if (!currentRender && !isServerRender) throw new Error(\"useEffect must be called within a render\");\r\n\r\n // Skip effects on server\r\n if (isServer || isServerRender) {\r\n const effectIndex = stateIndices.get(currentRender) || 0;\r\n stateIndices.set(currentRender, effectIndex + 1);\r\n return;\r\n }\r\n\r\n const effectIndex = stateIndices.get(currentRender) || 0;\r\n \r\n if (!effects.has(currentRender)) {\r\n effects.set(currentRender, []);\r\n }\r\n \r\n const componentEffects = effects.get(currentRender)!;\r\n const prevEffect = componentEffects[effectIndex];\r\n \r\n if (!prevEffect || !deps || !prevEffect.deps || deps.some((dep, i) => dep !== prevEffect.deps[i])) {\r\n if (prevEffect?.cleanup) {\r\n prevEffect.cleanup();\r\n }\r\n \r\n // Schedule effect execution after render is complete\r\n queueMicrotask(() => {\r\n const cleanup = callback() || undefined;\r\n componentEffects[effectIndex] = { cleanup, deps: deps || [] };\r\n });\r\n }\r\n \r\n stateIndices.set(currentRender, effectIndex + 1);\r\n}\r\n\r\nexport function useMemo<T>(factory: () => T, deps?: any[]): T {\r\n if (!currentRender && !isServerRender) throw new Error(\"useMemo must be called within a render\");\r\n \r\n const memoIndex = stateIndices.get(currentRender) || 0;\r\n \r\n if (!memos.has(currentRender)) {\r\n memos.set(currentRender, []);\r\n }\r\n \r\n const componentMemos = memos.get(currentRender)!;\r\n const prevMemo = componentMemos[memoIndex];\r\n \r\n if (!prevMemo || (deps && deps.some((dep, i) => !Object.is(dep, prevMemo.deps[i])))) {\r\n const value = factory();\r\n componentMemos[memoIndex] = { value, deps: deps || [] };\r\n stateIndices.set(currentRender, memoIndex + 1);\r\n return value;\r\n }\r\n \r\n stateIndices.set(currentRender, memoIndex + 1);\r\n return prevMemo.value;\r\n}\r\n\r\nexport function useRef<T>(initial: T): { current: T } {\r\n if (!currentRender && !isServerRender) throw new Error(\"useRef must be called within a render\");\r\n \r\n const refIndex = stateIndices.get(currentRender) || 0;\r\n \r\n if (!refs.has(currentRender)) {\r\n refs.set(currentRender, []);\r\n }\r\n \r\n const componentRefs = refs.get(currentRender)!;\r\n \r\n if (refIndex >= componentRefs.length) {\r\n const ref = { current: initial };\r\n componentRefs.push(ref);\r\n stateIndices.set(currentRender, refIndex + 1);\r\n return ref;\r\n }\r\n \r\n const ref = componentRefs[refIndex];\r\n stateIndices.set(currentRender, refIndex + 1);\r\n return ref;\r\n}\r\n\r\nasync function rerender(rendererId: number): Promise<void> {\r\n try {\r\n // Clean up effects\r\n const componentEffects = effects.get(rendererId);\r\n if (componentEffects) {\r\n componentEffects.forEach(effect => {\r\n if (effect.cleanup) effect.cleanup();\r\n });\r\n effects.set(rendererId, []);\r\n }\r\n \r\n // Trigger re-render\r\n if (globalRenderCallback && globalContainer && currentElement) {\r\n await globalRenderCallback(currentElement, globalContainer);\r\n }\r\n } catch (error) {\r\n console.error('Error during rerender:', error);\r\n }\r\n}\r\n\r\nexport function useErrorBoundary() {\r\n const [error, setError] = useState(null);\r\n return [error, () => setError(null)];\r\n}\r\n\r\n// Re-export from context to match index.js\r\nexport { createContext, useContext };\r\n","import { VNode } from './types.js';\r\nimport { prepareRender, finishRender } from './hooks.js';\r\n\r\nexport async function renderToString(element: any): Promise<string> {\r\n const renderId = prepareRender(true); // Mark as SSR\r\n \r\n try {\r\n const html = await renderNodeToString(element);\r\n return html;\r\n } finally {\r\n finishRender();\r\n }\r\n}\r\n\r\nasync function renderNodeToString(node: any): Promise<string> {\r\n // Handle null, undefined, boolean\r\n if (node == null || typeof node === 'boolean') {\r\n return '';\r\n }\r\n\r\n // Handle primitives\r\n if (typeof node === 'string' || typeof node === 'number') {\r\n return escapeHtml(String(node));\r\n }\r\n\r\n // Handle arrays\r\n if (Array.isArray(node)) {\r\n const results = await Promise.all(node.map(child => renderNodeToString(child)));\r\n return results.join('');\r\n }\r\n\r\n // Handle objects with type and props (React-like elements)\r\n if (node && typeof node === 'object' && 'type' in node) {\r\n const { type, props = {} } = node;\r\n\r\n // Handle function components\r\n if (typeof type === 'function') {\r\n try {\r\n const result = await type(props);\r\n return await renderNodeToString(result);\r\n } catch (error:any) {\r\n console.error('Error rendering component:', error);\r\n return `<!-- Error rendering component: ${error.message} -->`;\r\n }\r\n }\r\n\r\n // Handle DOM elements\r\n if (typeof type === 'string') {\r\n return await renderDOMElement(type, props);\r\n }\r\n }\r\n\r\n // Fallback for other objects\r\n if (typeof node === 'object') {\r\n return escapeHtml(JSON.stringify(node));\r\n }\r\n\r\n return escapeHtml(String(node));\r\n}\r\n\r\nasync function renderDOMElement(tagName: string, props: any): Promise<string> {\r\n const { children, ...attrs } = props;\r\n \r\n // Self-closing tags\r\n const voidElements = new Set([\r\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\r\n 'link', 'meta', 'param', 'source', 'track', 'wbr'\r\n ]);\r\n\r\n // Build attributes string\r\n const attributeString = Object.entries(attrs)\r\n .filter(([key, value]) => {\r\n // Filter out React-specific props and event handlers\r\n if (key.startsWith('on') || key === 'key' || key === 'ref') return false;\r\n if (value == null || value === false) return false;\r\n return true;\r\n })\r\n .map(([key, value]) => {\r\n // Handle className -> class\r\n if (key === 'className') key = 'class';\r\n \r\n // Handle boolean attributes\r\n if (value === true) return key;\r\n \r\n // Handle style objects\r\n if (key === 'style' && typeof value === 'object' && value !== null) {\r\n const styleString = Object.entries(value)\r\n .map(([prop, val]) => `${kebabCase(prop)}:${val}`)\r\n .join(';');\r\n return `style=\"${escapeHtml(styleString)}\"`;\r\n }\r\n \r\n return `${key}=\"${escapeHtml(String(value))}\"`;\r\n })\r\n .join(' ');\r\n\r\n const openTag = `<${tagName}${attributeString ? ' ' + attributeString : ''}>`;\r\n \r\n // Self-closing elements\r\n if (voidElements.has(tagName)) {\r\n return openTag.slice(0, -1) + '/>';\r\n }\r\n\r\n // Elements with children\r\n const closeTag = `</${tagName}>`;\r\n \r\n if (children != null) {\r\n const childrenString = await renderNodeToString(children);\r\n return openTag + childrenString + closeTag;\r\n }\r\n \r\n return openTag + closeTag;\r\n}\r\n\r\nfunction escapeHtml(text: string): string {\r\n const htmlEscapes: Record<string, string> = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '\"': '"',\r\n \"'\": ''',\r\n '/': '/'\r\n };\r\n \r\n return text.replace(/[&<>\"'/]/g, (match) => htmlEscapes[match]);\r\n}\r\n\r\nfunction kebabCase(str: string): string {\r\n return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);\r\n}\r\n"],"names":["index","state","setState","effectIndex","ref"],"mappings":"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"}
|