elit 1.0.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +348 -0
- package/dist/index.d.mts +501 -0
- package/dist/index.d.ts +501 -0
- package/dist/index.global.js +2064 -0
- package/dist/index.js +2266 -0
- package/dist/index.mjs +2039 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# Elit
|
|
2
|
+
|
|
3
|
+
A lightweight, zero-dependency library for building reactive web applications with direct DOM manipulation.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/elit)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Why Elit?
|
|
9
|
+
|
|
10
|
+
- **Tiny Bundle Size**: ~5KB gzipped - no framework bloat
|
|
11
|
+
- **Zero Dependencies**: Pure TypeScript, no external dependencies
|
|
12
|
+
- **Direct DOM Manipulation**: No virtual DOM overhead
|
|
13
|
+
- **TypeScript First**: Full type safety out of the box
|
|
14
|
+
- **Reactive State**: Simple but powerful reactive state management
|
|
15
|
+
- **Modern Features**: Router, SSR, virtual scrolling, and more
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install elit
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- 🎯 **Lightweight**: Optimized for performance and small bundle size
|
|
26
|
+
- ⚡ **Reactive State**: Built-in reactive state management with `createState`
|
|
27
|
+
- 🔄 **Computed Values**: Automatic dependency tracking with `computed`
|
|
28
|
+
- 🎨 **CSS-in-JS**: Type-safe styling with `CreateStyle`
|
|
29
|
+
- 🛣️ **Client-Side Router**: Hash and history mode routing with dynamic parameters
|
|
30
|
+
- 📱 **Virtual Scrolling**: Handle 100k+ items efficiently
|
|
31
|
+
- 🖥️ **SSR Support**: Server-side rendering capabilities
|
|
32
|
+
- 🎭 **SVG & MathML**: Full support for SVG and MathML elements
|
|
33
|
+
- 🔧 **Utilities**: Throttle, debounce, and batch rendering
|
|
34
|
+
- 📦 **Tree-shakeable**: Import only what you need
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { div, h1, p, button, createState, reactive, domNode } from 'elit';
|
|
40
|
+
|
|
41
|
+
// Create reactive state
|
|
42
|
+
const count = createState(0);
|
|
43
|
+
|
|
44
|
+
// Create elements
|
|
45
|
+
const app = div({ className: 'app' },
|
|
46
|
+
h1('Hello Elit!'),
|
|
47
|
+
p('A lightweight DOM library'),
|
|
48
|
+
reactive(count, (value) =>
|
|
49
|
+
button({ onclick: () => count.value++ }, `Count: ${value}`)
|
|
50
|
+
)
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Render to DOM
|
|
54
|
+
domNode.render('#app', app);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## API
|
|
58
|
+
|
|
59
|
+
### Element Factories
|
|
60
|
+
|
|
61
|
+
Create virtual DOM nodes using element factory functions:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { div, span, a, button, input, form } from 'elit';
|
|
65
|
+
|
|
66
|
+
const element = div({ className: 'container' },
|
|
67
|
+
span('Hello'),
|
|
68
|
+
a({ href: '/about' }, 'About')
|
|
69
|
+
);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### State Management
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { createState, computed, effect } from 'elit';
|
|
76
|
+
|
|
77
|
+
// Create state
|
|
78
|
+
const name = createState('World');
|
|
79
|
+
const count = createState(0);
|
|
80
|
+
|
|
81
|
+
// Computed values
|
|
82
|
+
const greeting = computed([name], (n) => `Hello, ${n}!`);
|
|
83
|
+
|
|
84
|
+
// State with options
|
|
85
|
+
const throttledState = createState(0, { throttle: 100 });
|
|
86
|
+
const deepState = createState({ nested: { value: 1 } }, { deep: true });
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Reactive Rendering
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { reactive, text, bindValue } from 'elit';
|
|
93
|
+
|
|
94
|
+
const message = createState('Hello');
|
|
95
|
+
|
|
96
|
+
// Reactive element - re-renders when state changes
|
|
97
|
+
const display = reactive(message, (value) =>
|
|
98
|
+
div({ className: 'message' }, value)
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Reactive text
|
|
102
|
+
const label = text(message);
|
|
103
|
+
|
|
104
|
+
// Two-way binding for inputs
|
|
105
|
+
const inputEl = input({ type: 'text', ...bindValue(message) });
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Server-Side Rendering
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { div, p, renderToString } from 'elit';
|
|
112
|
+
|
|
113
|
+
const html = renderToString(
|
|
114
|
+
div({ className: 'app' },
|
|
115
|
+
p('Server rendered content')
|
|
116
|
+
),
|
|
117
|
+
{ pretty: true }
|
|
118
|
+
);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Routing
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { createRouter, createRouterView, routerLink } from 'elit';
|
|
125
|
+
|
|
126
|
+
const router = createRouter({
|
|
127
|
+
mode: 'history', // or 'hash'
|
|
128
|
+
routes: [
|
|
129
|
+
{ path: '/', component: () => div('Home') },
|
|
130
|
+
{ path: '/about', component: () => div('About') },
|
|
131
|
+
{ path: '/user/:id', component: (params) => div(`User ${params.id}`) }
|
|
132
|
+
],
|
|
133
|
+
notFound: () => div('404 Not Found'),
|
|
134
|
+
beforeEach: (to, from, next) => {
|
|
135
|
+
// Navigation guard
|
|
136
|
+
console.log(`Navigating from ${from} to ${to}`);
|
|
137
|
+
next();
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Create navigation links
|
|
142
|
+
const nav = routerLink(router, { to: '/about' }, 'Go to About');
|
|
143
|
+
|
|
144
|
+
// Programmatic navigation
|
|
145
|
+
router.navigate('/user/123');
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### CSS-in-JS with CreateStyle
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { CreateStyle } from 'elit';
|
|
152
|
+
|
|
153
|
+
const styles = new CreateStyle();
|
|
154
|
+
|
|
155
|
+
// Define styles
|
|
156
|
+
const buttonClass = styles.class('button', {
|
|
157
|
+
padding: '10px 20px',
|
|
158
|
+
backgroundColor: '#007bff',
|
|
159
|
+
color: 'white',
|
|
160
|
+
border: 'none',
|
|
161
|
+
borderRadius: '4px',
|
|
162
|
+
cursor: 'pointer',
|
|
163
|
+
'&:hover': {
|
|
164
|
+
backgroundColor: '#0056b3'
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Use in elements
|
|
169
|
+
const btn = button({ className: buttonClass }, 'Click me');
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Performance Utilities
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { batchRender, renderChunked, createVirtualList, throttle, debounce } from 'elit';
|
|
176
|
+
|
|
177
|
+
// Batch render multiple elements
|
|
178
|
+
batchRender('#container', elements);
|
|
179
|
+
|
|
180
|
+
// Chunked rendering for very large lists
|
|
181
|
+
renderChunked('#container', largeArray, 5000, (current, total) => {
|
|
182
|
+
console.log(`Rendered ${current}/${total}`);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Virtual scrolling
|
|
186
|
+
const virtualList = createVirtualList(
|
|
187
|
+
container,
|
|
188
|
+
items,
|
|
189
|
+
(item, index) => div(item.name),
|
|
190
|
+
50, // item height
|
|
191
|
+
5 // buffer size
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
// Throttle and debounce
|
|
195
|
+
const throttledFn = throttle(handleScroll, 100);
|
|
196
|
+
const debouncedFn = debounce(handleInput, 300);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### JSON Rendering
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { renderJson, renderVNode, renderJsonToString } from 'elit';
|
|
203
|
+
|
|
204
|
+
// Render from JSON structure (tag, attributes, children)
|
|
205
|
+
renderJson('#app', {
|
|
206
|
+
tag: 'div',
|
|
207
|
+
attributes: { class: 'container' },
|
|
208
|
+
children: [
|
|
209
|
+
{ tag: 'h1', children: 'Title' },
|
|
210
|
+
{ tag: 'p', children: 'Content' }
|
|
211
|
+
]
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Render from VNode JSON structure (tagName, props, children)
|
|
215
|
+
renderVNode('#app', {
|
|
216
|
+
tagName: 'div',
|
|
217
|
+
props: { className: 'container' },
|
|
218
|
+
children: [
|
|
219
|
+
{ tagName: 'h1', children: ['Title'] }
|
|
220
|
+
]
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Head Management
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import { setTitle, addMeta, addLink, addStyle, renderToHead } from 'elit';
|
|
228
|
+
|
|
229
|
+
setTitle('My App');
|
|
230
|
+
addMeta({ name: 'description', content: 'My awesome app' });
|
|
231
|
+
addLink({ rel: 'stylesheet', href: '/styles.css' });
|
|
232
|
+
addStyle('body { margin: 0; }');
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Available Elements
|
|
236
|
+
|
|
237
|
+
### HTML Elements (100+)
|
|
238
|
+
All standard HTML elements are available as factory functions:
|
|
239
|
+
|
|
240
|
+
**Layout**: `div`, `span`, `section`, `article`, `header`, `footer`, `nav`, `main`, `aside`
|
|
241
|
+
|
|
242
|
+
**Text**: `p`, `h1`-`h6`, `strong`, `em`, `code`, `pre`, `blockquote`, `hr`, `br`
|
|
243
|
+
|
|
244
|
+
**Forms**: `form`, `input`, `button`, `textarea`, `select`, `option`, `label`, `fieldset`, `legend`
|
|
245
|
+
|
|
246
|
+
**Lists**: `ul`, `ol`, `li`, `dl`, `dt`, `dd`
|
|
247
|
+
|
|
248
|
+
**Tables**: `table`, `thead`, `tbody`, `tfoot`, `tr`, `th`, `td`, `caption`, `colgroup`, `col`
|
|
249
|
+
|
|
250
|
+
**Media**: `img`, `video`, `audio`, `source`, `track`, `picture`, `canvas`, `svg`
|
|
251
|
+
|
|
252
|
+
**Links**: `a`, `link`, `meta`, `base`
|
|
253
|
+
|
|
254
|
+
**Semantic**: `time`, `progress`, `meter`, `details`, `summary`, `dialog`, `mark`, `abbr`
|
|
255
|
+
|
|
256
|
+
And many more...
|
|
257
|
+
|
|
258
|
+
### SVG Elements
|
|
259
|
+
All SVG elements are prefixed with `svg`:
|
|
260
|
+
|
|
261
|
+
`svgSvg`, `svgCircle`, `svgRect`, `svgPath`, `svgLine`, `svgPolyline`, `svgPolygon`, `svgEllipse`, `svgG`, `svgText`, `svgDefs`, `svgLinearGradient`, `svgRadialGradient`, `svgStop`, `svgUse`, `svgSymbol`, and more.
|
|
262
|
+
|
|
263
|
+
### MathML Elements
|
|
264
|
+
All MathML elements are prefixed with `math`:
|
|
265
|
+
|
|
266
|
+
`mathMath`, `mathMi`, `mathMn`, `mathMo`, `mathMfrac`, `mathMsqrt`, `mathMroot`, `mathMsup`, `mathMsub`, `mathMsubsup`, `mathMover`, `mathMunder`, `mathMunderover`, and more.
|
|
267
|
+
|
|
268
|
+
## Browser Usage
|
|
269
|
+
|
|
270
|
+
When loaded via script tag, all exports are available on the `window` object:
|
|
271
|
+
|
|
272
|
+
```html
|
|
273
|
+
<script src="https://unpkg.com/elit@latest/dist/index.global.js"></script>
|
|
274
|
+
<script>
|
|
275
|
+
const { div, span, createState, domNode } = window;
|
|
276
|
+
// or use DomLib global
|
|
277
|
+
const app = DomLib.div('Hello');
|
|
278
|
+
</script>
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Examples
|
|
282
|
+
|
|
283
|
+
### Todo App
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import { div, input, button, ul, li, createState, reactive, bindValue } from 'elit';
|
|
287
|
+
|
|
288
|
+
const todos = createState<string[]>([]);
|
|
289
|
+
const newTodo = createState('');
|
|
290
|
+
|
|
291
|
+
const TodoApp = div({ className: 'todo-app' },
|
|
292
|
+
div({ className: 'input-group' },
|
|
293
|
+
input({ type: 'text', placeholder: 'Add a todo...', ...bindValue(newTodo) }),
|
|
294
|
+
button({
|
|
295
|
+
onclick: () => {
|
|
296
|
+
if (newTodo.value.trim()) {
|
|
297
|
+
todos.value = [...todos.value, newTodo.value];
|
|
298
|
+
newTodo.value = '';
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}, 'Add')
|
|
302
|
+
),
|
|
303
|
+
reactive(todos, (items) =>
|
|
304
|
+
ul(
|
|
305
|
+
...items.map((todo, index) =>
|
|
306
|
+
li(
|
|
307
|
+
todo,
|
|
308
|
+
button({
|
|
309
|
+
onclick: () => {
|
|
310
|
+
todos.value = todos.value.filter((_, i) => i !== index);
|
|
311
|
+
}
|
|
312
|
+
}, 'Delete')
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
)
|
|
316
|
+
)
|
|
317
|
+
);
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Counter with Computed Values
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
import { div, button, createState, computed, reactive } from 'elit';
|
|
324
|
+
|
|
325
|
+
const count = createState(0);
|
|
326
|
+
const doubled = computed([count], (c) => c * 2);
|
|
327
|
+
const isEven = computed([count], (c) => c % 2 === 0);
|
|
328
|
+
|
|
329
|
+
const Counter = div(
|
|
330
|
+
reactive(count, (c) => div(`Count: ${c}`)),
|
|
331
|
+
reactive(doubled, (d) => div(`Doubled: ${d}`)),
|
|
332
|
+
reactive(isEven, (even) => div(`Is even: ${even}`)),
|
|
333
|
+
button({ onclick: () => count.value++ }, 'Increment'),
|
|
334
|
+
button({ onclick: () => count.value-- }, 'Decrement')
|
|
335
|
+
);
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Documentation
|
|
339
|
+
|
|
340
|
+
For detailed documentation, examples, and guides, visit the official documentation (coming soon).
|
|
341
|
+
|
|
342
|
+
## Contributing
|
|
343
|
+
|
|
344
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
345
|
+
|
|
346
|
+
## License
|
|
347
|
+
|
|
348
|
+
MIT
|