@cardenelabs/dragon 0.13.0 → 0.14.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 +46 -34
- package/dist/index.cjs +1511 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +402 -3
- package/dist/index.d.ts +402 -3
- package/dist/index.js +1512 -31
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/color.ts +31 -6
- package/src/compile.ts +268 -9
- package/src/input-size.ts +12 -0
- package/src/json-parser.ts +456 -21
- package/src/schemas/diagram.json +356 -0
- package/src/types.ts +91 -0
- package/src/v05/input-table.generated.ts +136 -0
- package/src/v05/parser-types.ts +5 -1
- package/src/v05/parser.ts +695 -8
package/package.json
CHANGED
package/src/color.ts
CHANGED
|
@@ -106,11 +106,13 @@ export type StrippedPaint = { path: string; value: string };
|
|
|
106
106
|
/**
|
|
107
107
|
* 図の中から、 色を塗る位置に入った外部参照を落とす。
|
|
108
108
|
*
|
|
109
|
-
* 対象は
|
|
109
|
+
* 対象は 3 種類ある。
|
|
110
110
|
*
|
|
111
111
|
* - 色を塗る key (`fill` / `stroke` / `bg` 等) の値
|
|
112
112
|
* - 状態の値 (`states[].initial` と、 phase が状態へ入れる値)。 状態は `{名前}` の形で
|
|
113
113
|
* `fill` に差し込まれるため、 色を塗る位置に届く
|
|
114
|
+
* - つまみの値 (`inputs[].defaultValue` / `defaultValues` / `options`)。 これらも状態を上書きし、
|
|
115
|
+
* 同じ形で paint に届く
|
|
114
116
|
*
|
|
115
117
|
* 説明文 (`title` / `subtitle` / `value` / `rows`) は対象外。 文字として出るだけで
|
|
116
118
|
* 属性にはならないため、 URL を書く正当な用途を壊さない。
|
|
@@ -119,23 +121,40 @@ export type StrippedPaint = { path: string; value: string };
|
|
|
119
121
|
*/
|
|
120
122
|
export function stripExternalPaint(diagram: unknown): StrippedPaint[] {
|
|
121
123
|
const stripped: StrippedPaint[] = [];
|
|
122
|
-
walk(diagram, "", false, stripped);
|
|
124
|
+
walk(diagram, "", false, stripped, false);
|
|
123
125
|
return stripped;
|
|
124
126
|
}
|
|
125
127
|
|
|
128
|
+
/** つまみから状態値になり得る文字列の欄。 */
|
|
129
|
+
const INPUT_VALUE_KEYS: ReadonlySet<string> = new Set(["defaultvalue", "defaultvalues", "options"]);
|
|
130
|
+
|
|
126
131
|
/**
|
|
127
132
|
* 図の中を辿って外部参照を落とす。
|
|
128
133
|
*
|
|
129
134
|
* `inStateValue` = 今見ている場所が状態の値かどうか。 状態は key の名前が `initial` や
|
|
130
135
|
* 状態名そのもの (phase の `sets`) になるため、 key の名前だけでは色かどうか分からない。
|
|
131
136
|
* 「状態を入れる箱の中にいる」 ことを引き継いで判断する。
|
|
137
|
+
* `inInputs` は `inputs` の中にいることを示し、値になる欄だけを `inStateValue` へ合流させる。
|
|
132
138
|
*/
|
|
133
|
-
function walk(
|
|
139
|
+
function walk(
|
|
140
|
+
node: unknown,
|
|
141
|
+
path: string,
|
|
142
|
+
inStateValue: boolean,
|
|
143
|
+
out: StrippedPaint[],
|
|
144
|
+
inInputs: boolean,
|
|
145
|
+
): void {
|
|
134
146
|
if (node === null || typeof node !== "object") return;
|
|
135
147
|
|
|
136
148
|
if (Array.isArray(node)) {
|
|
137
149
|
for (let i = 0; i < node.length; i++) {
|
|
138
|
-
|
|
150
|
+
const here = `${path}[${i}]`;
|
|
151
|
+
const value = node[i];
|
|
152
|
+
if (inStateValue && typeof value === "string" && pointsOutside(value)) {
|
|
153
|
+
node[i] = SAFE_PAINT;
|
|
154
|
+
out.push({ path: here, value });
|
|
155
|
+
} else {
|
|
156
|
+
walk(value, here, inStateValue, out, inInputs);
|
|
157
|
+
}
|
|
139
158
|
}
|
|
140
159
|
return;
|
|
141
160
|
}
|
|
@@ -145,7 +164,13 @@ function walk(node: unknown, path: string, inStateValue: boolean, out: StrippedP
|
|
|
145
164
|
const here = path ? `${path}.${key}` : key;
|
|
146
165
|
const lower = key.toLowerCase();
|
|
147
166
|
// 状態を入れる箱に入ったら、 その中の値はすべて状態の値として扱う
|
|
148
|
-
const nextInState =
|
|
167
|
+
const nextInState =
|
|
168
|
+
inStateValue ||
|
|
169
|
+
lower === "states" ||
|
|
170
|
+
lower === "sets" ||
|
|
171
|
+
lower === "tweens" ||
|
|
172
|
+
(inInputs && INPUT_VALUE_KEYS.has(lower));
|
|
173
|
+
const nextInInputs = inInputs || lower === "inputs";
|
|
149
174
|
|
|
150
175
|
if (typeof value === "string") {
|
|
151
176
|
const isPaint = PAINT_KEYS.has(lower) || (nextInState && (lower === "initial" || lower === "to" || lower === "from" || !isReservedStateKey(lower)));
|
|
@@ -155,7 +180,7 @@ function walk(node: unknown, path: string, inStateValue: boolean, out: StrippedP
|
|
|
155
180
|
}
|
|
156
181
|
continue;
|
|
157
182
|
}
|
|
158
|
-
walk(value, here, nextInState, out);
|
|
183
|
+
walk(value, here, nextInState, out, nextInInputs);
|
|
159
184
|
}
|
|
160
185
|
}
|
|
161
186
|
|
package/src/compile.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
DslLane,
|
|
16
16
|
DslPhase,
|
|
17
17
|
DslStep,
|
|
18
|
+
DslEventBinding,
|
|
18
19
|
DslValue,
|
|
19
20
|
PresetType,
|
|
20
21
|
} from "./types";
|
|
@@ -40,6 +41,8 @@ import {
|
|
|
40
41
|
NODE_KINDS,
|
|
41
42
|
applyDerivedValues,
|
|
42
43
|
parseFormula,
|
|
44
|
+
extractIdentifiers,
|
|
45
|
+
inputDefaultValue,
|
|
43
46
|
} from "@cardenelabs/cdl";
|
|
44
47
|
import { parseFocusEntry } from "./focus";
|
|
45
48
|
import { DRAW_TARGETS } from "./v05/parser";
|
|
@@ -121,7 +124,11 @@ export type CompileNotice = {
|
|
|
121
124
|
// 起点から描く動きを持たない図種で段に `draw:` を書いた (#1312)
|
|
122
125
|
| "draw-not-honored"
|
|
123
126
|
// `draw:` の語がその図種と食い違う (`type: bar` に `draw: pie`、 #1314)
|
|
124
|
-
| "draw-target-mismatch"
|
|
127
|
+
| "draw-target-mismatch"
|
|
128
|
+
// 式が、どこにも書かれていない名前を読んだ (#1391)
|
|
129
|
+
| "formula-unresolved"
|
|
130
|
+
// 出来事が指す相手が図に無い (#1393)
|
|
131
|
+
| "event-target-missing";
|
|
125
132
|
/** 対象の名前。 光らせる相手なら書かれた指定そのまま */
|
|
126
133
|
actor: string;
|
|
127
134
|
/** 書かれていた行 */
|
|
@@ -315,6 +322,132 @@ export function compileToCdl(doc: DslDocument, opts?: CompileToCdlOpts): CdlDiag
|
|
|
315
322
|
);
|
|
316
323
|
merged.readouts = [...(merged.readouts ?? []), ...ownReadouts];
|
|
317
324
|
}
|
|
325
|
+
/*
|
|
326
|
+
* 読む人が動かすつまみを図に載せる (#1389)。
|
|
327
|
+
*
|
|
328
|
+
* 部品と同じく写して載せる = 出口の paint 検査が diagram を直接書き換えるため、doc の
|
|
329
|
+
* object を共有すると `compileToCdl` の入力まで書き換わって入力不変性が壊れる。
|
|
330
|
+
*/
|
|
331
|
+
if (doc.inputs && doc.inputs.length > 0) {
|
|
332
|
+
const ownInputs = doc.inputs.map(
|
|
333
|
+
(input) => deepRewriteStrings(input, (value) => value) as typeof input,
|
|
334
|
+
);
|
|
335
|
+
merged.inputs = [...(merged.inputs ?? []), ...ownInputs];
|
|
336
|
+
}
|
|
337
|
+
/*
|
|
338
|
+
* つまみの値から決まる値を図に載せる (#1391)。
|
|
339
|
+
*
|
|
340
|
+
* 部品やつまみと同じく写して載せる。 式の文字列は色を塗る位置に届かないが、
|
|
341
|
+
* 入力を書き換えない形を 3 経路で揃える方が読み手に説明しやすい。
|
|
342
|
+
*/
|
|
343
|
+
if (doc.formulas && doc.formulas.length > 0) {
|
|
344
|
+
const ownFormulas = doc.formulas.map((formula) => ({
|
|
345
|
+
id: formula.id,
|
|
346
|
+
expression: formula.expression,
|
|
347
|
+
line: formula.pos?.line ?? 0,
|
|
348
|
+
}));
|
|
349
|
+
/*
|
|
350
|
+
* 式が読む名前が、どこにも書かれていないことを知らせる (#1391)。
|
|
351
|
+
*
|
|
352
|
+
* engine が読める名前は、つまみと **先に宣言した式** だけ。綴り違い、状態、
|
|
353
|
+
* `values:`、後から宣言する式を渡すと、描画時に未定義参照として例外になる。
|
|
354
|
+
*
|
|
355
|
+
* **解けない式は図へ載せない**。 engine は未定義参照や input/formula の同名衝突を
|
|
356
|
+
* runtime error にするため、残すと図全体が描けない。式以外の図は出し、知らせを返す。
|
|
357
|
+
*/
|
|
358
|
+
const つまみ = new Map((merged.inputs ?? []).map((input) => [input.id, input]));
|
|
359
|
+
const 先に書かれた式 = new Set((merged.formulas ?? []).map((formula) => formula.id));
|
|
360
|
+
const 載せる式 = [...(merged.formulas ?? [])];
|
|
361
|
+
for (const formula of ownFormulas) {
|
|
362
|
+
if (つまみ.has(formula.id) || 先に書かれた式.has(formula.id)) {
|
|
363
|
+
opts?.onNotice?.({
|
|
364
|
+
kind: "formula-unresolved",
|
|
365
|
+
actor: formula.id,
|
|
366
|
+
line: formula.line,
|
|
367
|
+
message: `式 "${formula.id}" の名前が、先に書かれたつまみまたは式と重なっています`,
|
|
368
|
+
hint: "`inputs:` と `formulas:` では重ならない名前を使う",
|
|
369
|
+
});
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
let 名前たち: Set<string>;
|
|
373
|
+
try {
|
|
374
|
+
名前たち = extractIdentifiers(parseFormula(formula.expression));
|
|
375
|
+
} catch {
|
|
376
|
+
// 読めない式は記法の読み取りが既に知らせている。 ここで二重に出さない
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
let 解けない = false;
|
|
380
|
+
for (const 名 of 名前たち) {
|
|
381
|
+
if (先に書かれた式.has(名)) continue;
|
|
382
|
+
const input = つまみ.get(名);
|
|
383
|
+
if (input) {
|
|
384
|
+
const 初期値 = inputDefaultValue(input);
|
|
385
|
+
if (typeof 初期値 === "number" || typeof 初期値 === "boolean") continue;
|
|
386
|
+
解けない = true;
|
|
387
|
+
opts?.onNotice?.({
|
|
388
|
+
kind: "formula-unresolved",
|
|
389
|
+
actor: formula.id,
|
|
390
|
+
line: formula.line,
|
|
391
|
+
message: `式 "${formula.id}" が、数でも真偽でもないつまみ "${名}" を読んでいます`,
|
|
392
|
+
hint: "式が読めるつまみは slider / number / stepper / timeline / toggle",
|
|
393
|
+
});
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
解けない = true;
|
|
397
|
+
opts?.onNotice?.({
|
|
398
|
+
kind: "formula-unresolved",
|
|
399
|
+
actor: formula.id,
|
|
400
|
+
line: formula.line,
|
|
401
|
+
message: `式 "${formula.id}" が、つまみまたは先に書かれた式ではない名前 "${名}" を読んでいます`,
|
|
402
|
+
hint: "`inputs:` に書くか、参照される式をこの式より前に書く",
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
if (解けない) continue;
|
|
406
|
+
載せる式.push({ id: formula.id, expression: formula.expression });
|
|
407
|
+
先に書かれた式.add(formula.id);
|
|
408
|
+
}
|
|
409
|
+
if (載せる式.length > 0) merged.formulas = 載せる式;
|
|
410
|
+
else delete merged.formulas;
|
|
411
|
+
}
|
|
412
|
+
/*
|
|
413
|
+
* 押下などの出来事で動く仕掛けを図に載せる (#1393)。
|
|
414
|
+
*
|
|
415
|
+
* **相手の名前を識別子へ直す**。 記法は識別子を書けないため名前で指す。 指す先が
|
|
416
|
+
* 見つからない形は載せずに知らせる = 描画側は知らない識別子を黙って無視するため、
|
|
417
|
+
* 残すと「書いたのに押しても何も起きない」 が手掛かりなしで起きる。
|
|
418
|
+
*
|
|
419
|
+
* 識別子は書いた順に `evt-1` から振る (組み立て API と同じ)。
|
|
420
|
+
*/
|
|
421
|
+
if (doc.events && doc.events.length > 0) {
|
|
422
|
+
const 載せる: NonNullable<CdlDiagram["eventBindings"]> = [...(merged.eventBindings ?? [])];
|
|
423
|
+
for (const e of doc.events) {
|
|
424
|
+
const 相手 = 出来事の相手を解く(merged, doc, e);
|
|
425
|
+
if (相手 === undefined) {
|
|
426
|
+
opts?.onNotice?.({
|
|
427
|
+
kind: "event-target-missing",
|
|
428
|
+
actor: e.handlerId,
|
|
429
|
+
line: e.pos.line,
|
|
430
|
+
message: `出来事 "${e.handlerId}" が指す相手が見つかりません`,
|
|
431
|
+
hint: "box は箱の名前、 lane は縦列の名前、 arrow は `A -> B` で書く",
|
|
432
|
+
});
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
載せる.push({
|
|
436
|
+
id: `evt-${載せる.length + 1}`,
|
|
437
|
+
event: e.event,
|
|
438
|
+
target: 相手,
|
|
439
|
+
handlerId: e.handlerId,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
if (載せる.length > 0) merged.eventBindings = 載せる;
|
|
443
|
+
}
|
|
444
|
+
// 巻き上げに応じて進む値を図に載せる (#1393)。 相手を持たないのでそのまま写す
|
|
445
|
+
if (doc.scrolls && doc.scrolls.length > 0) {
|
|
446
|
+
merged.scrollTriggers = [
|
|
447
|
+
...(merged.scrollTriggers ?? []),
|
|
448
|
+
...doc.scrolls.map((x) => ({ ...x })),
|
|
449
|
+
];
|
|
450
|
+
}
|
|
318
451
|
// 図の外を指す値を、 色を塗る位置から落とす (#1004)。
|
|
319
452
|
//
|
|
320
453
|
// 入口ごとに塞ぐ形は採らない。 状態の上書き / phase が入れる値 / 画面が直接書く背景色 /
|
|
@@ -997,6 +1130,26 @@ function disambiguateActorIds(
|
|
|
997
1130
|
const to = 直す(s.to);
|
|
998
1131
|
return from === s.from && to === s.to ? s : { ...s, from, to };
|
|
999
1132
|
}),
|
|
1133
|
+
events: doc.events?.map((event) => {
|
|
1134
|
+
const target = event.target;
|
|
1135
|
+
if (target.kind === "node") {
|
|
1136
|
+
const name = 直す(target.name);
|
|
1137
|
+
return name === target.name ? event : { ...event, target: { ...target, name } };
|
|
1138
|
+
}
|
|
1139
|
+
if (target.kind === "edge") {
|
|
1140
|
+
const from = 直す(target.from);
|
|
1141
|
+
const to = 直す(target.to);
|
|
1142
|
+
return from === target.from && to === target.to
|
|
1143
|
+
? event
|
|
1144
|
+
: { ...event, target: { ...target, from, to } };
|
|
1145
|
+
}
|
|
1146
|
+
// sequence 系の縦列は登場人物から作るため、同じ名前の読み替えが必要。
|
|
1147
|
+
if (target.kind === "lane" && (doc.type === "sequence" || doc.type === "solidity")) {
|
|
1148
|
+
const name = 直す(target.name);
|
|
1149
|
+
return name === target.name ? event : { ...event, target: { ...target, name } };
|
|
1150
|
+
}
|
|
1151
|
+
return event;
|
|
1152
|
+
}),
|
|
1000
1153
|
};
|
|
1001
1154
|
// 光らせる指定と位置の基準も名前で書くので、 同じ表で直す
|
|
1002
1155
|
if (次.animate) {
|
|
@@ -3048,6 +3201,49 @@ function applyEdgeInlineOptions(
|
|
|
3048
3201
|
});
|
|
3049
3202
|
}
|
|
3050
3203
|
|
|
3204
|
+
/**
|
|
3205
|
+
* 出来事が指す相手を、図の識別子へ直す (#1393)。 見つからなければ `undefined`。
|
|
3206
|
+
*
|
|
3207
|
+
* 箱と縦列は **名前から作った識別子** と **描かれる題** の両方で探す。 記法は名前で書き、
|
|
3208
|
+
* 図の識別子はそこから作られるが、見本を重ねた図など識別子が名前と揃わない形もある。
|
|
3209
|
+
* 矢印は両端の名前から識別子を作って突き合わせる。
|
|
3210
|
+
*/
|
|
3211
|
+
function 出来事の相手を解く(
|
|
3212
|
+
diagram: CdlDiagram,
|
|
3213
|
+
doc: DslDocument,
|
|
3214
|
+
e: DslEventBinding,
|
|
3215
|
+
): NonNullable<CdlDiagram["eventBindings"]>[number]["target"] | undefined {
|
|
3216
|
+
if (e.target.kind === "diagram") return { kind: "diagram" };
|
|
3217
|
+
if (e.target.kind === "edge") {
|
|
3218
|
+
const from = slugify(e.target.from);
|
|
3219
|
+
const to = slugify(e.target.to);
|
|
3220
|
+
let 矢印: CdlEdge | undefined;
|
|
3221
|
+
if (doc.type === "sequence" || doc.type === "solidity") {
|
|
3222
|
+
const stepIdx = doc.flow.findIndex(
|
|
3223
|
+
(step) => slugify(step.from) === from && slugify(step.to) === to,
|
|
3224
|
+
);
|
|
3225
|
+
if (stepIdx >= 0) {
|
|
3226
|
+
矢印 = diagram.edges.find(
|
|
3227
|
+
(x) =>
|
|
3228
|
+
(x.from === `s${stepIdx}-${from}` || x.from === from) &&
|
|
3229
|
+
(x.to === `s${stepIdx}-${to}` || x.from === x.to),
|
|
3230
|
+
);
|
|
3231
|
+
}
|
|
3232
|
+
} else {
|
|
3233
|
+
矢印 = diagram.edges.find((x) => x.from === from && x.to === to);
|
|
3234
|
+
}
|
|
3235
|
+
return 矢印 ? { kind: "edge", id: 矢印.id } : undefined;
|
|
3236
|
+
}
|
|
3237
|
+
const 名 = e.target.name;
|
|
3238
|
+
const slug = slugify(名);
|
|
3239
|
+
if (e.target.kind === "node") {
|
|
3240
|
+
const 箱 = diagram.nodes.find((n) => n.id === slug || n.id === 名 || n.title === 名);
|
|
3241
|
+
return 箱 ? { kind: "node", id: 箱.id } : undefined;
|
|
3242
|
+
}
|
|
3243
|
+
const 列 = (diagram.lanes ?? []).find((l) => l.id === slug || l.id === 名 || l.label === 名);
|
|
3244
|
+
return 列 ? { kind: "lane", id: 列.id } : undefined;
|
|
3245
|
+
}
|
|
3246
|
+
|
|
3051
3247
|
/** 本文に書いた矢印の指定を、対応が取れた矢印へ書き写す。 対応の取り方は呼出側が決める。 */
|
|
3052
3248
|
function 矢印へ書き写す(target: CdlEdge, s: DslStep, doc: DslDocument): void {
|
|
3053
3249
|
// **書いた補足が勝つ** (#1275)。 ここで写さないと 2 つ落ちる。 静止した `type: flow` は
|
|
@@ -3071,6 +3267,11 @@ function 矢印へ書き写す(target: CdlEdge, s: DslStep, doc: DslDocument): v
|
|
|
3071
3267
|
if (s.labelOffsetX !== undefined) target.labelOffsetX = s.labelOffsetX;
|
|
3072
3268
|
if (s.labelOffsetY !== undefined) target.labelOffsetY = s.labelOffsetY;
|
|
3073
3269
|
if (s.overlay !== undefined) target.overlay = s.overlay;
|
|
3270
|
+
// 値に追随する 3 欄 (#1396)。 太さ / 色 / 破線の位置が値に合わせて動く。
|
|
3271
|
+
// 通り道そのものは動かないので、配置計算と重なり解消には影響しない
|
|
3272
|
+
if (s.widthBind !== undefined) target.widthBind = s.widthBind;
|
|
3273
|
+
if (s.strokeBind !== undefined) target.strokeBind = s.strokeBind;
|
|
3274
|
+
if (s.dashOffsetBind !== undefined) target.dashOffsetBind = s.dashOffsetBind;
|
|
3074
3275
|
}
|
|
3075
3276
|
|
|
3076
3277
|
/**
|
|
@@ -5255,7 +5456,13 @@ type 放射で描けない欄 =
|
|
|
5255
5456
|
// 箱の中に描く図形 (#1374)。 放射の枝は箱の中に図形を持たない
|
|
5256
5457
|
| "shape"
|
|
5257
5458
|
// 出す条件 (#1381)。 放射の枝は個別に出し分けられない
|
|
5258
|
-
| "visibleIf"
|
|
5459
|
+
| "visibleIf"
|
|
5460
|
+
// 値に追随する 5 欄 (#1392)。 放射の枝は大きさも位置も中心からの配置で決まる
|
|
5461
|
+
| "wBind"
|
|
5462
|
+
| "hBind"
|
|
5463
|
+
| "opacity"
|
|
5464
|
+
| "renderOffsetX"
|
|
5465
|
+
| "renderOffsetY";
|
|
5259
5466
|
|
|
5260
5467
|
/** 引数が `never` でなければ型検査が落ちる */
|
|
5261
5468
|
type 空であること<T extends never> = T;
|
|
@@ -5312,6 +5519,11 @@ const 放射で描けない欄の名前: Record<放射で描けない欄, string
|
|
|
5312
5519
|
layoutPos: "配置のずらし",
|
|
5313
5520
|
shape: "箱の中の図形",
|
|
5314
5521
|
visibleIf: "出す条件",
|
|
5522
|
+
wBind: "値に追随する大きさ",
|
|
5523
|
+
hBind: "値に追随する大きさ",
|
|
5524
|
+
opacity: "濃さ",
|
|
5525
|
+
renderOffsetX: "描く時のずらし",
|
|
5526
|
+
renderOffsetY: "描く時のずらし",
|
|
5315
5527
|
};
|
|
5316
5528
|
|
|
5317
5529
|
/**
|
|
@@ -5737,6 +5949,18 @@ function applyV05Extensions(
|
|
|
5737
5949
|
if (footer) footer.visibleIf = a.visibleIf;
|
|
5738
5950
|
}
|
|
5739
5951
|
}
|
|
5952
|
+
/*
|
|
5953
|
+
* 値に追随する 5 欄 (#1392)。
|
|
5954
|
+
*
|
|
5955
|
+
* **名札と足へは渡さない**。 `visibleIf` は片方だけ隠すと順序図の縦線の頭と足が
|
|
5956
|
+
* 食い違うため揃えるが、こちらは見た目の大きさ / 濃さ / ずらしで、名札まで同じだけ
|
|
5957
|
+
* 動かすと縦線の頭が本体から離れる。 書いた箱にだけ効かせる。
|
|
5958
|
+
*/
|
|
5959
|
+
if (a.wBind !== undefined) node.wBind = a.wBind;
|
|
5960
|
+
if (a.hBind !== undefined) node.hBind = a.hBind;
|
|
5961
|
+
if (a.opacity !== undefined) node.opacity = a.opacity;
|
|
5962
|
+
if (a.renderOffsetX !== undefined) node.renderOffsetX = a.renderOffsetX;
|
|
5963
|
+
if (a.renderOffsetY !== undefined) node.renderOffsetY = a.renderOffsetY;
|
|
5740
5964
|
}
|
|
5741
5965
|
}
|
|
5742
5966
|
// 名札の高さを揃える。 kind ごとに高さが変わると縦線の始まる位置がばらけ、 順序図の
|
|
@@ -6441,23 +6665,58 @@ function compileGenericWithAnimate(doc: DslDocument, opts: GenericOpts): CdlDiag
|
|
|
6441
6665
|
// lane / node 配置 ... preset kind に応じて切替
|
|
6442
6666
|
const actorToNodeId = new Map<string, string>();
|
|
6443
6667
|
if (書いた縦列に置く(kind, doc)) {
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6668
|
+
/*
|
|
6669
|
+
* **書いた縦列に置く** (#1263)。 縦列を並べるための入れ物として使う図種でだけ効く。
|
|
6670
|
+
*
|
|
6671
|
+
* 縦列の並びは **`lanes:` に書いた順** を優先する (#1394)。 箱が最初に使った順で
|
|
6672
|
+
* 並べていた間、`lanes:` で左から順に宣言しても箱の書き順で入れ替わっていた
|
|
6673
|
+
* (実測 = 中心を先に書いた放射の図で、左端の縦列が中心の右へ回った)。
|
|
6674
|
+
*
|
|
6675
|
+
* `lanes:` に無い縦列は、これまでどおり箱が使った順で後ろに続ける。
|
|
6676
|
+
*/
|
|
6677
|
+
const 使った: string[] = [];
|
|
6447
6678
|
for (const a of doc.actors) {
|
|
6448
6679
|
const lid = a.lane;
|
|
6449
|
-
if (lid !== undefined &&
|
|
6680
|
+
if (lid !== undefined && !使った.includes(lid)) 使った.push(lid);
|
|
6450
6681
|
}
|
|
6682
|
+
const 書いた順 = doc.lanes ? Object.keys(doc.lanes) : [];
|
|
6683
|
+
const 並び = [
|
|
6684
|
+
...書いた順.filter((lid) => 使った.includes(lid)),
|
|
6685
|
+
...使った.filter((lid) => !書いた順.includes(lid)),
|
|
6686
|
+
];
|
|
6451
6687
|
for (const lid of 並び) {
|
|
6452
6688
|
b.lane(lid, { width: laneWidth, ...(kind === "topology" ? { contain: true } : {}) });
|
|
6453
6689
|
}
|
|
6454
|
-
|
|
6690
|
+
/*
|
|
6691
|
+
* 段は **書いた番号をそのまま持つ** (#1394)。
|
|
6692
|
+
*
|
|
6693
|
+
* 書き順で 0 から詰め直していた間、`stack: 1` と書いた箱が 0 へ落ちていた。
|
|
6694
|
+
* 決定木のように「同じ高さに並ばない」 ことが図の意味そのものになる形では、
|
|
6695
|
+
* 詰めた瞬間に別の図になる (実測 = 3 段の木の根が 1 段目へ上がった)。
|
|
6696
|
+
*
|
|
6697
|
+
* 書かなかった箱は、その縦列で **空いている一番小さい段** に置く。 単に数え上げると
|
|
6698
|
+
* 書いた番号と重なり、2 つの箱が同じ段に載る。
|
|
6699
|
+
*/
|
|
6700
|
+
const 埋まった段 = new Map<string, Set<number>>();
|
|
6701
|
+
const 埋める = (lid: string, stack: number): void => {
|
|
6702
|
+
const 集合 = 埋まった段.get(lid) ?? new Set<number>();
|
|
6703
|
+
集合.add(stack);
|
|
6704
|
+
埋まった段.set(lid, 集合);
|
|
6705
|
+
};
|
|
6706
|
+
for (const a of doc.actors) {
|
|
6707
|
+
if (a.lane !== undefined && a.stack !== undefined) 埋める(a.lane, a.stack);
|
|
6708
|
+
}
|
|
6455
6709
|
doc.actors.forEach((a, idx) => {
|
|
6456
6710
|
const id = slugify(a.name) || `n${idx}`;
|
|
6457
6711
|
actorToNodeId.set(a.name, id);
|
|
6458
6712
|
const lid = a.lane!;
|
|
6459
|
-
|
|
6460
|
-
|
|
6713
|
+
let stack = a.stack;
|
|
6714
|
+
if (stack === undefined) {
|
|
6715
|
+
stack = 0;
|
|
6716
|
+
const 集合 = 埋まった段.get(lid);
|
|
6717
|
+
while (集合?.has(stack)) stack += 1;
|
|
6718
|
+
埋める(lid, stack);
|
|
6719
|
+
}
|
|
6461
6720
|
b.node(id, { lane: lid, stack, kind: a.kind, title: 箱の題(a) });
|
|
6462
6721
|
});
|
|
6463
6722
|
} else if (kind === "flow" || kind === "topology") {
|
package/src/input-size.ts
CHANGED
|
@@ -69,6 +69,18 @@ export function countDocElements(doc: DslDocument): number {
|
|
|
69
69
|
// 値を見せる部品も描画時に 1 widget ずつ展開される (#1374)。 数えないと、 actors が
|
|
70
70
|
// 少ないまま readouts だけを大量に並べた入力が組み立て前の上限をすり抜ける。
|
|
71
71
|
(doc.readouts?.length ?? 0) +
|
|
72
|
+
// 読む人が動かすつまみも 1 widget ずつ展開される (#1389)。 readouts と同じ上限で守る。
|
|
73
|
+
(doc.inputs?.length ?? 0) +
|
|
74
|
+
/*
|
|
75
|
+
* 式 (#1391) と、押下 / 巻き上げ (#1393) も数える。
|
|
76
|
+
*
|
|
77
|
+
* **図の側 (`countDiagramElements`) は既に数えている**。 記法側だけ数えないと、
|
|
78
|
+
* 箱が少ないままこれらを大量に並べた入力が組み立て前の上限をすり抜け、
|
|
79
|
+
* 組み立て終わってから (= 時間をかけてから) 図の側で弾かれる。
|
|
80
|
+
*/
|
|
81
|
+
(doc.formulas?.length ?? 0) +
|
|
82
|
+
(doc.events?.length ?? 0) +
|
|
83
|
+
(doc.scrolls?.length ?? 0) +
|
|
72
84
|
(doc.groups ? Object.keys(doc.groups).length : 0) +
|
|
73
85
|
(doc.lanes ? Object.keys(doc.lanes).length : 0)
|
|
74
86
|
);
|