@jackens/nnn 2024.3.3 → 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.
Files changed (4) hide show
  1. package/nnn.d.ts +6 -33
  2. package/nnn.js +42 -152
  3. package/package.json +1 -1
  4. package/readme.md +12 -40
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
  */
@@ -107,6 +75,11 @@ export declare const s: {
107
75
  (tagOrNode: string | Node, ...args1: HArgs1[]): Node;
108
76
  };
109
77
 
78
+ /**
79
+ * A convenient shortcut for `s('svg', ['use', { 'xlink:href': '#' + id }], ...args)`.
80
+ */
81
+ export declare const svgUse: (id: string, ...args: HArgs1[]) => SVGSVGElement;
82
+
110
83
  /**
111
84
  * A replacement for the `in` operator (not to be confused with the `for-in` loop) that works properly.
112
85
  */
@@ -139,7 +112,7 @@ export type JcNode = {
139
112
  export type JcRoot = Partial<Record<PropertyKey, JcNode>>;
140
113
 
141
114
  /**
142
- * A simple CSS-in-JS helper.
115
+ * A simple JS-to-CSS (aka CSS-in-JS) helper.
143
116
  *
144
117
  * The `root` parameter provides a hierarchical description of CSS rules.
145
118
  *
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
 
@@ -64,156 +103,7 @@ var _h = (namespaceURI) => {
64
103
  };
65
104
  var h = _h();
66
105
  var s = _h("http://www.w3.org/2000/svg");
67
-
68
- // src/nnn/chartable.ts
69
- var COLORS = ["#e22", "#e73", "#fc3", "#ad4", "#4d9", "#3be", "#45d", "#c3e"];
70
- var PADDING = 15;
71
- var _svg = s("svg", { viewBox: "0 0 0 0", width: 0, height: 0 });
72
- h(document.body, ["div", { $style: { width: 0, height: 0 } }, _svg]);
73
- var toFixed2 = (value) => value.toFixed(2);
74
- var chartable = ({
75
- table,
76
- headerColumn = false,
77
- xGap = 70,
78
- xLabelsMinHeight = 0,
79
- xLabelsRotate = false,
80
- xReverse = false,
81
- yGap = 30,
82
- yLabelsLeftMinWidth = 100,
83
- yLabelsRightMinWidth = 100,
84
- yMax = 10,
85
- zLabelsMinWidth = 0,
86
- zyMappings = []
87
- }) => {
88
- const zxValues = [];
89
- const xLabels = [];
90
- const zLabels = [];
91
- table.querySelectorAll("tr").forEach((row, r) => row.querySelectorAll("td,th").forEach((col, c) => {
92
- const x = r - 1;
93
- const z = c - +headerColumn;
94
- const value = col.innerText;
95
- if (x >= 0 && z >= 0) {
96
- zxValues[z] = zxValues[z] ?? [];
97
- zxValues[z][x] = (zyMappings?.[z]?.[0] ?? parseFloat)(value);
98
- } else if (x >= 0 && z < 0) {
99
- xLabels[x] = value;
100
- } else if (x < 0 && z >= 0) {
101
- zLabels[z] = value;
102
- }
103
- }));
104
- if (xReverse) {
105
- xLabels.reverse();
106
- zxValues.forEach((xValues) => xValues.reverse());
107
- }
108
- const xMax = zxValues[0].length;
109
- const xPx = Array.from({ length: xMax }, (_, x) => x * xGap);
110
- const yPx = Array.from({ length: yMax }, (_, y) => y * yGap);
111
- const width = xGap * (xMax - 1);
112
- const height = yGap * (yMax - 1);
113
- 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" }]));
114
- const gXLabels = s("g");
115
- const gYLabelsL = zxValues.map((_) => s("g"));
116
- const gYLabelsR = zxValues.map((_) => s("g"));
117
- const gZColors = s("g");
118
- const gZLabels = [];
119
- const onclick = (z) => {
120
- gYLabelsL.forEach((g, z2) => s(g, { $style: { display: z2 === z ? "block" : "none" } }));
121
- gYLabelsR.forEach((g, z2) => s(g, { $style: { display: z2 === z ? "block" : "none" } }));
122
- gZLabels.forEach((text, z2) => s(text, { $style: { fontWeight: z2 === z ? "bold" : "normal", cursor: "pointer" } }));
123
- };
124
- zxValues.forEach((values, z) => {
125
- const fill = COLORS[z % COLORS.length];
126
- const minValue = Math.min(...values.filter((value) => !isNaN(value)));
127
- const maxValue = Math.max(...values.filter((value) => !isNaN(value)));
128
- yPx.forEach((yp, y) => {
129
- const value = (zyMappings?.[z]?.[1] ?? toFixed2)(maxValue - (maxValue - minValue) / yMax * (y + 1));
130
- const textL = s("text", { x: 0, y: yp, "text-anchor": "end", "alignment-baseline": "middle" }, value);
131
- const textR = s("text", { x: 0, y: yp, "text-anchor": "start", "alignment-baseline": "middle" }, value);
132
- s(_svg, textL, textR);
133
- yLabelsLeftMinWidth = Math.max(yLabelsLeftMinWidth, textL.getBBox().width);
134
- yLabelsRightMinWidth = Math.max(yLabelsRightMinWidth, textR.getBBox().width);
135
- s(gYLabelsL[z], textL);
136
- s(gYLabelsR[z], textR);
137
- });
138
- const text = s("text", {
139
- x: 0,
140
- y: yGap * z,
141
- "text-anchor": "start",
142
- "alignment-baseline": "middle",
143
- $onclick: () => onclick(z),
144
- $style: { fontWeight: "bold" }
145
- }, zLabels[z]);
146
- s(_svg, text);
147
- zLabelsMinWidth = Math.max(zLabelsMinWidth, text.getBBox().width);
148
- gZLabels[z] = text;
149
- s(gZColors, ["circle", { cx: 0, cy: yGap * z, r: 5, fill }]);
150
- const valuesPx = values.map((value) => isNaN(value) ? value : height * (maxValue - value) / (maxValue - minValue));
151
- xPx.forEach((px, x) => {
152
- if (!isNaN(valuesPx[x])) {
153
- s(gGraph, ["g", ["circle", { cx: px, cy: valuesPx[x], r: 5, fill }]]);
154
- if (!isNaN(valuesPx[x - 1])) {
155
- s(gGraph, ["line", { x1: px, x2: xPx[x - 1], y1: valuesPx[x], y2: valuesPx[x - 1], stroke: fill }]);
156
- }
157
- }
158
- });
159
- });
160
- if (xLabels.length > 0) {
161
- xPx.forEach((xp, x) => {
162
- 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]);
163
- s(_svg, text);
164
- xLabelsMinHeight = Math.max(xLabelsMinHeight, xLabelsRotate ? text.getBBox().width : text.getBBox().height);
165
- s(gXLabels, text);
166
- });
167
- }
168
- const svgW = width + yLabelsLeftMinWidth + yLabelsRightMinWidth + zLabelsMinWidth + 4 * PADDING;
169
- const svgH = height + xLabelsMinHeight + (xLabels.length > 0 ? 3 : 2) * PADDING;
170
- s(gGraph, { transform: `translate(${yLabelsLeftMinWidth + PADDING} ${PADDING})` });
171
- gYLabelsL.forEach((g) => s(g, { transform: `translate(${yLabelsLeftMinWidth} ${PADDING})` }));
172
- gYLabelsR.forEach((g) => s(g, { transform: `translate(${width + yLabelsLeftMinWidth + 2 * PADDING} ${PADDING})` }));
173
- s(gZColors, { transform: `translate(${width + yLabelsLeftMinWidth + yLabelsRightMinWidth + 3 * PADDING} ${PADDING})` });
174
- s(gXLabels, { transform: `translate(${yLabelsLeftMinWidth + PADDING} ${height + 2 * PADDING})` });
175
- onclick(0);
176
- 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]);
177
- };
178
-
179
- // src/nnn/eq.ts
180
- var eq = (x, y) => {
181
- if (x === y) {
182
- return true;
183
- }
184
- const xConstructor = x?.constructor;
185
- if (xConstructor === y?.constructor) {
186
- if (xConstructor === Number) {
187
- return isNaN(x) && isNaN(y) || +x === +y;
188
- }
189
- if (xConstructor === Date) {
190
- return +x === +y;
191
- }
192
- if (xConstructor === String || xConstructor === RegExp) {
193
- return "" + x === "" + y;
194
- }
195
- if (xConstructor === Array) {
196
- return x.length === y.length && x.every((item, index) => eq(item, y[index]));
197
- }
198
- if (xConstructor === Object) {
199
- const keysOfX = Object.keys(x);
200
- return keysOfX.length === Object.keys(y).length && keysOfX.every((key) => eq(x[key], y[key]));
201
- }
202
- if (xConstructor === Set || xConstructor === Map) {
203
- if (x.size !== y.size) {
204
- return false;
205
- }
206
- const xa = [...x];
207
- const ya = [...y];
208
- return xa.every((xv) => ya.find((yv) => eq(xv, yv)));
209
- }
210
- }
211
- return false;
212
- };
213
-
214
- // src/nnn/escape.ts
215
- var escapeValues = (escapeMap, values) => values.map((value) => (escapeMap.get(value?.constructor) ?? escapeMap.get(undefined))?.(value) ?? "");
216
- var escape = (escapeMap, template, ...values) => String.raw(template, ...escapeValues(escapeMap, values));
106
+ var svgUse = (id, ...args) => s("svg", ["use", { "xlink:href": "#" + id }], ...args);
217
107
 
218
108
  // src/nnn/fixTypography.ts
219
109
  var TAGS_TO_SKIP = ["IFRAME", "NOSCRIPT", "PRE", "SCRIPT", "STYLE", "TEXTAREA"];
@@ -398,6 +288,7 @@ var uuid1 = ({
398
288
  };
399
289
  export {
400
290
  uuid1,
291
+ svgUse,
401
292
  s,
402
293
  refsInfo,
403
294
  pro,
@@ -415,6 +306,5 @@ export {
415
306
  fixTypography,
416
307
  escapeValues,
417
308
  escape,
418
- eq,
419
- chartable
309
+ eq
420
310
  };
package/package.json CHANGED
@@ -43,5 +43,5 @@
43
43
  "types": "nnn.d.ts",
44
44
  "name": "@jackens/nnn",
45
45
  "type": "module",
46
- "version": "2024.3.3"
46
+ "version": "2024.3.8"
47
47
  }
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.3</code></sub>
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`.
@@ -53,7 +51,7 @@ import { «something» } from './node_modules/@jackens/nnn/nnn.js'
53
51
  - `h`: A lightweight [HyperScript](https://github.com/hyperhype/hyperscript)-style helper for creating and modifying `HTMLElement`s (see also `s`).
54
52
  - `has`: A replacement for the `in` operator (not to be confused with the `for-in` loop) that works properly.
55
53
  - `is`: A helper that checks if the given argument is of a certain type.
56
- - `jc`: A simple CSS-in-JS helper.
54
+ - `jc`: A simple JS-to-CSS (aka CSS-in-JS) helper.
57
55
  - `jsOnParse`: `JSON.parse` with “JavaScript turned on”.
58
56
  - `locale`: Language translations helper.
59
57
  - `nanolight`: A generic helper for syntax highlighting (see also `nanolightJs`).
@@ -64,6 +62,7 @@ import { «something» } from './node_modules/@jackens/nnn/nnn.js'
64
62
  - `pro`: A helper that protects calls to nested properties by a `Proxy` that initializes non-existent values with an empty object.
65
63
  - `refsInfo`: A helper that provides information about the given `refs`.
66
64
  - `s`: A lightweight [HyperScript](https://github.com/hyperhype/hyperscript)-style helper for creating and modifying `SVGElement`s (see also `h`).
65
+ - `svgUse`: A convenient shortcut for `s('svg', ['use', { 'xlink:href': '#' + id }], ...args)`.
67
66
  - `uuid1`: A helper that generates a UUID v1 identifier (with a creation timestamp).
68
67
 
69
68
  ### EscapeMap
@@ -108,41 +107,6 @@ type JcRoot = Partial<Record<PropertyKey, JcNode>>;
108
107
 
109
108
  The type of arguments of the `jc` helper.
110
109
 
111
- ### chartable
112
-
113
- ```ts
114
- const chartable: ({ table, headerColumn, xGap, xLabelsMinHeight, xLabelsRotate, xReverse, yGap, yLabelsLeftMinWidth, yLabelsRightMinWidth, yMax, zLabelsMinWidth, zyMappings }: {
115
- table: HTMLTableElement;
116
- headerColumn?: boolean | undefined;
117
- xGap?: number | undefined;
118
- xLabelsMinHeight?: number | undefined;
119
- xLabelsRotate?: boolean | undefined;
120
- xReverse?: boolean | undefined;
121
- yGap?: number | undefined;
122
- yLabelsLeftMinWidth?: number | undefined;
123
- yLabelsRightMinWidth?: number | undefined;
124
- yMax?: number | undefined;
125
- zLabelsMinWidth?: number | undefined;
126
- zyMappings?: [(label: string) => number, (value: number) => string][] | undefined;
127
- }) => SVGSVGElement;
128
- ```
129
-
130
- A helper for creating a chart based on a table (conf. <https://jackens.github.io/nnn/chartable/>).
131
-
132
- Options:
133
- - `table`: `HTMLTableElement` to extract data, data series labels and X axis labels
134
- - `headerColumn`: flag indicating that `table` has a header column with X axis labels
135
- - `xGap`: X axis spacing
136
- - `xLabelsMinHeight`: minimal height of X axis labels
137
- - `xLabelsRotate`: flag to rotate X axis labels
138
- - `xReverse`: flag to reverse all data series
139
- - `yGap`: Y axis spacing
140
- - `yLabelsLeftMinWidth`: minimal width of Y axis left labels
141
- - `yLabelsRightMinWidth`: minimal width of Y axis right labels
142
- - `yMax`: number of Y axis lines
143
- - `zLabelsMinWidth`: minimal width of data series labels
144
- - `zyMappings`: mappings per data series
145
-
146
110
  ### eq
147
111
 
148
112
  ```ts
@@ -459,7 +423,7 @@ expect(is(Number, num)).toBeTrue()
459
423
  const jc: (root: JcRoot, splitter?: string) => string;
460
424
  ```
461
425
 
462
- A simple CSS-in-JS helper.
426
+ A simple JS-to-CSS (aka CSS-in-JS) helper.
463
427
 
464
428
  The `root` parameter provides a hierarchical description of CSS rules.
465
429
 
@@ -957,6 +921,14 @@ A lightweight [HyperScript](https://github.com/hyperhype/hyperscript)-style help
957
921
  - All other arguments of type `string`/`number` are converted to `Text` nodes and appended to the element being created or modified.
958
922
  - All other arguments of type `HArgs` are passed to `s` and the results are appended to the element being created or modified.
959
923
 
924
+ ### svgUse
925
+
926
+ ```ts
927
+ const svgUse: (id: string, ...args: HArgs1[]) => SVGSVGElement;
928
+ ```
929
+
930
+ A convenient shortcut for `s('svg', ['use', { 'xlink:href': '#' + id }], ...args)`.
931
+
960
932
  ### uuid1
961
933
 
962
934
  ```ts