@rse/gradia 1.2.4 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -36
- package/dst/gradia-api-config.d.ts +8 -0
- package/dst/gradia-api-config.js +10 -0
- package/dst/gradia-api-render-base.d.ts +4 -6
- package/dst/gradia-api-render-base.js +9 -6
- package/dst/gradia-api-render-container.js +5 -5
- package/dst/gradia-api-render-edge.d.ts +3 -0
- package/dst/gradia-api-render-edge.js +11 -1
- package/dst/gradia-api-render-node.d.ts +7 -7
- package/dst/gradia-api-render-node.js +25 -15
- package/dst/gradia-api-render-svg.js +142 -100
- package/dst/gradia-api-type-graph.js +19 -17
- package/dst/gradia-api-type-hub.js +88 -32
- package/dst/gradia-api.umd.js +2 -2
- package/dst/gradia-cli.js +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -307,40 +307,51 @@ command-line options (`font-embed` being command-line only, see below).
|
|
|
307
307
|
The recognized options, and their default values, are:
|
|
308
308
|
|
|
309
309
|
```
|
|
310
|
-
font-family Helvetica size-node-
|
|
311
|
-
font-embed false size-
|
|
312
|
-
color-node-regular-name #336699 size-
|
|
313
|
-
color-node-regular-box #e0f0ff size-edge-
|
|
314
|
-
color-node-regular-border #c0d0e0 size-edge-
|
|
315
|
-
color-node-primary-name #ffffff
|
|
316
|
-
color-node-primary-box #336699
|
|
317
|
-
color-node-primary-border #003366
|
|
318
|
-
color-node-ghost-name #666666
|
|
319
|
-
color-node-ghost-box #f0f0f0
|
|
320
|
-
color-node-ghost-border #a0a0a0 graph-
|
|
321
|
-
color-node-self-name #444444 graph-
|
|
322
|
-
color-node-self-box #e0e0e0 graph-
|
|
323
|
-
color-node-self-border #707070 graph-
|
|
324
|
-
color-group-name #6699cc graph-
|
|
325
|
-
color-group-box #f4f8fc graph-node-
|
|
326
|
-
color-group-border #c0d0e0
|
|
327
|
-
color-container-name #666666
|
|
328
|
-
color-container-box #f6f6f6 hub-
|
|
329
|
-
color-container-border #a0a0a0 hub-
|
|
330
|
-
color-edge-line #999999
|
|
331
|
-
color-edge-name #333333
|
|
332
|
-
color-edge-arity #333333
|
|
333
|
-
color-edge-halo #ffffff grid-
|
|
334
|
-
size-
|
|
335
|
-
size-
|
|
336
|
-
size-
|
|
310
|
+
font-family Helvetica size-node-width-min 220
|
|
311
|
+
font-embed false size-node-width-max 0
|
|
312
|
+
color-node-regular-name #336699 size-node-height-scale 2.25
|
|
313
|
+
color-node-regular-box #e0f0ff size-edge-corner-radius 20
|
|
314
|
+
color-node-regular-border #c0d0e0 size-edge-hop-radius 8
|
|
315
|
+
color-node-primary-name #ffffff size-edge-track-gap 12
|
|
316
|
+
color-node-primary-box #336699 size-edge-port-gap 24
|
|
317
|
+
color-node-primary-border #003366 group-box-padding 30
|
|
318
|
+
color-node-ghost-name #666666 group-box-gap 40
|
|
319
|
+
color-node-ghost-box #f0f0f0 container-box-padding 30
|
|
320
|
+
color-node-ghost-border #a0a0a0 graph-columns-max 4
|
|
321
|
+
color-node-self-name #444444 graph-channel-width-max 140
|
|
322
|
+
color-node-self-box #e0e0e0 graph-channel-width-min 24
|
|
323
|
+
color-node-self-border #707070 graph-gutter-height-max 90
|
|
324
|
+
color-group-name #6699cc graph-gutter-height-min 20
|
|
325
|
+
color-group-box #f4f8fc graph-node-separation 30
|
|
326
|
+
color-group-border #c0d0e0 graph-rank-separation 60
|
|
327
|
+
color-container-name #666666 graph-node-degree-max 3
|
|
328
|
+
color-container-box #f6f6f6 hub-channel-width-max 340
|
|
329
|
+
color-container-border #a0a0a0 hub-channel-width-min 240
|
|
330
|
+
color-edge-line #999999 hub-node-gap 20
|
|
331
|
+
color-edge-name #333333 hub-node-degree-max 3
|
|
332
|
+
color-edge-arity #333333 hub-node-count-max 0
|
|
333
|
+
color-edge-halo #ffffff grid-columns-max 4
|
|
334
|
+
size-font-node 30 grid-columns-min 3
|
|
335
|
+
size-font-type 16 grid-gap-horizontal 40
|
|
336
|
+
size-font-prop 22 grid-gap-vertical 20
|
|
337
|
+
size-font-edge 16 grid-node-width-equal true
|
|
338
|
+
size-font-arity 16 grid-node-height-equal true
|
|
339
|
+
size-canvas-margin 40
|
|
337
340
|
```
|
|
338
341
|
|
|
339
342
|
The `size-*`, `group-*`, `container-*`, `graph-*`, `hub-*`, and
|
|
340
|
-
`grid-*` options control the rendering geometry (
|
|
341
|
-
box sizing, edge routing, group and container box spacing,
|
|
342
|
-
per-diagram-type layout) and take non-negative numbers, except
|
|
343
|
-
booleans `grid-node-width-equal` and `grid-node-height-equal`.
|
|
343
|
+
`grid-*` options control the rendering geometry (text font sizes, canvas
|
|
344
|
+
margin, node box sizing, edge routing, group and container box spacing,
|
|
345
|
+
and the per-diagram-type layout) and take non-negative numbers, except
|
|
346
|
+
the booleans `grid-node-width-equal` and `grid-node-height-equal`.
|
|
347
|
+
|
|
348
|
+
The `size-font-*` options control the font sizes of the node names
|
|
349
|
+
(`size-font-node`), the node types (`size-font-type`), the node
|
|
350
|
+
properties (`size-font-prop`), the edge names (`size-font-edge`), and
|
|
351
|
+
the edge arities (`size-font-arity`). They are geometry options, as they
|
|
352
|
+
size the node boxes and the channels the edge labels land in: the line
|
|
353
|
+
heights inside a node box, the head of a container box, and the width
|
|
354
|
+
demand of a channel all track their font size.
|
|
344
355
|
|
|
345
356
|
The `size-node-width-max` option additionally enables the word-wrapping
|
|
346
357
|
of the node box texts: given a positive value, the node name, its type,
|
|
@@ -359,11 +370,16 @@ like all channels by the edges routed through them.
|
|
|
359
370
|
|
|
360
371
|
The `size-edge-port-gap` option controls the distance of the edge
|
|
361
372
|
attachment ports along a node side, and hence the vertical distance of
|
|
362
|
-
the parallel edges and their labels.
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
distance per additional edge
|
|
373
|
+
the parallel edges and their labels. A node box grows in height until
|
|
374
|
+
it holds all of its ports at that distance, so the edges of a node stay
|
|
375
|
+
readably apart however short its textual content is, and a node side
|
|
376
|
+
carrying more edges than `graph-node-degree-max` or
|
|
377
|
+
`hub-node-degree-max` grows by this very distance per additional edge
|
|
378
|
+
on top of that. In a diagram whose edges carry arities, the distance is
|
|
379
|
+
raised to what those labels demand, as an arity sits beside its own
|
|
380
|
+
edge line and has to stay clear of the arrow head above it. Only the
|
|
381
|
+
fixed-size box of a container placeholder cannot grow and hence packs
|
|
382
|
+
its ports closer together instead.
|
|
367
383
|
|
|
368
384
|
The `graph-channel-width-*` and `graph-gutter-height-*` options control
|
|
369
385
|
the spacing of a `graph` diagram: its inter-column channels and
|
|
@@ -376,6 +392,19 @@ unlabeled diagram can be spaced out, while the `-max` options cap it, so
|
|
|
376
392
|
that neither many parallel edges nor a long label can push the nodes
|
|
377
393
|
apart without bound.
|
|
378
394
|
|
|
395
|
+
The `hub-node-count-max` option controls the wrapping of the input and
|
|
396
|
+
output column of a `hub` diagram: given a positive value, a column of
|
|
397
|
+
more nodes than this wraps into two staggered sub-columns, whose nodes
|
|
398
|
+
alternate between the outer sub-column and the inner one, with every
|
|
399
|
+
outer node vertically centered onto a gap between two inner nodes,
|
|
400
|
+
through which its edges reach the primary node. This trades nearly
|
|
401
|
+
half of the height of a large diagram for additional width. The option
|
|
402
|
+
decides for the larger of the two columns alone, as the height of the
|
|
403
|
+
diagram follows that one: the node count of its tallest resulting
|
|
404
|
+
(sub-)column implicitly caps the smaller column, which hence wraps
|
|
405
|
+
exactly if that lowers the height of the diagram. The value `0`
|
|
406
|
+
disables the wrapping entirely.
|
|
407
|
+
|
|
379
408
|
The `grid-columns-min` and `grid-columns-max` options control the column
|
|
380
409
|
count of a `grid` diagram, which by default is derived from the node
|
|
381
410
|
count as a roughly square grid: `grid-columns-min` raises the derived
|
|
@@ -404,6 +433,21 @@ hard-coded into the SVG instead. The resulting precedence is: first
|
|
|
404
433
|
`#config`/`--config`, then the CSS custom property `--gradia-<option>`,
|
|
405
434
|
and finally the built-in default.
|
|
406
435
|
|
|
436
|
+
The `size-font-*` options are embedded into the generated SVG, too, but
|
|
437
|
+
as they also sized the layout, the SVG always references the CSS custom
|
|
438
|
+
property `--gradia-<option>` and falls back to the *effective* value
|
|
439
|
+
(configured or default). An override at display time hence retains the
|
|
440
|
+
layout of the configured size and should stay close to it.
|
|
441
|
+
|
|
442
|
+
The styling is not repeated on every element of the generated SVG, but
|
|
443
|
+
declared once as CSS classes in a `<style>` element, which the elements
|
|
444
|
+
reference. A class is named `gradia-<digest>` after the digest of its
|
|
445
|
+
own declarations, so equal declarations always yield the very same
|
|
446
|
+
class. As the class names are global once multiple diagrams are embedded
|
|
447
|
+
into the very same document, the embedding document can hence strip the
|
|
448
|
+
`<style>` elements off the diagrams and declare the union of their
|
|
449
|
+
rules (one per line) a single time on its own.
|
|
450
|
+
|
|
407
451
|
The `font-family` option is either a built-in font family, a plain font
|
|
408
452
|
family name, or the path to a WOFF2 file. The only built-in font family
|
|
409
453
|
is `Source Sans 3`, shipped as a variable WOFF2 file by the NPM
|
|
@@ -23,6 +23,11 @@ export declare const configDefaults: {
|
|
|
23
23
|
"color-edge-name": string;
|
|
24
24
|
"color-edge-arity": string;
|
|
25
25
|
"color-edge-halo": string;
|
|
26
|
+
"size-font-node": number;
|
|
27
|
+
"size-font-type": number;
|
|
28
|
+
"size-font-prop": number;
|
|
29
|
+
"size-font-edge": number;
|
|
30
|
+
"size-font-arity": number;
|
|
26
31
|
"size-canvas-margin": number;
|
|
27
32
|
"size-node-width-min": number;
|
|
28
33
|
"size-node-width-max": number;
|
|
@@ -46,6 +51,7 @@ export declare const configDefaults: {
|
|
|
46
51
|
"hub-channel-width-min": number;
|
|
47
52
|
"hub-node-gap": number;
|
|
48
53
|
"hub-node-degree-max": number;
|
|
54
|
+
"hub-node-count-max": number;
|
|
49
55
|
"grid-columns-max": number;
|
|
50
56
|
"grid-columns-min": number;
|
|
51
57
|
"grid-gap-horizontal": number;
|
|
@@ -57,6 +63,8 @@ export type Config = typeof configDefaults;
|
|
|
57
63
|
export type ConfigEmbedded = {
|
|
58
64
|
[K in keyof Config]: Config[K] extends string ? K : never;
|
|
59
65
|
}[keyof Config];
|
|
66
|
+
export type ConfigFontSize = "size-font-node" | "size-font-type" | "size-font-prop" | "size-font-edge" | "size-font-arity";
|
|
67
|
+
export declare const cssSizeOf: (config: Config, key: ConfigFontSize) => string;
|
|
60
68
|
export declare const cssValueOf: (explicit: Partial<Config>, key: ConfigEmbedded) => string;
|
|
61
69
|
export declare const validateConfig: (raw: Record<string, unknown>, options: {
|
|
62
70
|
trusted: boolean;
|
package/dst/gradia-api-config.js
CHANGED
|
@@ -33,6 +33,11 @@ export const configDefaults = {
|
|
|
33
33
|
"color-edge-name": "#333333",
|
|
34
34
|
"color-edge-arity": "#333333",
|
|
35
35
|
"color-edge-halo": "#ffffff",
|
|
36
|
+
"size-font-node": 30, /* font size of node names */
|
|
37
|
+
"size-font-type": 16, /* font size of node types */
|
|
38
|
+
"size-font-prop": 22, /* font size of node properties */
|
|
39
|
+
"size-font-edge": 16, /* font size of edge names */
|
|
40
|
+
"size-font-arity": 16, /* font size of edge arities */
|
|
36
41
|
"size-canvas-margin": 40, /* outer margin of the canvas */
|
|
37
42
|
"size-node-width-min": 220, /* minimum node box width */
|
|
38
43
|
"size-node-width-max": 0, /* maximum node box width (0 = unlimited) */
|
|
@@ -56,6 +61,7 @@ export const configDefaults = {
|
|
|
56
61
|
"hub-channel-width-min": 240, /* hub: min width of column channels */
|
|
57
62
|
"hub-node-gap": 20, /* hub: vertical gap of stacked nodes */
|
|
58
63
|
"hub-node-degree-max": 3, /* hub: max edges/side before growth */
|
|
64
|
+
"hub-node-count-max": 0, /* hub: max nodes/column before wrapping */
|
|
59
65
|
"grid-columns-max": 4, /* grid: max side-by-side tiles */
|
|
60
66
|
"grid-columns-min": 3, /* grid: min side-by-side tiles */
|
|
61
67
|
"grid-gap-horizontal": 40, /* grid: horizontal gap between tiles */
|
|
@@ -63,6 +69,10 @@ export const configDefaults = {
|
|
|
63
69
|
"grid-node-width-equal": true, /* grid: force equal width of node boxes */
|
|
64
70
|
"grid-node-height-equal": true /* grid: force equal height of node boxes */
|
|
65
71
|
};
|
|
72
|
+
/* resolve a font size option into a CSS value: the effective value is
|
|
73
|
+
the fallback of the CSS custom property "--gradia-<option>", as it
|
|
74
|
+
also sized the layout, so an override should stay close to it */
|
|
75
|
+
export const cssSizeOf = (config, key) => `var(--gradia-${key}, ${config[key]}px)`;
|
|
66
76
|
/* resolve a directly embedded configuration option into a CSS value:
|
|
67
77
|
an explicitly configured value is hard-coded (stripped of the
|
|
68
78
|
characters which would end the CSS declaration, to prevent a CSS
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { Node, Edge } from "./gradia-api-model.js";
|
|
2
|
-
import { ConfigEmbedded } from "./gradia-api-config.js";
|
|
3
|
-
export declare const FS_NAME = 30;
|
|
4
|
-
export declare const FS_TYPE = 16;
|
|
5
|
-
export declare const FS_ATTR = 22;
|
|
6
|
-
export declare const FS_EDGE = 16;
|
|
7
|
-
export declare const FS_ARITY = 16;
|
|
2
|
+
import { Config, ConfigEmbedded } from "./gradia-api-config.js";
|
|
8
3
|
export declare const FS_GROUP = 24;
|
|
9
4
|
export declare const ARITY_OFF = 24;
|
|
5
|
+
export declare const ARITY_PAD = 4;
|
|
6
|
+
export declare const ARROW_H = 17;
|
|
7
|
+
export declare const arityHeight: (config: Config) => number;
|
|
10
8
|
export declare const textWidth: (text: string, size: number) => number;
|
|
11
9
|
export declare const textWrap: (text: string, size: number, maxWidth: number) => string[];
|
|
12
10
|
export declare const escapeXML: (text: string) => string;
|
|
@@ -3,17 +3,20 @@
|
|
|
3
3
|
** Copyright (c) 2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
4
|
** Distributed under MIT license <https://spdx.org/licenses/MIT.html>
|
|
5
5
|
*/
|
|
6
|
-
/* rendering font size
|
|
6
|
+
/* rendering font size constant of the group tags (the font sizes of
|
|
7
|
+
the node and edge texts and the further geometry are configurable,
|
|
7
8
|
see the "size-*" and per-diagram-type options in the configuration) */
|
|
8
|
-
export const FS_NAME = 30; /* font size of node names */
|
|
9
|
-
export const FS_TYPE = 16; /* font size of node types */
|
|
10
|
-
export const FS_ATTR = 22; /* font size of node attributes */
|
|
11
|
-
export const FS_EDGE = 16; /* font size of edge labels */
|
|
12
|
-
export const FS_ARITY = 16; /* font size of edge arities */
|
|
13
9
|
export const FS_GROUP = 24; /* font size of group tags */
|
|
14
10
|
/* the setback of an edge arity label from the arrow head it annotates
|
|
15
11
|
(shared by the SVG rendering and the graph channel sizing) */
|
|
16
12
|
export const ARITY_OFF = 24;
|
|
13
|
+
/* the offset of an edge arity label from its own edge line, and the
|
|
14
|
+
rendered height of an arrow head (the 21px marker of the SVG
|
|
15
|
+
covering 8 of the 10 units of its own viewBox) */
|
|
16
|
+
export const ARITY_PAD = 4;
|
|
17
|
+
export const ARROW_H = 17;
|
|
18
|
+
/* the height of the box an edge arity label occupies */
|
|
19
|
+
export const arityHeight = (config) => Math.round(config["size-font-arity"] * 0.8);
|
|
17
20
|
/* estimate rendered text width (no canvas available under Node,
|
|
18
21
|
factor tuned for the average glyph advance of Source Sans 3) */
|
|
19
22
|
export const textWidth = (text, size) => text.length * size * 0.52;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
** Copyright (c) 2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
4
|
** Distributed under MIT license <https://spdx.org/licenses/MIT.html>
|
|
5
5
|
*/
|
|
6
|
-
import { FS_GROUP,
|
|
6
|
+
import { FS_GROUP, textWidth } from "./gradia-api-render-base.js";
|
|
7
7
|
import { parentOf, containerTypeOf, containerHead, typeOf, defaultStyleOf } from "./gradia-api-render-node.js";
|
|
8
8
|
import { simplifyPoly } from "./gradia-api-render-edge.js";
|
|
9
9
|
/* determine the containment tree of the graph (in order of node
|
|
@@ -116,10 +116,10 @@ export const renderContained = async (graph, containment, type, types, config) =
|
|
|
116
116
|
/* the box size of a container placeholder: its content plus
|
|
117
117
|
the padding and the head, at least as wide as its tag */
|
|
118
118
|
const sizeOf = (member, b) => {
|
|
119
|
-
const tag = Math.max(textWidth(member.name, FS_GROUP), textWidth(typeOf(member) ?? "",
|
|
119
|
+
const tag = Math.max(textWidth(member.name, FS_GROUP), textWidth(typeOf(member) ?? "", config["size-font-type"]));
|
|
120
120
|
return {
|
|
121
121
|
w: Math.max(b.maxX - b.minX, tag) + pad * 2,
|
|
122
|
-
h: b.maxY - b.minY + pad * 2 + containerHead(member)
|
|
122
|
+
h: b.maxY - b.minY + pad * 2 + containerHead(member, config)
|
|
123
123
|
};
|
|
124
124
|
};
|
|
125
125
|
/* compose a laid out containment level with its inner levels: the
|
|
@@ -148,7 +148,7 @@ export const renderContained = async (graph, containment, type, types, config) =
|
|
|
148
148
|
const k = parts.push({
|
|
149
149
|
layout: level.layout,
|
|
150
150
|
dx: cx - w / 2 + pad - level.bounds.minX,
|
|
151
|
-
dy: cy - h / 2 + containerHead(member) + pad - level.bounds.minY
|
|
151
|
+
dy: cy - h / 2 + containerHead(member, config) + pad - level.bounds.minY
|
|
152
152
|
}) - 1;
|
|
153
153
|
for (const node of level.layout.nodes)
|
|
154
154
|
partOf.set(node.id, k);
|
|
@@ -237,7 +237,7 @@ export const renderContained = async (graph, containment, type, types, config) =
|
|
|
237
237
|
attaches at: the position of its inner gate */
|
|
238
238
|
const portOffset = (member, y) => {
|
|
239
239
|
const { bounds } = inner.get(member.id);
|
|
240
|
-
return (y - bounds.minY) + containerHead(member) + pad - sizeOf(member, bounds).h / 2;
|
|
240
|
+
return (y - bounds.minY) + containerHead(member, config) + pad - sizeOf(member, bounds).h / 2;
|
|
241
241
|
};
|
|
242
242
|
/* derive the edges of this level: the edges lifted to it (both
|
|
243
243
|
endpoints in the subtree, but not both inside the same member
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Edge } from "./gradia-api-model.js";
|
|
2
|
+
import { Config } from "./gradia-api-config.js";
|
|
2
3
|
import { Poly } from "./gradia-api-render-base.js";
|
|
4
|
+
export declare const PORT_PAD = 10;
|
|
5
|
+
export declare const portGapOf: (edges: Edge[], config: Config) => number;
|
|
3
6
|
export declare const simplifyPoly: (pts: Poly) => Poly;
|
|
4
7
|
export type Side = "e" | "w" | "n";
|
|
5
8
|
export declare const assignPorts: (edges: Edge[], sides: {
|
|
@@ -3,8 +3,18 @@
|
|
|
3
3
|
** Copyright (c) 2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
4
|
** Distributed under MIT license <https://spdx.org/licenses/MIT.html>
|
|
5
5
|
*/
|
|
6
|
+
import { ARITY_PAD, ARROW_H, arityHeight } from "./gradia-api-render-base.js";
|
|
6
7
|
/* edge attachment port geometry */
|
|
7
|
-
const PORT_PAD = 10; /* padding of the port band inside the box */
|
|
8
|
+
export const PORT_PAD = 10; /* padding of the port band inside the box */
|
|
9
|
+
/* the separation of the adjacent ports of a node side: the configured
|
|
10
|
+
one, raised to what the arity labels demand once any edge carries
|
|
11
|
+
one, as such a label sits ARITY_PAD below the line of the port above
|
|
12
|
+
it, whose arrow head reaches down by half its height, and needs two
|
|
13
|
+
more ARITY_PAD of air between the two, so it stays attributable to
|
|
14
|
+
its own arrow instead of drifting into the neighboring one */
|
|
15
|
+
export const portGapOf = (edges, config) => edges.some((edge) => edge.arity !== undefined) ?
|
|
16
|
+
Math.max(config["size-edge-port-gap"], Math.ceil(3 * ARITY_PAD + arityHeight(config) + ARROW_H / 2)) :
|
|
17
|
+
config["size-edge-port-gap"];
|
|
8
18
|
/* drop duplicate and collinear intermediate points of a polyline */
|
|
9
19
|
export const simplifyPoly = (pts) => {
|
|
10
20
|
/* drop the collinear intermediate points first, as collapsing
|
|
@@ -7,19 +7,19 @@ export declare const typeOf: (node: Node) => string | undefined;
|
|
|
7
7
|
export declare const parentOf: (node: Node) => string | undefined;
|
|
8
8
|
export declare const containerTypeOf: (node: Node) => string | undefined;
|
|
9
9
|
export declare const HEAD_H = 34;
|
|
10
|
-
export declare const HEAD_T
|
|
11
|
-
export declare const containerHead: (node: Node) => number;
|
|
10
|
+
export declare const HEAD_T: (config: Config) => number;
|
|
11
|
+
export declare const containerHead: (node: Node, config: Config) => number;
|
|
12
12
|
export declare const orderOf: (node: Node) => number | undefined;
|
|
13
13
|
export declare const attrsOfNode: (node: Node) => Attr[];
|
|
14
14
|
export declare const urlOf: (node: Node) => string | undefined;
|
|
15
15
|
export declare const PAD_W = 18;
|
|
16
|
-
export declare const MIN_H = 56;
|
|
17
|
-
export declare const NAME_H = 34;
|
|
18
|
-
export declare const ATTR_H = 30;
|
|
19
16
|
export declare const ATTR_P = 12;
|
|
20
17
|
export declare const ATTR_B = 20;
|
|
21
|
-
export declare const
|
|
22
|
-
export declare const
|
|
18
|
+
export declare const MIN_H: (config: Config) => number;
|
|
19
|
+
export declare const NAME_H: (config: Config) => number;
|
|
20
|
+
export declare const ATTR_H: (config: Config) => number;
|
|
21
|
+
export declare const TYPE_H: (config: Config) => number;
|
|
22
|
+
export declare const TYPE_D: (config: Config) => number;
|
|
23
23
|
export interface NodeLines {
|
|
24
24
|
name: string[];
|
|
25
25
|
type: string[];
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
** Copyright (c) 2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
4
|
** Distributed under MIT license <https://spdx.org/licenses/MIT.html>
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { textWidth, textWrap } from "./gradia-api-render-base.js";
|
|
7
7
|
/* the special "primary" annotation (rendered as a distinct node coloring) */
|
|
8
8
|
export const isPrimary = (node) => node.attrs.findLast((attr) => attr.key === "primary")?.val === "true";
|
|
9
9
|
/* the default node box coloring (primary node boxes get a distinct one) */
|
|
@@ -26,8 +26,8 @@ export const containerTypeOf = (node) => node.attrs.findLast((attr) => attr.key
|
|
|
26
26
|
/* container box head geometry (the head holds the name tag and the
|
|
27
27
|
optional type line above it) */
|
|
28
28
|
export const HEAD_H = 34; /* height of the container box head */
|
|
29
|
-
export const HEAD_T =
|
|
30
|
-
export const containerHead = (node) => HEAD_H + (typeOf(node) !== undefined ? HEAD_T : 0);
|
|
29
|
+
export const HEAD_T = (config) => config["size-font-type"] + 4; /* extra head height of a type line */
|
|
30
|
+
export const containerHead = (node, config) => HEAD_H + (typeOf(node) !== undefined ? HEAD_T(config) : 0);
|
|
31
31
|
/* the special "order" annotation (the explicit top-down placement
|
|
32
32
|
order of the node, a finite number, an invalid value being ignored) */
|
|
33
33
|
export const orderOf = (node) => {
|
|
@@ -57,15 +57,24 @@ export const urlOf = (node) => {
|
|
|
57
57
|
return undefined;
|
|
58
58
|
return url;
|
|
59
59
|
};
|
|
60
|
-
/* node box text metrics (shared by the measuring and the text
|
|
60
|
+
/* node box text metrics (shared by the measuring and the text
|
|
61
|
+
placement), where the vertical ones track the font size of the text
|
|
62
|
+
they space out, so that a configured font size never lets a line
|
|
63
|
+
collide with its neighbor */
|
|
61
64
|
export const PAD_W = 18; /* left/right node box text padding */
|
|
62
|
-
export const MIN_H = 56; /* minimum node box height */
|
|
63
|
-
export const NAME_H = 34; /* height of an additional name line */
|
|
64
|
-
export const ATTR_H = 30; /* height of a single attribute line */
|
|
65
65
|
export const ATTR_P = 12; /* top padding of the attribute block */
|
|
66
66
|
export const ATTR_B = 20; /* bottom padding of the attribute block */
|
|
67
|
-
|
|
68
|
-
export const
|
|
67
|
+
/* minimum node box height (one name line plus its vertical padding) */
|
|
68
|
+
export const MIN_H = (config) => config["size-font-node"] + 26;
|
|
69
|
+
/* height of an additional name line */
|
|
70
|
+
export const NAME_H = (config) => config["size-font-node"] + 4;
|
|
71
|
+
/* height of a single attribute line */
|
|
72
|
+
export const ATTR_H = (config) => config["size-font-prop"] + 8;
|
|
73
|
+
/* extra height of a single type line */
|
|
74
|
+
export const TYPE_H = (config) => config["size-font-type"] + 10;
|
|
75
|
+
/* baseline distance of the type line to the name line (the cap height
|
|
76
|
+
of the name plus the line box of the type) */
|
|
77
|
+
export const TYPE_D = (config) => config["size-font-node"] * 0.72 + config["size-font-type"] * 0.90;
|
|
69
78
|
/* break the textual content of a node into its rendered lines, word-wrapped
|
|
70
79
|
to the configured maximum box width (shared by the measuring and the text
|
|
71
80
|
placement, so both can never disagree on the resulting line count) */
|
|
@@ -74,9 +83,9 @@ export const linesOfNode = (node, config) => {
|
|
|
74
83
|
config["size-node-width-max"] - PAD_W * 2 : 0;
|
|
75
84
|
const type = typeOf(node);
|
|
76
85
|
return {
|
|
77
|
-
name: textWrap(node.name,
|
|
78
|
-
type: type !== undefined ? textWrap(type,
|
|
79
|
-
attrs: attrsOfNode(node).flatMap((attr) => textWrap(`${attr.key}: ${attr.val}`,
|
|
86
|
+
name: textWrap(node.name, config["size-font-node"], max),
|
|
87
|
+
type: type !== undefined ? textWrap(type, config["size-font-type"], max) : [],
|
|
88
|
+
attrs: attrsOfNode(node).flatMap((attr) => textWrap(`${attr.key}: ${attr.val}`, config["size-font-prop"], max))
|
|
80
89
|
};
|
|
81
90
|
};
|
|
82
91
|
/* determine node box sizes from their textual content (boxes are
|
|
@@ -99,9 +108,10 @@ export const measureNodes = (nodes, config, scaleOf, fixed) => {
|
|
|
99
108
|
continue;
|
|
100
109
|
}
|
|
101
110
|
const lines = linesOfNode(node, config);
|
|
102
|
-
const w = Math.max(0, ...lines.name.map((line) => textWidth(line,
|
|
103
|
-
const h = MIN_H + (lines.name.length - 1) * NAME_H +
|
|
104
|
-
|
|
111
|
+
const w = Math.max(0, ...lines.name.map((line) => textWidth(line, config["size-font-node"])), ...lines.type.map((line) => textWidth(line, config["size-font-type"])), ...lines.attrs.map((line) => textWidth(line, config["size-font-prop"])));
|
|
112
|
+
const h = MIN_H(config) + (lines.name.length - 1) * NAME_H(config) +
|
|
113
|
+
lines.type.length * TYPE_H(config) +
|
|
114
|
+
(lines.attrs.length > 0 ? ATTR_P + (lines.attrs.length - 1) * ATTR_H(config) + ATTR_B : 0);
|
|
105
115
|
boxW.set(node.id, Math.max(config["size-node-width-min"], Math.ceil(w) + PAD_W * 2));
|
|
106
116
|
boxH.set(node.id, Math.max(h, Math.ceil(h * scaleOf(node))));
|
|
107
117
|
contentH.set(node.id, h);
|