yandel 1.0.0
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/LICENSE +17 -0
- package/README.md +525 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/node.d.ts +116 -0
- package/dist/node.js +1017 -0
- package/dist/tags.d.ts +733 -0
- package/dist/tags.js +389 -0
- package/dist/utils.d.ts +7 -0
- package/dist/utils.js +18 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
ISC License Copyright (c) 2004-2010 by Internet Systems Consortium, Inc.
|
|
2
|
+
("ISC")
|
|
3
|
+
|
|
4
|
+
Copyright (c) 1995-2003 by Internet Software Consortium
|
|
5
|
+
|
|
6
|
+
Permission to
|
|
7
|
+
use, copy, modify, and /or distribute this software for any purpose with or
|
|
8
|
+
without fee is hereby granted, provided that the above copyright notice and this
|
|
9
|
+
permission notice appear in all copies.
|
|
10
|
+
|
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND
|
|
12
|
+
zzzeros0 DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
|
13
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL k4sp3r BE
|
|
14
|
+
LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
15
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
|
16
|
+
CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
|
17
|
+
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
# Yandel
|
|
2
|
+
|
|
3
|
+
**Yandel is reactive lightweight** typescript library that eases the creation of small to complex user interfaces without the complexity of project setup with **0-deps**.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Introduction](#introduction)
|
|
9
|
+
- [Tags](#tags)
|
|
10
|
+
- [Text](#text)
|
|
11
|
+
- [Portal](#portal)
|
|
12
|
+
- [Templates](#templates)
|
|
13
|
+
- [Components](#components)
|
|
14
|
+
- [Reactivity](#reactivity)
|
|
15
|
+
- [Ref](#ref)
|
|
16
|
+
- [Key](#key)
|
|
17
|
+
- [Usage](#usage)
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
Add this package to your npm project:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
npm install yandel
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Introduction
|
|
28
|
+
|
|
29
|
+
Yandel is a client frontend library that helps you create web UI.
|
|
30
|
+
|
|
31
|
+
### Tags
|
|
32
|
+
|
|
33
|
+
The default HTML Tag elements. They let you create different HTML Elements and customize them.
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { div, span, section } from "yandel";
|
|
37
|
+
|
|
38
|
+
div();
|
|
39
|
+
/**
|
|
40
|
+
* <div/>
|
|
41
|
+
*/
|
|
42
|
+
div({ id: "1234" });
|
|
43
|
+
/**
|
|
44
|
+
* <div id="1234"/>
|
|
45
|
+
*/
|
|
46
|
+
div({ id: "1234", "aria-hidden": "true" }, span());
|
|
47
|
+
/**
|
|
48
|
+
* <div id="1234" aria-hidden="true">
|
|
49
|
+
* <span/>
|
|
50
|
+
* </div>
|
|
51
|
+
*/
|
|
52
|
+
div(
|
|
53
|
+
{ id: "1234", className: "myClass", style: { backgroundColor: "red" } },
|
|
54
|
+
span(),
|
|
55
|
+
section()
|
|
56
|
+
);
|
|
57
|
+
/**
|
|
58
|
+
* <div id="1234" class="myClass" style="background-color: red;">
|
|
59
|
+
* <span/>
|
|
60
|
+
* <section/>
|
|
61
|
+
* </div>
|
|
62
|
+
*/
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Text
|
|
66
|
+
|
|
67
|
+
HTML text node.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { h1, div, p } from "yandel";
|
|
71
|
+
|
|
72
|
+
h1("I'm the text inside h1");
|
|
73
|
+
/**
|
|
74
|
+
* <h1>I'm the text inside h1</h1>
|
|
75
|
+
* */
|
|
76
|
+
|
|
77
|
+
div(p("I'm the text inside p"), "I'm the text inside div");
|
|
78
|
+
/**
|
|
79
|
+
* <div>
|
|
80
|
+
* <p>I'm the text inside p</p>I'm the text inside div
|
|
81
|
+
* </div>
|
|
82
|
+
* */
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Portal
|
|
86
|
+
|
|
87
|
+
Allows you to mount elements over a different parent, remaining in the same tree.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { Portal, div, span, h1 } from "yandel";
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
Portal(document.body, div());
|
|
94
|
+
// Renders a div inside body
|
|
95
|
+
|
|
96
|
+
Portal(document.querySelector("#someid")!, div(), span(), h1(), ...);
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Example:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { Portal, div, span, ValidTemplateReturn } from "yandel";
|
|
104
|
+
|
|
105
|
+
function Notification(): ValidTemplateReturn {
|
|
106
|
+
return div(
|
|
107
|
+
Portal(document.body, span("Notification 1"), span("Notification 2")),
|
|
108
|
+
button("Remove notifications")
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// The span will be rendered inside body instead of the parent div.
|
|
113
|
+
// When the parent div is deleted, the portal is deleted as well.
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Templates
|
|
117
|
+
|
|
118
|
+
Templates are functions that return nodes. They help you customize and modularize:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { div, p, strong, span, button, ValidTemplateReturn } from "yandel";
|
|
122
|
+
|
|
123
|
+
function TotalPriceButton(
|
|
124
|
+
qTy: number,
|
|
125
|
+
uPrice: number,
|
|
126
|
+
onClick: VoidFunction
|
|
127
|
+
): ValidTemplateReturn {
|
|
128
|
+
return div(
|
|
129
|
+
p(strong("Total is:"), span(`${qTy * uPrice} $`)),
|
|
130
|
+
button({ onclick: onClick }, "Buy")
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
TotalPriceButton(4, 2, () => buy());
|
|
135
|
+
/**
|
|
136
|
+
* <div>
|
|
137
|
+
* <p><strong>Total is:</strong>8 $</>
|
|
138
|
+
* <button>Buy</button>
|
|
139
|
+
* </div>
|
|
140
|
+
*/
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Components
|
|
144
|
+
|
|
145
|
+
Components add the reactivity to the game. They are defined extending the `Context` class and must provide a `render` method (a template):
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import {
|
|
149
|
+
Component,
|
|
150
|
+
div,
|
|
151
|
+
p,
|
|
152
|
+
strong,
|
|
153
|
+
span,
|
|
154
|
+
button,
|
|
155
|
+
ValidTemplateReturn,
|
|
156
|
+
} from "yandel";
|
|
157
|
+
|
|
158
|
+
class TotalPriceButton extends Component {
|
|
159
|
+
private readonly total: number;
|
|
160
|
+
private readonly onClick: VoidFunction;
|
|
161
|
+
constructor(qTy: number, uPrice: number, onClick: VoidFunction) {
|
|
162
|
+
super();
|
|
163
|
+
this.total = qTy * uPrice;
|
|
164
|
+
this.onClick = onClick;
|
|
165
|
+
}
|
|
166
|
+
public render(): ValidTemplateReturn {
|
|
167
|
+
return div(
|
|
168
|
+
p(strong("Total is:"), span(`${this.total} $`)),
|
|
169
|
+
button({ onclick: this.onClick }, "Buy")
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
To use a component, instantiate just like a common class:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
section(new TotalPriceButton(4, 2, buyFn));
|
|
179
|
+
/**
|
|
180
|
+
* <section>
|
|
181
|
+
* <div>
|
|
182
|
+
* <p><strong>Total is:</strong>8 $</>
|
|
183
|
+
* <button>Buy</button>
|
|
184
|
+
* </div>
|
|
185
|
+
* </section>
|
|
186
|
+
*/
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Components can return an array:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
class TotalPriceButton extends Component {
|
|
193
|
+
private readonly total: number;
|
|
194
|
+
private readonly onClick: VoidFunction;
|
|
195
|
+
constructor(qTy: number, uPrice: number, onClick: VoidFunction) {
|
|
196
|
+
super();
|
|
197
|
+
this.total = qTy * uPrice;
|
|
198
|
+
this.onClick = onClick;
|
|
199
|
+
}
|
|
200
|
+
public render(): ValidTemplateReturn {
|
|
201
|
+
return [
|
|
202
|
+
p(strong("Total is:"), span(`${this.total} $`)),
|
|
203
|
+
button({ onclick: this.onClick }, "Buy"),
|
|
204
|
+
];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
#### Reactivity
|
|
210
|
+
|
|
211
|
+
`State` object type is defined using the `Component's` generic argument. To set the `initial state`, `defineState` must be called in the constructor.
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
import {
|
|
215
|
+
Component,
|
|
216
|
+
div,
|
|
217
|
+
p,
|
|
218
|
+
strong,
|
|
219
|
+
span,
|
|
220
|
+
button,
|
|
221
|
+
ValidTemplateReturn,
|
|
222
|
+
} from "yandel";
|
|
223
|
+
|
|
224
|
+
class TotalPriceButton extends Component<{
|
|
225
|
+
loading: boolean;
|
|
226
|
+
}> {
|
|
227
|
+
private readonly total: number;
|
|
228
|
+
private readonly onClick: VoidFunction;
|
|
229
|
+
constructor(qTy: number, uPrice: number, onClick: VoidFunction) {
|
|
230
|
+
super();
|
|
231
|
+
this.total = qTy * uPrice;
|
|
232
|
+
this.onClick = onClick;
|
|
233
|
+
// `defineState` must be called in the constructor.
|
|
234
|
+
// It should not be called elsewhere.
|
|
235
|
+
this.defineState({
|
|
236
|
+
loading: false,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
public handleClick() {
|
|
240
|
+
// Perform state update
|
|
241
|
+
this.setState({
|
|
242
|
+
loading: true,
|
|
243
|
+
});
|
|
244
|
+
this.onClick();
|
|
245
|
+
}
|
|
246
|
+
public render(): ValidTemplateReturn {
|
|
247
|
+
return div(
|
|
248
|
+
p(strong("Total is:"), span(`${this.total} $`)),
|
|
249
|
+
button(
|
|
250
|
+
{
|
|
251
|
+
onclick: this.handleClick.bind(this), // Don't forget to bind if needed
|
|
252
|
+
disabled: this.state.loading,
|
|
253
|
+
},
|
|
254
|
+
"Buy"
|
|
255
|
+
)
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
`State` can be changed and accessed via `this.state`, but only `this.setState` will perform an update:
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
// This will set the value, but won't perform an update
|
|
265
|
+
this.state.loading = true;
|
|
266
|
+
|
|
267
|
+
// this will set the value and perform an update
|
|
268
|
+
this.setState({
|
|
269
|
+
loading: true,
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
This is usefull when you want to change a state value without performing an state update.
|
|
274
|
+
|
|
275
|
+
`setState` can receive a function that provides the current value and returns the new value:
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
this.setState((lastValue) => ({
|
|
279
|
+
...lastValue,
|
|
280
|
+
loading: true,
|
|
281
|
+
}));
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Important: Do not overwrite `state`**:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
this.state = { loading: false }; // state is a read-only property!
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
`Effects` are callbacks that are executed when the component is mounted or unmounted. They are created with `this.effect` and should be called inside the `render` method. Components can have more than one effect/cleanup and they're executed in the order they are defined (FIFO).
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
import { Component, p, ValidTemplateReturn } from "yandel";
|
|
296
|
+
interface User {
|
|
297
|
+
name: string;
|
|
298
|
+
}
|
|
299
|
+
class UserData extends Component<{
|
|
300
|
+
user: User | undefined;
|
|
301
|
+
}> {
|
|
302
|
+
private userId: string;
|
|
303
|
+
constructor(id: string) {
|
|
304
|
+
super();
|
|
305
|
+
this.userId = id;
|
|
306
|
+
this.defineState({ user: undefined });
|
|
307
|
+
}
|
|
308
|
+
public render(): ValidTemplateReturn {
|
|
309
|
+
this.effect(() => {
|
|
310
|
+
// Executed when the component is mounted
|
|
311
|
+
if (!this.state.user)
|
|
312
|
+
requestUser(this.userId).then((user) => {
|
|
313
|
+
this.setState({
|
|
314
|
+
user,
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
if (this.state.user) {
|
|
319
|
+
// If a callback is returned in the effect
|
|
320
|
+
// It will be executed when the component unmounts
|
|
321
|
+
return () => {
|
|
322
|
+
sendUserAnalytics(this.state.user);
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
return p(
|
|
327
|
+
this.state.user
|
|
328
|
+
? `Username is: ${this.state.user.name}`
|
|
329
|
+
: "User is undefined"
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Do not forget that calling `setState` inside an effect will perform an update. This example will create a never ending update loop:
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
|
|
339
|
+
public render(): ValidTemplateReturn {
|
|
340
|
+
this.effect(() => {
|
|
341
|
+
this.setState(
|
|
342
|
+
// some state...
|
|
343
|
+
);
|
|
344
|
+
});
|
|
345
|
+
return ...;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Add a condition to avoid a loop:
|
|
351
|
+
|
|
352
|
+
```ts
|
|
353
|
+
|
|
354
|
+
public render(): ValidTemplateReturn {
|
|
355
|
+
this.effect(() => {
|
|
356
|
+
if (!condition)
|
|
357
|
+
this.setState(
|
|
358
|
+
// some state...
|
|
359
|
+
);
|
|
360
|
+
});
|
|
361
|
+
return ...;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**Important:** You must handle calling `setState` or other actions if the component has been removed.
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
|
|
370
|
+
public render(): ValidTemplateReturn {
|
|
371
|
+
this.effect(() => {
|
|
372
|
+
setTimeout(() => {
|
|
373
|
+
this.setState(
|
|
374
|
+
// some state
|
|
375
|
+
);
|
|
376
|
+
}, 2000)
|
|
377
|
+
});
|
|
378
|
+
return ...;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
In this example, if the component is removed, if not handled the timeout will run and an error will be thrown (`Error: Component is deleted`).
|
|
384
|
+
Handle timeouts, deletions and more in the cleanups to prevent weird behavior:
|
|
385
|
+
|
|
386
|
+
```ts
|
|
387
|
+
|
|
388
|
+
public render(): ValidTemplateReturn {
|
|
389
|
+
this.effect(() => {
|
|
390
|
+
this.timeout = setTimeout(() => {
|
|
391
|
+
this.setState(
|
|
392
|
+
// some state
|
|
393
|
+
);
|
|
394
|
+
}, 2000)
|
|
395
|
+
return () => {
|
|
396
|
+
if (this.timeout) clearTimeout(this.timeout);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
return ...;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Ref
|
|
405
|
+
|
|
406
|
+
The `ref` prop allows you to have access to the element's reference:
|
|
407
|
+
|
|
408
|
+
```ts
|
|
409
|
+
import { Component, ElementRef, Stores, input } from "yandel";
|
|
410
|
+
|
|
411
|
+
class InputWithRef extends Component {
|
|
412
|
+
private readonly inputRef: ElementRef<"input"> = new Stores(undefined);
|
|
413
|
+
public render() {
|
|
414
|
+
this.effect(() => {
|
|
415
|
+
console.log(this.inputRef.stores); // HTMLInputElement
|
|
416
|
+
});
|
|
417
|
+
return input({
|
|
418
|
+
ref: this.inputRef,
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Key
|
|
425
|
+
|
|
426
|
+
The `key` prop allows you to identify elements. Elements with the same key won't be diffed.
|
|
427
|
+
|
|
428
|
+
```ts
|
|
429
|
+
// If keys are the same, node diffing won't be done,
|
|
430
|
+
// even if they are different tags
|
|
431
|
+
class WithKey extends Component {
|
|
432
|
+
public render() {
|
|
433
|
+
return condition
|
|
434
|
+
? div({
|
|
435
|
+
key: "key1",
|
|
436
|
+
})
|
|
437
|
+
: section({
|
|
438
|
+
key: "key1",
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
## Usage
|
|
445
|
+
|
|
446
|
+
Define your own templates and components. Then, use `createRoot` to render your UI.
|
|
447
|
+
|
|
448
|
+
```ts
|
|
449
|
+
import {
|
|
450
|
+
Component,
|
|
451
|
+
ElementRef,
|
|
452
|
+
Stores,
|
|
453
|
+
ValidTemplateReturn,
|
|
454
|
+
button,
|
|
455
|
+
createRoot,
|
|
456
|
+
div,
|
|
457
|
+
form,
|
|
458
|
+
input,
|
|
459
|
+
p,
|
|
460
|
+
} from "yandel";
|
|
461
|
+
|
|
462
|
+
function Message(msg: string) {
|
|
463
|
+
return p(`The message is: ${msg}`);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
class MessageInput extends Component<{ message: string }> {
|
|
467
|
+
private readonly inputRef: ElementRef<"input"> = new Stores(undefined);
|
|
468
|
+
constructor() {
|
|
469
|
+
super();
|
|
470
|
+
this.defineState({
|
|
471
|
+
message: "Default message",
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
private handleSubmit(v: SubmitEvent) {
|
|
475
|
+
v.preventDefault();
|
|
476
|
+
if (
|
|
477
|
+
this.inputRef.stores &&
|
|
478
|
+
this.inputRef.stores.value !== this.state.message
|
|
479
|
+
) {
|
|
480
|
+
this.setState({
|
|
481
|
+
message: this.inputRef.stores.value,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
public render(): ValidTemplateReturn {
|
|
486
|
+
this.effect(() => {
|
|
487
|
+
return () => {
|
|
488
|
+
// Reset the input when user
|
|
489
|
+
// sets new message
|
|
490
|
+
if (this.inputRef.stores) this.inputRef.stores.value = "";
|
|
491
|
+
};
|
|
492
|
+
});
|
|
493
|
+
return div(
|
|
494
|
+
form(
|
|
495
|
+
{
|
|
496
|
+
onsubmit: this.handleSubmit.bind(this),
|
|
497
|
+
},
|
|
498
|
+
input({
|
|
499
|
+
ref: this.inputRef,
|
|
500
|
+
type: "text",
|
|
501
|
+
}),
|
|
502
|
+
button(
|
|
503
|
+
{
|
|
504
|
+
type: "submit",
|
|
505
|
+
},
|
|
506
|
+
"Set new message"
|
|
507
|
+
)
|
|
508
|
+
),
|
|
509
|
+
Message(this.state.message)
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Entry point
|
|
515
|
+
createRoot(document.body).render(new MessageInput());
|
|
516
|
+
|
|
517
|
+
// Renders:
|
|
518
|
+
// <div>
|
|
519
|
+
// <form>
|
|
520
|
+
// <input type="text">
|
|
521
|
+
// <button type="submit">Set new message</button>
|
|
522
|
+
// </form>
|
|
523
|
+
// <p>The message is: Default message</p>
|
|
524
|
+
// </div>
|
|
525
|
+
```
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
type KeyedObject = Record<string | symbol | number, any>;
|
|
2
|
+
type HTMLTags = keyof HTMLElementTagNameMap;
|
|
3
|
+
type Element<K extends HTMLTags = HTMLTags> = HTMLElementTagNameMap[K];
|
|
4
|
+
interface SwitchableNode<T extends Node> {
|
|
5
|
+
switch(t: T): boolean;
|
|
6
|
+
}
|
|
7
|
+
type ValidNodeChild = Node | Component | Component<KeyedObject> | null | string;
|
|
8
|
+
type ValidTemplateReturn = ValidNodeChild | ValidNodeChild[];
|
|
9
|
+
type TemplateWithProps<P extends KeyedObject = KeyedObject> = (props: P) => ValidTemplateReturn;
|
|
10
|
+
type Template<P extends KeyedObject | void = void> = P extends void ? () => ValidTemplateReturn : TemplateWithProps<P extends void ? KeyedObject : P>;
|
|
11
|
+
type EffectHandler = (() => void) | (() => () => void);
|
|
12
|
+
type ExcludeFunctions<T> = {
|
|
13
|
+
[K in keyof T as T[K] extends Function ? never : K]: T[K];
|
|
14
|
+
};
|
|
15
|
+
type Styles = Partial<CSSStyleDeclaration>;
|
|
16
|
+
type HTMLExcludedProperties = `aria${string}` | "tagName" | "shadowRoot" | "slot" | "classList" | "style" | "attributes" | "attributeStyleMap" | "ATTRIBUTE_NODE" | "CDATA_SECTION_NODE" | "COMMENT_NODE" | "childElementCount" | "childNodes" | "children" | "isConnected" | "ownerDocument" | "lastChild" | "lastElement" | "nextElementSibling" | "nextSibling" | "previousElementSibling" | "previousSibling" | "parentElement" | "parent" | "outerContent" | "firstChild" | "firstElementChild" | "lastChild" | "lastElementChild" | "innerHTML" | "outerHTML" | "innerContent" | "textContent" | `DOCUMENT_${string}` | "NOTATION_NODE" | "PROCESSING_INSTRUCTION_NODE" | "CONTENT_NODE" | "ELEMENT_NODE" | "TEXT_NODE" | `ENTITY_${string}`;
|
|
17
|
+
type AriaWidgetProperties = "aria-autocomplete" | "aria-checked" | "aria-disabled" | "aria-errormessage" | "aria-expanded" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-label" | "aria-level" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-placeholder" | "aria-pressed" | "aria-readonly" | "aria-required" | "aria-selected" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext";
|
|
18
|
+
type AriaLiveRegionProperties = "aria-busy" | "aria-live" | "aria-relevant" | "aria-atomic";
|
|
19
|
+
type AriaDragAndDropProperties = "aria-dropeffect" | "aria-grabbed";
|
|
20
|
+
type AriaRelatonshipProperties = "aria-activedescendant" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-describedby" | "aria-description" | "aria-details" | "aria-errormessage" | "aria-flowto" | "aria-labelledby" | "aria-owns" | "aria-posinset" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-setsize";
|
|
21
|
+
type AriaGlobalProperties = "aria-atomic" | "aria-busy" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-description" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-live" | "aria-owns" | "aria-relevant" | "aria-roledescription";
|
|
22
|
+
type AriaProps = AriaWidgetProperties | AriaDragAndDropProperties | AriaLiveRegionProperties | AriaRelatonshipProperties | AriaGlobalProperties;
|
|
23
|
+
type AriaBoolean = "true" | "false";
|
|
24
|
+
type AriaValue = Exclude<string, AriaBoolean>;
|
|
25
|
+
type AriaValueMap = Partial<Record<AriaProps, AriaBoolean | AriaValue>>;
|
|
26
|
+
interface ElementOverridedProperties extends AriaValueMap {
|
|
27
|
+
style?: Styles;
|
|
28
|
+
}
|
|
29
|
+
type ElementRef<T extends HTMLTags = HTMLTags> = Stores<Element<T> | undefined>;
|
|
30
|
+
interface ElementProps<T extends HTMLTags = HTMLTags> {
|
|
31
|
+
ref?: ElementRef<T>;
|
|
32
|
+
key?: string | number | symbol;
|
|
33
|
+
}
|
|
34
|
+
type ExcludeProperties<T> = {
|
|
35
|
+
[K in keyof T as K extends HTMLExcludedProperties ? never : K]: T[K];
|
|
36
|
+
};
|
|
37
|
+
type HTMLProps<T extends HTMLTags = HTMLTags> = Partial<ExcludeProperties<ExcludeFunctions<Element<T>>> & ElementProps<T>> & ElementOverridedProperties;
|
|
38
|
+
type StateHandler<S extends KeyedObject | undefined = undefined> = S | ((l: S) => S);
|
|
39
|
+
declare const enum NodeType {
|
|
40
|
+
Tag = 0,
|
|
41
|
+
Content = 1,
|
|
42
|
+
Parent = 2,
|
|
43
|
+
Component = 3
|
|
44
|
+
}
|
|
45
|
+
declare class Stores<T> {
|
|
46
|
+
#private;
|
|
47
|
+
constructor(s: T);
|
|
48
|
+
get stores(): T;
|
|
49
|
+
set stores(s: T);
|
|
50
|
+
}
|
|
51
|
+
declare class Node {
|
|
52
|
+
readonly $typeof: NodeType;
|
|
53
|
+
private _index_of;
|
|
54
|
+
parent: Node | undefined;
|
|
55
|
+
needs_update: boolean;
|
|
56
|
+
protected _deleted: boolean;
|
|
57
|
+
constructor(t: NodeType);
|
|
58
|
+
get isTag(): boolean;
|
|
59
|
+
get isContent(): boolean;
|
|
60
|
+
get isParent(): boolean;
|
|
61
|
+
get isComponent(): boolean;
|
|
62
|
+
get deleted(): boolean;
|
|
63
|
+
get index_of(): number;
|
|
64
|
+
setIndex_of(i: number): this;
|
|
65
|
+
delete(): void;
|
|
66
|
+
}
|
|
67
|
+
declare class TagNode<T extends HTMLTags = HTMLTags> extends Node implements SwitchableNode<TagNode> {
|
|
68
|
+
static is(t: any): t is TagNode;
|
|
69
|
+
tag: T;
|
|
70
|
+
private _dom;
|
|
71
|
+
private _props;
|
|
72
|
+
private _children;
|
|
73
|
+
key: symbol | string | number | undefined;
|
|
74
|
+
index_length: number;
|
|
75
|
+
constructor(tag: T, props?: HTMLProps<T>, children?: ValidNodeChild[]);
|
|
76
|
+
private _apply_dom_props;
|
|
77
|
+
private _diff_dom_props;
|
|
78
|
+
private _clear_dom_props;
|
|
79
|
+
private _clear_dom_events;
|
|
80
|
+
get dom(): Element<T> | undefined;
|
|
81
|
+
get props(): HTMLProps<T> | undefined;
|
|
82
|
+
get children(): ValidNodeChild[] | undefined;
|
|
83
|
+
get hasChildren(): boolean;
|
|
84
|
+
create(): boolean;
|
|
85
|
+
clearChildren(): void;
|
|
86
|
+
clearDom(): void;
|
|
87
|
+
delete(): void;
|
|
88
|
+
switch(t: TagNode<HTMLTags>): boolean;
|
|
89
|
+
}
|
|
90
|
+
declare class ParentNode<T extends HTMLElement = HTMLElement> extends Node implements SwitchableNode<ParentNode> {
|
|
91
|
+
static is(t: any): t is ParentNode;
|
|
92
|
+
readonly dom: T;
|
|
93
|
+
private _children;
|
|
94
|
+
child_length: number;
|
|
95
|
+
constructor(root: T, children?: ValidNodeChild[]);
|
|
96
|
+
get children(): ValidNodeChild[] | undefined;
|
|
97
|
+
get hasChildren(): boolean;
|
|
98
|
+
clearChildren(): void;
|
|
99
|
+
delete(): void;
|
|
100
|
+
switch(t: ParentNode<HTMLElement>): boolean;
|
|
101
|
+
}
|
|
102
|
+
declare abstract class Component<S extends KeyedObject | void = void> {
|
|
103
|
+
static is(t: any): t is Component | Component<KeyedObject>;
|
|
104
|
+
constructor();
|
|
105
|
+
protected get state(): S;
|
|
106
|
+
protected set state(s: S);
|
|
107
|
+
protected setState(s: StateHandler<S extends void ? undefined : S>): void;
|
|
108
|
+
protected effect(effect: EffectHandler): void;
|
|
109
|
+
protected defineState(s: S): void;
|
|
110
|
+
abstract render(): ValidTemplateReturn;
|
|
111
|
+
}
|
|
112
|
+
declare function Portal<T extends HTMLElement>(root: T, ...children: ValidNodeChild[]): ParentNode<T>;
|
|
113
|
+
declare function createRoot<T extends Element>(root: T): {
|
|
114
|
+
render: (...children: ValidNodeChild[]) => void;
|
|
115
|
+
};
|
|
116
|
+
export { Component, createRoot, EffectHandler, Element, ElementRef, HTMLProps, HTMLTags, Node, Portal, Stores, Styles, TagNode, Template, TemplateWithProps, ValidNodeChild, ValidTemplateReturn, StateHandler, };
|