mantle-lit 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -52
- package/dist/index.cjs +158 -153
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +128 -88
- package/dist/index.d.ts +128 -88
- package/dist/index.js +156 -152
- package/dist/index.js.map +1 -1
- package/package.json +63 -60
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Mantle Lit
|
|
2
2
|
|
|
3
|
-
A lightweight library for building Lit web components with
|
|
3
|
+
A lightweight library for building Lit web components with MobX reactivity. Extends LitElement with automatic observable state, computed getters, and bound actions.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install mantle-lit
|
|
8
|
+
npm install mantle-lit lit mobx
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Requires Lit 3+ and MobX 6+.
|
|
@@ -15,16 +15,18 @@ Requires Lit 3+ and MobX 6+.
|
|
|
15
15
|
```ts
|
|
16
16
|
import { View, createView } from 'mantle-lit';
|
|
17
17
|
import { html } from 'lit';
|
|
18
|
+
import { property } from 'lit/decorators.js';
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
20
|
+
class CounterView extends View {
|
|
21
|
+
// Props - use @property for Lit reactivity and IDE autocomplete
|
|
22
|
+
@property({ type: Number, attribute: false })
|
|
23
|
+
initialCount = 0;
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
// Internal state - auto-observable
|
|
24
26
|
count = 0;
|
|
25
27
|
|
|
26
28
|
onCreate() {
|
|
27
|
-
this.count = this.
|
|
29
|
+
this.count = this.initialCount;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
increment() {
|
|
@@ -41,30 +43,104 @@ class CounterView extends View<CounterProps> {
|
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
export const Counter = createView(CounterView, { tag: 'x-counter' });
|
|
46
|
+
|
|
47
|
+
// Register type for IDE autocomplete in templates
|
|
48
|
+
declare global {
|
|
49
|
+
interface HTMLElementTagNameMap {
|
|
50
|
+
'x-counter': CounterView;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
44
53
|
```
|
|
45
54
|
|
|
46
55
|
**Usage in HTML (property binding with `.`):**
|
|
47
56
|
```html
|
|
48
|
-
<x-counter .
|
|
57
|
+
<x-counter .initialCount=${5}></x-counter>
|
|
49
58
|
```
|
|
50
59
|
|
|
51
|
-
**Everything is reactive by default.**
|
|
60
|
+
**Everything is reactive by default.** Internal state becomes observable, getters become computed, and methods become auto-bound actions. Props use Lit's standard `@property()` decorator.
|
|
52
61
|
|
|
53
|
-
|
|
62
|
+
## Defining Props
|
|
54
63
|
|
|
55
|
-
|
|
64
|
+
Use Lit's `@property()` decorator for props. Use `attribute: false` since we pass complex data via property binding:
|
|
56
65
|
|
|
57
|
-
|
|
66
|
+
```ts
|
|
67
|
+
import { View, createView } from 'mantle-lit';
|
|
68
|
+
import { html } from 'lit';
|
|
69
|
+
import { property } from 'lit/decorators.js';
|
|
70
|
+
|
|
71
|
+
interface TodoItem {
|
|
72
|
+
id: number;
|
|
73
|
+
text: string;
|
|
74
|
+
done: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
class TodoView extends View {
|
|
78
|
+
@property({ type: String, attribute: false })
|
|
79
|
+
title = '';
|
|
80
|
+
|
|
81
|
+
@property({ type: Array, attribute: false })
|
|
82
|
+
initialTodos: TodoItem[] = [];
|
|
83
|
+
|
|
84
|
+
@property({ attribute: false })
|
|
85
|
+
onComplete?: (count: number) => void;
|
|
86
|
+
|
|
87
|
+
// Internal state (auto-observable, no decorator needed)
|
|
88
|
+
todos: TodoItem[] = [];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const Todo = createView(TodoView, { tag: 'x-todo' });
|
|
92
|
+
|
|
93
|
+
declare global {
|
|
94
|
+
interface HTMLElementTagNameMap {
|
|
95
|
+
'x-todo': TodoView;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Why `attribute: false`?** We use property binding (`.prop=${value}`) to pass complex objects, arrays, and functions. Attribute reflection isn't needed and can cause issues with non-primitive types.
|
|
101
|
+
|
|
102
|
+
**No props?** Just extend `View` directly without any `@property()` decorators.
|
|
103
|
+
|
|
104
|
+
## Scoped Styles
|
|
105
|
+
|
|
106
|
+
Use Lit's `static styles` for component-scoped CSS:
|
|
58
107
|
|
|
59
108
|
```ts
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
109
|
+
import { View, createView } from 'mantle-lit';
|
|
110
|
+
import { html, css } from 'lit';
|
|
111
|
+
|
|
112
|
+
class MyView extends View {
|
|
113
|
+
static styles = css`
|
|
114
|
+
:host {
|
|
115
|
+
display: block;
|
|
116
|
+
padding: 1rem;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
button {
|
|
120
|
+
background: #6366f1;
|
|
121
|
+
color: white;
|
|
122
|
+
}
|
|
67
123
|
`;
|
|
124
|
+
|
|
125
|
+
render() {
|
|
126
|
+
return html`<button>Click me</button>`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
For larger components, extract styles to a separate file:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
// MyView.styles.ts
|
|
135
|
+
import { css } from 'lit';
|
|
136
|
+
export const styles = css`...`;
|
|
137
|
+
|
|
138
|
+
// MyView.ts
|
|
139
|
+
import { styles } from './MyView.styles';
|
|
140
|
+
|
|
141
|
+
class MyView extends View {
|
|
142
|
+
static styles = styles;
|
|
143
|
+
// ...
|
|
68
144
|
}
|
|
69
145
|
```
|
|
70
146
|
|
|
@@ -99,7 +175,7 @@ render() {
|
|
|
99
175
|
```ts
|
|
100
176
|
onCreate() {
|
|
101
177
|
this.watch(
|
|
102
|
-
() => this.
|
|
178
|
+
() => this.filter,
|
|
103
179
|
(filter) => this.applyFilter(filter)
|
|
104
180
|
);
|
|
105
181
|
}
|
|
@@ -136,7 +212,10 @@ this.watch(
|
|
|
136
212
|
**Basic example:**
|
|
137
213
|
|
|
138
214
|
```ts
|
|
139
|
-
class SearchView extends View
|
|
215
|
+
class SearchView extends View {
|
|
216
|
+
@property({ type: String, attribute: false })
|
|
217
|
+
placeholder = '';
|
|
218
|
+
|
|
140
219
|
query = '';
|
|
141
220
|
results: string[] = [];
|
|
142
221
|
|
|
@@ -158,9 +237,9 @@ class SearchView extends View<Props> {
|
|
|
158
237
|
|
|
159
238
|
```ts
|
|
160
239
|
onCreate() {
|
|
161
|
-
this.watch(() => this.
|
|
162
|
-
this.watch(() => this.
|
|
163
|
-
this.watch(() => this.
|
|
240
|
+
this.watch(() => this.filter, (filter) => this.applyFilter(filter));
|
|
241
|
+
this.watch(() => this.sort, (sort) => this.applySort(sort));
|
|
242
|
+
this.watch(() => this.page, (page) => this.fetchPage(page));
|
|
164
243
|
}
|
|
165
244
|
```
|
|
166
245
|
|
|
@@ -168,7 +247,7 @@ onCreate() {
|
|
|
168
247
|
|
|
169
248
|
```ts
|
|
170
249
|
onCreate() {
|
|
171
|
-
const stop = this.watch(() => this.
|
|
250
|
+
const stop = this.watch(() => this.token, (token) => {
|
|
172
251
|
this.authenticate(token);
|
|
173
252
|
stop(); // only needed once
|
|
174
253
|
});
|
|
@@ -177,35 +256,65 @@ onCreate() {
|
|
|
177
256
|
|
|
178
257
|
`this.watch` wraps MobX's `reaction` with automatic lifecycle disposal. For advanced MobX patterns (`autorun`, `when`, custom schedulers), use `reaction` directly and return a dispose function from `onMount`.
|
|
179
258
|
|
|
180
|
-
|
|
259
|
+
## Mounting Components
|
|
260
|
+
|
|
261
|
+
Use the `mount` helper to imperatively create and mount components:
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import { mount } from 'mantle-lit';
|
|
265
|
+
import './MyComponent';
|
|
266
|
+
|
|
267
|
+
// Mount with props
|
|
268
|
+
mount('x-my-component', {
|
|
269
|
+
title: 'Hello',
|
|
270
|
+
items: [1, 2, 3],
|
|
271
|
+
onSelect: (item) => console.log(item),
|
|
272
|
+
}, document.body);
|
|
273
|
+
|
|
274
|
+
// Returns the created element
|
|
275
|
+
const el = mount('x-counter', { initialCount: 5 }, container);
|
|
276
|
+
```
|
|
181
277
|
|
|
182
|
-
|
|
278
|
+
## IDE Autocomplete
|
|
183
279
|
|
|
184
|
-
|
|
280
|
+
For IDE autocomplete in Lit templates, add `HTMLElementTagNameMap` declarations:
|
|
185
281
|
|
|
186
282
|
```ts
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
);
|
|
283
|
+
declare global {
|
|
284
|
+
interface HTMLElementTagNameMap {
|
|
285
|
+
'x-my-component': MyComponentView;
|
|
286
|
+
}
|
|
192
287
|
}
|
|
193
288
|
```
|
|
194
289
|
|
|
195
|
-
|
|
290
|
+
Install the [lit-plugin](https://marketplace.visualstudio.com/items?itemName=runem.lit-plugin) VS Code extension for template type checking.
|
|
196
291
|
|
|
197
|
-
**
|
|
292
|
+
**CLI validation** (works reliably):
|
|
293
|
+
```bash
|
|
294
|
+
npx lit-analyzer "src/**/*.ts" --strict
|
|
295
|
+
```
|
|
198
296
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
297
|
+
Add to your `package.json`:
|
|
298
|
+
```json
|
|
299
|
+
{
|
|
300
|
+
"scripts": {
|
|
301
|
+
"lint:lit": "lit-analyzer \"src/**/*.ts\" --strict"
|
|
302
|
+
}
|
|
205
303
|
}
|
|
206
304
|
```
|
|
207
305
|
|
|
208
|
-
|
|
306
|
+
## TypeScript Configuration
|
|
307
|
+
|
|
308
|
+
For `@property()` decorators to work correctly:
|
|
309
|
+
|
|
310
|
+
```json
|
|
311
|
+
{
|
|
312
|
+
"compilerOptions": {
|
|
313
|
+
"experimentalDecorators": true,
|
|
314
|
+
"useDefineForClassFields": false
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
```
|
|
209
318
|
|
|
210
319
|
## Patterns
|
|
211
320
|
|
|
@@ -216,6 +325,7 @@ State, logic, and template in one class:
|
|
|
216
325
|
```ts
|
|
217
326
|
import { View, createView } from 'mantle-lit';
|
|
218
327
|
import { html } from 'lit';
|
|
328
|
+
import { property } from 'lit/decorators.js';
|
|
219
329
|
|
|
220
330
|
interface TodoItem {
|
|
221
331
|
id: number;
|
|
@@ -224,9 +334,16 @@ interface TodoItem {
|
|
|
224
334
|
}
|
|
225
335
|
|
|
226
336
|
class TodoView extends View {
|
|
337
|
+
@property({ type: Array, attribute: false })
|
|
338
|
+
initialTodos: TodoItem[] = [];
|
|
339
|
+
|
|
227
340
|
todos: TodoItem[] = [];
|
|
228
341
|
input = '';
|
|
229
342
|
|
|
343
|
+
onCreate() {
|
|
344
|
+
this.todos = this.initialTodos;
|
|
345
|
+
}
|
|
346
|
+
|
|
230
347
|
add() {
|
|
231
348
|
this.todos.push({ id: Date.now(), text: this.input, done: false });
|
|
232
349
|
this.input = '';
|
|
@@ -248,6 +365,12 @@ class TodoView extends View {
|
|
|
248
365
|
}
|
|
249
366
|
|
|
250
367
|
export const Todo = createView(TodoView, { tag: 'x-todo' });
|
|
368
|
+
|
|
369
|
+
declare global {
|
|
370
|
+
interface HTMLElementTagNameMap {
|
|
371
|
+
'x-todo': TodoView;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
251
374
|
```
|
|
252
375
|
|
|
253
376
|
### Separated
|
|
@@ -255,10 +378,10 @@ export const Todo = createView(TodoView, { tag: 'x-todo' });
|
|
|
255
378
|
ViewModel and template separate:
|
|
256
379
|
|
|
257
380
|
```ts
|
|
258
|
-
import {
|
|
381
|
+
import { View, createView } from 'mantle-lit';
|
|
259
382
|
import { html } from 'lit';
|
|
260
383
|
|
|
261
|
-
class TodoViewModel extends
|
|
384
|
+
class TodoViewModel extends View {
|
|
262
385
|
todos: TodoItem[] = [];
|
|
263
386
|
input = '';
|
|
264
387
|
|
|
@@ -281,8 +404,6 @@ const template = (vm: TodoViewModel) => html`
|
|
|
281
404
|
</div>
|
|
282
405
|
`;
|
|
283
406
|
|
|
284
|
-
// Note: For separated templates, extend the View class with a render method
|
|
285
|
-
// that calls the template function
|
|
286
407
|
class TodoView extends TodoViewModel {
|
|
287
408
|
render() {
|
|
288
409
|
return template(this);
|
|
@@ -299,8 +420,12 @@ For teams that prefer explicit annotations over auto-observable, Mantle provides
|
|
|
299
420
|
```ts
|
|
300
421
|
import { View, createView, observable, action, computed } from 'mantle-lit';
|
|
301
422
|
import { html } from 'lit';
|
|
423
|
+
import { property } from 'lit/decorators.js';
|
|
302
424
|
|
|
303
425
|
class TodoView extends View {
|
|
426
|
+
@property({ type: String, attribute: false })
|
|
427
|
+
title = '';
|
|
428
|
+
|
|
304
429
|
@observable todos: TodoItem[] = [];
|
|
305
430
|
@observable input = '';
|
|
306
431
|
|
|
@@ -355,8 +480,6 @@ class TodoView extends View {
|
|
|
355
480
|
export const Todo = createView(TodoView, { tag: 'x-todo' });
|
|
356
481
|
```
|
|
357
482
|
|
|
358
|
-
Note: `this.props` is always reactive regardless of decorator mode.
|
|
359
|
-
|
|
360
483
|
## Error Handling
|
|
361
484
|
|
|
362
485
|
Render errors propagate to the browser as usual. Lifecycle errors (`onMount`, `onUnmount`, `watch`) in both Views and Behaviors are caught and routed through a configurable handler.
|
|
@@ -531,19 +654,36 @@ configure({ autoObservable: false });
|
|
|
531
654
|
| `autoObservable` | `true` | Whether to automatically make View instances observable |
|
|
532
655
|
| `onError` | `console.error` | Global error handler for lifecycle errors (see [Error Handling](#error-handling)) |
|
|
533
656
|
|
|
534
|
-
### `View
|
|
657
|
+
### `View`
|
|
535
658
|
|
|
536
|
-
Base class for view components. `
|
|
659
|
+
Base class for view components. Extends `LitElement` with MobX integration.
|
|
537
660
|
|
|
538
661
|
| Property/Method | Description |
|
|
539
662
|
|-----------------|-------------|
|
|
540
|
-
| `props` | Current props (reactive) |
|
|
541
663
|
| `onCreate()` | Called when instance created |
|
|
542
664
|
| `onMount()` | Called when connected to DOM, return cleanup (optional) |
|
|
543
665
|
| `onUnmount()` | Called when disconnected from DOM (optional) |
|
|
544
666
|
| `render()` | Return Lit `TemplateResult` |
|
|
545
667
|
| `watch(expr, callback, options?)` | Watch reactive expression, auto-disposed on unmount |
|
|
546
668
|
|
|
669
|
+
### `mount(tag, props, container)`
|
|
670
|
+
|
|
671
|
+
Imperatively create and mount a custom element:
|
|
672
|
+
|
|
673
|
+
```ts
|
|
674
|
+
import { mount } from 'mantle-lit';
|
|
675
|
+
|
|
676
|
+
const element = mount('x-my-component', { title: 'Hello' }, document.body);
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
| Argument | Type | Description |
|
|
680
|
+
|----------|------|-------------|
|
|
681
|
+
| `tag` | `string` | Custom element tag name |
|
|
682
|
+
| `props` | `object` | Properties to set on the element |
|
|
683
|
+
| `container` | `Element \| string` | Container element or selector |
|
|
684
|
+
|
|
685
|
+
Returns the created element.
|
|
686
|
+
|
|
547
687
|
### `Behavior`
|
|
548
688
|
|
|
549
689
|
Base class for behaviors. Extend it and wrap with `createBehavior()`.
|
|
@@ -571,7 +711,7 @@ export const withMyBehavior = createBehavior(MyBehavior);
|
|
|
571
711
|
|
|
572
712
|
### `createView(ViewClass, options)`
|
|
573
713
|
|
|
574
|
-
Function that
|
|
714
|
+
Function that registers a View class as a custom element.
|
|
575
715
|
|
|
576
716
|
```ts
|
|
577
717
|
// Basic
|