@jackens/nnn 2024.2.12 → 2024.2.17
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/chartable.d.ts +4 -10
- package/chartable.js +83 -142
- package/escape.js +2 -2
- package/h.js +9 -9
- package/has.js +2 -2
- package/is.js +1 -1
- package/jsOnParse.js +1 -1
- package/nanolightJs.js +1 -1
- package/package.json +1 -1
- package/pick.js +6 -32
- package/plUral.js +2 -2
- package/pro.js +1 -1
- package/readme.md +5 -11
- package/refsInfo.js +1 -1
- package/uuid1.js +1 -1
package/chartable.d.ts
CHANGED
|
@@ -6,32 +6,26 @@
|
|
|
6
6
|
* - `gapX`: X axis spacing
|
|
7
7
|
* - `gapY`: Y axis spacing
|
|
8
8
|
* - `headerColumn`: flag indicating that `table` has a header column (with X axis labels)
|
|
9
|
-
* - `
|
|
10
|
-
* - `left`: left padding (for data series labels)
|
|
9
|
+
* - `left`: left padding
|
|
11
10
|
* - `maxY`: number of Y axis lines
|
|
12
11
|
* - `reverse`: flag to reverse all data series
|
|
13
12
|
* - `right`: right padding (for data series labels)
|
|
14
13
|
* - `rotate`: flag to rotate X axis labels
|
|
15
|
-
* - `singleScale`: flag to force single scale
|
|
16
14
|
* - `table`: `HTMLTableElement` to extract data, data series labels and X axis labels
|
|
17
|
-
* - `
|
|
18
|
-
* - `top`: top padding (for the title)
|
|
15
|
+
* - `top`: top padding
|
|
19
16
|
*/
|
|
20
|
-
export function chartable({ bottom, gapX, gapY, headerColumn,
|
|
17
|
+
export function chartable({ bottom, gapX, gapY, headerColumn, left, maxY, reverse, right, rotate, table, top }: {
|
|
21
18
|
bottom?: number;
|
|
22
19
|
gapX?: number;
|
|
23
20
|
gapY?: number;
|
|
24
21
|
headerColumn?: boolean;
|
|
25
|
-
id?: string;
|
|
26
22
|
left?: number;
|
|
27
23
|
maxY?: number;
|
|
28
24
|
reverse?: boolean;
|
|
29
25
|
right?: number;
|
|
30
26
|
rotate?: boolean;
|
|
31
|
-
singleScale?: boolean;
|
|
32
27
|
table: HTMLTableElement;
|
|
33
|
-
title?: string;
|
|
34
28
|
top?: number;
|
|
35
|
-
}):
|
|
29
|
+
}): Node;
|
|
36
30
|
|
|
37
31
|
export const tests: {};
|
package/chartable.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { s } from './h.js'
|
|
2
2
|
|
|
3
3
|
const COLORS = ['#e22', '#e73', '#fc3', '#ad4', '#4d9', '#3be', '#45d', '#c3e']
|
|
4
|
+
const PADDING = 15
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* A helper for creating a chart based on a table (conf. <https://jackens.github.io/nnn/chartable/>).
|
|
@@ -10,31 +11,25 @@ const COLORS = ['#e22', '#e73', '#fc3', '#ad4', '#4d9', '#3be', '#45d', '#c3e']
|
|
|
10
11
|
* - `gapX`: X axis spacing
|
|
11
12
|
* - `gapY`: Y axis spacing
|
|
12
13
|
* - `headerColumn`: flag indicating that `table` has a header column (with X axis labels)
|
|
13
|
-
* - `
|
|
14
|
-
* - `left`: left padding (for data series labels)
|
|
14
|
+
* - `left`: left padding
|
|
15
15
|
* - `maxY`: number of Y axis lines
|
|
16
16
|
* - `reverse`: flag to reverse all data series
|
|
17
17
|
* - `right`: right padding (for data series labels)
|
|
18
18
|
* - `rotate`: flag to rotate X axis labels
|
|
19
|
-
* - `singleScale`: flag to force single scale
|
|
20
19
|
* - `table`: `HTMLTableElement` to extract data, data series labels and X axis labels
|
|
21
|
-
* - `
|
|
22
|
-
* - `top`: top padding (for the title)
|
|
20
|
+
* - `top`: top padding
|
|
23
21
|
*
|
|
24
22
|
* @param {{
|
|
25
23
|
* bottom?: number;
|
|
26
24
|
* gapX?: number;
|
|
27
25
|
* gapY?: number;
|
|
28
26
|
* headerColumn?: boolean;
|
|
29
|
-
* id?: string;
|
|
30
27
|
* left?: number;
|
|
31
28
|
* maxY?: number;
|
|
32
29
|
* reverse?: boolean;
|
|
33
30
|
* right?: number;
|
|
34
31
|
* rotate?: boolean;
|
|
35
|
-
* singleScale?: boolean;
|
|
36
32
|
* table: HTMLTableElement;
|
|
37
|
-
* title?: string;
|
|
38
33
|
* top?: number;
|
|
39
34
|
* }} options
|
|
40
35
|
*/
|
|
@@ -43,31 +38,28 @@ export const chartable = ({
|
|
|
43
38
|
gapX = 70,
|
|
44
39
|
gapY = 30,
|
|
45
40
|
headerColumn = false,
|
|
46
|
-
|
|
47
|
-
left = 200,
|
|
41
|
+
left = 300,
|
|
48
42
|
maxY = 10,
|
|
49
43
|
reverse = false,
|
|
50
|
-
right =
|
|
44
|
+
right = 100,
|
|
51
45
|
rotate = false,
|
|
52
|
-
singleScale = false,
|
|
53
46
|
table,
|
|
54
|
-
|
|
55
|
-
top = 70
|
|
47
|
+
top = PADDING
|
|
56
48
|
}) => {
|
|
57
|
-
const /** @type {number[][]} */
|
|
49
|
+
const /** @type {number[][]} */ zxValues = []
|
|
58
50
|
const /** @type {string[]} */ xLabels = []
|
|
59
51
|
const /** @type {string[]} */ zLabels = []
|
|
60
52
|
|
|
61
53
|
table.querySelectorAll('tr').forEach((row, r) =>
|
|
62
|
-
// @ts-
|
|
54
|
+
// @ts-expect-error
|
|
63
55
|
row.querySelectorAll('td,th').forEach((/** @type {HTMLTableCellElement} */ col, c) => {
|
|
64
56
|
const x = r - 1
|
|
65
57
|
const z = c - +headerColumn
|
|
66
58
|
const value = col.innerText
|
|
67
59
|
|
|
68
60
|
if (x >= 0 && z >= 0) {
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
zxValues[z] = zxValues[z] ?? []
|
|
62
|
+
zxValues[z][x] = parseFloat(value)
|
|
71
63
|
} else if (x >= 0 && z < 0) {
|
|
72
64
|
xLabels[x] = value
|
|
73
65
|
} else if (x < 0 && z >= 0) {
|
|
@@ -77,141 +69,90 @@ export const chartable = ({
|
|
|
77
69
|
|
|
78
70
|
if (reverse) {
|
|
79
71
|
xLabels.reverse()
|
|
80
|
-
|
|
72
|
+
zxValues.forEach(xValues => xValues.reverse())
|
|
81
73
|
}
|
|
82
74
|
|
|
83
|
-
|
|
75
|
+
const maxX = zxValues[0].length
|
|
76
|
+
const xPx = Array.from({ length: maxX }, (_, x) => x * gapX)
|
|
77
|
+
const yPx = Array.from({ length: maxY }, (_, y) => y * gapY)
|
|
78
|
+
const width = gapX * (maxX - 1)
|
|
79
|
+
const height = gapY * (maxY - 1)
|
|
80
|
+
const svgW = left + width + right
|
|
81
|
+
const svgH = top + height + bottom
|
|
82
|
+
|
|
83
|
+
const /** @type {import('./h.js').HArgs1} */ gGraph = ['g', { transform: `translate(${left} ${top})` },
|
|
84
|
+
...yPx.map(px => ['line', { x1: 0, x2: width, y1: px, y2: px, stroke: '#8888' }]),
|
|
85
|
+
...xPx.map(px => ['line', { x1: px, x2: px, y1: 0, y2: height, stroke: '#8888' }])]
|
|
86
|
+
const /** @type {import('./h.js').HArgs1} */ gZColors = ['g', { transform: `translate(${PADDING} ${top})` }]
|
|
87
|
+
const /** @type {import('./h.js').HArgs1} */ gZLabels = ['g', { transform: `translate(${2 * PADDING} ${top})` }]
|
|
88
|
+
const /** @type {import('./h.js').HArgs} */ svg = ['svg',
|
|
89
|
+
{ viewBox: `0 0 ${svgW} ${svgH}`, width: `${svgW}px`, height: `${svgH}px`, class: 'chartable' },
|
|
90
|
+
gGraph, gZColors, gZLabels]
|
|
91
|
+
|
|
92
|
+
if (xLabels.length > 0) {
|
|
93
|
+
svg.push(['g', { transform: `translate(${left} ${top + height + PADDING})` },
|
|
94
|
+
...xPx.slice(0).map((xp, x) => ['text',
|
|
95
|
+
rotate
|
|
96
|
+
? { x: 0, y: -xp, 'text-anchor': 'start', 'alignment-baseline': 'middle', transform: 'rotate(90)' }
|
|
97
|
+
: { x: xp, y: PADDING, 'text-anchor': 'middle', 'alignment-baseline': 'middle' },
|
|
98
|
+
xLabels[x]])])
|
|
99
|
+
}
|
|
84
100
|
|
|
85
|
-
const
|
|
86
|
-
xY = xY.filter(y => !isNaN(y))
|
|
87
|
-
return [Math.min(...xY), Math.max(...xY)]
|
|
88
|
-
})
|
|
101
|
+
const /** @type {[SVGElement, SVGElement, SVGTextElement][]} */ toUpdate = []
|
|
89
102
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
} else {
|
|
96
|
-
ranges.forEach(([min1, max0]) => ranges.forEach(([min0, max1]) => {
|
|
97
|
-
if (min0 >= min1 && max1 >= max0) {
|
|
98
|
-
let min2 = Infinity
|
|
99
|
-
let max2 = -Infinity
|
|
100
|
-
|
|
101
|
-
ranges.forEach(([min, max]) => {
|
|
102
|
-
if (min < min1 || max1 < max) {
|
|
103
|
-
if (min2 > min) {
|
|
104
|
-
min2 = min
|
|
105
|
-
}
|
|
106
|
-
if (max2 < max) {
|
|
107
|
-
max2 = max
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
let cost = 0
|
|
113
|
-
|
|
114
|
-
ranges.forEach(([min, max]) => {
|
|
115
|
-
if (min < max) {
|
|
116
|
-
cost += 1 - (max - min) / (min1 <= min && max <= max1 ? max1 - min1 : max2 - min2)
|
|
117
|
-
}
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
if (bestScales[4] > cost) {
|
|
121
|
-
bestScales = [min1, max1, min2, max2, cost]
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}))
|
|
125
|
-
}
|
|
103
|
+
const onclick = (/** @type {number} */ z) => toUpdate.forEach(([gLeftYLabels, gRightYLabels, zLabel], z2) => {
|
|
104
|
+
s(gLeftYLabels, { $style: { display: z2 === z ? 'block' : 'none' } })
|
|
105
|
+
s(gRightYLabels, { $style: { display: z2 === z ? 'block' : 'none' } })
|
|
106
|
+
s(zLabel, { $style: { textDecoration: z2 === z ? 'underline' : 'none', cursor: 'pointer' } })
|
|
107
|
+
})
|
|
126
108
|
|
|
127
|
-
|
|
128
|
-
const maxX = zxY[0].length
|
|
129
|
-
const xi = Array.from({ length: maxX }, (_, x) => x * gapX)
|
|
130
|
-
const yi = Array.from({ length: maxY }, (_, y) => y * gapY)
|
|
131
|
-
const w = gapX * (maxX - 1)
|
|
132
|
-
const h = gapY * (maxY - 1)
|
|
133
|
-
const svgW = left + w + right
|
|
134
|
-
const svgH = top + h + bottom
|
|
135
|
-
const /** @type {import('./h.js').HArgs1} */ graph = ['g', { transform: `translate(${left} ${top})` },
|
|
136
|
-
...yi.map(y => ['line', { x1: 0, x2: w, y1: y, y2: y, stroke: '#8888' }]),
|
|
137
|
-
...xi.map(x => ['line', { x1: x, x2: x, y1: 0, y2: h, stroke: '#8888' }])]
|
|
138
|
-
const /** @type {import('./h.js').HArgs1} */ lColors = ['g', { class: 'chartable-z-colors', transform: 'translate(-80 0)' }]
|
|
139
|
-
const /** @type {import('./h.js').HArgs1} */ lLabels = ['g', { class: 'chartable-z-labels', transform: 'translate(-95 0)' }]
|
|
140
|
-
const /** @type {import('./h.js').HArgs1} */ rColors = ['g', { class: 'chartable-z-colors', transform: 'translate(80 0)' }]
|
|
141
|
-
const /** @type {import('./h.js').HArgs1} */ rLabels = ['g', { class: 'chartable-z-labels', transform: 'translate(95 0)' }]
|
|
142
|
-
const yLabel = (/** @type {number} */ min, /** @type {number} */ max, /** @type {number} */ y) =>
|
|
143
|
-
(min + (max - min) * (maxY - 1 - y) / (maxY - 1)).toFixed(2).replace(/\.?0+$/, '')
|
|
144
|
-
|
|
145
|
-
zxY.forEach((xa, z) => {
|
|
146
|
-
const cls = `chartable-z-${z + 1}`
|
|
109
|
+
zxValues.forEach((values, z) => {
|
|
147
110
|
const fill = COLORS[z % COLORS.length]
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
{ x: 0, y:
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
111
|
+
const minValue = Math.min(...values.filter(value => !isNaN(value)))
|
|
112
|
+
const maxValue = Math.max(...values.filter(value => !isNaN(value)))
|
|
113
|
+
|
|
114
|
+
const valuesPx = values.map(value => isNaN(value) ? value : height * (maxValue - value) / (maxValue - minValue))
|
|
115
|
+
|
|
116
|
+
const gLeftYLabels = s('g', { transform: `translate(${left - PADDING} ${top})` },
|
|
117
|
+
...yPx.map((yp, y) => ['text',
|
|
118
|
+
{ x: 0, y: yp, 'text-anchor': 'end', 'alignment-baseline': 'middle' },
|
|
119
|
+
(maxValue - (maxValue - minValue) / maxY * (y + 1)).toFixed(2)]))
|
|
120
|
+
|
|
121
|
+
const gRightYLabels = s('g', { transform: `translate(${left + width + PADDING} ${top})` },
|
|
122
|
+
...yPx.map((yp, y) => ['text',
|
|
123
|
+
{ x: 0, y: yp, 'text-anchor': 'start', 'alignment-baseline': 'middle' },
|
|
124
|
+
(maxValue - (maxValue - minValue) / maxY * (y + 1)).toFixed(2)]))
|
|
125
|
+
|
|
126
|
+
svg.push(gLeftYLabels, gRightYLabels)
|
|
127
|
+
|
|
128
|
+
const zLabel = s('text', {
|
|
129
|
+
x: 0,
|
|
130
|
+
y: gapY * (gZLabels.length - 2),
|
|
131
|
+
'text-anchor': 'start',
|
|
132
|
+
'alignment-baseline': 'middle',
|
|
133
|
+
$onclick: () => onclick(z)
|
|
134
|
+
},
|
|
135
|
+
zLabels[z])
|
|
136
|
+
|
|
137
|
+
gZLabels.push(zLabel)
|
|
138
|
+
gZColors.push(['circle', { cx: 0, cy: gapY * (gZColors.length - 2), r: 5, fill }])
|
|
139
|
+
|
|
140
|
+
toUpdate.push([gLeftYLabels, gRightYLabels, zLabel])
|
|
141
|
+
|
|
142
|
+
xPx.forEach((px, x) => {
|
|
143
|
+
if (!isNaN(valuesPx[x])) {
|
|
144
|
+
gGraph.push(['g', ['circle', { cx: px, cy: valuesPx[x], r: 5, fill }]])
|
|
145
|
+
|
|
146
|
+
if (!isNaN(valuesPx[x - 1])) {
|
|
147
|
+
gGraph.push(['line', { x1: px, x2: xPx[x - 1], y1: valuesPx[x], y2: valuesPx[x - 1], stroke: fill }])
|
|
174
148
|
}
|
|
175
149
|
}
|
|
176
150
|
})
|
|
177
151
|
})
|
|
178
152
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
height: `${svgH}px`,
|
|
183
|
-
class: 'chartable'
|
|
184
|
-
}, id != null ? { id } : null, title != null
|
|
185
|
-
? ['text',
|
|
186
|
-
{ class: 'chartable-title', x: svgW / 2, y: top / 2, 'text-anchor': 'middle', 'alignment-baseline': 'middle' },
|
|
187
|
-
title]
|
|
188
|
-
: null,
|
|
189
|
-
graph,
|
|
190
|
-
min1 < Infinity
|
|
191
|
-
? ['g', { class: 'chartable-left', transform: `translate(${left} ${top})` },
|
|
192
|
-
['g', { class: 'chartable-y-labels', transform: 'translate(-15 0)' },
|
|
193
|
-
...yi.map((y, i) => ['text',
|
|
194
|
-
{ x: 0, y, 'text-anchor': 'end', 'alignment-baseline': 'middle' },
|
|
195
|
-
yLabel(min1, max1, i)])],
|
|
196
|
-
lColors, lLabels]
|
|
197
|
-
: null,
|
|
198
|
-
min2 < Infinity
|
|
199
|
-
? ['g', { class: 'chartable-right', transform: `translate(${left + w} ${top})` },
|
|
200
|
-
['g', { class: 'chartable-y-labels', transform: 'translate(15 0)' },
|
|
201
|
-
...yi.map((y, i) => ['text',
|
|
202
|
-
{ x: 0, y, 'text-anchor': 'start', 'alignment-baseline': 'middle' },
|
|
203
|
-
yLabel(min2, max2, i)])],
|
|
204
|
-
rColors, rLabels]
|
|
205
|
-
: null,
|
|
206
|
-
xLabels.length > 0
|
|
207
|
-
? ['g', { transform: `translate(${left} ${top + h})` },
|
|
208
|
-
['g', { class: 'chartable-x-labels', transform: 'translate(0 15)' },
|
|
209
|
-
...xi.slice(0).map((x, i) => ['text', rotate
|
|
210
|
-
? { x: 0, y: -x, 'text-anchor': 'start', 'alignment-baseline': 'hanging', transform: 'rotate(90)' }
|
|
211
|
-
: { x, y: 0, 'text-anchor': 'middle', 'alignment-baseline': 'hanging' },
|
|
212
|
-
xLabels[i]])]]
|
|
213
|
-
: null
|
|
214
|
-
)
|
|
153
|
+
onclick(0)
|
|
154
|
+
|
|
155
|
+
return s(...svg)
|
|
215
156
|
}
|
|
216
157
|
|
|
217
158
|
export const tests = {}
|
package/escape.js
CHANGED
|
@@ -23,7 +23,7 @@ export const escape = (
|
|
|
23
23
|
|
|
24
24
|
export const tests = {
|
|
25
25
|
escape: (/** @type {import('bun:test').Expect} */ expect) => {
|
|
26
|
-
// @ts-
|
|
26
|
+
// @ts-expect-error
|
|
27
27
|
const /** @type {EscapeMap} */ escapeMap = new Map([
|
|
28
28
|
[undefined, () => 'NULL'],
|
|
29
29
|
[Array, (/** @type {any[]} */ values) => escapeValues(escapeMap, values).join(', ')],
|
|
@@ -33,7 +33,7 @@ export const tests = {
|
|
|
33
33
|
[String, (/** @type {string} */ value) => `'${value.replace(/'/g, "''")}'`]
|
|
34
34
|
])
|
|
35
35
|
|
|
36
|
-
// @ts-
|
|
36
|
+
// @ts-expect-error
|
|
37
37
|
const sql = escape.bind(null, escapeMap)
|
|
38
38
|
|
|
39
39
|
const actual = sql`
|
package/h.js
CHANGED
|
@@ -44,10 +44,10 @@ const _h = (/** @type {string?=} */ namespaceURI) => {
|
|
|
44
44
|
if (arg instanceof Node) {
|
|
45
45
|
child = arg
|
|
46
46
|
} else if (is(String, arg) || is(Number, arg)) {
|
|
47
|
-
// @ts-
|
|
47
|
+
// @ts-expect-error
|
|
48
48
|
child = new Text(arg)
|
|
49
49
|
} else if (is(Array, arg)) {
|
|
50
|
-
// @ts-
|
|
50
|
+
// @ts-expect-error
|
|
51
51
|
child = h(...arg)
|
|
52
52
|
} else if (arg != null) {
|
|
53
53
|
for (const name in arg) {
|
|
@@ -57,19 +57,19 @@ const _h = (/** @type {string?=} */ namespaceURI) => {
|
|
|
57
57
|
const name1 = name.slice(1)
|
|
58
58
|
|
|
59
59
|
if (is(Object, value)) {
|
|
60
|
-
// @ts-
|
|
60
|
+
// @ts-expect-error
|
|
61
61
|
node[name1] = node[name1] ?? {}
|
|
62
|
-
// @ts-
|
|
62
|
+
// @ts-expect-error
|
|
63
63
|
Object.assign(node[name1], value)
|
|
64
64
|
} else {
|
|
65
|
-
// @ts-
|
|
65
|
+
// @ts-expect-error
|
|
66
66
|
node[name1] = value
|
|
67
67
|
}
|
|
68
68
|
} else if (node instanceof Element) {
|
|
69
69
|
const indexOfColon = name.indexOf(':')
|
|
70
70
|
|
|
71
71
|
if (indexOfColon >= 0) {
|
|
72
|
-
// @ts-
|
|
72
|
+
// @ts-expect-error
|
|
73
73
|
const /** @type {string=} */ ns = NS[name.slice(0, indexOfColon)]
|
|
74
74
|
|
|
75
75
|
if (ns != null) {
|
|
@@ -197,17 +197,17 @@ export const tests = {
|
|
|
197
197
|
'h: nested properties': (/** @type {import('bun:test').Expect} */ expect) => {
|
|
198
198
|
const div = h('div')
|
|
199
199
|
|
|
200
|
-
// @ts-
|
|
200
|
+
// @ts-expect-error
|
|
201
201
|
expect(div.key).toBeUndefined()
|
|
202
202
|
|
|
203
203
|
h(div, { $key: { one: 1 } })
|
|
204
204
|
|
|
205
|
-
// @ts-
|
|
205
|
+
// @ts-expect-error
|
|
206
206
|
expect(div.key).toEqual({ one: 1 })
|
|
207
207
|
|
|
208
208
|
h(div, { $key: { two: 2 } })
|
|
209
209
|
|
|
210
|
-
// @ts-
|
|
210
|
+
// @ts-expect-error
|
|
211
211
|
expect(div.key).toEqual({ one: 1, two: 2 })
|
|
212
212
|
}
|
|
213
213
|
}
|
package/has.js
CHANGED
|
@@ -16,7 +16,7 @@ export const tests = {
|
|
|
16
16
|
expect('null' in obj).toBe(true)
|
|
17
17
|
expect(has('null', obj)).toBe(true)
|
|
18
18
|
|
|
19
|
-
// @ts-
|
|
19
|
+
// @ts-expect-error
|
|
20
20
|
expect(null in obj).toBe(true)
|
|
21
21
|
expect(has(null, obj)).toBe(false)
|
|
22
22
|
|
|
@@ -28,7 +28,7 @@ export const tests = {
|
|
|
28
28
|
let typeError
|
|
29
29
|
|
|
30
30
|
try {
|
|
31
|
-
// @ts-
|
|
31
|
+
// @ts-expect-error
|
|
32
32
|
'key' in null
|
|
33
33
|
} catch (error) {
|
|
34
34
|
typeError = error
|
package/is.js
CHANGED
package/jsOnParse.js
CHANGED
package/nanolightJs.js
CHANGED
|
@@ -4,7 +4,7 @@ import { nanolight } from './nanolight.js'
|
|
|
4
4
|
/**
|
|
5
5
|
* A helper for highlighting JavaScript.
|
|
6
6
|
*/
|
|
7
|
-
// @ts-
|
|
7
|
+
// @ts-expect-error
|
|
8
8
|
export const nanolightJs = nanolight.bind(0,
|
|
9
9
|
/('.*?'|".*?"|`[\s\S]*?`)|(\/\/.*?\n|\/\*[\s\S]*?\*\/)|(any|bigint|break|boolean|case|catch|const|continue|debugger|default|delete|do|else|eval|export|extends|false|finally|for|from|function|goto|if|import|in|instanceof|is|keyof|let|NaN|new|number|null|package|return|string|super|switch|symbol|this|throw|true|try|type|typeof|undefined|unknown|var|void|while|with|yield)(?!\w)|([<>=.?:&|!^~*/%+-])|(0x[\dabcdef]+|0o[01234567]+|0b[01]+|\d+(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?)|([$\w]+)(?=\()|([$\wąćęłńóśżźĄĆĘŁŃÓŚŻŹ]+)/,
|
|
10
10
|
[
|
package/package.json
CHANGED
package/pick.js
CHANGED
|
@@ -2,45 +2,19 @@
|
|
|
2
2
|
* A helper that implements TypeScript’s `Pick` utility type.
|
|
3
3
|
*
|
|
4
4
|
* @type {<T extends Partial<Record<PropertyKey, any>>, K extends (keyof T)[]>(obj: T, keys: K) => Pick<T, K[number]>}
|
|
5
|
-
* @template {Partial<Record<PropertyKey, any>>} T
|
|
6
|
-
* @template {keyof T} K
|
|
7
5
|
*/
|
|
8
|
-
export const pick = (/** @type {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
for (const key in obj) {
|
|
12
|
-
// @ts-ignore
|
|
13
|
-
if (keys.includes(key)) {
|
|
14
|
-
// @ts-ignore
|
|
15
|
-
result[key] = obj[key]
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// @ts-ignore
|
|
20
|
-
return result
|
|
21
|
-
}
|
|
6
|
+
export const pick = (/** @type {Partial<Record<PropertyKey, any>>} */ obj, /** @type {any[]} */ keys) =>
|
|
7
|
+
// @ts-expect-error
|
|
8
|
+
Object.fromEntries(Object.entries(obj).filter(([key]) => keys.includes(key)))
|
|
22
9
|
|
|
23
10
|
/**
|
|
24
11
|
* A helper that implements TypeScript’s `Omit` utility type.
|
|
25
12
|
*
|
|
26
13
|
* @type {<T extends Partial<Record<PropertyKey, any>>, K extends (keyof T)[]>(obj: T, keys: K) => Omit<T, K[number]>}
|
|
27
|
-
* @template {Partial<Record<PropertyKey, any>>} T
|
|
28
|
-
* @template {keyof T} K
|
|
29
14
|
*/
|
|
30
|
-
export const omit = (/** @type {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
for (const key in obj) {
|
|
34
|
-
// @ts-ignore
|
|
35
|
-
if (!keys.includes(key)) {
|
|
36
|
-
// @ts-ignore
|
|
37
|
-
result[key] = obj[key]
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// @ts-ignore
|
|
42
|
-
return result
|
|
43
|
-
}
|
|
15
|
+
export const omit = (/** @type {Partial<Record<PropertyKey, any>>} */ obj, /** @type {any[]} */ keys) =>
|
|
16
|
+
// @ts-expect-error
|
|
17
|
+
Object.fromEntries(Object.entries(obj).filter(([key]) => !keys.includes(key)))
|
|
44
18
|
|
|
45
19
|
export const tests = {
|
|
46
20
|
pick: (/** @type {import('bun:test').Expect} */ expect) => {
|
package/plUral.js
CHANGED
|
@@ -20,7 +20,7 @@ export const plUral = (
|
|
|
20
20
|
|
|
21
21
|
export const tests = {
|
|
22
22
|
plUral: (/** @type {import('bun:test').Expect} */ expect) => {
|
|
23
|
-
// @ts-
|
|
23
|
+
// @ts-expect-error
|
|
24
24
|
const auto = plUral.bind(null, 'auto', 'auta', 'aut')
|
|
25
25
|
|
|
26
26
|
expect(auto(0)).toEqual('aut')
|
|
@@ -28,7 +28,7 @@ export const tests = {
|
|
|
28
28
|
expect(auto(17)).toEqual('aut')
|
|
29
29
|
expect(auto(42)).toEqual('auta')
|
|
30
30
|
|
|
31
|
-
// @ts-
|
|
31
|
+
// @ts-expect-error
|
|
32
32
|
const car = plUral.bind(null, 'car', 'cars', 'cars')
|
|
33
33
|
|
|
34
34
|
expect(car(0)).toEqual('cars')
|
package/pro.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A helper that protects calls to nested properties by a `Proxy` that initializes non-existent values with an empty object.
|
|
3
3
|
*/
|
|
4
|
-
// @ts-
|
|
4
|
+
// @ts-expect-error
|
|
5
5
|
export const pro = (/** @type {any} */ ref) => new Proxy(ref, {
|
|
6
6
|
get (target, key) {
|
|
7
7
|
return pro(target[key] = target[key] ?? {})
|
package/readme.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Jackens’ JavaScript helpers.
|
|
4
4
|
|
|
5
|
-
<sub>Version: <code class="version">2024.2.
|
|
5
|
+
<sub>Version: <code class="version">2024.2.17</code></sub>
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -103,22 +103,19 @@ The type of arguments of the `jcss` helper.
|
|
|
103
103
|
### chartable
|
|
104
104
|
|
|
105
105
|
```ts
|
|
106
|
-
export function chartable({ bottom, gapX, gapY, headerColumn,
|
|
106
|
+
export function chartable({ bottom, gapX, gapY, headerColumn, left, maxY, reverse, right, rotate, table, top }: {
|
|
107
107
|
bottom?: number;
|
|
108
108
|
gapX?: number;
|
|
109
109
|
gapY?: number;
|
|
110
110
|
headerColumn?: boolean;
|
|
111
|
-
id?: string;
|
|
112
111
|
left?: number;
|
|
113
112
|
maxY?: number;
|
|
114
113
|
reverse?: boolean;
|
|
115
114
|
right?: number;
|
|
116
115
|
rotate?: boolean;
|
|
117
|
-
singleScale?: boolean;
|
|
118
116
|
table: HTMLTableElement;
|
|
119
|
-
title?: string;
|
|
120
117
|
top?: number;
|
|
121
|
-
}):
|
|
118
|
+
}): Node;
|
|
122
119
|
```
|
|
123
120
|
|
|
124
121
|
A helper for creating a chart based on a table (conf. <https://jackens.github.io/nnn/chartable/>).
|
|
@@ -128,16 +125,13 @@ Options:
|
|
|
128
125
|
- `gapX`: X axis spacing
|
|
129
126
|
- `gapY`: Y axis spacing
|
|
130
127
|
- `headerColumn`: flag indicating that `table` has a header column (with X axis labels)
|
|
131
|
-
- `
|
|
132
|
-
- `left`: left padding (for data series labels)
|
|
128
|
+
- `left`: left padding
|
|
133
129
|
- `maxY`: number of Y axis lines
|
|
134
130
|
- `reverse`: flag to reverse all data series
|
|
135
131
|
- `right`: right padding (for data series labels)
|
|
136
132
|
- `rotate`: flag to rotate X axis labels
|
|
137
|
-
- `singleScale`: flag to force single scale
|
|
138
133
|
- `table`: `HTMLTableElement` to extract data, data series labels and X axis labels
|
|
139
|
-
- `
|
|
140
|
-
- `top`: top padding (for the title)
|
|
134
|
+
- `top`: top padding
|
|
141
135
|
|
|
142
136
|
### eq
|
|
143
137
|
|
package/refsInfo.js
CHANGED
|
@@ -32,7 +32,7 @@ export const tests = {
|
|
|
32
32
|
|
|
33
33
|
'refsInfo: browserFingerprint': (/** @type {import('bun:test').Expect} */ expect) => {
|
|
34
34
|
const browserFingerprint = () => {
|
|
35
|
-
// @ts-
|
|
35
|
+
// @ts-expect-error
|
|
36
36
|
const refs = Object.getOwnPropertyNames(window).map(name => window[name])
|
|
37
37
|
const info = refsInfo(...refs)
|
|
38
38
|
const json = JSON.stringify(info)
|