subay 0.0.7 → 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 +543 -239
- package/README.zh.md +682 -377
- package/examples/todo-app.html +286 -0
- package/lib/h.d.ts.map +1 -1
- package/lib/h.js +50 -32
- 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 -2
- 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 +60 -64
- package/lib/s.js.map +1 -1
- package/package.json +12 -9
- package/src/h.ts +56 -42
- package/src/index.ts +0 -2
- package/src/name.ts +60 -0
- package/src/s.ts +95 -95
package/README.md
CHANGED
|
@@ -1,377 +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
|
-
- **Declarative Rendering**: Use template literals for declarative UI construction
|
|
9
|
-
- **Reactive State**: Simple yet powerful reactive state management
|
|
10
|
-
- **Efficient Updates**: Optimized DOM updates with minimal DOM operations
|
|
11
|
-
- **TypeScript Support**: Complete TypeScript type definitions
|
|
12
|
-
- **Zero Dependencies**: No external library dependencies
|
|
7
|
+
### root
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
**Introduction**
|
|
10
|
+
Creates a root update context for managing the lifecycle of reactive states.
|
|
15
11
|
|
|
16
|
-
|
|
12
|
+
**Syntax**
|
|
17
13
|
|
|
18
|
-
```
|
|
19
|
-
|
|
14
|
+
```typescript
|
|
15
|
+
root<T>(fn: (destroy: () => void) => T): T
|
|
20
16
|
```
|
|
21
17
|
|
|
22
|
-
|
|
18
|
+
**Parameters**
|
|
23
19
|
|
|
24
|
-
|
|
25
|
-
yarn add subay
|
|
26
|
-
```
|
|
20
|
+
- `fn`: A function that receives a `destroy` function as a parameter, used to manually destroy the root context.
|
|
27
21
|
|
|
28
|
-
|
|
22
|
+
**Return Value**
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
<script type="module">
|
|
32
|
-
import { html, o, S } from 'https://cdn.jsdelivr.net/npm/subay@latest/lib/index.js';
|
|
33
|
-
// Use Subay
|
|
34
|
-
</script>
|
|
35
|
-
```
|
|
24
|
+
- `T`: The return value of the `fn` function.
|
|
36
25
|
|
|
37
|
-
|
|
26
|
+
**Example**
|
|
38
27
|
|
|
39
|
-
|
|
28
|
+
```typescript
|
|
29
|
+
import { root, o, S } from 'subay';
|
|
40
30
|
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
root((destroy) => {
|
|
32
|
+
const count = o(0);
|
|
33
|
+
const doubled = S(() => count() * 2);
|
|
43
34
|
|
|
44
|
-
//
|
|
45
|
-
|
|
35
|
+
console.log(doubled()); // 0
|
|
36
|
+
count(1);
|
|
37
|
+
console.log(doubled()); // 2
|
|
46
38
|
|
|
47
|
-
//
|
|
48
|
-
|
|
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.
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
console.log(count()); // Output: 0
|
|
52
|
-
console.log(doubled()); // Output: 0
|
|
53
|
+
**Syntax**
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
console.log(count()); // Output: 5
|
|
57
|
-
console.log(doubled()); // Output: 10
|
|
55
|
+
```typescript
|
|
56
|
+
S<T>(fn: (pv: T | undefined) => T, value?: T): IS<T>
|
|
58
57
|
```
|
|
59
58
|
|
|
60
|
-
|
|
59
|
+
**Parameters**
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
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';
|
|
64
72
|
|
|
65
|
-
const name = o('World');
|
|
66
73
|
const count = o(0);
|
|
74
|
+
const doubled = S(() => count() * 2);
|
|
67
75
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<h1>Hello, ${name}!</h1>
|
|
72
|
-
<p>Count: ${count}</p>
|
|
73
|
-
<button onclick="${() => count(count() + 1)}">Increment</button>
|
|
74
|
-
<button onclick="${() => name('Subay')}">Change Name</button>
|
|
75
|
-
</div>
|
|
76
|
-
`,
|
|
77
|
-
);
|
|
76
|
+
console.log(doubled()); // 0
|
|
77
|
+
count(1);
|
|
78
|
+
console.log(doubled()); // 2
|
|
78
79
|
```
|
|
79
80
|
|
|
80
|
-
###
|
|
81
|
+
### o / observable
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
**Introduction**
|
|
84
|
+
Creates an observable value for storing and updating state.
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
**Syntax**
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
<ul>
|
|
90
|
-
${map(
|
|
91
|
-
() => items(),
|
|
92
|
-
(item) => html` <li>${item}</li> `,
|
|
93
|
-
)}
|
|
94
|
-
</ul>
|
|
95
|
-
<button onclick="${() => items([...items(), 'Date'])}">Add Item</button>
|
|
96
|
-
`,
|
|
97
|
-
);
|
|
88
|
+
```typescript
|
|
89
|
+
o<T>(value: T): IO<T>
|
|
98
90
|
```
|
|
99
91
|
|
|
100
|
-
|
|
92
|
+
**Parameters**
|
|
93
|
+
|
|
94
|
+
- `value`: Initial value.
|
|
101
95
|
|
|
102
|
-
|
|
96
|
+
**Return Value**
|
|
103
97
|
|
|
104
|
-
|
|
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.
|
|
105
99
|
|
|
106
|
-
|
|
100
|
+
**Example**
|
|
107
101
|
|
|
108
|
-
|
|
109
|
-
|
|
102
|
+
```typescript
|
|
103
|
+
import { o } from 'subay';
|
|
110
104
|
|
|
111
|
-
```javascript
|
|
112
105
|
const count = o(0);
|
|
113
|
-
count(); //
|
|
114
|
-
count(
|
|
106
|
+
console.log(count()); // 0
|
|
107
|
+
count(1);
|
|
108
|
+
console.log(count()); // 1
|
|
115
109
|
```
|
|
116
110
|
|
|
117
|
-
|
|
111
|
+
### cleanup
|
|
118
112
|
|
|
119
|
-
|
|
113
|
+
**Introduction**
|
|
114
|
+
Registers a cleanup function that executes when the current update context is destroyed.
|
|
120
115
|
|
|
121
|
-
|
|
122
|
-
- `fn` - Computation function, receives the previous value as a parameter
|
|
123
|
-
- `initialValue` - Optional initial value
|
|
124
|
-
- **Return**: A function that returns the computation result when called
|
|
116
|
+
**Syntax**
|
|
125
117
|
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
doubled(); // Read computation result
|
|
118
|
+
```typescript
|
|
119
|
+
cleanup<T extends () => void>(f: T): T
|
|
129
120
|
```
|
|
130
121
|
|
|
131
|
-
|
|
122
|
+
**Parameters**
|
|
132
123
|
|
|
133
|
-
|
|
124
|
+
- `f`: Cleanup function.
|
|
134
125
|
|
|
135
|
-
|
|
136
|
-
- **Return**: The result of the function execution
|
|
126
|
+
**Return Value**
|
|
137
127
|
|
|
138
|
-
|
|
139
|
-
|
|
128
|
+
- `T`: The passed cleanup function.
|
|
129
|
+
|
|
130
|
+
**Example**
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { root, S, o, cleanup } from 'subay';
|
|
134
|
+
|
|
135
|
+
root(() => {
|
|
140
136
|
const count = o(0);
|
|
141
|
-
const doubled = S(() => count() * 2);
|
|
142
137
|
|
|
143
|
-
|
|
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
|
|
144
151
|
|
|
145
|
-
|
|
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);
|
|
146
183
|
});
|
|
184
|
+
// Output: 5 (calculated only once)
|
|
147
185
|
```
|
|
148
186
|
|
|
149
|
-
|
|
187
|
+
### sample
|
|
150
188
|
|
|
151
|
-
|
|
189
|
+
**Introduction**
|
|
190
|
+
Gets the value of an observable without establishing a dependency relationship.
|
|
152
191
|
|
|
153
|
-
|
|
154
|
-
|
|
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;
|
|
155
213
|
|
|
156
|
-
```javascript
|
|
157
214
|
S(() => {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
|
161
225
|
|
|
162
|
-
|
|
226
|
+
**Introduction**
|
|
227
|
+
Checks if the current state is in reactive listening mode.
|
|
163
228
|
|
|
164
|
-
|
|
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
|
|
165
252
|
});
|
|
166
253
|
```
|
|
167
254
|
|
|
168
|
-
|
|
255
|
+
### subscribe
|
|
169
256
|
|
|
170
|
-
|
|
257
|
+
**Introduction**
|
|
258
|
+
Subscribes to a function that automatically executes when dependent observables change.
|
|
171
259
|
|
|
172
|
-
|
|
173
|
-
- **Return**: The result of the function execution
|
|
260
|
+
**Syntax**
|
|
174
261
|
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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());
|
|
180
282
|
});
|
|
283
|
+
|
|
284
|
+
count(1); // Output: 1
|
|
285
|
+
count(2); // Output: 2
|
|
286
|
+
|
|
287
|
+
unsubscribe();
|
|
288
|
+
count(3); // No output
|
|
181
289
|
```
|
|
182
290
|
|
|
183
|
-
###
|
|
291
|
+
### unsubscribe
|
|
184
292
|
|
|
185
|
-
|
|
293
|
+
**Introduction**
|
|
294
|
+
Unsubscribes a function.
|
|
186
295
|
|
|
187
|
-
|
|
296
|
+
**Syntax**
|
|
188
297
|
|
|
189
|
-
|
|
190
|
-
|
|
298
|
+
```typescript
|
|
299
|
+
unsubscribe(f: () => void): void
|
|
300
|
+
```
|
|
191
301
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
|
199
323
|
```
|
|
200
324
|
|
|
201
|
-
|
|
325
|
+
### isObservable
|
|
202
326
|
|
|
203
|
-
|
|
327
|
+
**Introduction**
|
|
328
|
+
Checks if a value is an observable.
|
|
204
329
|
|
|
205
|
-
|
|
206
|
-
- **Return**: An array of DOM nodes
|
|
330
|
+
**Syntax**
|
|
207
331
|
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
<svg width="24" height="24" viewBox="0 0 24 24">
|
|
211
|
-
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
|
|
212
|
-
</svg>
|
|
213
|
-
`;
|
|
214
|
-
document.body.append(...icon);
|
|
332
|
+
```typescript
|
|
333
|
+
isObservable(o: any): o is IO<any>
|
|
215
334
|
```
|
|
216
335
|
|
|
217
|
-
|
|
336
|
+
**Parameters**
|
|
337
|
+
|
|
338
|
+
- `o`: The value to check.
|
|
218
339
|
|
|
219
|
-
|
|
340
|
+
**Return Value**
|
|
220
341
|
|
|
221
|
-
-
|
|
222
|
-
- `arrayFn` - Function that returns an array
|
|
223
|
-
- `itemFn` - Function that creates DOM nodes for each array item
|
|
224
|
-
- **Return**: An array of DOM nodes
|
|
342
|
+
- `boolean`: Whether it is an observable.
|
|
225
343
|
|
|
226
|
-
|
|
344
|
+
**Example**
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
import { isObservable, o, S } from 'subay';
|
|
348
|
+
|
|
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).
|
|
391
|
+
|
|
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);
|
|
412
|
+
const doubled = S(() => count() * 2);
|
|
413
|
+
|
|
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[]
|
|
428
|
+
```
|
|
429
|
+
|
|
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']);
|
|
227
445
|
const list = map(
|
|
228
446
|
() => items(),
|
|
229
|
-
(item
|
|
447
|
+
(item) => html`<li>${item}</li>`,
|
|
230
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>
|
|
231
455
|
```
|
|
232
456
|
|
|
233
|
-
###
|
|
457
|
+
### svg
|
|
234
458
|
|
|
235
|
-
|
|
459
|
+
**Introduction**
|
|
460
|
+
Template tag function for creating SVG elements.
|
|
236
461
|
|
|
237
|
-
|
|
462
|
+
**Syntax**
|
|
238
463
|
|
|
239
|
-
|
|
240
|
-
|
|
464
|
+
```typescript
|
|
465
|
+
svg(segments: TemplateStringsArray, ...values: any[]): Node[]
|
|
466
|
+
```
|
|
241
467
|
|
|
242
|
-
|
|
468
|
+
**Parameters**
|
|
243
469
|
|
|
244
|
-
|
|
470
|
+
- `segments`: Static parts of the template string.
|
|
471
|
+
- `values`: Dynamic parts of the template string.
|
|
245
472
|
|
|
246
|
-
|
|
247
|
-
- **Return**: Boolean
|
|
473
|
+
**Return Value**
|
|
248
474
|
|
|
249
|
-
|
|
475
|
+
- `Node[]`: SVG element node array.
|
|
250
476
|
|
|
251
|
-
|
|
477
|
+
**Example**
|
|
252
478
|
|
|
253
|
-
|
|
254
|
-
|
|
479
|
+
```typescript
|
|
480
|
+
import { svg, o } from 'subay';
|
|
255
481
|
|
|
256
|
-
|
|
482
|
+
const radius = o(50);
|
|
483
|
+
const circle = svg`<circle cx="100" cy="100" r="${radius}" fill="red"/>`;
|
|
257
484
|
|
|
258
|
-
|
|
485
|
+
document.getElementById('svg').append(...circle);
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### html
|
|
259
489
|
|
|
260
|
-
|
|
261
|
-
|
|
490
|
+
**Introduction**
|
|
491
|
+
Template tag function for creating HTML elements.
|
|
262
492
|
|
|
263
|
-
|
|
264
|
-
|
|
493
|
+
**Syntax**
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
html(segments: TemplateStringsArray, ...values: any[]): Node[]
|
|
265
497
|
```
|
|
266
498
|
|
|
267
|
-
|
|
499
|
+
**Parameters**
|
|
268
500
|
|
|
269
|
-
|
|
501
|
+
- `segments`: Static parts of the template string.
|
|
502
|
+
- `values`: Dynamic parts of the template string.
|
|
270
503
|
|
|
271
|
-
|
|
504
|
+
**Return Value**
|
|
272
505
|
|
|
273
|
-
|
|
506
|
+
- `Node[]`: HTML element node array.
|
|
274
507
|
|
|
275
|
-
|
|
276
|
-
// Static tag
|
|
277
|
-
html`<div>Hello</div>`;
|
|
508
|
+
**Example**
|
|
278
509
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
html`<${tagName}>Hello</${tagName}>`;
|
|
510
|
+
```typescript
|
|
511
|
+
import { html, o } from 'subay';
|
|
282
512
|
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
|
|
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
|
+
`;
|
|
286
520
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
html`<div>${MyComponent()}</div>`;
|
|
521
|
+
document.body.append(...counter);
|
|
522
|
+
// Clicking the button will automatically update the count value
|
|
290
523
|
```
|
|
291
524
|
|
|
292
|
-
**
|
|
525
|
+
**Using HTML**
|
|
293
526
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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);
|
|
298
538
|
```
|
|
299
539
|
|
|
300
|
-
|
|
540
|
+
**Don't Do This**
|
|
541
|
+
|
|
542
|
+
```typescript
|
|
543
|
+
import { html } from 'subay';
|
|
301
544
|
|
|
302
|
-
|
|
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
|
+
```
|
|
303
551
|
|
|
304
|
-
**
|
|
552
|
+
**Passing Objects as Element Attributes**
|
|
305
553
|
|
|
306
|
-
|
|
307
|
-
// Using observable
|
|
308
|
-
const disabled = o(false);
|
|
309
|
-
html`<button disabled="${disabled}">Click</button>`;
|
|
554
|
+
If you need to pass an object as element attributes, you can use the `...` syntax.
|
|
310
555
|
|
|
311
|
-
|
|
312
|
-
const isDisabled = S(() => count() > 5);
|
|
313
|
-
html`<button disabled="${isDisabled}">Click</button>`;
|
|
556
|
+
**Syntax**
|
|
314
557
|
|
|
315
|
-
|
|
316
|
-
html`<
|
|
558
|
+
```typescript
|
|
559
|
+
html`<tag ...${props}></tag>`;
|
|
317
560
|
```
|
|
318
561
|
|
|
319
|
-
**
|
|
562
|
+
**Example**
|
|
320
563
|
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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);
|
|
325
575
|
```
|
|
326
576
|
|
|
327
|
-
|
|
577
|
+
**Component Declaration**
|
|
328
578
|
|
|
329
|
-
|
|
579
|
+
A component is a function that returns Node[], and can receive parameters. Unlike React, Vue, etc., component parameters are a list.
|
|
330
580
|
|
|
331
|
-
|
|
332
|
-
- **Performance Priority**: Optimize DOM updates, reduce unnecessary computations
|
|
333
|
-
- **Minimal Dependencies**: No external library dependencies, keep size small
|
|
334
|
-
- **TypeScript Support**: Provide complete type definitions, improve development experience
|
|
581
|
+
**Syntax**
|
|
335
582
|
|
|
336
|
-
|
|
583
|
+
```typescript
|
|
584
|
+
const Component = (text, children: () => Node[]) => {
|
|
585
|
+
return html`<div>${text}</div>`;
|
|
586
|
+
};
|
|
587
|
+
```
|
|
337
588
|
|
|
338
|
-
|
|
589
|
+
**Example**
|
|
339
590
|
|
|
340
|
-
|
|
591
|
+
```typescript
|
|
592
|
+
import { html } from 'subay';
|
|
341
593
|
|
|
342
|
-
|
|
594
|
+
const Greeting = (name: string, children: () => Node[]) => {
|
|
595
|
+
return html`
|
|
596
|
+
<div>
|
|
597
|
+
<h1>Hello, ${name}!</h1>
|
|
598
|
+
${children()}
|
|
599
|
+
</div>
|
|
600
|
+
`;
|
|
601
|
+
};
|
|
602
|
+
```
|
|
343
603
|
|
|
344
|
-
|
|
345
|
-
const userId = o('');
|
|
346
|
-
const defaultThemeId = o('rainbow');
|
|
604
|
+
**Component Usage**
|
|
347
605
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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.
|
|
610
|
+
|
|
611
|
+
**Example**
|
|
612
|
+
|
|
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';
|
|
363
630
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
```
|
|
631
|
+
const Greeting = (greet: string, name: () => string) => {
|
|
632
|
+
return html`<h1>${greet}, ${name}!</h1>`;
|
|
633
|
+
};
|
|
368
634
|
|
|
369
|
-
|
|
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);
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
**Dynamic Components**
|
|
653
|
+
|
|
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.
|
|
370
655
|
|
|
371
|
-
|
|
656
|
+
**Example**
|
|
372
657
|
|
|
373
|
-
|
|
658
|
+
```typescript
|
|
659
|
+
import { html, o, S } from 'subay';
|
|
374
660
|
|
|
375
|
-
|
|
661
|
+
const Greeting = (name: () => string) => {
|
|
662
|
+
return html`<h1>Hello, ${name}!</h1>`;
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
const Farewell = (name: () => string) => {
|
|
666
|
+
return html`<h1>Goodbye, ${name}!</h1>`;
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
const showGreeting = o(true);
|
|
670
|
+
const name = o('World');
|
|
671
|
+
const Component = S(() => (showGreeting() ? Greeting : Farewell));
|
|
672
|
+
|
|
673
|
+
const app = html`
|
|
674
|
+
<div>
|
|
675
|
+
<${Component} name=${name} ></${Component}>
|
|
676
|
+
<button onclick=${() => showGreeting(!showGreeting())}>Toggle</button>
|
|
677
|
+
</div>
|
|
678
|
+
`;
|
|
376
679
|
|
|
377
|
-
|
|
680
|
+
document.body.append(...app);
|
|
681
|
+
```
|