subay 0.0.6 → 0.0.8
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 +642 -64
- package/README.zh.md +682 -0
- package/examples/todo-app.html +286 -0
- package/lib/h.d.ts.map +1 -1
- package/lib/h.js +79 -54
- package/lib/h.js.map +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.dev.js +3 -0
- package/lib/index.js +1 -3
- package/lib/index.js.map +1 -1
- package/lib/name.d.ts +2 -0
- package/lib/name.d.ts.map +1 -0
- package/lib/name.js +58 -0
- package/lib/name.js.map +1 -0
- package/lib/s.d.ts +13 -1
- package/lib/s.d.ts.map +1 -1
- package/lib/s.js +62 -130
- package/lib/s.js.map +1 -1
- package/package.json +15 -7
- package/src/h.ts +583 -0
- package/src/index.ts +2 -0
- package/src/name.ts +60 -0
- package/src/s.ts +193 -0
package/README.md
CHANGED
|
@@ -1,103 +1,681 @@
|
|
|
1
1
|
# Subay
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Subay is a lightweight reactive programming library that provides state management and DOM manipulation capabilities.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## API
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- **Template Rendering**: Use tagged template literals for HTML and SVG
|
|
9
|
-
- **Efficient DOM Updates**: Optimized rendering with minimal DOM operations
|
|
10
|
-
- **TypeScript Support**: Full TypeScript integration
|
|
7
|
+
### root
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
**Introduction**
|
|
10
|
+
Creates a root update context for managing the lifecycle of reactive states.
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
**Syntax**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
root<T>(fn: (destroy: () => void) => T): T
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Parameters**
|
|
19
|
+
|
|
20
|
+
- `fn`: A function that receives a `destroy` function as a parameter, used to manually destroy the root context.
|
|
21
|
+
|
|
22
|
+
**Return Value**
|
|
23
|
+
|
|
24
|
+
- `T`: The return value of the `fn` function.
|
|
25
|
+
|
|
26
|
+
**Example**
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { root, o, S } from 'subay';
|
|
30
|
+
|
|
31
|
+
root((destroy) => {
|
|
32
|
+
const count = o(0);
|
|
33
|
+
const doubled = S(() => count() * 2);
|
|
34
|
+
|
|
35
|
+
console.log(doubled()); // 0
|
|
36
|
+
count(1);
|
|
37
|
+
console.log(doubled()); // 2
|
|
38
|
+
|
|
39
|
+
// Manual destruction
|
|
40
|
+
destroy();
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Note**
|
|
45
|
+
|
|
46
|
+
The only way to destroy a context created with `root` is to call the `destroy` callback.
|
|
47
|
+
|
|
48
|
+
### S
|
|
49
|
+
|
|
50
|
+
**Introduction**
|
|
51
|
+
Creates a computed value that automatically recalculates when its dependent observables change.
|
|
52
|
+
|
|
53
|
+
**Syntax**
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
S<T>(fn: (pv: T | undefined) => T, value?: T): IS<T>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Parameters**
|
|
60
|
+
|
|
61
|
+
- `fn`: A computation function that receives the previous computed value as a parameter.
|
|
62
|
+
- `value`: Optional initial value.
|
|
63
|
+
|
|
64
|
+
**Return Value**
|
|
65
|
+
|
|
66
|
+
- `IS<T>`: A function that returns the computed value when called.
|
|
67
|
+
|
|
68
|
+
**Example**
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { S, o } from 'subay';
|
|
72
|
+
|
|
73
|
+
const count = o(0);
|
|
74
|
+
const doubled = S(() => count() * 2);
|
|
75
|
+
|
|
76
|
+
console.log(doubled()); // 0
|
|
77
|
+
count(1);
|
|
78
|
+
console.log(doubled()); // 2
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### o / observable
|
|
82
|
+
|
|
83
|
+
**Introduction**
|
|
84
|
+
Creates an observable value for storing and updating state.
|
|
85
|
+
|
|
86
|
+
**Syntax**
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
o<T>(value: T): IO<T>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Parameters**
|
|
93
|
+
|
|
94
|
+
- `value`: Initial value.
|
|
95
|
+
|
|
96
|
+
**Return Value**
|
|
97
|
+
|
|
98
|
+
- `IO<T>`: A function that returns the current value when called without arguments, and updates the value and returns the new value when called with arguments.
|
|
99
|
+
|
|
100
|
+
**Example**
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { o } from 'subay';
|
|
104
|
+
|
|
105
|
+
const count = o(0);
|
|
106
|
+
console.log(count()); // 0
|
|
107
|
+
count(1);
|
|
108
|
+
console.log(count()); // 1
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### cleanup
|
|
112
|
+
|
|
113
|
+
**Introduction**
|
|
114
|
+
Registers a cleanup function that executes when the current update context is destroyed.
|
|
115
|
+
|
|
116
|
+
**Syntax**
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
cleanup<T extends () => void>(f: T): T
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Parameters**
|
|
123
|
+
|
|
124
|
+
- `f`: Cleanup function.
|
|
125
|
+
|
|
126
|
+
**Return Value**
|
|
127
|
+
|
|
128
|
+
- `T`: The passed cleanup function.
|
|
129
|
+
|
|
130
|
+
**Example**
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { root, S, o, cleanup } from 'subay';
|
|
134
|
+
|
|
135
|
+
root(() => {
|
|
136
|
+
const count = o(0);
|
|
137
|
+
|
|
138
|
+
S(() => {
|
|
139
|
+
console.log(count());
|
|
140
|
+
cleanup(() => {
|
|
141
|
+
console.log('Cleanup called');
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
count(1);
|
|
146
|
+
});
|
|
147
|
+
// Output: 0, 1, Cleanup called
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### transaction
|
|
151
|
+
|
|
152
|
+
**Introduction**
|
|
153
|
+
Creates a transaction for batch updating observables, avoiding intermediate state calculations.
|
|
154
|
+
|
|
155
|
+
**Syntax**
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
transaction<T>(f: () => T): T
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Parameters**
|
|
162
|
+
|
|
163
|
+
- `f`: Transaction function where multiple observables can be updated.
|
|
164
|
+
|
|
165
|
+
**Return Value**
|
|
166
|
+
|
|
167
|
+
- `T`: The return value of the `f` function.
|
|
168
|
+
|
|
169
|
+
**Example**
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
import { transaction, o, S } from 'subay';
|
|
173
|
+
|
|
174
|
+
const a = o(1);
|
|
175
|
+
const b = o(2);
|
|
176
|
+
const sum = S(() => a() + b());
|
|
177
|
+
|
|
178
|
+
S(() => console.log(sum())); // 3
|
|
179
|
+
|
|
180
|
+
transaction(() => {
|
|
181
|
+
a(2);
|
|
182
|
+
b(3);
|
|
183
|
+
});
|
|
184
|
+
// Output: 5 (calculated only once)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### sample
|
|
188
|
+
|
|
189
|
+
**Introduction**
|
|
190
|
+
Gets the value of an observable without establishing a dependency relationship.
|
|
191
|
+
|
|
192
|
+
**Syntax**
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
sample<T>(f: () => T): T
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Parameters**
|
|
199
|
+
|
|
200
|
+
- `f`: A function where observables can be accessed without establishing dependency relationships.
|
|
201
|
+
|
|
202
|
+
**Return Value**
|
|
203
|
+
|
|
204
|
+
- `T`: The return value of the `f` function.
|
|
205
|
+
|
|
206
|
+
**Example**
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { sample, o, S } from 'subay';
|
|
210
|
+
|
|
211
|
+
const count = o(0);
|
|
212
|
+
let cachedValue;
|
|
213
|
+
|
|
214
|
+
S(() => {
|
|
215
|
+
// No dependency relationship established
|
|
216
|
+
cachedValue = sample(() => count());
|
|
217
|
+
console.log(cachedValue);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
count(1); // Will not trigger recalculation
|
|
221
|
+
console.log(cachedValue); // 0
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### isListening
|
|
225
|
+
|
|
226
|
+
**Introduction**
|
|
227
|
+
Checks if the current state is in reactive listening mode.
|
|
228
|
+
|
|
229
|
+
**Syntax**
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
isListening(): boolean
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Parameters**
|
|
236
|
+
|
|
237
|
+
- None
|
|
238
|
+
|
|
239
|
+
**Return Value**
|
|
240
|
+
|
|
241
|
+
- `boolean`: Whether the current state is in reactive listening mode.
|
|
242
|
+
|
|
243
|
+
**Example**
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { isListening, S } from 'subay';
|
|
247
|
+
|
|
248
|
+
console.log(isListening()); // false
|
|
249
|
+
|
|
250
|
+
S(() => {
|
|
251
|
+
console.log(isListening()); // true
|
|
252
|
+
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### subscribe
|
|
256
|
+
|
|
257
|
+
**Introduction**
|
|
258
|
+
Subscribes to a function that automatically executes when dependent observables change.
|
|
259
|
+
|
|
260
|
+
**Syntax**
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
subscribe(f: () => void): () => void
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**Parameters**
|
|
267
|
+
|
|
268
|
+
- `f`: Subscription function.
|
|
269
|
+
|
|
270
|
+
**Return Value**
|
|
271
|
+
|
|
272
|
+
- `() => void`: A function to unsubscribe.
|
|
273
|
+
|
|
274
|
+
**Example**
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
import { subscribe, o } from 'subay';
|
|
278
|
+
|
|
279
|
+
const count = o(0);
|
|
280
|
+
const unsubscribe = subscribe(() => {
|
|
281
|
+
console.log(count());
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
count(1); // Output: 1
|
|
285
|
+
count(2); // Output: 2
|
|
286
|
+
|
|
287
|
+
unsubscribe();
|
|
288
|
+
count(3); // No output
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### unsubscribe
|
|
292
|
+
|
|
293
|
+
**Introduction**
|
|
294
|
+
Unsubscribes a function.
|
|
295
|
+
|
|
296
|
+
**Syntax**
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
unsubscribe(f: () => void): void
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**Parameters**
|
|
303
|
+
|
|
304
|
+
- `f`: The function to unsubscribe.
|
|
305
|
+
|
|
306
|
+
**Return Value**
|
|
307
|
+
|
|
308
|
+
- None
|
|
309
|
+
|
|
310
|
+
**Example**
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
import { subscribe, unsubscribe, o } from 'subay';
|
|
314
|
+
|
|
315
|
+
const count = o(0);
|
|
316
|
+
const fn = () => console.log(count());
|
|
317
|
+
|
|
318
|
+
subscribe(fn);
|
|
319
|
+
count(1); // Output: 1
|
|
320
|
+
|
|
321
|
+
unsubscribe(fn);
|
|
322
|
+
count(2); // No output
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### isObservable
|
|
326
|
+
|
|
327
|
+
**Introduction**
|
|
328
|
+
Checks if a value is an observable.
|
|
329
|
+
|
|
330
|
+
**Syntax**
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
isObservable(o: any): o is IO<any>
|
|
16
334
|
```
|
|
17
335
|
|
|
18
|
-
|
|
336
|
+
**Parameters**
|
|
19
337
|
|
|
20
|
-
|
|
338
|
+
- `o`: The value to check.
|
|
21
339
|
|
|
22
|
-
|
|
23
|
-
|
|
340
|
+
**Return Value**
|
|
341
|
+
|
|
342
|
+
- `boolean`: Whether it is an observable.
|
|
343
|
+
|
|
344
|
+
**Example**
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
import { isObservable, o, S } from 'subay';
|
|
24
348
|
|
|
25
|
-
// Create an observable value
|
|
26
349
|
const count = o(0);
|
|
350
|
+
const doubled = S(() => count() * 2);
|
|
351
|
+
|
|
352
|
+
console.log(isObservable(count)); // true
|
|
353
|
+
console.log(isObservable(doubled)); // false
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### isComputed
|
|
357
|
+
|
|
358
|
+
**Introduction**
|
|
359
|
+
Checks if a value is a computed value.
|
|
360
|
+
|
|
361
|
+
**Syntax**
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
isComputed(o: any): o is IS<any>
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**Parameters**
|
|
368
|
+
|
|
369
|
+
- `o`: The value to check.
|
|
370
|
+
|
|
371
|
+
**Return Value**
|
|
372
|
+
|
|
373
|
+
- `boolean`: Whether it is a computed value.
|
|
374
|
+
|
|
375
|
+
**Example**
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
import { isComputed, o, S } from 'subay';
|
|
379
|
+
|
|
380
|
+
const count = o(0);
|
|
381
|
+
const doubled = S(() => count() * 2);
|
|
382
|
+
|
|
383
|
+
console.log(isComputed(count)); // false
|
|
384
|
+
console.log(isComputed(doubled)); // true
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### isReactive
|
|
388
|
+
|
|
389
|
+
**Introduction**
|
|
390
|
+
Checks if a value is a reactive value (observable or computed value).
|
|
27
391
|
|
|
28
|
-
|
|
392
|
+
**Syntax**
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
isReactive(o: any): o is IO<any> | IS<any>
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
**Parameters**
|
|
399
|
+
|
|
400
|
+
- `o`: The value to check.
|
|
401
|
+
|
|
402
|
+
**Return Value**
|
|
403
|
+
|
|
404
|
+
- `boolean`: Whether it is a reactive value.
|
|
405
|
+
|
|
406
|
+
**Example**
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
import { isReactive, o, S } from 'subay';
|
|
410
|
+
|
|
411
|
+
const count = o(0);
|
|
29
412
|
const doubled = S(() => count() * 2);
|
|
30
413
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
console.log(
|
|
414
|
+
console.log(isReactive(count)); // true
|
|
415
|
+
console.log(isReactive(doubled)); // true
|
|
416
|
+
console.log(isReactive(42)); // false
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### map
|
|
420
|
+
|
|
421
|
+
**Introduction**
|
|
422
|
+
Maps an array to a DOM node array, automatically updating when the array changes.
|
|
423
|
+
|
|
424
|
+
**Syntax**
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
map<T>(array: () => T[], f: (item: T) => Node[]): Node[]
|
|
34
428
|
```
|
|
35
429
|
|
|
36
|
-
|
|
430
|
+
**Parameters**
|
|
431
|
+
|
|
432
|
+
- `array`: A function that returns an array.
|
|
433
|
+
- `f`: A mapping function that converts array items to DOM node arrays.
|
|
434
|
+
|
|
435
|
+
**Return Value**
|
|
436
|
+
|
|
437
|
+
- `Node[]`: DOM node array.
|
|
438
|
+
|
|
439
|
+
**Example**
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
import { map, o, html } from 'subay';
|
|
443
|
+
|
|
444
|
+
const items = o(['a', 'b', 'c']);
|
|
445
|
+
const list = map(
|
|
446
|
+
() => items(),
|
|
447
|
+
(item) => html`<li>${item}</li>`,
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
document.body.append(...list);
|
|
451
|
+
// Renders: <li>a</li><li>b</li><li>c</li>
|
|
452
|
+
|
|
453
|
+
items(['a', 'b', 'c', 'd']);
|
|
454
|
+
// Automatically updates: <li>a</li><li>b</li><li>c</li><li>d</li>
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
### svg
|
|
458
|
+
|
|
459
|
+
**Introduction**
|
|
460
|
+
Template tag function for creating SVG elements.
|
|
461
|
+
|
|
462
|
+
**Syntax**
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
svg(segments: TemplateStringsArray, ...values: any[]): Node[]
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
**Parameters**
|
|
469
|
+
|
|
470
|
+
- `segments`: Static parts of the template string.
|
|
471
|
+
- `values`: Dynamic parts of the template string.
|
|
472
|
+
|
|
473
|
+
**Return Value**
|
|
37
474
|
|
|
38
|
-
|
|
475
|
+
- `Node[]`: SVG element node array.
|
|
476
|
+
|
|
477
|
+
**Example**
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
import { svg, o } from 'subay';
|
|
481
|
+
|
|
482
|
+
const radius = o(50);
|
|
483
|
+
const circle = svg`<circle cx="100" cy="100" r="${radius}" fill="red"/>`;
|
|
484
|
+
|
|
485
|
+
document.getElementById('svg').append(...circle);
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### html
|
|
489
|
+
|
|
490
|
+
**Introduction**
|
|
491
|
+
Template tag function for creating HTML elements.
|
|
492
|
+
|
|
493
|
+
**Syntax**
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
html(segments: TemplateStringsArray, ...values: any[]): Node[]
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
**Parameters**
|
|
500
|
+
|
|
501
|
+
- `segments`: Static parts of the template string.
|
|
502
|
+
- `values`: Dynamic parts of the template string.
|
|
503
|
+
|
|
504
|
+
**Return Value**
|
|
505
|
+
|
|
506
|
+
- `Node[]`: HTML element node array.
|
|
507
|
+
|
|
508
|
+
**Example**
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
39
511
|
import { html, o } from 'subay';
|
|
40
512
|
|
|
41
|
-
const name = o('World');
|
|
42
513
|
const count = o(0);
|
|
514
|
+
const counter = html`
|
|
515
|
+
<div>
|
|
516
|
+
<p>Count: ${count}</p>
|
|
517
|
+
<button onclick=${() => count(count() + 1)}>Increment</button>
|
|
518
|
+
</div>
|
|
519
|
+
`;
|
|
520
|
+
|
|
521
|
+
document.body.append(...counter);
|
|
522
|
+
// Clicking the button will automatically update the count value
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
**Using HTML**
|
|
526
|
+
|
|
527
|
+
Subay uses `DOMParser` to parse HTML templates. The `html` parameter must be a valid HTML string (similarly, the `svg` parameter must be a valid SVG string), which is different from JSX.
|
|
528
|
+
|
|
529
|
+
**Example**
|
|
530
|
+
|
|
531
|
+
```typescript
|
|
532
|
+
import { html } from 'subay';
|
|
533
|
+
|
|
534
|
+
// Valid HTML string
|
|
535
|
+
const validHtml = html`<div><p>Hello</p></div>`;
|
|
536
|
+
|
|
537
|
+
document.body.append(...validHtml);
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
**Don't Do This**
|
|
541
|
+
|
|
542
|
+
```typescript
|
|
543
|
+
import { html } from 'subay';
|
|
544
|
+
|
|
545
|
+
// Self-closing tags
|
|
546
|
+
const invalidHtml = html`<div>
|
|
547
|
+
<span />
|
|
548
|
+
<span />
|
|
549
|
+
</div>`; // Will be parsed as <div><span><span></span></span></div>
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
**Passing Objects as Element Attributes**
|
|
553
|
+
|
|
554
|
+
If you need to pass an object as element attributes, you can use the `...` syntax.
|
|
555
|
+
|
|
556
|
+
**Syntax**
|
|
43
557
|
|
|
44
|
-
|
|
45
|
-
|
|
558
|
+
```typescript
|
|
559
|
+
html`<tag ...${props}></tag>`;
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
**Example**
|
|
563
|
+
|
|
564
|
+
```typescript
|
|
565
|
+
import { html, o } from 'subay';
|
|
566
|
+
|
|
567
|
+
const props = o({
|
|
568
|
+
className: 'container',
|
|
569
|
+
style: 'color: red;',
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
const element = html`<div ...${props}></div>`;
|
|
573
|
+
|
|
574
|
+
document.body.append(...element);
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
**Component Declaration**
|
|
578
|
+
|
|
579
|
+
A component is a function that returns Node[], and can receive parameters. Unlike React, Vue, etc., component parameters are a list.
|
|
580
|
+
|
|
581
|
+
**Syntax**
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
const Component = (text, children: () => Node[]) => {
|
|
585
|
+
return html`<div>${text}</div>`;
|
|
586
|
+
};
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
**Example**
|
|
590
|
+
|
|
591
|
+
```typescript
|
|
592
|
+
import { html } from 'subay';
|
|
593
|
+
|
|
594
|
+
const Greeting = (name: string, children: () => Node[]) => {
|
|
595
|
+
return html`
|
|
46
596
|
<div>
|
|
47
|
-
<h1>Hello ${name}!</h1>
|
|
48
|
-
|
|
49
|
-
<button onclick="${() => count(count() + 1)}">Increment</button>
|
|
50
|
-
<button onclick="${() => name('Subay')}">Change Name</button>
|
|
597
|
+
<h1>Hello, ${name}!</h1>
|
|
598
|
+
${children()}
|
|
51
599
|
</div>
|
|
52
|
-
|
|
53
|
-
|
|
600
|
+
`;
|
|
601
|
+
};
|
|
54
602
|
```
|
|
55
603
|
|
|
56
|
-
|
|
604
|
+
**Component Usage**
|
|
57
605
|
|
|
58
|
-
|
|
59
|
-
|
|
606
|
+
When using a component in a template, reference the component at the tag position.
|
|
607
|
+
Because the template is an HTML string, self-closing tags are not supported when referencing.
|
|
608
|
+
The parameters passed to the component must be in the same order as the component's declared parameters.
|
|
609
|
+
The parameters received by the component are always consistent with those passed in.
|
|
60
610
|
|
|
61
|
-
|
|
611
|
+
**Example**
|
|
62
612
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
);
|
|
613
|
+
```typescript
|
|
614
|
+
import { html, o } from 'subay';
|
|
615
|
+
|
|
616
|
+
const Greeting = (greet: string, name: () => string) => {
|
|
617
|
+
return html`<h1>${greet}, ${name}!</h1>`;
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
const name = o('World');
|
|
621
|
+
const app = html` <div><${Greeting} greet=${'Hello'} name=${name}></${Greeting}></div> `;
|
|
622
|
+
|
|
623
|
+
document.body.append(...app);
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
**Don't Do This**
|
|
627
|
+
|
|
628
|
+
```typescript
|
|
629
|
+
import { html, o } from 'subay';
|
|
630
|
+
|
|
631
|
+
const Greeting = (greet: string, name: () => string) => {
|
|
632
|
+
return html`<h1>${greet}, ${name}!</h1>`;
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
const name = o('World');
|
|
636
|
+
// Error, the following span will be treated as a child node of Greeting.
|
|
637
|
+
const app = html`
|
|
638
|
+
<div>
|
|
639
|
+
<${Greeting}
|
|
640
|
+
greet=${'Hello'}
|
|
641
|
+
name=${name}
|
|
642
|
+
/>
|
|
643
|
+
<span></span>
|
|
644
|
+
</div>
|
|
645
|
+
`;
|
|
646
|
+
// Error, parameter order is inconsistent with Greeting's parameters
|
|
647
|
+
const app = html` <div><${Greeting} name=${name} greet=${'Hi'} ></${Greeting}></div> `;
|
|
648
|
+
|
|
649
|
+
document.body.append(...app);
|
|
74
650
|
```
|
|
75
651
|
|
|
76
|
-
|
|
652
|
+
**Dynamic Components**
|
|
77
653
|
|
|
78
|
-
|
|
654
|
+
The interpolation at the tag position can be the return value of `S` or `o`, and components can be dynamically selected for rendering based on conditions.
|
|
79
655
|
|
|
80
|
-
|
|
81
|
-
- **`S(fn, initialValue?)`**: Create a computed property
|
|
82
|
-
- **`root(fn)`**: Create a root context for reactive computations
|
|
83
|
-
- **`cleanup(fn)`**: Register a cleanup function
|
|
84
|
-
- **`transaction(fn)`**: Batch multiple updates
|
|
85
|
-
- **`sample(fn)`**: Run a function without tracking dependencies
|
|
656
|
+
**Example**
|
|
86
657
|
|
|
87
|
-
|
|
658
|
+
```typescript
|
|
659
|
+
import { html, o, S } from 'subay';
|
|
88
660
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
661
|
+
const Greeting = (name: () => string) => {
|
|
662
|
+
return html`<h1>Hello, ${name}!</h1>`;
|
|
663
|
+
};
|
|
92
664
|
|
|
93
|
-
|
|
665
|
+
const Farewell = (name: () => string) => {
|
|
666
|
+
return html`<h1>Goodbye, ${name}!</h1>`;
|
|
667
|
+
};
|
|
94
668
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
- **`subscribe(fn)`**: Subscribe to changes
|
|
99
|
-
- **`unsubscribe(fn)`**: Unsubscribe from changes
|
|
669
|
+
const showGreeting = o(true);
|
|
670
|
+
const name = o('World');
|
|
671
|
+
const Component = S(() => (showGreeting() ? Greeting : Farewell));
|
|
100
672
|
|
|
101
|
-
|
|
673
|
+
const app = html`
|
|
674
|
+
<div>
|
|
675
|
+
<${Component} name=${name} ></${Component}>
|
|
676
|
+
<button onclick=${() => showGreeting(!showGreeting())}>Toggle</button>
|
|
677
|
+
</div>
|
|
678
|
+
`;
|
|
102
679
|
|
|
103
|
-
|
|
680
|
+
document.body.append(...app);
|
|
681
|
+
```
|