reladraw 0.19.1 → 0.21.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 +2 -2
- package/SYNTAX.md +59 -8
- package/dist/ast.d.ts +26 -2
- package/dist/ast.js +15 -2
- package/dist/checks.d.ts +1 -0
- package/dist/checks.js +79 -5
- package/dist/cli.js +35 -12
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +8 -0
- package/dist/constrain.d.ts +56 -0
- package/dist/constrain.js +95 -0
- package/dist/grammar.js +2 -0
- package/dist/model.d.ts +16 -1
- package/dist/model.js +7 -0
- package/dist/page.d.ts +14 -0
- package/dist/page.js +488 -0
- package/dist/parser.js +69 -4
- package/dist/place.d.ts +74 -1
- package/dist/place.js +303 -18
- package/dist/render.d.ts +17 -0
- package/dist/render.js +101 -29
- package/dist/resolve.d.ts +14 -0
- package/dist/resolve.js +34 -7
- package/dist/themes.d.ts +1 -0
- package/dist/themes.js +1 -1
- package/package.json +1 -1
package/dist/parser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BUNDLE_KEYS, COLOR_KEYS, POINT_KEYS, REGION_KEYS, WAYS, describePoint, fractionAxis, DEFAULT_KEYS, DEFAULT_TARGETS, DIAGRAM_KEYS, DIRECTIONS, describePlacement, SIDE_AXIS, SIDES, TEXT_KEYS, MARK_TEXT_KEYS, POINT_TEXT_SIDES, LINE_KEYS, LINE_VALUES, CONTENTS_KEYS, EDGE_ORDER_KEYS, LAYOUT_KEYS, PLACEMENT_KEYS, BOUNDARY_PARTS, INWARD, MARKS, MARK_GLYPHS, OPPOSITE, isDirection, isPart, isPosition, listTargets, nameTarget, } from './ast.js';
|
|
1
|
+
import { BUNDLE_KEYS, COLOR_KEYS, POINT_KEYS, REGION_KEYS, WAYS, describePoint, fractionAxis, DEFAULT_KEYS, DEFAULT_TARGETS, DIAGRAM_KEYS, DIRECTIONS, describePlacement, SIDE_AXIS, SIDES, TEXT_KEYS, MARK_TEXT_KEYS, POINT_TEXT_SIDES, LINE_KEYS, LINE_VALUES, BORDER_KEYS, CONTENTS_KEYS, EDGE_ORDER_KEYS, LAYOUT_KEYS, PLACEMENT_KEYS, BOUNDARY_PARTS, INWARD, MARKS, MARK_GLYPHS, OPPOSITE, isDirection, isPart, isPosition, listTargets, nameTarget, } from './ast.js';
|
|
2
2
|
import { CHECK_DEFAULTS, CHECK_KINDS, setCheck } from './checks.js';
|
|
3
3
|
import { SourceError } from './errors.js';
|
|
4
4
|
import { STATEMENT_KEYWORDS } from './grammar.js';
|
|
@@ -309,6 +309,7 @@ const BRACKET_KEYS = {
|
|
|
309
309
|
text: TEXT_KEYS,
|
|
310
310
|
contents: CONTENTS_KEYS,
|
|
311
311
|
line: LINE_KEYS,
|
|
312
|
+
border: BORDER_KEYS,
|
|
312
313
|
checks: CHECK_KINDS,
|
|
313
314
|
};
|
|
314
315
|
/** How each bracketed key's error quotes itself back, and what it is about. */
|
|
@@ -316,6 +317,7 @@ const BRACKET_ABOUT = {
|
|
|
316
317
|
text: { kind: 'a text', example: 'color: theme-muted' },
|
|
317
318
|
contents: { kind: 'a `contents:` bracket', example: 'widths: match' },
|
|
318
319
|
line: { kind: 'a line', example: 'path: square' },
|
|
320
|
+
border: { kind: 'a border', example: 'thickness: thick' },
|
|
319
321
|
checks: { kind: 'a `checks:` bracket', example: 'edge-crossings: on' },
|
|
320
322
|
};
|
|
321
323
|
/**
|
|
@@ -702,7 +704,8 @@ function readAttr(tokens, at, attrs, line, subject) {
|
|
|
702
704
|
// A line's color is stored where `line: red` puts it, so the short form
|
|
703
705
|
// and the bracket are one key: a style's `line: red` merges under an
|
|
704
706
|
// edge's `line: (color: blue)`, and writing both on one line is a repeat.
|
|
705
|
-
|
|
707
|
+
// A border's color the same.
|
|
708
|
+
const stored = (key === 'line' || key === 'border') && inner === 'color' ? key : `${key}.${inner}`;
|
|
706
709
|
setOnce(attrs, stored, value, subject, line, { key: `${key}: (${inner}: …)`, value: (v) => v });
|
|
707
710
|
}
|
|
708
711
|
return read.next;
|
|
@@ -720,6 +723,16 @@ function readAttr(tokens, at, attrs, line, subject) {
|
|
|
720
723
|
setOnce(attrs, key, value.text, subject, line, { key, value: quoteOf });
|
|
721
724
|
return at + 2;
|
|
722
725
|
}
|
|
726
|
+
if (key === 'title') {
|
|
727
|
+
// A title is a text, and every text is quoted. Unquoted, its first word
|
|
728
|
+
// would be the title and the rest read as stray words.
|
|
729
|
+
const value = tokens[at + 1];
|
|
730
|
+
if (!value || !value.quoted) {
|
|
731
|
+
throw new SourceError(`${subject}: a title is written in quotes — \`title: "How a login reaches the database"\``, line);
|
|
732
|
+
}
|
|
733
|
+
setOnce(attrs, key, value.text, subject, line, { key, value: quoteOf });
|
|
734
|
+
return at + 2;
|
|
735
|
+
}
|
|
723
736
|
if (key === 'style') {
|
|
724
737
|
// `style: base, critical and alarm` — several bundles, applied in the order
|
|
725
738
|
// written, a later one winning where two set the same key. The list reads
|
|
@@ -768,7 +781,7 @@ function readAttr(tokens, at, attrs, line, subject) {
|
|
|
768
781
|
});
|
|
769
782
|
return next;
|
|
770
783
|
}
|
|
771
|
-
if (bracketKeys !== undefined && key !== 'text' && key !== 'line') {
|
|
784
|
+
if (bracketKeys !== undefined && key !== 'text' && key !== 'line' && key !== 'border') {
|
|
772
785
|
// `contents: match` names the part and then says one of its two properties
|
|
773
786
|
// without saying which. The brackets are what make the level shift visible,
|
|
774
787
|
// so there is no unbracketed spelling to fall back to.
|
|
@@ -821,6 +834,11 @@ function readAttr(tokens, at, attrs, line, subject) {
|
|
|
821
834
|
`\`line: (${property}: ${valueToken.text})\``, line);
|
|
822
835
|
}
|
|
823
836
|
}
|
|
837
|
+
if (key === 'border' && !valueToken.quoted && /^(thin|normal|thick|\d*\.?\d+(px)?)$/.test(valueToken.text)) {
|
|
838
|
+
// `border: red` is the short form of the color, as `line: red` is.
|
|
839
|
+
throw new SourceError(`\`border: ${valueToken.text}\` — \`border:\` on its own takes a color. Its thickness goes in its bracket: ` +
|
|
840
|
+
`\`border: (thickness: ${valueToken.text})\``, line);
|
|
841
|
+
}
|
|
824
842
|
if (key === 'align' && valueToken.text === 'widths') {
|
|
825
843
|
// Removed in 0.3.0. It was a size operation wearing an alignment's name,
|
|
826
844
|
// and its value set had one member — a flag in a property's clothes. Its
|
|
@@ -1238,7 +1256,15 @@ function parseNode(head, line) {
|
|
|
1238
1256
|
listed: NODE_TEXT_LISTED,
|
|
1239
1257
|
});
|
|
1240
1258
|
refuseNodeReads(bracket.values['reads'], subject, line);
|
|
1241
|
-
const
|
|
1259
|
+
const limits = [];
|
|
1260
|
+
const tail = parseTail(head, bracket.next, line, subject, (tokens, index) => {
|
|
1261
|
+
const word = tokens[index];
|
|
1262
|
+
if (word.quoted || word.text !== 'stays')
|
|
1263
|
+
return undefined;
|
|
1264
|
+
const read = readLimit(tokens, index, line, subject);
|
|
1265
|
+
limits.push(read.limit);
|
|
1266
|
+
return read.next;
|
|
1267
|
+
});
|
|
1242
1268
|
refuseTextKey(tail.attrs, subject, 'after the name', line);
|
|
1243
1269
|
return {
|
|
1244
1270
|
kind: 'node',
|
|
@@ -1248,10 +1274,43 @@ function parseNode(head, line) {
|
|
|
1248
1274
|
textAttrs: place === undefined ? bracket.values : { ...bracket.values, place },
|
|
1249
1275
|
...(titlePlacements.length > 0 ? { titlePlacements } : {}),
|
|
1250
1276
|
placements: tail.placements,
|
|
1277
|
+
...(limits.length > 0 ? { limits } : {}),
|
|
1251
1278
|
attrs: tail.attrs,
|
|
1252
1279
|
line,
|
|
1253
1280
|
};
|
|
1254
1281
|
}
|
|
1282
|
+
/**
|
|
1283
|
+
* `stays above laptop.photos (gap: wide)`: a limit (design.org 5.6). The words
|
|
1284
|
+
* after `stays` are read as a placement would be, then held to one straight
|
|
1285
|
+
* way and one target, since a limit says which side of one thing to keep to.
|
|
1286
|
+
*/
|
|
1287
|
+
function readLimit(tokens, at, line, subject) {
|
|
1288
|
+
const way = tokens[at + 1];
|
|
1289
|
+
const straight = ['above', 'below', 'left', 'right'];
|
|
1290
|
+
if (!way || way.quoted || !straight.includes(way.text)) {
|
|
1291
|
+
const diagonal = way !== undefined && !way.quoted && isDirection(way.text);
|
|
1292
|
+
throw new SourceError(diagonal
|
|
1293
|
+
? `${subject}: "stays ${way.text}" keeps to two sides at once — write one limit for each, such as \`stays above a stays right of a\``
|
|
1294
|
+
: `${subject}: "stays" is followed by the way it keeps to — stays above, stays below, stays left of or stays right of`, line);
|
|
1295
|
+
}
|
|
1296
|
+
const read = readPlacement(tokens, at + 1, line, subject, true);
|
|
1297
|
+
const placement = read.placement;
|
|
1298
|
+
if (placement.kind !== 'offset' || placement.own !== undefined || placement.gaps !== undefined) {
|
|
1299
|
+
throw new SourceError(`${subject}: "stays" names a way and one thing, such as \`stays above laptop.photos\``, line);
|
|
1300
|
+
}
|
|
1301
|
+
if (placement.targets.length !== 1) {
|
|
1302
|
+
throw new SourceError(`${subject}: "${describePlacement(placement)}" after "stays" names ${placement.targets.length} things — a limit keeps clear of one; write one for each`, line);
|
|
1303
|
+
}
|
|
1304
|
+
return {
|
|
1305
|
+
limit: {
|
|
1306
|
+
way: way.text,
|
|
1307
|
+
target: placement.targets[0],
|
|
1308
|
+
...(placement.gap !== undefined ? { gap: placement.gap } : {}),
|
|
1309
|
+
line,
|
|
1310
|
+
},
|
|
1311
|
+
next: read.next,
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1255
1314
|
/**
|
|
1256
1315
|
* A node's text takes the look keys and `reads:` (design.org 7.2). `at` is
|
|
1257
1316
|
* read only to be refused with the side form, so it isn't listed as one it takes.
|
|
@@ -2394,6 +2453,12 @@ function checkColorWord(value, subject, line) {
|
|
|
2394
2453
|
if (value.startsWith('theme-') && !THEME_COLORS.includes(value)) {
|
|
2395
2454
|
throw new SourceError(`${subject}: there is no theme color "${value}" — the theme colors are ${THEME_COLORS.join(', ')}`, line);
|
|
2396
2455
|
}
|
|
2456
|
+
// Hex digits without their `#` are no CSS color, so the viewer would draw nothing.
|
|
2457
|
+
if (/^([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(value)) {
|
|
2458
|
+
// `bad` and `face` are spelled in hex digits too, so without a digit it is only a guess.
|
|
2459
|
+
const fix = /\d/.test(value) ? 'a hex color is written' : 'if it is a hex color, write';
|
|
2460
|
+
throw new SourceError(`${subject}: \`${value}\` is not a color — ${fix} \`#${value}\``, line);
|
|
2461
|
+
}
|
|
2397
2462
|
}
|
|
2398
2463
|
function follows(tokens, at, word) {
|
|
2399
2464
|
const token = tokens[at];
|
package/dist/place.d.ts
CHANGED
|
@@ -130,6 +130,8 @@ export declare class Placer {
|
|
|
130
130
|
private readonly sizes;
|
|
131
131
|
private readonly frames;
|
|
132
132
|
private readonly outlines;
|
|
133
|
+
/** A container's box while a limit tries it out (`roomFor`), in place of its final one. */
|
|
134
|
+
private readonly trials;
|
|
133
135
|
private readonly working;
|
|
134
136
|
/** What is being worked out, innermost last, for reporting a circle. */
|
|
135
137
|
private readonly trail;
|
|
@@ -151,6 +153,8 @@ export declare class Placer {
|
|
|
151
153
|
private readonly origins;
|
|
152
154
|
/** Containers whose anchor is being placed on the page, so a box outside them is measured on the page rather than in their frame. */
|
|
153
155
|
private readonly onPage;
|
|
156
|
+
/** Boxes that anchor their frame on one axis only, when no box in it says nothing at all (`splitAnchors`). */
|
|
157
|
+
private readonly axisAnchors;
|
|
154
158
|
/** Named points and regions being worked out, innermost last, to refuse one defined through itself. */
|
|
155
159
|
private readonly expanding;
|
|
156
160
|
constructor(roots: LayoutNode[], byName: Map<string, LayoutNode>, facts: Map<LayoutNode, NodeFacts>,
|
|
@@ -181,6 +185,20 @@ export declare class Placer {
|
|
|
181
185
|
};
|
|
182
186
|
/** Work out everything, and write it onto the nodes. */
|
|
183
187
|
run(): void;
|
|
188
|
+
/**
|
|
189
|
+
* What a limit may relate (design.org 5.6): two things in one box that
|
|
190
|
+
* nothing else ties together, since the box's own spacing between them is
|
|
191
|
+
* the only distance it may lengthen.
|
|
192
|
+
*/
|
|
193
|
+
private checkLimitTargets;
|
|
194
|
+
/**
|
|
195
|
+
* Which of the things a box holds that nothing ties to each other a member
|
|
196
|
+
* belongs to, on one axis: its title, what is placed against the box's
|
|
197
|
+
* sides, or the rest, placed against each other.
|
|
198
|
+
*/
|
|
199
|
+
private groupOf;
|
|
200
|
+
/** A limit the box couldn't make room for: no side helps, or a stated size holds it in. */
|
|
201
|
+
private checkLimitsMet;
|
|
184
202
|
/** Where a node starts on the page. */
|
|
185
203
|
page(node: LayoutNode, axis: Axis): number;
|
|
186
204
|
/**
|
|
@@ -226,6 +244,26 @@ export declare class Placer {
|
|
|
226
244
|
private frame;
|
|
227
245
|
/** A container's outline around what it holds, before anything states its size. */
|
|
228
246
|
private outline;
|
|
247
|
+
/**
|
|
248
|
+
* A container's outline with room made for its children's limits
|
|
249
|
+
* (design.org 5.6). The only distance a limit may lengthen is between what
|
|
250
|
+
* the box holds and its sides, which every placement inside leaves unstated.
|
|
251
|
+
* Each side's room moves a limit's two ends apart at a fixed rate (all of
|
|
252
|
+
* it for a corner, half for the middle of a side), so one trial of each
|
|
253
|
+
* side gives the rate, and the room is the shortfall over the rate. A side
|
|
254
|
+
* that doesn't help is never used; with both helping, as for two corner
|
|
255
|
+
* children, the box grows evenly. A limit no side helps is left unmet, and
|
|
256
|
+
* `checkLimits` refuses it.
|
|
257
|
+
*/
|
|
258
|
+
private roomFor;
|
|
259
|
+
/** The limits a container's children state on one axis. */
|
|
260
|
+
private limitsIn;
|
|
261
|
+
/** How much nearer than its gap a limit's two ends are, in a trial frame of their container. */
|
|
262
|
+
private shortfall;
|
|
263
|
+
/** The distance a limit keeps: its gap, or the usual one; beside a text, as beside any text. */
|
|
264
|
+
private limitGap;
|
|
265
|
+
/** Where a child of `node` is on one axis, in a trial frame of `node`. */
|
|
266
|
+
private memberAt;
|
|
229
267
|
private remember;
|
|
230
268
|
/**
|
|
231
269
|
* Work something out, refusing a circle. The trail holds what was being
|
|
@@ -238,6 +276,14 @@ export declare class Placer {
|
|
|
238
276
|
private unitStart;
|
|
239
277
|
/** A box's start from its own placements. */
|
|
240
278
|
private placed;
|
|
279
|
+
/**
|
|
280
|
+
* Opposite placements center a box between two others, but not when one of
|
|
281
|
+
* them is its own container's box: `inside c top-right right of c.k` would
|
|
282
|
+
* be neither in the corner nor a gap from `c.k` (design.org 5.3). Refused,
|
|
283
|
+
* naming the two ways out: the corner's other half beside the sibling, or
|
|
284
|
+
* the corner kept and the sibling held off with `stays`.
|
|
285
|
+
*/
|
|
286
|
+
private refuseBoxAndSibling;
|
|
241
287
|
private edgeOf;
|
|
242
288
|
/** A box's start from one statement. */
|
|
243
289
|
private apply;
|
|
@@ -314,6 +360,15 @@ export declare class Placer {
|
|
|
314
360
|
private textOf;
|
|
315
361
|
/** A child placed against its own container's box, which it does not grow. */
|
|
316
362
|
framed(node: LayoutNode): boolean;
|
|
363
|
+
/**
|
|
364
|
+
* Framed on one axis: what says where it goes on that axis is against its
|
|
365
|
+
* container's box. `right of c.k inside c top` is framed down, so its height
|
|
366
|
+
* is measured from the finished box, and is content across, so the box holds
|
|
367
|
+
* it there (design.org 5.6).
|
|
368
|
+
*/
|
|
369
|
+
framedOn(node: LayoutNode, axis: Axis): boolean;
|
|
370
|
+
/** Whether a placement names its container's box, any part but the text. */
|
|
371
|
+
private againstBox;
|
|
317
372
|
/**
|
|
318
373
|
* A child placed against its container's text, or against its heading,
|
|
319
374
|
* which makes the title its frame's anchor.
|
|
@@ -334,7 +389,8 @@ export declare class Placer {
|
|
|
334
389
|
/**
|
|
335
390
|
* The children a container's box is the outline of, besides its title: not
|
|
336
391
|
* its heading, not what is joined to its text, and not those placed against
|
|
337
|
-
* the box.
|
|
392
|
+
* the box. Given an axis, a child placed against the box only on the other
|
|
393
|
+
* axis is content on this one.
|
|
338
394
|
*/
|
|
339
395
|
private contentOf;
|
|
340
396
|
/** Whether a node has a title: its text, what is joined to it, or a heading. */
|
|
@@ -480,6 +536,23 @@ export declare class Placer {
|
|
|
480
536
|
* title something is placed against anchors the frame instead.
|
|
481
537
|
*/
|
|
482
538
|
private checkAnchors;
|
|
539
|
+
/**
|
|
540
|
+
* Every box in a frame says something about where it goes, so the frame is
|
|
541
|
+
* anchored one axis at a time (design.org 5.3): across by the one box that
|
|
542
|
+
* says nothing across, up and down by the one that says nothing up and
|
|
543
|
+
* down. A lone direction's centering counts as saying nothing when its
|
|
544
|
+
* target's place on that axis is worked out from the box itself, as with
|
|
545
|
+
* `b right of a` and `a even with b.1`: the centering gives way to what was
|
|
546
|
+
* written. An axis nobody leaves free is a circle, reported when placing,
|
|
547
|
+
* and an axis a container is placed on by what it holds needs no anchor.
|
|
548
|
+
*/
|
|
549
|
+
private splitAnchors;
|
|
550
|
+
/** Whether a box with nothing stated on this axis is left free there: no lone direction centers it, or the one that would is placed from the box itself. */
|
|
551
|
+
private loneGivesWay;
|
|
552
|
+
/** The boxes a placement names, through points and regions. */
|
|
553
|
+
private namedFrom;
|
|
554
|
+
/** Whether a box's place on one axis is worked out, however indirectly, from `from` or something inside it. */
|
|
555
|
+
private placedFrom;
|
|
483
556
|
/**
|
|
484
557
|
* A box in a venn's part may be placed against a box in another part, and
|
|
485
558
|
* then it says where its whole part goes up and down: so it must be the
|