mantle-lit 0.1.1 → 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 +154 -116
- package/dist/index.cjs +162 -198
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +121 -162
- package/dist/index.d.ts +121 -162
- package/dist/index.js +160 -197
- package/dist/index.js.map +1 -1
- package/package.json +6 -7
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,14 +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
|
-
class CounterView extends View
|
|
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;
|
|
24
|
+
|
|
25
|
+
// Internal state - auto-observable
|
|
22
26
|
count = 0;
|
|
23
27
|
|
|
24
28
|
onCreate() {
|
|
25
|
-
this.count = this.
|
|
29
|
+
this.count = this.initialCount;
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
increment() {
|
|
@@ -39,23 +43,30 @@ class CounterView extends View.props({
|
|
|
39
43
|
}
|
|
40
44
|
|
|
41
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
|
+
}
|
|
42
53
|
```
|
|
43
54
|
|
|
44
55
|
**Usage in HTML (property binding with `.`):**
|
|
45
56
|
```html
|
|
46
|
-
<x-counter .
|
|
57
|
+
<x-counter .initialCount=${5}></x-counter>
|
|
47
58
|
```
|
|
48
59
|
|
|
49
|
-
**Everything is reactive by default.**
|
|
50
|
-
|
|
51
|
-
> Want explicit control? See [Decorators](#decorators) below to opt into manual annotations.
|
|
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
|
-
Use
|
|
64
|
+
Use Lit's `@property()` decorator for props. Use `attribute: false` since we pass complex data via property binding:
|
|
56
65
|
|
|
57
66
|
```ts
|
|
58
|
-
import { View, createView
|
|
67
|
+
import { View, createView } from 'mantle-lit';
|
|
68
|
+
import { html } from 'lit';
|
|
69
|
+
import { property } from 'lit/decorators.js';
|
|
59
70
|
|
|
60
71
|
interface TodoItem {
|
|
61
72
|
id: number;
|
|
@@ -63,46 +74,73 @@ interface TodoItem {
|
|
|
63
74
|
done: boolean;
|
|
64
75
|
}
|
|
65
76
|
|
|
66
|
-
class TodoView extends View
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
+
}
|
|
74
97
|
}
|
|
75
98
|
```
|
|
76
99
|
|
|
77
|
-
**
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
- `Boolean` → `boolean`
|
|
81
|
-
- `Array` → `any[]` (use `PropType<T[]>` for specific types)
|
|
82
|
-
- `Object` → `object` (use `PropType<T>` for specific types)
|
|
83
|
-
- `Function` → `Function` (use `PropType<(args) => R>` for specific signatures)
|
|
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.
|
|
84
103
|
|
|
85
|
-
|
|
104
|
+
## Scoped Styles
|
|
105
|
+
|
|
106
|
+
Use Lit's `static styles` for component-scoped CSS:
|
|
86
107
|
|
|
87
108
|
```ts
|
|
88
|
-
|
|
89
|
-
|
|
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
|
+
}
|
|
123
|
+
`;
|
|
124
|
+
|
|
125
|
+
render() {
|
|
126
|
+
return html`<button>Click me</button>`;
|
|
127
|
+
}
|
|
90
128
|
}
|
|
91
129
|
```
|
|
92
130
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
This library is designed for **property binding** (`.prop=${value}`) rather than attribute binding (`attr="value"`). This allows passing complex objects, arrays, and functions as props.
|
|
131
|
+
For larger components, extract styles to a separate file:
|
|
96
132
|
|
|
97
133
|
```ts
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
+
// ...
|
|
106
144
|
}
|
|
107
145
|
```
|
|
108
146
|
|
|
@@ -137,7 +175,7 @@ render() {
|
|
|
137
175
|
```ts
|
|
138
176
|
onCreate() {
|
|
139
177
|
this.watch(
|
|
140
|
-
() => this.
|
|
178
|
+
() => this.filter,
|
|
141
179
|
(filter) => this.applyFilter(filter)
|
|
142
180
|
);
|
|
143
181
|
}
|
|
@@ -174,9 +212,10 @@ this.watch(
|
|
|
174
212
|
**Basic example:**
|
|
175
213
|
|
|
176
214
|
```ts
|
|
177
|
-
class SearchView extends View
|
|
178
|
-
|
|
179
|
-
|
|
215
|
+
class SearchView extends View {
|
|
216
|
+
@property({ type: String, attribute: false })
|
|
217
|
+
placeholder = '';
|
|
218
|
+
|
|
180
219
|
query = '';
|
|
181
220
|
results: string[] = [];
|
|
182
221
|
|
|
@@ -198,9 +237,9 @@ class SearchView extends View.props({
|
|
|
198
237
|
|
|
199
238
|
```ts
|
|
200
239
|
onCreate() {
|
|
201
|
-
this.watch(() => this.
|
|
202
|
-
this.watch(() => this.
|
|
203
|
-
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));
|
|
204
243
|
}
|
|
205
244
|
```
|
|
206
245
|
|
|
@@ -208,7 +247,7 @@ onCreate() {
|
|
|
208
247
|
|
|
209
248
|
```ts
|
|
210
249
|
onCreate() {
|
|
211
|
-
const stop = this.watch(() => this.
|
|
250
|
+
const stop = this.watch(() => this.token, (token) => {
|
|
212
251
|
this.authenticate(token);
|
|
213
252
|
stop(); // only needed once
|
|
214
253
|
});
|
|
@@ -217,36 +256,6 @@ onCreate() {
|
|
|
217
256
|
|
|
218
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`.
|
|
219
258
|
|
|
220
|
-
### Props Reactivity
|
|
221
|
-
|
|
222
|
-
`this.props` is reactive: your component re-renders when accessed props change.
|
|
223
|
-
|
|
224
|
-
**Option 1: `this.watch`** — the recommended way to react to state changes:
|
|
225
|
-
|
|
226
|
-
```ts
|
|
227
|
-
onCreate() {
|
|
228
|
-
this.watch(
|
|
229
|
-
() => this.props.filter,
|
|
230
|
-
(filter) => this.applyFilter(filter)
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
Watchers are automatically disposed on unmount. No cleanup needed.
|
|
236
|
-
|
|
237
|
-
**Option 2: `reaction`** — for advanced MobX patterns (autorun, when, custom schedulers):
|
|
238
|
-
|
|
239
|
-
```ts
|
|
240
|
-
onMount() {
|
|
241
|
-
return reaction(
|
|
242
|
-
() => this.props.filter,
|
|
243
|
-
(filter) => this.applyFilter(filter)
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
Or access props directly in `render()` and MobX handles re-renders when they change.
|
|
249
|
-
|
|
250
259
|
## Mounting Components
|
|
251
260
|
|
|
252
261
|
Use the `mount` helper to imperatively create and mount components:
|
|
@@ -263,7 +272,48 @@ mount('x-my-component', {
|
|
|
263
272
|
}, document.body);
|
|
264
273
|
|
|
265
274
|
// Returns the created element
|
|
266
|
-
const el = mount('x-counter', {
|
|
275
|
+
const el = mount('x-counter', { initialCount: 5 }, container);
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## IDE Autocomplete
|
|
279
|
+
|
|
280
|
+
For IDE autocomplete in Lit templates, add `HTMLElementTagNameMap` declarations:
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
declare global {
|
|
284
|
+
interface HTMLElementTagNameMap {
|
|
285
|
+
'x-my-component': MyComponentView;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Install the [lit-plugin](https://marketplace.visualstudio.com/items?itemName=runem.lit-plugin) VS Code extension for template type checking.
|
|
291
|
+
|
|
292
|
+
**CLI validation** (works reliably):
|
|
293
|
+
```bash
|
|
294
|
+
npx lit-analyzer "src/**/*.ts" --strict
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Add to your `package.json`:
|
|
298
|
+
```json
|
|
299
|
+
{
|
|
300
|
+
"scripts": {
|
|
301
|
+
"lint:lit": "lit-analyzer \"src/**/*.ts\" --strict"
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
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
|
+
}
|
|
267
317
|
```
|
|
268
318
|
|
|
269
319
|
## Patterns
|
|
@@ -275,6 +325,7 @@ State, logic, and template in one class:
|
|
|
275
325
|
```ts
|
|
276
326
|
import { View, createView } from 'mantle-lit';
|
|
277
327
|
import { html } from 'lit';
|
|
328
|
+
import { property } from 'lit/decorators.js';
|
|
278
329
|
|
|
279
330
|
interface TodoItem {
|
|
280
331
|
id: number;
|
|
@@ -283,9 +334,16 @@ interface TodoItem {
|
|
|
283
334
|
}
|
|
284
335
|
|
|
285
336
|
class TodoView extends View {
|
|
337
|
+
@property({ type: Array, attribute: false })
|
|
338
|
+
initialTodos: TodoItem[] = [];
|
|
339
|
+
|
|
286
340
|
todos: TodoItem[] = [];
|
|
287
341
|
input = '';
|
|
288
342
|
|
|
343
|
+
onCreate() {
|
|
344
|
+
this.todos = this.initialTodos;
|
|
345
|
+
}
|
|
346
|
+
|
|
289
347
|
add() {
|
|
290
348
|
this.todos.push({ id: Date.now(), text: this.input, done: false });
|
|
291
349
|
this.input = '';
|
|
@@ -307,6 +365,12 @@ class TodoView extends View {
|
|
|
307
365
|
}
|
|
308
366
|
|
|
309
367
|
export const Todo = createView(TodoView, { tag: 'x-todo' });
|
|
368
|
+
|
|
369
|
+
declare global {
|
|
370
|
+
interface HTMLElementTagNameMap {
|
|
371
|
+
'x-todo': TodoView;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
310
374
|
```
|
|
311
375
|
|
|
312
376
|
### Separated
|
|
@@ -314,10 +378,10 @@ export const Todo = createView(TodoView, { tag: 'x-todo' });
|
|
|
314
378
|
ViewModel and template separate:
|
|
315
379
|
|
|
316
380
|
```ts
|
|
317
|
-
import {
|
|
381
|
+
import { View, createView } from 'mantle-lit';
|
|
318
382
|
import { html } from 'lit';
|
|
319
383
|
|
|
320
|
-
class TodoViewModel extends
|
|
384
|
+
class TodoViewModel extends View {
|
|
321
385
|
todos: TodoItem[] = [];
|
|
322
386
|
input = '';
|
|
323
387
|
|
|
@@ -340,8 +404,6 @@ const template = (vm: TodoViewModel) => html`
|
|
|
340
404
|
</div>
|
|
341
405
|
`;
|
|
342
406
|
|
|
343
|
-
// Note: For separated templates, extend the View class with a render method
|
|
344
|
-
// that calls the template function
|
|
345
407
|
class TodoView extends TodoViewModel {
|
|
346
408
|
render() {
|
|
347
409
|
return template(this);
|
|
@@ -358,8 +420,12 @@ For teams that prefer explicit annotations over auto-observable, Mantle provides
|
|
|
358
420
|
```ts
|
|
359
421
|
import { View, createView, observable, action, computed } from 'mantle-lit';
|
|
360
422
|
import { html } from 'lit';
|
|
423
|
+
import { property } from 'lit/decorators.js';
|
|
361
424
|
|
|
362
425
|
class TodoView extends View {
|
|
426
|
+
@property({ type: String, attribute: false })
|
|
427
|
+
title = '';
|
|
428
|
+
|
|
363
429
|
@observable todos: TodoItem[] = [];
|
|
364
430
|
@observable input = '';
|
|
365
431
|
|
|
@@ -414,8 +480,6 @@ class TodoView extends View {
|
|
|
414
480
|
export const Todo = createView(TodoView, { tag: 'x-todo' });
|
|
415
481
|
```
|
|
416
482
|
|
|
417
|
-
Note: `this.props` is always reactive regardless of decorator mode.
|
|
418
|
-
|
|
419
483
|
## Error Handling
|
|
420
484
|
|
|
421
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.
|
|
@@ -590,44 +654,18 @@ configure({ autoObservable: false });
|
|
|
590
654
|
| `autoObservable` | `true` | Whether to automatically make View instances observable |
|
|
591
655
|
| `onError` | `console.error` | Global error handler for lifecycle errors (see [Error Handling](#error-handling)) |
|
|
592
656
|
|
|
593
|
-
### `View`
|
|
657
|
+
### `View`
|
|
594
658
|
|
|
595
|
-
Base class for view components. `
|
|
596
|
-
|
|
597
|
-
**Defining props:**
|
|
598
|
-
|
|
599
|
-
```ts
|
|
600
|
-
class MyView extends View.props({
|
|
601
|
-
title: String,
|
|
602
|
-
items: Array as PropType<Item[]>,
|
|
603
|
-
}) {
|
|
604
|
-
// this.props is fully typed
|
|
605
|
-
}
|
|
606
|
-
```
|
|
659
|
+
Base class for view components. Extends `LitElement` with MobX integration.
|
|
607
660
|
|
|
608
661
|
| Property/Method | Description |
|
|
609
662
|
|-----------------|-------------|
|
|
610
|
-
| `props` | Current props (reactive, typed based on `View.props()`) |
|
|
611
663
|
| `onCreate()` | Called when instance created |
|
|
612
664
|
| `onMount()` | Called when connected to DOM, return cleanup (optional) |
|
|
613
665
|
| `onUnmount()` | Called when disconnected from DOM (optional) |
|
|
614
666
|
| `render()` | Return Lit `TemplateResult` |
|
|
615
667
|
| `watch(expr, callback, options?)` | Watch reactive expression, auto-disposed on unmount |
|
|
616
668
|
|
|
617
|
-
### `PropType<T>`
|
|
618
|
-
|
|
619
|
-
Type helper for complex prop types:
|
|
620
|
-
|
|
621
|
-
```ts
|
|
622
|
-
import { PropType } from 'mantle-lit';
|
|
623
|
-
|
|
624
|
-
View.props({
|
|
625
|
-
items: Array as PropType<MyItem[]>,
|
|
626
|
-
onSelect: Function as PropType<(item: MyItem) => void>,
|
|
627
|
-
config: Object as PropType<{ theme: string; debug: boolean }>,
|
|
628
|
-
})
|
|
629
|
-
```
|
|
630
|
-
|
|
631
669
|
### `mount(tag, props, container)`
|
|
632
670
|
|
|
633
671
|
Imperatively create and mount a custom element:
|
|
@@ -642,7 +680,7 @@ const element = mount('x-my-component', { title: 'Hello' }, document.body);
|
|
|
642
680
|
|----------|------|-------------|
|
|
643
681
|
| `tag` | `string` | Custom element tag name |
|
|
644
682
|
| `props` | `object` | Properties to set on the element |
|
|
645
|
-
| `container` | `Element` | Container
|
|
683
|
+
| `container` | `Element \| string` | Container element or selector |
|
|
646
684
|
|
|
647
685
|
Returns the created element.
|
|
648
686
|
|
|
@@ -673,7 +711,7 @@ export const withMyBehavior = createBehavior(MyBehavior);
|
|
|
673
711
|
|
|
674
712
|
### `createView(ViewClass, options)`
|
|
675
713
|
|
|
676
|
-
Function that
|
|
714
|
+
Function that registers a View class as a custom element.
|
|
677
715
|
|
|
678
716
|
```ts
|
|
679
717
|
// Basic
|