@jackens/nnn 2026.4.12 → 2026.4.14
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/nnn.d.ts +213 -104
- package/nnn.js +59 -63
- package/package.json +1 -1
- package/readme.md +522 -236
package/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# nnn
|
|
2
2
|
|
|
3
|
-
A collection of Jackens’ JavaScript helper utilities (version: `2026.4.
|
|
3
|
+
A collection of Jackens’ JavaScript helper utilities (version: `2026.4.14`).
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -19,62 +19,118 @@ npm i @jackens/nnn
|
|
|
19
19
|
```js
|
|
20
20
|
import {
|
|
21
21
|
c,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
22
|
+
csvParse,
|
|
23
|
+
fixPlTypography,
|
|
24
|
+
h,
|
|
25
|
+
hasOwn,
|
|
26
|
+
isArray,
|
|
27
|
+
isFiniteNumber,
|
|
28
|
+
isInteger,
|
|
29
|
+
isNumber,
|
|
30
|
+
isRecord,
|
|
31
|
+
isString,
|
|
32
|
+
jsOnParse,
|
|
33
|
+
monokai,
|
|
34
|
+
nanolightTs,
|
|
35
|
+
newEscape,
|
|
36
|
+
newNounForm,
|
|
37
|
+
newTokenizer,
|
|
38
|
+
omit,
|
|
39
|
+
pick,
|
|
40
|
+
rwd,
|
|
41
|
+
s,
|
|
42
|
+
svgUse,
|
|
43
|
+
uuidV1,
|
|
44
|
+
vivify,
|
|
45
|
+
|
|
45
46
|
} from '@jackens/nnn' // or './node_modules/@jackens/nnn/nnn.js'
|
|
46
47
|
```
|
|
47
48
|
|
|
48
49
|
## Exports
|
|
49
50
|
|
|
50
|
-
- [`CNode`](#CNode):
|
|
51
|
-
|
|
52
|
-
- [`
|
|
53
|
-
|
|
54
|
-
- [`
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
- [`
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
- [`
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
- [`
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
- [`
|
|
68
|
-
-
|
|
69
|
-
|
|
70
|
-
- [`
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
- [`
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
- [`
|
|
51
|
+
- [`CNode`](#CNode):
|
|
52
|
+
Represents a CSS rule node for the [`c`](#c) helper.
|
|
53
|
+
- [`CRoot`](#CRoot):
|
|
54
|
+
Represents the root CSS object for the [`c`](#c) helper.
|
|
55
|
+
- [`HArgs`](#HArgs):
|
|
56
|
+
Tuple argument type for the [`h`](#h) and [`s`](#s)
|
|
57
|
+
helpers.
|
|
58
|
+
- [`HArgs1`](#HArgs1):
|
|
59
|
+
Single argument type for the [`h`](#h) and [`s`](#s)
|
|
60
|
+
helpers.
|
|
61
|
+
- [`c`](#c):
|
|
62
|
+
A minimal CSS-in-JS helper
|
|
63
|
+
that converts a JavaScript object hierarchy
|
|
64
|
+
into a CSS string.
|
|
65
|
+
- [`csvParse`](#csvParse):
|
|
66
|
+
Parses a CSV string
|
|
67
|
+
into a two-dimensional array of strings.
|
|
68
|
+
- [`fixPlTypography`](#fixPlTypography):
|
|
69
|
+
Applies Polish-specific typographic corrections
|
|
70
|
+
to a DOM subtree.
|
|
71
|
+
- [`h`](#h):
|
|
72
|
+
A lightweight *HyperScript*-style helper
|
|
73
|
+
for creating and modifying `HTMLElement`s
|
|
74
|
+
(see also [`s`](#s)).
|
|
75
|
+
- [`hasOwn`](#hasOwn):
|
|
76
|
+
Checks whether an object has the specified key
|
|
77
|
+
as its own property.
|
|
78
|
+
- [`isArray`](#isArray):
|
|
79
|
+
Checks whether the argument is an array.
|
|
80
|
+
- [`isFiniteNumber`](#isFiniteNumber):
|
|
81
|
+
Checks whether the argument is a finite number
|
|
82
|
+
(excludes `±Infinity` and `NaN`).
|
|
83
|
+
- [`isInteger`](#isInteger):
|
|
84
|
+
Checks whether the argument is an integer number.
|
|
85
|
+
- [`isNumber`](#isNumber):
|
|
86
|
+
Checks whether the argument is of type `number`
|
|
87
|
+
(includes `NaN` and `±Infinity`).
|
|
88
|
+
- [`isRecord`](#isRecord):
|
|
89
|
+
Checks whether the argument is a plain object
|
|
90
|
+
(not `null` and not an array).
|
|
91
|
+
- [`isString`](#isString):
|
|
92
|
+
Checks whether the argument is a string.
|
|
93
|
+
- [`jsOnParse`](#jsOnParse):
|
|
94
|
+
Parses JSON with support for
|
|
95
|
+
handler-based value transformation (“JavaScript ON”).
|
|
96
|
+
- [`monokai`](#monokai):
|
|
97
|
+
A Monokai-inspired color scheme
|
|
98
|
+
for use with the [`c`](#c) helper
|
|
99
|
+
and [`nanolightTs`](#nanolightTs) tokenizer.
|
|
100
|
+
- [`nanolightTs`](#nanolightTs):
|
|
101
|
+
A TypeScript/JavaScript syntax highlighting tokenizer
|
|
102
|
+
built using [`newTokenizer`](#newTokenizer).
|
|
103
|
+
- [`newEscape`](#newEscape):
|
|
104
|
+
Creates a tag function
|
|
105
|
+
for escaping interpolated values in template literals.
|
|
106
|
+
- [`newNounForm`](#newNounForm):
|
|
107
|
+
Creates a function that
|
|
108
|
+
returns the appropriate noun form
|
|
109
|
+
based on a numeric value using `Intl.PluralRules`.
|
|
110
|
+
- [`newTokenizer`](#newTokenizer):
|
|
111
|
+
A helper for building simple tokenizers
|
|
112
|
+
(see also [`nanolightTs`](#nanolightTs)).
|
|
113
|
+
- [`omit`](#omit):
|
|
114
|
+
Creates a new object excluding the specified keys
|
|
115
|
+
from the source object.
|
|
116
|
+
- [`pick`](#pick):
|
|
117
|
+
Creates a new object containing only the specified keys
|
|
118
|
+
from the source object.
|
|
119
|
+
- [`rwd`](#rwd):
|
|
120
|
+
A responsive web design helper
|
|
121
|
+
that generates CSS rules for a grid-like layout.
|
|
122
|
+
- [`s`](#s):
|
|
123
|
+
A lightweight *HyperScript*-style helper
|
|
124
|
+
for creating and modifying `SVGElement`s
|
|
125
|
+
(see also [`h`](#h)).
|
|
126
|
+
- [`svgUse`](#svgUse):
|
|
127
|
+
Shorthand for creating an SVG element
|
|
128
|
+
with a `<use>` child referencing an icon by ID.
|
|
129
|
+
- [`uuidV1`](#uuidV1):
|
|
130
|
+
Generates a UUID v1 (time-based) identifier.
|
|
131
|
+
- [`vivify`](#vivify):
|
|
132
|
+
A Proxy-based helper for auto-vivification
|
|
133
|
+
of nested object structures.
|
|
78
134
|
|
|
79
135
|
### CNode
|
|
80
136
|
|
|
@@ -84,7 +140,8 @@ type CNode = {
|
|
|
84
140
|
};
|
|
85
141
|
```
|
|
86
142
|
|
|
87
|
-
Represents a CSS rule node for the [`c`](#c) helper.
|
|
143
|
+
Represents a CSS rule node for the [`c`](#c) helper.
|
|
144
|
+
Keys are CSS properties or nested selectors.
|
|
88
145
|
|
|
89
146
|
### CRoot
|
|
90
147
|
|
|
@@ -92,7 +149,8 @@ Represents a CSS rule node for the [`c`](#c) helper. Keys are CSS properties or
|
|
|
92
149
|
type CRoot = Record<PropertyKey, CNode>;
|
|
93
150
|
```
|
|
94
151
|
|
|
95
|
-
Represents the root CSS object for the [`c`](#c) helper.
|
|
152
|
+
Represents the root CSS object for the [`c`](#c) helper.
|
|
153
|
+
Keys are top-level selectors or at-rules.
|
|
96
154
|
|
|
97
155
|
### HArgs
|
|
98
156
|
|
|
@@ -100,7 +158,8 @@ Represents the root CSS object for the [`c`](#c) helper. Keys are top-level sele
|
|
|
100
158
|
type HArgs = [string | Node, ...HArgs1[]];
|
|
101
159
|
```
|
|
102
160
|
|
|
103
|
-
Tuple argument type for the [`h`](#h) and [`s`](#s)
|
|
161
|
+
Tuple argument type for the [`h`](#h) and [`s`](#s)
|
|
162
|
+
helpers.
|
|
104
163
|
|
|
105
164
|
### HArgs1
|
|
106
165
|
|
|
@@ -108,7 +167,8 @@ Tuple argument type for the [`h`](#h) and [`s`](#s) helpers.
|
|
|
108
167
|
type HArgs1 = Record<PropertyKey, unknown> | null | undefined | Node | string | number | HArgs;
|
|
109
168
|
```
|
|
110
169
|
|
|
111
|
-
Single argument type for the [`h`](#h) and [`s`](#s)
|
|
170
|
+
Single argument type for the [`h`](#h) and [`s`](#s)
|
|
171
|
+
helpers.
|
|
112
172
|
|
|
113
173
|
### c
|
|
114
174
|
|
|
@@ -116,17 +176,22 @@ Single argument type for the [`h`](#h) and [`s`](#s) helpers.
|
|
|
116
176
|
const c: (root: CRoot, splitter?: string) => string;
|
|
117
177
|
```
|
|
118
178
|
|
|
119
|
-
A minimal CSS-in-JS helper
|
|
179
|
+
A minimal CSS-in-JS helper
|
|
180
|
+
that converts a JavaScript object hierarchy
|
|
181
|
+
into a CSS string.
|
|
120
182
|
|
|
121
183
|
#### root
|
|
122
184
|
|
|
123
185
|
An object describing CSS rules.
|
|
124
|
-
Keys are selectors or at-rules;
|
|
186
|
+
Keys are selectors or at-rules;
|
|
187
|
+
values are either CSS property values
|
|
188
|
+
or nested rule objects.
|
|
125
189
|
|
|
126
190
|
#### splitter
|
|
127
191
|
|
|
128
192
|
A delimiter used to create unique keys (default: `'$$'`).
|
|
129
|
-
The substring from `splitter` to the end of a key
|
|
193
|
+
The substring from `splitter` to the end of a key
|
|
194
|
+
is ignored (e.g., `src$$1` → `src`).
|
|
130
195
|
|
|
131
196
|
#### Returns
|
|
132
197
|
|
|
@@ -134,12 +199,19 @@ A CSS string representing the compiled rules.
|
|
|
134
199
|
|
|
135
200
|
#### Remarks
|
|
136
201
|
|
|
137
|
-
- Keys whose values are primitives (`string` | `number`)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
(e.g., `
|
|
142
|
-
|
|
202
|
+
- Keys whose values are primitives (`string` | `number`)
|
|
203
|
+
are treated as CSS properties.
|
|
204
|
+
- In property keys, uppercase letters
|
|
205
|
+
become lowercase with a `-` prefix
|
|
206
|
+
(e.g., `fontFamily` → `font-family`);
|
|
207
|
+
underscores become hyphens
|
|
208
|
+
(e.g., `font_family` → `font-family`).
|
|
209
|
+
- Comma-separated selector keys
|
|
210
|
+
expand into multiple selectors
|
|
211
|
+
(e.g., `{ div: { '.a,.b': { margin: 1 } } }` →
|
|
212
|
+
`div.a,div.b{margin:1}`).
|
|
213
|
+
- Top-level keys starting with `@` (at-rules)
|
|
214
|
+
are not concatenated with child selectors.
|
|
143
215
|
|
|
144
216
|
#### Usage Examples
|
|
145
217
|
|
|
@@ -333,9 +405,11 @@ expect(actual6).to.equal(expected6)
|
|
|
333
405
|
const csvParse: (csv: string, separator?: string) => string[][];
|
|
334
406
|
```
|
|
335
407
|
|
|
336
|
-
Parses a CSV string
|
|
408
|
+
Parses a CSV string
|
|
409
|
+
into a two-dimensional array of strings.
|
|
337
410
|
|
|
338
|
-
Supports quoted fields with escaped double quotes (`""`).
|
|
411
|
+
Supports quoted fields with escaped double quotes (`""`).
|
|
412
|
+
Carriage returns are normalized.
|
|
339
413
|
|
|
340
414
|
#### csv
|
|
341
415
|
|
|
@@ -347,7 +421,8 @@ The field delimiter (default: `','`).
|
|
|
347
421
|
|
|
348
422
|
#### Returns
|
|
349
423
|
|
|
350
|
-
A 2D array where each inner array
|
|
424
|
+
A 2D array where each inner array
|
|
425
|
+
represents a row of fields.
|
|
351
426
|
|
|
352
427
|
#### Usage Examples
|
|
353
428
|
|
|
@@ -374,22 +449,30 @@ expect(csvParse(text)).to.deep.equal([
|
|
|
374
449
|
const fixPlTypography: (node: Node) => void;
|
|
375
450
|
```
|
|
376
451
|
|
|
377
|
-
Applies Polish-specific typographic corrections
|
|
452
|
+
Applies Polish-specific typographic corrections
|
|
453
|
+
to a DOM subtree.
|
|
378
454
|
|
|
379
|
-
This function prevents orphaned conjunctions
|
|
380
|
-
|
|
381
|
-
|
|
455
|
+
This function prevents orphaned conjunctions
|
|
456
|
+
(single-letter words like “a”, “i”, “o”, “u”, “w”, “z”)
|
|
457
|
+
from appearing at the end of a line
|
|
458
|
+
by wrapping them with the following word
|
|
459
|
+
in a non-breaking span.
|
|
460
|
+
It also inserts zero-width spaces
|
|
461
|
+
after slashes and dots to allow line breaks.
|
|
382
462
|
|
|
383
463
|
#### node
|
|
384
464
|
|
|
385
|
-
The root DOM node to process.
|
|
386
|
-
|
|
465
|
+
The root DOM node to process.
|
|
466
|
+
All descendant text nodes are corrected recursively,
|
|
467
|
+
except those inside `IFRAME`, `NOSCRIPT`, `PRE`,
|
|
468
|
+
`SCRIPT`, `STYLE`, or `TEXTAREA` elements.
|
|
387
469
|
|
|
388
470
|
#### Usage Examples
|
|
389
471
|
|
|
390
472
|
```ts
|
|
391
473
|
const p = h('p',
|
|
392
|
-
'Pchnąć w tę łódź jeża lub ośm skrzyń fig
|
|
474
|
+
'Pchnąć w tę łódź jeża lub ośm skrzyń fig ' +
|
|
475
|
+
'(zob. https://pl.wikipedia.org/wiki/Pangram).',
|
|
393
476
|
['br'],
|
|
394
477
|
['b', 'Zażółć gęślą jaźń.'],
|
|
395
478
|
)
|
|
@@ -397,9 +480,13 @@ const p = h('p',
|
|
|
397
480
|
fixPlTypography(p)
|
|
398
481
|
|
|
399
482
|
expect(p.innerHTML).to.deep.equal(
|
|
400
|
-
'Pchnąć
|
|
401
|
-
'
|
|
402
|
-
'
|
|
483
|
+
'Pchnąć ' +
|
|
484
|
+
'<span style="white-space:nowrap">w </span>' +
|
|
485
|
+
'tę łódź jeża lub ośm skrzyń fig ' +
|
|
486
|
+
'(zob. https://\u200Bpl.\u200Bwikipedia.\u200Borg/' +
|
|
487
|
+
'\u200Bwiki/\u200BPangram).' +
|
|
488
|
+
'<br>' +
|
|
489
|
+
'<b>Zażółć gęślą jaźń.</b>')
|
|
403
490
|
```
|
|
404
491
|
|
|
405
492
|
### h
|
|
@@ -412,21 +499,28 @@ const h: {
|
|
|
412
499
|
};
|
|
413
500
|
```
|
|
414
501
|
|
|
415
|
-
A lightweight
|
|
502
|
+
A lightweight *HyperScript*-style helper
|
|
503
|
+
for creating and modifying `HTMLElement`s
|
|
504
|
+
(see also [`s`](#s)).
|
|
416
505
|
|
|
417
506
|
#### tagOrNode
|
|
418
507
|
|
|
419
|
-
If a `string`, it is treated as the tag name
|
|
508
|
+
If a `string`, it is treated as the tag name
|
|
509
|
+
for a new element.
|
|
420
510
|
If a `Node`, that node is modified in place.
|
|
421
511
|
|
|
422
512
|
#### args
|
|
423
513
|
|
|
424
514
|
Additional arguments processed as follows:
|
|
425
|
-
- `Object`: maps attributes/properties.
|
|
426
|
-
|
|
515
|
+
- `Object`: maps attributes/properties.
|
|
516
|
+
Keys starting with `$` set element properties
|
|
517
|
+
(without the `$` prefix);
|
|
518
|
+
other keys set attributes via `setAttribute`.
|
|
519
|
+
A value of `false` removes the attribute.
|
|
427
520
|
- `null`/`undefined`: ignored.
|
|
428
521
|
- `Node`: appended as a child.
|
|
429
|
-
- `string`/`number`: converted to a `Text` node
|
|
522
|
+
- `string`/`number`: converted to a `Text` node
|
|
523
|
+
and appended.
|
|
430
524
|
- [`HArgs`](#HArgs) array: processed recursively.
|
|
431
525
|
|
|
432
526
|
#### Returns
|
|
@@ -449,21 +543,32 @@ expect(b.outerHTML).to.equal('<b><i>text</i></b>')
|
|
|
449
543
|
|
|
450
544
|
h(i, { $className: 'some class' })
|
|
451
545
|
|
|
452
|
-
expect(i.outerHTML)
|
|
453
|
-
|
|
546
|
+
expect(i.outerHTML)
|
|
547
|
+
.to.equal('<i class="some class">text</i>')
|
|
548
|
+
expect(b.outerHTML)
|
|
549
|
+
.to.equal('<b><i class="some class">text</i></b>')
|
|
454
550
|
|
|
455
|
-
expect(h('span', 'text').outerHTML)
|
|
456
|
-
|
|
551
|
+
expect(h('span', 'text').outerHTML)
|
|
552
|
+
.to.equal('<span>text</span>')
|
|
553
|
+
expect(h('span', { $innerText: 'text' }).outerHTML)
|
|
554
|
+
.to.equal('<span>text</span>')
|
|
457
555
|
|
|
458
|
-
expect(h('span', '42').outerHTML)
|
|
556
|
+
expect(h('span', '42').outerHTML)
|
|
557
|
+
.to.equal('<span>42</span>')
|
|
459
558
|
expect(h('span', 42).outerHTML).to.equal('<span>42</span>')
|
|
460
559
|
|
|
461
560
|
expect(h('div', { style: 'margin:0;padding:0' }).outerHTML)
|
|
462
561
|
.to.equal('<div style="margin:0;padding:0"></div>')
|
|
463
562
|
expect(h('div', { $style: 'margin:0;padding:0' }).outerHTML)
|
|
464
|
-
.to.equal(
|
|
465
|
-
|
|
466
|
-
|
|
563
|
+
.to.equal(
|
|
564
|
+
'<div style="margin: 0px; padding: 0px;"></div>',
|
|
565
|
+
)
|
|
566
|
+
expect(
|
|
567
|
+
h(
|
|
568
|
+
'div',
|
|
569
|
+
{ $style: { margin: 0, padding: 0 } },
|
|
570
|
+
).outerHTML,
|
|
571
|
+
).to.equal('<div style="margin: 0px; padding: 0px;"></div>')
|
|
467
572
|
|
|
468
573
|
const input1 = h('input', { value: 42 })
|
|
469
574
|
const input2 = h('input', { $value: '42' })
|
|
@@ -474,14 +579,22 @@ expect(input2.value).to.equal('42')
|
|
|
474
579
|
expect(input1.outerHTML).to.equal('<input value="42">')
|
|
475
580
|
expect(input2.outerHTML).to.equal('<input>')
|
|
476
581
|
|
|
477
|
-
const checkbox1 = h(
|
|
478
|
-
|
|
582
|
+
const checkbox1 = h(
|
|
583
|
+
'input',
|
|
584
|
+
{ type: 'checkbox', checked: true },
|
|
585
|
+
)
|
|
586
|
+
const checkbox2 = h(
|
|
587
|
+
'input',
|
|
588
|
+
{ type: 'checkbox', $checked: true },
|
|
589
|
+
)
|
|
479
590
|
|
|
480
591
|
expect(checkbox1.checked).to.be.true
|
|
481
592
|
expect(checkbox2.checked).to.be.true
|
|
482
593
|
|
|
483
|
-
expect(checkbox1.outerHTML)
|
|
484
|
-
|
|
594
|
+
expect(checkbox1.outerHTML)
|
|
595
|
+
.to.equal('<input type="checkbox" checked="">')
|
|
596
|
+
expect(checkbox2.outerHTML)
|
|
597
|
+
.to.equal('<input type="checkbox">')
|
|
485
598
|
|
|
486
599
|
const div = h('div')
|
|
487
600
|
|
|
@@ -502,11 +615,15 @@ expect(elemWithClass.getAttribute('class')).to.equal('test')
|
|
|
502
615
|
const elemWithText = h('div', 'initial')
|
|
503
616
|
|
|
504
617
|
h(elemWithText, ' more')
|
|
505
|
-
expect(elemWithText.outerHTML)
|
|
618
|
+
expect(elemWithText.outerHTML)
|
|
619
|
+
.to.equal('<div>initial more</div>')
|
|
506
620
|
|
|
507
|
-
const elemWithNested = h('div',
|
|
621
|
+
const elemWithNested = h('div',
|
|
622
|
+
['span', 'hello'],
|
|
623
|
+
['b', 'world'])
|
|
508
624
|
|
|
509
|
-
expect(elemWithNested.outerHTML)
|
|
625
|
+
expect(elemWithNested.outerHTML)
|
|
626
|
+
.to.equal('<div><span>hello</span><b>world</b></div>')
|
|
510
627
|
```
|
|
511
628
|
|
|
512
629
|
### hasOwn
|
|
@@ -515,7 +632,8 @@ expect(elemWithNested.outerHTML).to.equal('<div><span>hello</span><b>world</b></
|
|
|
515
632
|
const hasOwn: (ref: unknown, key: unknown) => boolean;
|
|
516
633
|
```
|
|
517
634
|
|
|
518
|
-
Checks whether an object has the specified key
|
|
635
|
+
Checks whether an object has the specified key
|
|
636
|
+
as its own property.
|
|
519
637
|
|
|
520
638
|
A null-safe wrapper around `Object.hasOwn`.
|
|
521
639
|
|
|
@@ -529,7 +647,8 @@ The property key to look for.
|
|
|
529
647
|
|
|
530
648
|
#### Returns
|
|
531
649
|
|
|
532
|
-
`true` if `ref` is not nullish
|
|
650
|
+
`true` if `ref` is not nullish
|
|
651
|
+
and has `key` as an own property, `false` otherwise.
|
|
533
652
|
|
|
534
653
|
#### Usage Examples
|
|
535
654
|
|
|
@@ -584,8 +703,15 @@ The value to check.
|
|
|
584
703
|
|
|
585
704
|
```ts
|
|
586
705
|
expect(isArray([])).to.be.true
|
|
587
|
-
|
|
588
|
-
|
|
706
|
+
|
|
707
|
+
const fakeArray1 = Object.create({ constructor: Array })
|
|
708
|
+
|
|
709
|
+
expect(isArray(fakeArray1)).to.be.false
|
|
710
|
+
|
|
711
|
+
const fakeArray2 =
|
|
712
|
+
Object.create({ [Symbol.toStringTag]: Array.name })
|
|
713
|
+
|
|
714
|
+
expect(isArray(fakeArray2)).to.be.false
|
|
589
715
|
```
|
|
590
716
|
|
|
591
717
|
### isFiniteNumber
|
|
@@ -594,7 +720,8 @@ expect(isArray(Object.create({ [Symbol.toStringTag]: Array.name }))).to.be.false
|
|
|
594
720
|
const isFiniteNumber: (arg: unknown) => arg is number;
|
|
595
721
|
```
|
|
596
722
|
|
|
597
|
-
Checks whether the argument is a finite number
|
|
723
|
+
Checks whether the argument is a finite number
|
|
724
|
+
(excludes `±Infinity` and `NaN`).
|
|
598
725
|
|
|
599
726
|
#### arg
|
|
600
727
|
|
|
@@ -635,7 +762,8 @@ The value to check.
|
|
|
635
762
|
```ts
|
|
636
763
|
expect(isInteger(42)).to.be.true
|
|
637
764
|
expect(isInteger(42.00000000000001)).to.be.false
|
|
638
|
-
expect(isInteger(42.000000000000001)).to.be.true
|
|
765
|
+
expect(isInteger(42.000000000000001)).to.be.true
|
|
766
|
+
// ^because of loss of precision
|
|
639
767
|
expect(isInteger(Number(42))).to.be.true
|
|
640
768
|
expect(isInteger(new Number(42))).to.be.false
|
|
641
769
|
expect(isInteger(NaN)).to.be.false
|
|
@@ -648,7 +776,8 @@ expect(isInteger(Infinity)).to.be.false
|
|
|
648
776
|
const isNumber: (arg: unknown) => arg is number;
|
|
649
777
|
```
|
|
650
778
|
|
|
651
|
-
Checks whether the argument is of type `number`
|
|
779
|
+
Checks whether the argument is of type `number`
|
|
780
|
+
(includes `NaN` and `±Infinity`).
|
|
652
781
|
|
|
653
782
|
#### arg
|
|
654
783
|
|
|
@@ -674,7 +803,8 @@ expect(isNumber(Infinity)).to.be.true
|
|
|
674
803
|
const isRecord: (arg: unknown) => arg is Record<PropertyKey, unknown>;
|
|
675
804
|
```
|
|
676
805
|
|
|
677
|
-
Checks whether the argument is a plain object
|
|
806
|
+
Checks whether the argument is a plain object
|
|
807
|
+
(not `null` and not an array).
|
|
678
808
|
|
|
679
809
|
#### arg
|
|
680
810
|
|
|
@@ -690,8 +820,16 @@ The value to check.
|
|
|
690
820
|
expect(isRecord({})).to.be.true
|
|
691
821
|
expect(isRecord([])).to.be.false
|
|
692
822
|
expect(isRecord(Object.create(null))).to.be.true
|
|
693
|
-
|
|
694
|
-
|
|
823
|
+
|
|
824
|
+
const fakeNumber1 = Object.create({ constructor: Number })
|
|
825
|
+
|
|
826
|
+
expect(isRecord(fakeNumber1)).to.be.true
|
|
827
|
+
|
|
828
|
+
const fakeNumber2 =
|
|
829
|
+
Object.create({ [Symbol.toStringTag]: Number.name })
|
|
830
|
+
|
|
831
|
+
expect(isRecord(fakeNumber2)).to.be.true
|
|
832
|
+
|
|
695
833
|
expect(isRecord(new Number(42))).to.be.true
|
|
696
834
|
expect(isRecord(new String('42'))).to.be.true
|
|
697
835
|
|
|
@@ -730,10 +868,14 @@ expect(isString(new String('42'))).to.be.false
|
|
|
730
868
|
const jsOnParse: (handlers: Record<PropertyKey, Function>, text: string) => any;
|
|
731
869
|
```
|
|
732
870
|
|
|
733
|
-
Parses JSON with support for
|
|
871
|
+
Parses JSON with support for
|
|
872
|
+
handler-based value transformation (“JavaScript ON”).
|
|
734
873
|
|
|
735
|
-
Objects with exactly one property
|
|
736
|
-
|
|
874
|
+
Objects with exactly one property
|
|
875
|
+
whose key exists in `handlers`
|
|
876
|
+
and whose value is an array
|
|
877
|
+
are replaced by invoking the corresponding handler
|
|
878
|
+
with the array elements as arguments.
|
|
737
879
|
|
|
738
880
|
#### handlers
|
|
739
881
|
|
|
@@ -790,23 +932,37 @@ expect(actual1).to.deep.equal([
|
|
|
790
932
|
},
|
|
791
933
|
'bar',
|
|
792
934
|
{
|
|
793
|
-
$foo: [
|
|
935
|
+
$foo: [
|
|
936
|
+
'The parent object does not have ' +
|
|
937
|
+
'exactly one property!',
|
|
938
|
+
],
|
|
794
939
|
one: 1,
|
|
795
940
|
two: 2,
|
|
796
941
|
},
|
|
797
942
|
])
|
|
798
943
|
|
|
799
|
-
const actual2 = jsOnParse(
|
|
944
|
+
const actual2 = jsOnParse(
|
|
945
|
+
{ $notFunc: 'handler not being a function' } as any,
|
|
946
|
+
'{"$notFunc": [1, 2]}',
|
|
947
|
+
)
|
|
800
948
|
|
|
801
949
|
expect(actual2).to.deep.equal({ $notFunc: [1, 2] })
|
|
802
950
|
|
|
803
|
-
const actual3 = jsOnParse(
|
|
951
|
+
const actual3 = jsOnParse(
|
|
952
|
+
handlers,
|
|
953
|
+
'{"$unknown_handler_key": [1, 2]}',
|
|
954
|
+
)
|
|
804
955
|
|
|
805
|
-
expect(actual3)
|
|
956
|
+
expect(actual3)
|
|
957
|
+
.to.deep.equal({ $unknown_handler_key: [1, 2] })
|
|
806
958
|
|
|
807
|
-
const actual4 = jsOnParse(
|
|
959
|
+
const actual4 = jsOnParse(
|
|
960
|
+
handlers,
|
|
961
|
+
'{"$add": {"not": "array"}}',
|
|
962
|
+
)
|
|
808
963
|
|
|
809
|
-
expect(actual4)
|
|
964
|
+
expect(actual4)
|
|
965
|
+
.to.deep.equal({ $add: { not: 'array' } })
|
|
810
966
|
```
|
|
811
967
|
|
|
812
968
|
### monokai
|
|
@@ -815,7 +971,9 @@ expect(actual4).to.deep.equal({ $add: { not: 'array' } })
|
|
|
815
971
|
const monokai: CRoot;
|
|
816
972
|
```
|
|
817
973
|
|
|
818
|
-
A Monokai-inspired color scheme
|
|
974
|
+
A Monokai-inspired color scheme
|
|
975
|
+
for use with the [`c`](#c) helper
|
|
976
|
+
and [`nanolightTs`](#nanolightTs) tokenizer.
|
|
819
977
|
|
|
820
978
|
### nanolightTs
|
|
821
979
|
|
|
@@ -823,7 +981,8 @@ A Monokai-inspired color scheme for use with the [`c`](#c) helper and [`nanoligh
|
|
|
823
981
|
const nanolightTs: (code: string) => HArgs1[];
|
|
824
982
|
```
|
|
825
983
|
|
|
826
|
-
A TypeScript/JavaScript syntax highlighting tokenizer
|
|
984
|
+
A TypeScript/JavaScript syntax highlighting tokenizer
|
|
985
|
+
built using [`newTokenizer`](#newTokenizer).
|
|
827
986
|
|
|
828
987
|
#### code
|
|
829
988
|
|
|
@@ -831,17 +990,23 @@ The source code string to tokenize.
|
|
|
831
990
|
|
|
832
991
|
#### Returns
|
|
833
992
|
|
|
834
|
-
An array of [`HArgs1`](#HArgs1) elements
|
|
993
|
+
An array of [`HArgs1`](#HArgs1) elements
|
|
994
|
+
suitable for rendering with [`h`](#h).
|
|
835
995
|
|
|
836
996
|
#### Usage Examples
|
|
837
997
|
|
|
838
998
|
```ts
|
|
839
|
-
const codeJs =
|
|
999
|
+
const codeJs =
|
|
1000
|
+
'const answerToLifeTheUniverseAndEverything = ' +
|
|
1001
|
+
"{ 42: 42 }['42'] /* 42 */"
|
|
840
1002
|
|
|
841
1003
|
expect(nanolightTs(codeJs)).to.deep.equal([
|
|
842
1004
|
['span', { class: 'keyword-1' }, 'const'],
|
|
843
1005
|
' ',
|
|
844
|
-
['span',
|
|
1006
|
+
['span',
|
|
1007
|
+
{ class: 'identifier-4' },
|
|
1008
|
+
'answerToLifeTheUniverseAndEverything',
|
|
1009
|
+
],
|
|
845
1010
|
' ',
|
|
846
1011
|
['span', { class: 'operator' }, '='],
|
|
847
1012
|
' ',
|
|
@@ -867,15 +1032,18 @@ expect(nanolightTs(codeJs)).to.deep.equal([
|
|
|
867
1032
|
const newEscape: (escapeFn: (value: any) => string) => (template: TemplateStringsArray, ...values: unknown[]) => string;
|
|
868
1033
|
```
|
|
869
1034
|
|
|
870
|
-
Creates a tag function
|
|
1035
|
+
Creates a tag function
|
|
1036
|
+
for escaping interpolated values in template literals.
|
|
871
1037
|
|
|
872
1038
|
#### escapeFn
|
|
873
1039
|
|
|
874
|
-
A function that takes a value
|
|
1040
|
+
A function that takes a value
|
|
1041
|
+
and returns its escaped string representation.
|
|
875
1042
|
|
|
876
1043
|
#### Returns
|
|
877
1044
|
|
|
878
|
-
A tag function that escapes interpolated values
|
|
1045
|
+
A tag function that escapes interpolated values
|
|
1046
|
+
using the provided escape map.
|
|
879
1047
|
|
|
880
1048
|
#### Usage Examples
|
|
881
1049
|
|
|
@@ -898,7 +1066,18 @@ const sql = newEscape(escapeFn)
|
|
|
898
1066
|
const actual = sql`
|
|
899
1067
|
SELECT *
|
|
900
1068
|
FROM table_name
|
|
901
|
-
WHERE column_name IN (${[
|
|
1069
|
+
WHERE column_name IN (${[
|
|
1070
|
+
true,
|
|
1071
|
+
null,
|
|
1072
|
+
undefined,
|
|
1073
|
+
NaN,
|
|
1074
|
+
Infinity,
|
|
1075
|
+
42,
|
|
1076
|
+
'42',
|
|
1077
|
+
"4'2",
|
|
1078
|
+
/42/,
|
|
1079
|
+
new Date(323325000000),
|
|
1080
|
+
]})`
|
|
902
1081
|
|
|
903
1082
|
const expected = `
|
|
904
1083
|
SELECT *
|
|
@@ -914,14 +1093,20 @@ expect(actual).to.equal(expected)
|
|
|
914
1093
|
const newNounForm: (locale: string, forms: Partial<Record<Intl.LDMLPluralRule, string>>) => (value: number) => string;
|
|
915
1094
|
```
|
|
916
1095
|
|
|
917
|
-
Creates a function that
|
|
1096
|
+
Creates a function that
|
|
1097
|
+
returns the appropriate noun form
|
|
1098
|
+
based on a numeric value using `Intl.PluralRules`.
|
|
918
1099
|
|
|
919
|
-
Different languages have different plural rules.
|
|
1100
|
+
Different languages have different plural rules.
|
|
1101
|
+
The `Intl.PluralRules` API provides locale-aware
|
|
1102
|
+
plural category selection.
|
|
920
1103
|
Possible categories are:
|
|
921
1104
|
|
|
922
|
-
- `zero`: for zero items
|
|
1105
|
+
- `zero`: for zero items
|
|
1106
|
+
(used in some languages like Arabic, Latvian)
|
|
923
1107
|
- `one`: for singular (e.g., 1 item)
|
|
924
|
-
- `two`: for dual
|
|
1108
|
+
- `two`: for dual
|
|
1109
|
+
(used in some languages like Arabic, Hebrew)
|
|
925
1110
|
- `few`: for small plurals (e.g., 2-4 in Polish)
|
|
926
1111
|
- `many`: for larger plurals (e.g., 5-21 in Polish)
|
|
927
1112
|
- `other`: fallback category (used by all languages)
|
|
@@ -932,17 +1117,24 @@ A BCP 47 language tag (e.g., `pl`, `en`).
|
|
|
932
1117
|
|
|
933
1118
|
#### forms
|
|
934
1119
|
|
|
935
|
-
An object mapping plural categories to noun forms.
|
|
936
|
-
|
|
1120
|
+
An object mapping plural categories to noun forms.
|
|
1121
|
+
Not all categories need to be provided;
|
|
1122
|
+
if a category is missing,
|
|
1123
|
+
the function falls back to `other`,
|
|
1124
|
+
then to an empty string.
|
|
937
1125
|
|
|
938
1126
|
#### Returns
|
|
939
1127
|
|
|
940
|
-
A function that takes a numeric value
|
|
1128
|
+
A function that takes a numeric value
|
|
1129
|
+
and returns the appropriate noun form.
|
|
941
1130
|
|
|
942
1131
|
#### Usage Examples
|
|
943
1132
|
|
|
944
1133
|
```ts
|
|
945
|
-
const auto = newNounForm(
|
|
1134
|
+
const auto = newNounForm(
|
|
1135
|
+
'pl',
|
|
1136
|
+
{ one: 'auto', few: 'auta', other: 'aut' },
|
|
1137
|
+
)
|
|
946
1138
|
|
|
947
1139
|
expect(auto(0)).to.equal('aut')
|
|
948
1140
|
expect(auto(1)).to.equal('auto')
|
|
@@ -970,29 +1162,39 @@ expect(empty(42)).to.equal('')
|
|
|
970
1162
|
const newTokenizer: <M, T>(decorator: (chunk: string, metadata?: M) => T, ...specs: [M, string | RegExp][]) => (code: string) => T[];
|
|
971
1163
|
```
|
|
972
1164
|
|
|
973
|
-
A helper for building simple tokenizers
|
|
1165
|
+
A helper for building simple tokenizers
|
|
1166
|
+
(see also [`nanolightTs`](#nanolightTs)).
|
|
974
1167
|
|
|
975
1168
|
#### decorator
|
|
976
1169
|
|
|
977
|
-
A function that wraps each matched chunk.
|
|
978
|
-
|
|
979
|
-
|
|
1170
|
+
A function that wraps each matched chunk.
|
|
1171
|
+
It receives the matched text (`chunk`)
|
|
1172
|
+
and optionally the `metadata`
|
|
1173
|
+
associated with the pattern that produced the match.
|
|
1174
|
+
For unmatched text between patterns,
|
|
1175
|
+
`metadata` is `undefined`.
|
|
980
1176
|
|
|
981
1177
|
#### specs
|
|
982
1178
|
|
|
983
1179
|
An array of tuples `[metadata, pattern]` where:
|
|
984
|
-
- `metadata`: arbitrary data (e.g., a CSS class name)
|
|
985
|
-
|
|
1180
|
+
- `metadata`: arbitrary data (e.g., a CSS class name)
|
|
1181
|
+
passed to `decorator` when the pattern matches.
|
|
1182
|
+
- `pattern`: a `string` or `RegExp`
|
|
1183
|
+
to match against the input.
|
|
986
1184
|
|
|
987
1185
|
#### Returns
|
|
988
1186
|
|
|
989
|
-
A tokenizer function that accepts a code string
|
|
1187
|
+
A tokenizer function that accepts a code string
|
|
1188
|
+
and returns an array of decorated tokens.
|
|
990
1189
|
|
|
991
1190
|
#### Remarks
|
|
992
1191
|
|
|
993
|
-
1. Matches starting at an earlier position
|
|
994
|
-
|
|
995
|
-
|
|
1192
|
+
1. Matches starting at an earlier position
|
|
1193
|
+
take precedence.
|
|
1194
|
+
2. Among matches at the same position,
|
|
1195
|
+
the longer one wins.
|
|
1196
|
+
3. Among matches of the same position and length,
|
|
1197
|
+
the one defined earlier wins.
|
|
996
1198
|
|
|
997
1199
|
#### Usage Examples
|
|
998
1200
|
|
|
@@ -1021,7 +1223,13 @@ const tokenizer2 = newTokenizer(
|
|
|
1021
1223
|
)
|
|
1022
1224
|
const result2 = tokenizer2('aBEGINbENDc')
|
|
1023
1225
|
|
|
1024
|
-
expect(result2).to.deep.equal([
|
|
1226
|
+
expect(result2).to.deep.equal([
|
|
1227
|
+
'undefined:a',
|
|
1228
|
+
'tag:BEGIN',
|
|
1229
|
+
'undefined:b',
|
|
1230
|
+
'end:END',
|
|
1231
|
+
'undefined:c',
|
|
1232
|
+
])
|
|
1025
1233
|
|
|
1026
1234
|
const tokenizer3 = newTokenizer(
|
|
1027
1235
|
(chunk) => chunk,
|
|
@@ -1078,7 +1286,7 @@ const result8 = tokenizer8('abc')
|
|
|
1078
1286
|
expect(result8).to.deep.equal(['abc'])
|
|
1079
1287
|
|
|
1080
1288
|
const tokenizer9 = newTokenizer(
|
|
1081
|
-
(
|
|
1289
|
+
(_chunk, metadata) => metadata,
|
|
1082
1290
|
['a', 'a'],
|
|
1083
1291
|
['b', 'b'],
|
|
1084
1292
|
)
|
|
@@ -1093,9 +1301,12 @@ expect(result9).to.deep.equal(['a', 'a', 'b', 'b'])
|
|
|
1093
1301
|
const omit: <T, K extends keyof T>(ref: T, keys: unknown[]) => Omit<T, K>;
|
|
1094
1302
|
```
|
|
1095
1303
|
|
|
1096
|
-
Creates a new object excluding the specified keys
|
|
1304
|
+
Creates a new object excluding the specified keys
|
|
1305
|
+
from the source object.
|
|
1097
1306
|
|
|
1098
|
-
A runtime equivalent
|
|
1307
|
+
A runtime equivalent
|
|
1308
|
+
of TypeScript’s `Omit<T, K>` utility type.
|
|
1309
|
+
See also [`pick`](#pick).
|
|
1099
1310
|
|
|
1100
1311
|
#### ref
|
|
1101
1312
|
|
|
@@ -1120,12 +1331,15 @@ expect(omit(ref, ['c'])).to.deep.equal({ a: 42, b: '42' })
|
|
|
1120
1331
|
### pick
|
|
1121
1332
|
|
|
1122
1333
|
```ts
|
|
1123
|
-
const pick: <T, K extends keyof T>(ref: T, keys:
|
|
1334
|
+
const pick: <T, K extends keyof T>(ref: T, keys: unknown[]) => Pick<T, K>;
|
|
1124
1335
|
```
|
|
1125
1336
|
|
|
1126
|
-
Creates a new object containing only the specified keys
|
|
1337
|
+
Creates a new object containing only the specified keys
|
|
1338
|
+
from the source object.
|
|
1127
1339
|
|
|
1128
|
-
A runtime equivalent
|
|
1340
|
+
A runtime equivalent
|
|
1341
|
+
of TypeScript’s `Pick<T, K>` utility type.
|
|
1342
|
+
See also [`omit`](#omit).
|
|
1129
1343
|
|
|
1130
1344
|
#### ref
|
|
1131
1345
|
|
|
@@ -1144,7 +1358,9 @@ A new object with only the specified keys.
|
|
|
1144
1358
|
```ts
|
|
1145
1359
|
const ref = { a: 42, b: '42', c: 17 }
|
|
1146
1360
|
|
|
1147
|
-
expect(pick(ref, ['a', 'b'])).to.deep.equal(
|
|
1361
|
+
expect(pick(ref, ['a', 'b'])).to.deep.equal(
|
|
1362
|
+
{ a: 42, b: '42' },
|
|
1363
|
+
)
|
|
1148
1364
|
```
|
|
1149
1365
|
|
|
1150
1366
|
### rwd
|
|
@@ -1153,7 +1369,8 @@ expect(pick(ref, ['a', 'b'])).to.deep.equal({ a: 42, b: '42' })
|
|
|
1153
1369
|
const rwd: (root: CRoot, selector: string, cellWidthPx: number, cellHeightPx: number, ...specs: [number, number?, number?][]) => void;
|
|
1154
1370
|
```
|
|
1155
1371
|
|
|
1156
|
-
A responsive web design helper
|
|
1372
|
+
A responsive web design helper
|
|
1373
|
+
that generates CSS rules for a grid-like layout.
|
|
1157
1374
|
|
|
1158
1375
|
#### root
|
|
1159
1376
|
|
|
@@ -1174,9 +1391,12 @@ The base cell height in pixels.
|
|
|
1174
1391
|
#### specs
|
|
1175
1392
|
|
|
1176
1393
|
An array of breakpoint specifications, each a tuple of:
|
|
1177
|
-
- `maxWidth`: maximum number of cells per row
|
|
1178
|
-
|
|
1179
|
-
- `
|
|
1394
|
+
- `maxWidth`: maximum number of cells per row
|
|
1395
|
+
(defines the viewport breakpoint).
|
|
1396
|
+
- `width` (optional, default `1`):
|
|
1397
|
+
number of horizontal cells the element spans.
|
|
1398
|
+
- `height` (optional, default `1`):
|
|
1399
|
+
number of vertical cells the element spans.
|
|
1180
1400
|
|
|
1181
1401
|
#### Usage Examples
|
|
1182
1402
|
|
|
@@ -1195,34 +1415,36 @@ const style: CRoot = {
|
|
|
1195
1415
|
|
|
1196
1416
|
rwd(style, '.r6', 200, 50, [6], [3], [1, 1, 2])
|
|
1197
1417
|
|
|
1198
|
-
expect(style).to.deep.equal(
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
'.r6': {
|
|
1203
|
-
border: 'solid red 1px',
|
|
1204
|
-
'.no-border': {
|
|
1205
|
-
border: 'none',
|
|
1418
|
+
expect(style).to.deep.equal(
|
|
1419
|
+
{
|
|
1420
|
+
body: {
|
|
1421
|
+
margin: 0,
|
|
1206
1422
|
},
|
|
1207
|
-
boxSizing: 'border-box',
|
|
1208
|
-
display: 'block',
|
|
1209
|
-
float: 'left',
|
|
1210
|
-
width: '100%',
|
|
1211
|
-
height: '100px',
|
|
1212
|
-
},
|
|
1213
|
-
'@media(min-width:600px)': {
|
|
1214
1423
|
'.r6': {
|
|
1215
|
-
|
|
1216
|
-
|
|
1424
|
+
border: 'solid red 1px',
|
|
1425
|
+
'.no-border': {
|
|
1426
|
+
border: 'none',
|
|
1427
|
+
},
|
|
1428
|
+
boxSizing: 'border-box',
|
|
1429
|
+
display: 'block',
|
|
1430
|
+
float: 'left',
|
|
1431
|
+
width: '100%',
|
|
1432
|
+
height: '100px',
|
|
1217
1433
|
},
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1434
|
+
'@media(min-width:600px)': {
|
|
1435
|
+
'.r6': {
|
|
1436
|
+
width: 'calc(100% / 3)',
|
|
1437
|
+
height: '50px',
|
|
1438
|
+
},
|
|
1439
|
+
},
|
|
1440
|
+
'@media(min-width:1200px)': {
|
|
1441
|
+
'.r6': {
|
|
1442
|
+
width: 'calc(50% / 3)',
|
|
1443
|
+
height: '50px',
|
|
1444
|
+
},
|
|
1223
1445
|
},
|
|
1224
1446
|
},
|
|
1225
|
-
|
|
1447
|
+
)
|
|
1226
1448
|
```
|
|
1227
1449
|
|
|
1228
1450
|
### s
|
|
@@ -1235,21 +1457,28 @@ const s: {
|
|
|
1235
1457
|
};
|
|
1236
1458
|
```
|
|
1237
1459
|
|
|
1238
|
-
A lightweight
|
|
1460
|
+
A lightweight *HyperScript*-style helper
|
|
1461
|
+
for creating and modifying `SVGElement`s
|
|
1462
|
+
(see also [`h`](#h)).
|
|
1239
1463
|
|
|
1240
1464
|
#### tagOrNode
|
|
1241
1465
|
|
|
1242
|
-
If a `string`, it is treated as the tag name
|
|
1466
|
+
If a `string`, it is treated as the tag name
|
|
1467
|
+
for a new element.
|
|
1243
1468
|
If a `Node`, that node is modified in place.
|
|
1244
1469
|
|
|
1245
1470
|
#### args
|
|
1246
1471
|
|
|
1247
1472
|
Additional arguments processed as follows:
|
|
1248
|
-
- `Object`: maps attributes/properties.
|
|
1249
|
-
|
|
1473
|
+
- `Object`: maps attributes/properties.
|
|
1474
|
+
Keys starting with `$` set element properties
|
|
1475
|
+
(without the `$` prefix);
|
|
1476
|
+
other keys set attributes via `setAttributeNS`.
|
|
1477
|
+
A value of `false` removes the attribute.
|
|
1250
1478
|
- `null`/`undefined`: ignored.
|
|
1251
1479
|
- `Node`: appended as a child.
|
|
1252
|
-
- `string`/`number`: converted to a `Text` node
|
|
1480
|
+
- `string`/`number`: converted to a `Text` node
|
|
1481
|
+
and appended.
|
|
1253
1482
|
- [`HArgs`](#HArgs) array: processed recursively.
|
|
1254
1483
|
|
|
1255
1484
|
#### Returns
|
|
@@ -1259,25 +1488,28 @@ The created or modified `SVGElement`.
|
|
|
1259
1488
|
#### Usage Examples
|
|
1260
1489
|
|
|
1261
1490
|
```ts
|
|
1262
|
-
const
|
|
1491
|
+
const XLINK_NS = 'http://www.w3.org/1999/xlink'
|
|
1263
1492
|
|
|
1264
|
-
s(svg, { 'xlink:href': true })
|
|
1265
|
-
expect(svg.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).to.equal('')
|
|
1493
|
+
const svg1 = s('svg', { 'xlink:href': true })
|
|
1266
1494
|
|
|
1267
|
-
|
|
1495
|
+
expect(svg1.getAttributeNS(XLINK_NS, 'href')).to.equal('')
|
|
1268
1496
|
|
|
1269
|
-
s(
|
|
1270
|
-
expect(svg2.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).to.be.null
|
|
1497
|
+
const svg2 = s('svg', { 'xlink:href': false })
|
|
1271
1498
|
|
|
1272
|
-
|
|
1499
|
+
expect(svg2.getAttributeNS(XLINK_NS, 'href')).to.be.null
|
|
1273
1500
|
|
|
1274
|
-
|
|
1275
|
-
|
|
1501
|
+
const svg3 = s(
|
|
1502
|
+
'svg',
|
|
1503
|
+
{ 'xlink:href': 'http://example.com' },
|
|
1504
|
+
)
|
|
1505
|
+
|
|
1506
|
+
expect(svg3.getAttributeNS(XLINK_NS, 'href'))
|
|
1507
|
+
.to.equal('http://example.com')
|
|
1276
1508
|
|
|
1277
|
-
const svg4 = s('svg')
|
|
1509
|
+
const svg4 = s('svg', { 'xlink:title': 42 })
|
|
1278
1510
|
|
|
1279
|
-
|
|
1280
|
-
|
|
1511
|
+
expect(svg4.getAttributeNS(XLINK_NS, 'title'))
|
|
1512
|
+
.to.equal('42')
|
|
1281
1513
|
```
|
|
1282
1514
|
|
|
1283
1515
|
### svgUse
|
|
@@ -1286,13 +1518,16 @@ expect(svg4.getAttributeNS('http://www.w3.org/1999/xlink', 'title')).to.equal('4
|
|
|
1286
1518
|
const svgUse: (id: string, ...args: HArgs1[]) => SVGSVGElement;
|
|
1287
1519
|
```
|
|
1288
1520
|
|
|
1289
|
-
Shorthand for creating an SVG element
|
|
1521
|
+
Shorthand for creating an SVG element
|
|
1522
|
+
with a `<use>` child referencing an icon by ID.
|
|
1290
1523
|
|
|
1291
|
-
Equivalent to:
|
|
1524
|
+
Equivalent to:
|
|
1525
|
+
`s('svg', ['use', { 'xlink:href': '#' + id }], ...args)`.
|
|
1292
1526
|
|
|
1293
1527
|
#### id
|
|
1294
1528
|
|
|
1295
|
-
The ID of the symbol to reference
|
|
1529
|
+
The ID of the symbol to reference
|
|
1530
|
+
(without the `#` prefix).
|
|
1296
1531
|
|
|
1297
1532
|
#### args
|
|
1298
1533
|
|
|
@@ -1309,40 +1544,55 @@ const XLINK_NS = 'http://www.w3.org/1999/xlink'
|
|
|
1309
1544
|
|
|
1310
1545
|
const svg = svgUse('icon-home')
|
|
1311
1546
|
|
|
1312
|
-
expect(svg.tagName).to.equal('SVG')
|
|
1313
1547
|
expect(svg.children.length).to.equal(1)
|
|
1314
1548
|
|
|
1315
1549
|
const useElement = svg.children[0]
|
|
1316
1550
|
|
|
1317
|
-
expect(useElement.
|
|
1318
|
-
|
|
1551
|
+
expect(useElement.getAttributeNS(XLINK_NS, 'href'))
|
|
1552
|
+
.to.equal('#icon-home')
|
|
1319
1553
|
|
|
1320
|
-
const svgWithViewBox =
|
|
1554
|
+
const svgWithViewBox =
|
|
1555
|
+
svgUse('icon-star', { viewBox: '0 0 24 24' })
|
|
1321
1556
|
|
|
1322
|
-
expect(svgWithViewBox.getAttribute('viewBox'))
|
|
1557
|
+
expect(svgWithViewBox.getAttribute('viewBox'))
|
|
1558
|
+
.to.equal('0 0 24 24')
|
|
1323
1559
|
|
|
1324
1560
|
const useViewBox = svgWithViewBox.children[0]
|
|
1325
1561
|
|
|
1326
|
-
expect(useViewBox.getAttributeNS(XLINK_NS, 'href'))
|
|
1562
|
+
expect(useViewBox.getAttributeNS(XLINK_NS, 'href'))
|
|
1563
|
+
.to.equal('#icon-star')
|
|
1564
|
+
|
|
1565
|
+
const svgWithClass =
|
|
1566
|
+
svgUse('icon-menu', { class: 'icon-btn' })
|
|
1327
1567
|
|
|
1328
|
-
|
|
1568
|
+
expect(svgWithClass.getAttribute('class'))
|
|
1569
|
+
.to.equal('icon-btn')
|
|
1329
1570
|
|
|
1330
|
-
expect(svgWithClass.getAttribute('class')).to.equal('icon-btn')
|
|
1331
1571
|
expect(svgWithClass.children.length).to.equal(1)
|
|
1332
1572
|
|
|
1333
1573
|
const useClass = svgWithClass.children[0]
|
|
1334
1574
|
|
|
1335
|
-
expect(useClass.getAttributeNS(XLINK_NS, 'href'))
|
|
1575
|
+
expect(useClass.getAttributeNS(XLINK_NS, 'href'))
|
|
1576
|
+
.to.equal('#icon-menu')
|
|
1336
1577
|
|
|
1337
|
-
const svgWithMultipleAttrs = svgUse(
|
|
1578
|
+
const svgWithMultipleAttrs = svgUse(
|
|
1579
|
+
'icon-settings',
|
|
1580
|
+
{ width: 24, height: 24, class: 'icon' },
|
|
1581
|
+
)
|
|
1582
|
+
|
|
1583
|
+
expect(svgWithMultipleAttrs.getAttribute('width'))
|
|
1584
|
+
.to.equal('24')
|
|
1585
|
+
|
|
1586
|
+
expect(svgWithMultipleAttrs.getAttribute('height'))
|
|
1587
|
+
.to.equal('24')
|
|
1338
1588
|
|
|
1339
|
-
expect(svgWithMultipleAttrs.getAttribute('
|
|
1340
|
-
|
|
1341
|
-
expect(svgWithMultipleAttrs.getAttribute('class')).to.equal('icon')
|
|
1589
|
+
expect(svgWithMultipleAttrs.getAttribute('class'))
|
|
1590
|
+
.to.equal('icon')
|
|
1342
1591
|
|
|
1343
1592
|
const useMultiple = svgWithMultipleAttrs.children[0]
|
|
1344
1593
|
|
|
1345
|
-
expect(useMultiple.getAttributeNS(XLINK_NS, 'href'))
|
|
1594
|
+
expect(useMultiple.getAttributeNS(XLINK_NS, 'href'))
|
|
1595
|
+
.to.equal('#icon-settings')
|
|
1346
1596
|
```
|
|
1347
1597
|
|
|
1348
1598
|
### uuidV1
|
|
@@ -1355,16 +1605,21 @@ Generates a UUID v1 (time-based) identifier.
|
|
|
1355
1605
|
|
|
1356
1606
|
#### date
|
|
1357
1607
|
|
|
1358
|
-
The date to use for the timestamp portion
|
|
1608
|
+
The date to use for the timestamp portion
|
|
1609
|
+
(default: current date/time).
|
|
1359
1610
|
|
|
1360
1611
|
#### node
|
|
1361
1612
|
|
|
1362
|
-
A hexadecimal `string` for the node portion
|
|
1363
|
-
|
|
1613
|
+
A hexadecimal `string` for the node portion
|
|
1614
|
+
(default: random).
|
|
1615
|
+
Must match `/^[0-9a-f]*$/`;
|
|
1616
|
+
it is trimmed to the last 12 characters
|
|
1617
|
+
and left-padded with zeros if shorter.
|
|
1364
1618
|
|
|
1365
1619
|
#### Returns
|
|
1366
1620
|
|
|
1367
|
-
A UUID v1 `string` in the standard format
|
|
1621
|
+
A UUID v1 `string` in the standard format
|
|
1622
|
+
`xxxxxxxx-xxxx-1xxx-xxxx-xxxxxxxxxxxx`.
|
|
1368
1623
|
|
|
1369
1624
|
#### Usage Examples
|
|
1370
1625
|
|
|
@@ -1401,10 +1656,17 @@ for (let i = 1; i <= 22136; ++i) {
|
|
|
1401
1656
|
}
|
|
1402
1657
|
}
|
|
1403
1658
|
|
|
1404
|
-
expect(uuidV1(new Date(), '000123456789abc').split('-')[4])
|
|
1405
|
-
|
|
1659
|
+
expect(uuidV1(new Date(), '000123456789abc').split('-')[4])
|
|
1660
|
+
.to.equal('123456789abc')
|
|
1406
1661
|
|
|
1407
|
-
expect(uuidV1(new Date(
|
|
1662
|
+
expect(uuidV1(new Date(), '123456789').split('-')[4])
|
|
1663
|
+
.to.equal('000123456789')
|
|
1664
|
+
|
|
1665
|
+
expect(
|
|
1666
|
+
uuidV1(new Date(323325000000))
|
|
1667
|
+
.startsWith('c1399400-9a71-11bd'),
|
|
1668
|
+
)
|
|
1669
|
+
.to.be.true
|
|
1408
1670
|
```
|
|
1409
1671
|
|
|
1410
1672
|
### vivify
|
|
@@ -1413,22 +1675,36 @@ expect(uuidV1(new Date(323325000000)).startsWith('c1399400-9a71-11bd')).to.be.tr
|
|
|
1413
1675
|
const vivify: (ref: unknown) => any;
|
|
1414
1676
|
```
|
|
1415
1677
|
|
|
1416
|
-
A Proxy-based helper for auto-vivification
|
|
1678
|
+
A Proxy-based helper for auto-vivification
|
|
1679
|
+
of nested object structures.
|
|
1417
1680
|
|
|
1418
|
-
Accessing, assigning, or deleting any nested property
|
|
1419
|
-
|
|
1681
|
+
Accessing, assigning, or deleting any nested property
|
|
1682
|
+
on the returned proxy
|
|
1683
|
+
automatically creates intermediate objects
|
|
1684
|
+
(or arrays for numeric-string keys matching
|
|
1685
|
+
`^(0|[1-9]\d*)$`) as needed,
|
|
1686
|
+
allowing deep operations without explicit null checks.
|
|
1420
1687
|
|
|
1421
|
-
Intermediates of the last level
|
|
1422
|
-
|
|
1688
|
+
Intermediates of the last level
|
|
1689
|
+
in a get-only property chain are NOT auto-created.
|
|
1690
|
+
For example, `vivify(ref).one.two` will create
|
|
1691
|
+
`ref.one` as `{}`, but will NOT create `ref.one.two`.
|
|
1423
1692
|
Only when a deeper access, assignment, or deletion occurs
|
|
1424
|
-
(e.g. `delete vivify(ref).one.two.three`
|
|
1693
|
+
(e.g. `delete vivify(ref).one.two.three`
|
|
1694
|
+
or `vivify(ref).one.two.three = 4`)
|
|
1695
|
+
will `ref.one.two` be materialized.
|
|
1425
1696
|
|
|
1426
|
-
When traversal reaches a primitive value,
|
|
1697
|
+
When traversal reaches a primitive value,
|
|
1698
|
+
no auto-creation happens;
|
|
1427
1699
|
the primitive’s own property is returned instead
|
|
1428
|
-
(e.g. accessing `.toString.name` on a number yields
|
|
1700
|
+
(e.g. accessing `.toString.name` on a number yields
|
|
1701
|
+
`'toString'` without modifying the underlying structure).
|
|
1429
1702
|
|
|
1430
|
-
Deletion on a non-existing intermediate path
|
|
1431
|
-
|
|
1703
|
+
Deletion on a non-existing intermediate path
|
|
1704
|
+
will auto-create intermediates up to the parent
|
|
1705
|
+
of the deleted key
|
|
1706
|
+
(e.g. `delete vivify(ref).one.two.three`
|
|
1707
|
+
will create `ref.one.two` as `{}` if it does not exist).
|
|
1432
1708
|
|
|
1433
1709
|
#### ref
|
|
1434
1710
|
|
|
@@ -1436,7 +1712,8 @@ The root object to wrap.
|
|
|
1436
1712
|
|
|
1437
1713
|
#### Returns
|
|
1438
1714
|
|
|
1439
|
-
A proxy that auto-creates nested objects/arrays
|
|
1715
|
+
A proxy that auto-creates nested objects/arrays
|
|
1716
|
+
on property access.
|
|
1440
1717
|
|
|
1441
1718
|
#### Usage Examples
|
|
1442
1719
|
|
|
@@ -1455,7 +1732,12 @@ vivify(ref).one.two[3][4] = 5
|
|
|
1455
1732
|
|
|
1456
1733
|
expect(ref).to.deep.equal({
|
|
1457
1734
|
one: {
|
|
1458
|
-
two: [
|
|
1735
|
+
two: [
|
|
1736
|
+
undefined,
|
|
1737
|
+
undefined,
|
|
1738
|
+
undefined,
|
|
1739
|
+
[undefined, undefined, undefined, undefined, 5],
|
|
1740
|
+
],
|
|
1459
1741
|
},
|
|
1460
1742
|
})
|
|
1461
1743
|
|
|
@@ -1495,7 +1777,8 @@ delete vivify(ref).one.two
|
|
|
1495
1777
|
|
|
1496
1778
|
expect(ref).to.deep.equal({ one: {} })
|
|
1497
1779
|
|
|
1498
|
-
expect(vivify(ref).one.toString instanceof Function)
|
|
1780
|
+
expect(vivify(ref).one.toString instanceof Function)
|
|
1781
|
+
.to.be.true
|
|
1499
1782
|
|
|
1500
1783
|
expect(vivify(ref).one.toString.name).to.equal('toString')
|
|
1501
1784
|
|
|
@@ -1515,11 +1798,14 @@ expect(ref).to.deep.equal({ one: { two: { three: {} } } })
|
|
|
1515
1798
|
|
|
1516
1799
|
vivify(ref).one.two.three.four = 5
|
|
1517
1800
|
|
|
1518
|
-
expect(ref)
|
|
1801
|
+
expect(ref)
|
|
1802
|
+
.to.deep.equal({ one: { two: { three: { four: 5 } } } })
|
|
1519
1803
|
|
|
1520
|
-
expect(vivify(ref).one.two.three.four.toString.name)
|
|
1804
|
+
expect(vivify(ref).one.two.three.four.toString.name)
|
|
1805
|
+
.to.equal('toString')
|
|
1521
1806
|
|
|
1522
|
-
expect(ref)
|
|
1807
|
+
expect(ref)
|
|
1808
|
+
.to.deep.equal({ one: { two: { three: { four: 5 } } } })
|
|
1523
1809
|
|
|
1524
1810
|
const u = undefined
|
|
1525
1811
|
|