@jackens/nnn 2024.3.5 → 2024.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/nnn.d.ts +0 -32
- package/nnn.js +40 -152
- package/package.json +1 -1
- package/readme.md +1 -38
package/nnn.d.ts
CHANGED
|
@@ -1,35 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A helper for creating a chart based on a table (conf. <https://jackens.github.io/nnn/chartable/>).
|
|
3
|
-
*
|
|
4
|
-
* Options:
|
|
5
|
-
* - `table`: `HTMLTableElement` to extract data, data series labels and X axis labels
|
|
6
|
-
* - `headerColumn`: flag indicating that `table` has a header column with X axis labels
|
|
7
|
-
* - `xGap`: X axis spacing
|
|
8
|
-
* - `xLabelsMinHeight`: minimal height of X axis labels
|
|
9
|
-
* - `xLabelsRotate`: flag to rotate X axis labels
|
|
10
|
-
* - `xReverse`: flag to reverse all data series
|
|
11
|
-
* - `yGap`: Y axis spacing
|
|
12
|
-
* - `yLabelsLeftMinWidth`: minimal width of Y axis left labels
|
|
13
|
-
* - `yLabelsRightMinWidth`: minimal width of Y axis right labels
|
|
14
|
-
* - `yMax`: number of Y axis lines
|
|
15
|
-
* - `zLabelsMinWidth`: minimal width of data series labels
|
|
16
|
-
* - `zyMappings`: mappings per data series
|
|
17
|
-
*/
|
|
18
|
-
export declare const chartable: ({ table, headerColumn, xGap, xLabelsMinHeight, xLabelsRotate, xReverse, yGap, yLabelsLeftMinWidth, yLabelsRightMinWidth, yMax, zLabelsMinWidth, zyMappings }: {
|
|
19
|
-
table: HTMLTableElement;
|
|
20
|
-
headerColumn?: boolean | undefined;
|
|
21
|
-
xGap?: number | undefined;
|
|
22
|
-
xLabelsMinHeight?: number | undefined;
|
|
23
|
-
xLabelsRotate?: boolean | undefined;
|
|
24
|
-
xReverse?: boolean | undefined;
|
|
25
|
-
yGap?: number | undefined;
|
|
26
|
-
yLabelsLeftMinWidth?: number | undefined;
|
|
27
|
-
yLabelsRightMinWidth?: number | undefined;
|
|
28
|
-
yMax?: number | undefined;
|
|
29
|
-
zLabelsMinWidth?: number | undefined;
|
|
30
|
-
zyMappings?: [(label: string) => number, (value: number) => string][] | undefined;
|
|
31
|
-
}) => SVGSVGElement;
|
|
32
|
-
|
|
33
1
|
/**
|
|
34
2
|
* A helper that checks equality of the given arguments.
|
|
35
3
|
*/
|
package/nnn.js
CHANGED
|
@@ -1,3 +1,42 @@
|
|
|
1
|
+
// src/nnn/eq.ts
|
|
2
|
+
var eq = (x, y) => {
|
|
3
|
+
if (x === y) {
|
|
4
|
+
return true;
|
|
5
|
+
}
|
|
6
|
+
const xConstructor = x?.constructor;
|
|
7
|
+
if (xConstructor === y?.constructor) {
|
|
8
|
+
if (xConstructor === Number) {
|
|
9
|
+
return isNaN(x) && isNaN(y) || +x === +y;
|
|
10
|
+
}
|
|
11
|
+
if (xConstructor === Date) {
|
|
12
|
+
return +x === +y;
|
|
13
|
+
}
|
|
14
|
+
if (xConstructor === String || xConstructor === RegExp) {
|
|
15
|
+
return "" + x === "" + y;
|
|
16
|
+
}
|
|
17
|
+
if (xConstructor === Array) {
|
|
18
|
+
return x.length === y.length && x.every((item, index) => eq(item, y[index]));
|
|
19
|
+
}
|
|
20
|
+
if (xConstructor === Object) {
|
|
21
|
+
const keysOfX = Object.keys(x);
|
|
22
|
+
return keysOfX.length === Object.keys(y).length && keysOfX.every((key) => eq(x[key], y[key]));
|
|
23
|
+
}
|
|
24
|
+
if (xConstructor === Set || xConstructor === Map) {
|
|
25
|
+
if (x.size !== y.size) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const xa = [...x];
|
|
29
|
+
const ya = [...y];
|
|
30
|
+
return xa.every((xv) => ya.find((yv) => eq(xv, yv)));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// src/nnn/escape.ts
|
|
37
|
+
var escapeValues = (escapeMap, values) => values.map((value) => (escapeMap.get(value?.constructor) ?? escapeMap.get(undefined))?.(value) ?? "");
|
|
38
|
+
var escape = (escapeMap, template, ...values) => String.raw(template, ...escapeValues(escapeMap, values));
|
|
39
|
+
|
|
1
40
|
// src/nnn/is.ts
|
|
2
41
|
var is = (type, arg) => arg?.constructor === type;
|
|
3
42
|
|
|
@@ -66,156 +105,6 @@ var h = _h();
|
|
|
66
105
|
var s = _h("http://www.w3.org/2000/svg");
|
|
67
106
|
var svgUse = (id, ...args) => s("svg", ["use", { "xlink:href": "#" + id }], ...args);
|
|
68
107
|
|
|
69
|
-
// src/nnn/chartable.ts
|
|
70
|
-
var COLORS = ["#e22", "#e73", "#fc3", "#ad4", "#4d9", "#3be", "#45d", "#c3e"];
|
|
71
|
-
var PADDING = 15;
|
|
72
|
-
var _svg = s("svg", { viewBox: "0 0 0 0", width: 0, height: 0 });
|
|
73
|
-
h(document.body, ["div", { $style: { width: 0, height: 0 } }, _svg]);
|
|
74
|
-
var toFixed2 = (value) => value.toFixed(2);
|
|
75
|
-
var chartable = ({
|
|
76
|
-
table,
|
|
77
|
-
headerColumn = false,
|
|
78
|
-
xGap = 70,
|
|
79
|
-
xLabelsMinHeight = 0,
|
|
80
|
-
xLabelsRotate = false,
|
|
81
|
-
xReverse = false,
|
|
82
|
-
yGap = 30,
|
|
83
|
-
yLabelsLeftMinWidth = 100,
|
|
84
|
-
yLabelsRightMinWidth = 100,
|
|
85
|
-
yMax = 10,
|
|
86
|
-
zLabelsMinWidth = 0,
|
|
87
|
-
zyMappings = []
|
|
88
|
-
}) => {
|
|
89
|
-
const zxValues = [];
|
|
90
|
-
const xLabels = [];
|
|
91
|
-
const zLabels = [];
|
|
92
|
-
table.querySelectorAll("tr").forEach((row, r) => row.querySelectorAll("td,th").forEach((col, c) => {
|
|
93
|
-
const x = r - 1;
|
|
94
|
-
const z = c - +headerColumn;
|
|
95
|
-
const value = col.innerText;
|
|
96
|
-
if (x >= 0 && z >= 0) {
|
|
97
|
-
zxValues[z] = zxValues[z] ?? [];
|
|
98
|
-
zxValues[z][x] = (zyMappings?.[z]?.[0] ?? parseFloat)(value);
|
|
99
|
-
} else if (x >= 0 && z < 0) {
|
|
100
|
-
xLabels[x] = value;
|
|
101
|
-
} else if (x < 0 && z >= 0) {
|
|
102
|
-
zLabels[z] = value;
|
|
103
|
-
}
|
|
104
|
-
}));
|
|
105
|
-
if (xReverse) {
|
|
106
|
-
xLabels.reverse();
|
|
107
|
-
zxValues.forEach((xValues) => xValues.reverse());
|
|
108
|
-
}
|
|
109
|
-
const xMax = zxValues[0].length;
|
|
110
|
-
const xPx = Array.from({ length: xMax }, (_, x) => x * xGap);
|
|
111
|
-
const yPx = Array.from({ length: yMax }, (_, y) => y * yGap);
|
|
112
|
-
const width = xGap * (xMax - 1);
|
|
113
|
-
const height = yGap * (yMax - 1);
|
|
114
|
-
const gGraph = s("g", ...yPx.map((px) => ["line", { x1: 0, x2: width, y1: px, y2: px, stroke: "#8888" }]), ...xPx.map((px) => ["line", { x1: px, x2: px, y1: 0, y2: height, stroke: "#8888" }]));
|
|
115
|
-
const gXLabels = s("g");
|
|
116
|
-
const gYLabelsL = zxValues.map((_) => s("g"));
|
|
117
|
-
const gYLabelsR = zxValues.map((_) => s("g"));
|
|
118
|
-
const gZColors = s("g");
|
|
119
|
-
const gZLabels = [];
|
|
120
|
-
const onclick = (z) => {
|
|
121
|
-
gYLabelsL.forEach((g, z2) => s(g, { $style: { display: z2 === z ? "block" : "none" } }));
|
|
122
|
-
gYLabelsR.forEach((g, z2) => s(g, { $style: { display: z2 === z ? "block" : "none" } }));
|
|
123
|
-
gZLabels.forEach((text, z2) => s(text, { $style: { fontWeight: z2 === z ? "bold" : "normal", cursor: "pointer" } }));
|
|
124
|
-
};
|
|
125
|
-
zxValues.forEach((values, z) => {
|
|
126
|
-
const fill = COLORS[z % COLORS.length];
|
|
127
|
-
const minValue = Math.min(...values.filter((value) => !isNaN(value)));
|
|
128
|
-
const maxValue = Math.max(...values.filter((value) => !isNaN(value)));
|
|
129
|
-
yPx.forEach((yp, y) => {
|
|
130
|
-
const value = (zyMappings?.[z]?.[1] ?? toFixed2)(maxValue - (maxValue - minValue) / yMax * (y + 1));
|
|
131
|
-
const textL = s("text", { x: 0, y: yp, "text-anchor": "end", "alignment-baseline": "middle" }, value);
|
|
132
|
-
const textR = s("text", { x: 0, y: yp, "text-anchor": "start", "alignment-baseline": "middle" }, value);
|
|
133
|
-
s(_svg, textL, textR);
|
|
134
|
-
yLabelsLeftMinWidth = Math.max(yLabelsLeftMinWidth, textL.getBBox().width);
|
|
135
|
-
yLabelsRightMinWidth = Math.max(yLabelsRightMinWidth, textR.getBBox().width);
|
|
136
|
-
s(gYLabelsL[z], textL);
|
|
137
|
-
s(gYLabelsR[z], textR);
|
|
138
|
-
});
|
|
139
|
-
const text = s("text", {
|
|
140
|
-
x: 0,
|
|
141
|
-
y: yGap * z,
|
|
142
|
-
"text-anchor": "start",
|
|
143
|
-
"alignment-baseline": "middle",
|
|
144
|
-
$onclick: () => onclick(z),
|
|
145
|
-
$style: { fontWeight: "bold" }
|
|
146
|
-
}, zLabels[z]);
|
|
147
|
-
s(_svg, text);
|
|
148
|
-
zLabelsMinWidth = Math.max(zLabelsMinWidth, text.getBBox().width);
|
|
149
|
-
gZLabels[z] = text;
|
|
150
|
-
s(gZColors, ["circle", { cx: 0, cy: yGap * z, r: 5, fill }]);
|
|
151
|
-
const valuesPx = values.map((value) => isNaN(value) ? value : height * (maxValue - value) / (maxValue - minValue));
|
|
152
|
-
xPx.forEach((px, x) => {
|
|
153
|
-
if (!isNaN(valuesPx[x])) {
|
|
154
|
-
s(gGraph, ["g", ["circle", { cx: px, cy: valuesPx[x], r: 5, fill }]]);
|
|
155
|
-
if (!isNaN(valuesPx[x - 1])) {
|
|
156
|
-
s(gGraph, ["line", { x1: px, x2: xPx[x - 1], y1: valuesPx[x], y2: valuesPx[x - 1], stroke: fill }]);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
if (xLabels.length > 0) {
|
|
162
|
-
xPx.forEach((xp, x) => {
|
|
163
|
-
const text = s("text", xLabelsRotate ? { x: 0, y: -xp, "text-anchor": "start", "alignment-baseline": "middle", transform: "rotate(90)" } : { x: xp, y: PADDING, "text-anchor": "middle", "alignment-baseline": "middle" }, xLabels[x]);
|
|
164
|
-
s(_svg, text);
|
|
165
|
-
xLabelsMinHeight = Math.max(xLabelsMinHeight, xLabelsRotate ? text.getBBox().width : text.getBBox().height);
|
|
166
|
-
s(gXLabels, text);
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
const svgW = width + yLabelsLeftMinWidth + yLabelsRightMinWidth + zLabelsMinWidth + 4 * PADDING;
|
|
170
|
-
const svgH = height + xLabelsMinHeight + (xLabels.length > 0 ? 3 : 2) * PADDING;
|
|
171
|
-
s(gGraph, { transform: `translate(${yLabelsLeftMinWidth + PADDING} ${PADDING})` });
|
|
172
|
-
gYLabelsL.forEach((g) => s(g, { transform: `translate(${yLabelsLeftMinWidth} ${PADDING})` }));
|
|
173
|
-
gYLabelsR.forEach((g) => s(g, { transform: `translate(${width + yLabelsLeftMinWidth + 2 * PADDING} ${PADDING})` }));
|
|
174
|
-
s(gZColors, { transform: `translate(${width + yLabelsLeftMinWidth + yLabelsRightMinWidth + 3 * PADDING} ${PADDING})` });
|
|
175
|
-
s(gXLabels, { transform: `translate(${yLabelsLeftMinWidth + PADDING} ${height + 2 * PADDING})` });
|
|
176
|
-
onclick(0);
|
|
177
|
-
return s("svg", { viewBox: `0 0 ${svgW} ${svgH}`, width: `${svgW}px`, height: `${svgH}px`, class: "chartable" }, gGraph, gXLabels, ...gYLabelsL, ...gYLabelsR, gZColors, ["g", { transform: `translate(${width + yLabelsLeftMinWidth + yLabelsRightMinWidth + 4 * PADDING} ${PADDING})` }, ...gZLabels]);
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
// src/nnn/eq.ts
|
|
181
|
-
var eq = (x, y) => {
|
|
182
|
-
if (x === y) {
|
|
183
|
-
return true;
|
|
184
|
-
}
|
|
185
|
-
const xConstructor = x?.constructor;
|
|
186
|
-
if (xConstructor === y?.constructor) {
|
|
187
|
-
if (xConstructor === Number) {
|
|
188
|
-
return isNaN(x) && isNaN(y) || +x === +y;
|
|
189
|
-
}
|
|
190
|
-
if (xConstructor === Date) {
|
|
191
|
-
return +x === +y;
|
|
192
|
-
}
|
|
193
|
-
if (xConstructor === String || xConstructor === RegExp) {
|
|
194
|
-
return "" + x === "" + y;
|
|
195
|
-
}
|
|
196
|
-
if (xConstructor === Array) {
|
|
197
|
-
return x.length === y.length && x.every((item, index) => eq(item, y[index]));
|
|
198
|
-
}
|
|
199
|
-
if (xConstructor === Object) {
|
|
200
|
-
const keysOfX = Object.keys(x);
|
|
201
|
-
return keysOfX.length === Object.keys(y).length && keysOfX.every((key) => eq(x[key], y[key]));
|
|
202
|
-
}
|
|
203
|
-
if (xConstructor === Set || xConstructor === Map) {
|
|
204
|
-
if (x.size !== y.size) {
|
|
205
|
-
return false;
|
|
206
|
-
}
|
|
207
|
-
const xa = [...x];
|
|
208
|
-
const ya = [...y];
|
|
209
|
-
return xa.every((xv) => ya.find((yv) => eq(xv, yv)));
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return false;
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
// src/nnn/escape.ts
|
|
216
|
-
var escapeValues = (escapeMap, values) => values.map((value) => (escapeMap.get(value?.constructor) ?? escapeMap.get(undefined))?.(value) ?? "");
|
|
217
|
-
var escape = (escapeMap, template, ...values) => String.raw(template, ...escapeValues(escapeMap, values));
|
|
218
|
-
|
|
219
108
|
// src/nnn/fixTypography.ts
|
|
220
109
|
var TAGS_TO_SKIP = ["IFRAME", "NOSCRIPT", "PRE", "SCRIPT", "STYLE", "TEXTAREA"];
|
|
221
110
|
var fixTypography = (node) => {
|
|
@@ -417,6 +306,5 @@ export {
|
|
|
417
306
|
fixTypography,
|
|
418
307
|
escapeValues,
|
|
419
308
|
escape,
|
|
420
|
-
eq
|
|
421
|
-
chartable
|
|
309
|
+
eq
|
|
422
310
|
};
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Jackens’ JavaScript helpers.
|
|
4
4
|
|
|
5
|
-
<sub>Version: <code class="version">2024.3.
|
|
5
|
+
<sub>Version: <code class="version">2024.3.8</code></sub>
|
|
6
6
|
|
|
7
7
|
## Examples
|
|
8
8
|
|
|
9
|
-
- [Chartable Demo](https://jackens.github.io/nnn/chartable/)
|
|
10
9
|
- [Chessboard Demo](https://jackens.github.io/nnn/chessboard/)
|
|
11
10
|
- [Documentation](https://jackens.github.io/nnn/doc/)
|
|
12
11
|
- [Gant Chart Demo](https://jackens.github.io/nnn/gantt/)
|
|
@@ -45,7 +44,6 @@ import { «something» } from './node_modules/@jackens/nnn/nnn.js'
|
|
|
45
44
|
- `HArgs1`: The type of arguments of the `h` and `s` helpers.
|
|
46
45
|
- `JcNode`: The type of arguments of the `jc` helper.
|
|
47
46
|
- `JcRoot`: The type of arguments of the `jc` helper.
|
|
48
|
-
- `chartable`: A helper for creating a chart based on a table (conf. <https://jackens.github.io/nnn/chartable/>).
|
|
49
47
|
- `eq`: A helper that checks equality of the given arguments.
|
|
50
48
|
- `escape`: A generic helper for escaping `values` by given `escapeMap` (in *TemplateStrings* flavor).
|
|
51
49
|
- `escapeValues`: A generic helper for escaping `values` by given `escapeMap`.
|
|
@@ -109,41 +107,6 @@ type JcRoot = Partial<Record<PropertyKey, JcNode>>;
|
|
|
109
107
|
|
|
110
108
|
The type of arguments of the `jc` helper.
|
|
111
109
|
|
|
112
|
-
### chartable
|
|
113
|
-
|
|
114
|
-
```ts
|
|
115
|
-
const chartable: ({ table, headerColumn, xGap, xLabelsMinHeight, xLabelsRotate, xReverse, yGap, yLabelsLeftMinWidth, yLabelsRightMinWidth, yMax, zLabelsMinWidth, zyMappings }: {
|
|
116
|
-
table: HTMLTableElement;
|
|
117
|
-
headerColumn?: boolean | undefined;
|
|
118
|
-
xGap?: number | undefined;
|
|
119
|
-
xLabelsMinHeight?: number | undefined;
|
|
120
|
-
xLabelsRotate?: boolean | undefined;
|
|
121
|
-
xReverse?: boolean | undefined;
|
|
122
|
-
yGap?: number | undefined;
|
|
123
|
-
yLabelsLeftMinWidth?: number | undefined;
|
|
124
|
-
yLabelsRightMinWidth?: number | undefined;
|
|
125
|
-
yMax?: number | undefined;
|
|
126
|
-
zLabelsMinWidth?: number | undefined;
|
|
127
|
-
zyMappings?: [(label: string) => number, (value: number) => string][] | undefined;
|
|
128
|
-
}) => SVGSVGElement;
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
A helper for creating a chart based on a table (conf. <https://jackens.github.io/nnn/chartable/>).
|
|
132
|
-
|
|
133
|
-
Options:
|
|
134
|
-
- `table`: `HTMLTableElement` to extract data, data series labels and X axis labels
|
|
135
|
-
- `headerColumn`: flag indicating that `table` has a header column with X axis labels
|
|
136
|
-
- `xGap`: X axis spacing
|
|
137
|
-
- `xLabelsMinHeight`: minimal height of X axis labels
|
|
138
|
-
- `xLabelsRotate`: flag to rotate X axis labels
|
|
139
|
-
- `xReverse`: flag to reverse all data series
|
|
140
|
-
- `yGap`: Y axis spacing
|
|
141
|
-
- `yLabelsLeftMinWidth`: minimal width of Y axis left labels
|
|
142
|
-
- `yLabelsRightMinWidth`: minimal width of Y axis right labels
|
|
143
|
-
- `yMax`: number of Y axis lines
|
|
144
|
-
- `zLabelsMinWidth`: minimal width of data series labels
|
|
145
|
-
- `zyMappings`: mappings per data series
|
|
146
|
-
|
|
147
110
|
### eq
|
|
148
111
|
|
|
149
112
|
```ts
|