@yanqirenshi/model.deployment 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +129 -2
- package/dist/check.d.ts +15 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +50 -0
- package/dist/cjs/check.js +55 -0
- package/dist/cjs/defaults.js +119 -0
- package/dist/cjs/fields.js +44 -0
- package/dist/cjs/index.js +46 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/patch.js +262 -0
- package/dist/cjs/schema.js +97 -0
- package/dist/cjs/types.js +7 -0
- package/dist/defaults.d.ts +61 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +111 -0
- package/dist/fields.d.ts +8 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +40 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/patch.d.ts +35 -0
- package/dist/patch.d.ts.map +1 -0
- package/dist/patch.js +251 -0
- package/dist/schema.d.ts +274 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +94 -0
- package/dist/types.d.ts +89 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/package.json +54 -5
- package/src/check.ts +66 -0
- package/src/defaults.ts +138 -0
- package/src/fields.ts +46 -0
- package/src/index.ts +28 -0
- package/src/patch.ts +317 -0
- package/src/schema.ts +100 -0
- package/src/types.ts +102 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { Background, Border, Font, Position, Size, Stroke } from '@yanqirenshi/types';
|
|
2
|
+
/** レコードの id(nodes / edges とも。ノードは子も含めて一意) */
|
|
3
|
+
export type DeploymentId = number | string;
|
|
4
|
+
/** ノードの種類。NODE(通常のノード)か COMPONENT */
|
|
5
|
+
export type DeploymentNodeType = 'NODE' | 'COMPONENT';
|
|
6
|
+
/** ノードのラベル */
|
|
7
|
+
export interface DeploymentNodeLabel {
|
|
8
|
+
text?: string | null;
|
|
9
|
+
position?: Position;
|
|
10
|
+
font?: Font;
|
|
11
|
+
}
|
|
12
|
+
/** ノードのレコード(入れ子可)。ライブラリが解釈しない項目も持てる(index signature) */
|
|
13
|
+
export interface DeploymentNodeRecord {
|
|
14
|
+
id: DeploymentId;
|
|
15
|
+
type?: DeploymentNodeType | string;
|
|
16
|
+
label?: DeploymentNodeLabel | null;
|
|
17
|
+
/** ホバーのツールチップ(SVG title) */
|
|
18
|
+
description?: string | null;
|
|
19
|
+
size?: Size;
|
|
20
|
+
position?: Position;
|
|
21
|
+
padding?: number;
|
|
22
|
+
border?: Border;
|
|
23
|
+
background?: Background;
|
|
24
|
+
/** ノードの左下に出すリンク */
|
|
25
|
+
link?: {
|
|
26
|
+
url?: string | null;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
} | null;
|
|
29
|
+
children?: DeploymentNodeRecord[];
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
/** 線の端点。position はノードの中心から見た角度(0=真下・時計回り。90=左 / 180=真上 / 270=右) */
|
|
33
|
+
export interface DeploymentEdgeEnd {
|
|
34
|
+
id: DeploymentId;
|
|
35
|
+
position?: number;
|
|
36
|
+
}
|
|
37
|
+
/** 線の見た目。marker は両端の矢印の有無 */
|
|
38
|
+
export interface DeploymentEdgeStroke extends Stroke {
|
|
39
|
+
marker?: {
|
|
40
|
+
start?: boolean;
|
|
41
|
+
end?: boolean;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** 線のレコード */
|
|
45
|
+
export interface DeploymentEdgeRecord {
|
|
46
|
+
id: DeploymentId;
|
|
47
|
+
from: DeploymentEdgeEnd;
|
|
48
|
+
to: DeploymentEdgeEnd;
|
|
49
|
+
stroke?: DeploymentEdgeStroke;
|
|
50
|
+
[key: string]: unknown;
|
|
51
|
+
}
|
|
52
|
+
export type DeploymentRecord = DeploymentNodeRecord | DeploymentEdgeRecord;
|
|
53
|
+
/** 図全体(d3.deployment の data() に渡すもの) */
|
|
54
|
+
export interface DeploymentGraph {
|
|
55
|
+
nodes?: DeploymentNodeRecord[];
|
|
56
|
+
edges?: DeploymentEdgeRecord[];
|
|
57
|
+
}
|
|
58
|
+
/** レコードを置くコレクション */
|
|
59
|
+
export type DeploymentCollection = 'nodes' | 'edges';
|
|
60
|
+
/** JSON Merge Patch(RFC 7386)。null は「その項目を除く」 */
|
|
61
|
+
export type MergePatch = {
|
|
62
|
+
[key: string]: unknown;
|
|
63
|
+
};
|
|
64
|
+
/** 保存する変更。利用側は図の種類を知らずにこれを保存すればよい */
|
|
65
|
+
export interface DeploymentChange {
|
|
66
|
+
collection: DeploymentCollection;
|
|
67
|
+
id: DeploymentId;
|
|
68
|
+
patch: MergePatch;
|
|
69
|
+
}
|
|
70
|
+
/** ドラッグの移動量(世界座標。d3.deployment の callbacks.node.dragend が渡す move と同じ) */
|
|
71
|
+
export interface DeploymentMove {
|
|
72
|
+
dx: number;
|
|
73
|
+
dy: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 編集できる項目。colonoscope の ColonoscopeField(path / label / type)と同じ形で、
|
|
77
|
+
* 取りうる値の一覧 options だけ足している(型は構造的に互換。colonoscope には依存しない)。
|
|
78
|
+
*/
|
|
79
|
+
export type DeploymentFieldType = 'text' | 'textarea' | 'number' | 'readonly';
|
|
80
|
+
export interface DeploymentField {
|
|
81
|
+
/** レコードの項目のパス(ドット区切り。例: 'border.color')。写し先ではなく実際の項目を指す */
|
|
82
|
+
path: string;
|
|
83
|
+
label: string;
|
|
84
|
+
type: DeploymentFieldType;
|
|
85
|
+
options?: readonly string[];
|
|
86
|
+
}
|
|
87
|
+
/** インスペクタの「適用」が渡す値(path → 入力した文字列) */
|
|
88
|
+
export type DeploymentValues = Record<string, string>;
|
|
89
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE3F,6CAA6C;AAC7C,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3C,qCAAqC;AACrC,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,WAAW,CAAC;AAEtD,cAAc;AACd,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAED,wDAAwD;AACxD,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,YAAY,CAAC;IACjB,IAAI,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAAC;IACnC,KAAK,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACnC,4BAA4B;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,mBAAmB;IACnB,IAAI,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC9D,QAAQ,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAClC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,mEAAmE;AACnE,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,YAAY,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,6BAA6B;AAC7B,MAAM,WAAW,oBAAqB,SAAQ,MAAM;IAClD,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,GAAG,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC7C;AAED,aAAa;AACb,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,YAAY,CAAC;IACjB,IAAI,EAAE,iBAAiB,CAAC;IACxB,EAAE,EAAE,iBAAiB,CAAC;IACtB,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GAAG,oBAAoB,CAAC;AAE3E,wCAAwC;AACxC,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAC/B,KAAK,CAAC,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAED,oBAAoB;AACpB,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,OAAO,CAAC;AAErD,iDAAiD;AACjD,MAAM,MAAM,UAAU,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAEpD,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,oBAAoB,CAAC;IACjC,EAAE,EAAE,YAAY,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,yEAAyE;AACzE,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE9E,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED,sCAAsC;AACtC,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// model.deployment の公開型。
|
|
2
|
+
//
|
|
3
|
+
// ここで言う「レコード」は、d3.deployment の data() に渡す入力そのもの({ nodes, edges })の 1 要素。
|
|
4
|
+
// iHub では data/deployment/{nodes,edges}.json の 1 要素。d3.deployment が正規化したあとの datum(_id / _core / _position を持つもの)ではない。
|
|
5
|
+
// ノードと線の図なので model.unchi / model.sitemap と形が近い。ラベルは label.text(unchi と同じ。sitemap は label.contents)。
|
|
6
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,55 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@yanqirenshi/model.deployment",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Diagram model for deployment diagrams (d3.deployment) — JSON Schema, consistency rules, drawing defaults, editable fields and patch conversion. Pure data and functions: no React / d3 dependency",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/cjs/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"src"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc && tsc -p tsconfig.cjs.json && node scripts/cjs-package.cjs",
|
|
22
|
+
"typecheck": "tsc --noEmit",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"prepare": "npm run build",
|
|
25
|
+
"prepublishOnly": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/Foolsgolds/Assholes.git",
|
|
30
|
+
"directory": "packages/model.deployment"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/Foolsgolds/Assholes/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/Foolsgolds/Assholes/tree/main/packages/model.deployment#readme",
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@yanqirenshi/types": "^0.1.2"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"typescript": "~5.9.3",
|
|
44
|
+
"vitest": "^2.1.8"
|
|
45
|
+
},
|
|
46
|
+
"author": "yanqirenshi@gmail.com",
|
|
47
|
+
"keywords": [
|
|
48
|
+
"diagram",
|
|
49
|
+
"model",
|
|
50
|
+
"json-schema",
|
|
51
|
+
"deployment",
|
|
52
|
+
"d3.deployment"
|
|
53
|
+
],
|
|
54
|
+
"license": "MIT"
|
|
55
|
+
}
|
package/src/check.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// コレクションをまたぐ整合性の検査(id の重複・参照先の存在)。
|
|
2
|
+
// スキーマ(schema.ts)に合っているデータを渡す。戻り値はエラーのメッセージの配列(空なら問題なし)。
|
|
3
|
+
//
|
|
4
|
+
// iHub の server/validation/rules.mjs の checkGraph('deployment', data) と同じ検査・同じ文言。
|
|
5
|
+
// model.unchi / model.sitemap の check.ts の複製(graph 系の 3 つ目。共通化の判断は #146 の 7 — 別 issue)。
|
|
6
|
+
// メッセージの先頭はコレクション名で、prefix を渡すと 'deployment/nodes: ...' のように付けられる。
|
|
7
|
+
|
|
8
|
+
import type { DeploymentGraph, DeploymentId, DeploymentNodeRecord, DeploymentRecord } from './types.js';
|
|
9
|
+
|
|
10
|
+
/** 入れ子のノード(children)を平らにする(親 → 子 の順) */
|
|
11
|
+
export function flatten(nodes: DeploymentNodeRecord[]): DeploymentNodeRecord[] {
|
|
12
|
+
return nodes.flatMap(node => [node, ...flatten(node.children || [])]);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// list の中で id が重複していればエラーにする。id の集合を返す。
|
|
16
|
+
function checkUnique(errors: string[], label: string, list: { id: DeploymentId }[]): Set<DeploymentId> {
|
|
17
|
+
const values = new Set<DeploymentId>();
|
|
18
|
+
|
|
19
|
+
for (const d of list) {
|
|
20
|
+
if (values.has(d.id))
|
|
21
|
+
errors.push(`${label}: id が重複しています。(id=${String(d.id)})`);
|
|
22
|
+
|
|
23
|
+
values.add(d.id);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return values;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 図全体を検査する。
|
|
31
|
+
* - nodes(子を含む)の id が重複していない
|
|
32
|
+
* - edges の id が重複していない
|
|
33
|
+
* - edges の from.id / to.id がノードに存在する
|
|
34
|
+
*
|
|
35
|
+
* @param prefix メッセージのコレクション名の前に付ける文字列(例: 'deployment/')
|
|
36
|
+
*/
|
|
37
|
+
export function check(data: DeploymentGraph, prefix = ''): string[] {
|
|
38
|
+
const errors: string[] = [];
|
|
39
|
+
|
|
40
|
+
const node_ids = checkUnique(errors, `${prefix}nodes`, flatten(data.nodes || []));
|
|
41
|
+
|
|
42
|
+
checkUnique(errors, `${prefix}edges`, data.edges || []);
|
|
43
|
+
|
|
44
|
+
for (const edge of data.edges || [])
|
|
45
|
+
for (const end of ['from', 'to'] as const)
|
|
46
|
+
if (!edge[end] || !node_ids.has(edge[end].id))
|
|
47
|
+
errors.push(`${prefix}edges: ${end}.id のノードが見つかりません。(id=${String(edge.id)}, ${end}.id=${String(edge[end]?.id)})`);
|
|
48
|
+
|
|
49
|
+
return errors;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** ノード(子を含む)か線を id で探す(無ければ null) */
|
|
53
|
+
export function findById<T extends DeploymentRecord>(list: readonly T[] | undefined, id: DeploymentId): T | null {
|
|
54
|
+
for (const d of list || []) {
|
|
55
|
+
if (d.id === id)
|
|
56
|
+
return d;
|
|
57
|
+
|
|
58
|
+
const children = (d as DeploymentNodeRecord).children;
|
|
59
|
+
const hit = children ? findById(children as T[], id) : null;
|
|
60
|
+
|
|
61
|
+
if (hit)
|
|
62
|
+
return hit;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return null;
|
|
66
|
+
}
|
package/src/defaults.ts
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// d3.deployment が「書いていない項目」に既定で描く値。
|
|
2
|
+
//
|
|
3
|
+
// 値の出どころは、d3.deployment の datamodels/Node.js が使う共通基盤 assh0le のデータクラス
|
|
4
|
+
// (Stroke.template = 枠線、Background.template = 背景、Label.template = ラベル、Padding / Position.template)と、
|
|
5
|
+
// datamodels/Edge.js の dataTemplate(stroke と矢印)、Geometry.getPortLineTo(角度の既定 90)。
|
|
6
|
+
// d3.deployment はこのファイルを読む(#146)ので、ここが正。
|
|
7
|
+
//
|
|
8
|
+
// d3.deployment は border.type を正規化するが描画には使わない(dasharray を付けない)ので、モデルも項目にしない。
|
|
9
|
+
|
|
10
|
+
import type { Background, Border, Font, Position } from '@yanqirenshi/types';
|
|
11
|
+
import type { DeploymentEdgeRecord, DeploymentNodeRecord, DeploymentNodeType, DeploymentRecord } from './types.js';
|
|
12
|
+
|
|
13
|
+
/** ノードの種類 */
|
|
14
|
+
export const NODE_TYPES: readonly DeploymentNodeType[] = ['NODE', 'COMPONENT'];
|
|
15
|
+
|
|
16
|
+
/** ノードの枠線・背景の既定(assh0le の Stroke.template / Background.template) */
|
|
17
|
+
export const NODE_STYLE_DEFAULTS = {
|
|
18
|
+
border: { color: '#afafb0', width: 1, type: 'solid' } as Required<Pick<Border, 'color' | 'width' | 'type'>>,
|
|
19
|
+
background: { color: '#fff', opacity: 1.0 } as Required<Pick<Background, 'color' | 'opacity'>>,
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* ラベルの既定(assh0le の Label.template)。text が無いとき d3.deployment は '????????' を描く
|
|
24
|
+
* (withDefaults では '' を入れる — 「書いていない」ことをインスペクタで見せるため)。
|
|
25
|
+
*/
|
|
26
|
+
export const LABEL_DEFAULTS = {
|
|
27
|
+
placeholder: '????????',
|
|
28
|
+
position: { x: 0, y: 0 } as Position,
|
|
29
|
+
font: { size: 16, color: '#333333' } as Font,
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
/** ノードの既定(Padding / Position.template)。size の 0 は「子に合わせる」 */
|
|
33
|
+
export const NODE_DEFAULTS = {
|
|
34
|
+
type: 'NODE' as DeploymentNodeType,
|
|
35
|
+
padding: 0,
|
|
36
|
+
position: { x: 0, y: 0 } as Position,
|
|
37
|
+
} as const;
|
|
38
|
+
|
|
39
|
+
/** 線の見た目(色・太さ・両端の矢印の有無) */
|
|
40
|
+
export interface DeploymentEdgeStyle {
|
|
41
|
+
color: string;
|
|
42
|
+
width: number;
|
|
43
|
+
marker: { start: boolean; end: boolean };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 線の既定(Edge.dataTemplate の stroke、端点の角度は Geometry.getPortLineTo の 90=左) */
|
|
47
|
+
export const EDGE_DEFAULTS = {
|
|
48
|
+
stroke: {
|
|
49
|
+
color: '#333',
|
|
50
|
+
width: 1.5,
|
|
51
|
+
marker: { start: false, end: true },
|
|
52
|
+
} as DeploymentEdgeStyle,
|
|
53
|
+
position: 90,
|
|
54
|
+
} as const;
|
|
55
|
+
|
|
56
|
+
/** 線か(from と to を持つ)。ノードは持たない */
|
|
57
|
+
export function isEdge(record: DeploymentRecord | null | undefined): record is DeploymentEdgeRecord {
|
|
58
|
+
return !!(record && (record as DeploymentEdgeRecord).from && (record as DeploymentEdgeRecord).to);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** 既定に入力の指定(truthy な項目だけ。assh0le の Stroke.build / Background.build と同じ判定)を重ねる */
|
|
62
|
+
function mergeStyle<T extends object>(defaults: T, given: Partial<T> | null | undefined): T {
|
|
63
|
+
const out: T = { ...defaults };
|
|
64
|
+
|
|
65
|
+
for (const key of Object.keys(given || {}) as (keyof T)[]) {
|
|
66
|
+
const v = (given as T)[key];
|
|
67
|
+
|
|
68
|
+
if (v)
|
|
69
|
+
out[key] = v;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** ノードが描かれる枠線・背景(既定に入力を重ねた、実際に描かれる値) */
|
|
76
|
+
export function nodeStyle(node: DeploymentNodeRecord): typeof NODE_STYLE_DEFAULTS {
|
|
77
|
+
return {
|
|
78
|
+
border: mergeStyle(NODE_STYLE_DEFAULTS.border, node.border as Partial<typeof NODE_STYLE_DEFAULTS.border>),
|
|
79
|
+
background: mergeStyle(NODE_STYLE_DEFAULTS.background, node.background as Partial<typeof NODE_STYLE_DEFAULTS.background>),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 線が描かれる値(Edge.normalize と同じ: stroke の color / width / marker はそれぞれ truthy ならそれ)。
|
|
85
|
+
* marker は入力があれば丸ごと置き換わる(片方だけ書くと、もう片方は偽)。
|
|
86
|
+
* d3.deployment のペインタが描くのは終点の矢印(marker.end)だけで、start は描かない。
|
|
87
|
+
*/
|
|
88
|
+
export function edgeStyle(edge: DeploymentEdgeRecord): { stroke: DeploymentEdgeStyle } {
|
|
89
|
+
return {
|
|
90
|
+
stroke: {
|
|
91
|
+
color: edge.stroke?.color || EDGE_DEFAULTS.stroke.color,
|
|
92
|
+
width: edge.stroke?.width || EDGE_DEFAULTS.stroke.width,
|
|
93
|
+
marker: edge.stroke?.marker
|
|
94
|
+
? { start: !!edge.stroke.marker.start, end: !!edge.stroke.marker.end }
|
|
95
|
+
: { ...EDGE_DEFAULTS.stroke.marker },
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** 端点の角度(無ければ 90。Geometry.getPortLineTo と同じ: 0 は 0、それ以外の falsy は 90) */
|
|
101
|
+
export function degreeOf(position: number | undefined | null): number {
|
|
102
|
+
if (position === 0)
|
|
103
|
+
return 0;
|
|
104
|
+
|
|
105
|
+
return position ? position : EDGE_DEFAULTS.position;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* レコードに、d3.deployment が既定で描いている値を重ねて返す(入力は書き換えない)。
|
|
110
|
+
* インスペクタの初期値に使う。children は重ねない(子は右クリックしたときに自分で通す)。
|
|
111
|
+
*
|
|
112
|
+
* 重ねる項目: ノードは type・padding・position・label.text(無ければ '')・border・background、
|
|
113
|
+
* 線は stroke(矢印を含む)・from.position・to.position。size は view が子に合わせて埋めるので触らない。
|
|
114
|
+
*/
|
|
115
|
+
export function withDefaults<T extends DeploymentRecord>(record: T): T {
|
|
116
|
+
if (isEdge(record)) {
|
|
117
|
+
return {
|
|
118
|
+
...record,
|
|
119
|
+
...edgeStyle(record),
|
|
120
|
+
from: { ...record.from, position: degreeOf(record.from.position) },
|
|
121
|
+
to: { ...record.to, position: degreeOf(record.to.position) },
|
|
122
|
+
} as T;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const node = record as DeploymentNodeRecord;
|
|
126
|
+
const style = nodeStyle(node);
|
|
127
|
+
const label = node.label && typeof node.label === 'object' ? node.label : {};
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
...node,
|
|
131
|
+
type: node.type || NODE_DEFAULTS.type,
|
|
132
|
+
label: { ...label, text: typeof label.text === 'string' ? label.text : '' },
|
|
133
|
+
padding: node.padding || node.padding === 0 ? node.padding : NODE_DEFAULTS.padding,
|
|
134
|
+
position: { ...NODE_DEFAULTS.position, ...(node.position || {}) },
|
|
135
|
+
border: style.border,
|
|
136
|
+
background: style.background,
|
|
137
|
+
} as T;
|
|
138
|
+
}
|
package/src/fields.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// 編集できる項目の一覧。インスペクタ(colonoscope の ModelInspector)に渡す。
|
|
2
|
+
//
|
|
3
|
+
// path はレコードの実際の項目を指す。既定値は withDefaults() で重ねてから渡すので、
|
|
4
|
+
// 書いていない項目(枠線の色など)も、図に描かれている値が初期値として出る。
|
|
5
|
+
// type: 'readonly' は表示だけ。options は取りうる値の一覧(検証にも使う)。
|
|
6
|
+
//
|
|
7
|
+
// iHub の SystemConfigurationDiagram.js の fields(label.text / type / id)から始め、
|
|
8
|
+
// d3.deployment が描く説明(ツールチップ)・リンク・幅・高さ・背景色・枠線の色 / 太さ、線の矢印を足した。
|
|
9
|
+
// border.type は d3.deployment が描かないので出さない。
|
|
10
|
+
|
|
11
|
+
import { NODE_TYPES, isEdge } from './defaults.js';
|
|
12
|
+
import type { DeploymentField, DeploymentRecord } from './types.js';
|
|
13
|
+
|
|
14
|
+
const BOOLEAN_OPTIONS = ['true', 'false'];
|
|
15
|
+
|
|
16
|
+
/** ノードの項目 */
|
|
17
|
+
export const NODE_FIELDS: readonly DeploymentField[] = [
|
|
18
|
+
{ path: 'label.text', label: '名称', type: 'text' },
|
|
19
|
+
{ path: 'type', label: '種類', type: 'text', options: NODE_TYPES },
|
|
20
|
+
{ path: 'id', label: 'ID', type: 'readonly' },
|
|
21
|
+
{ path: 'description', label: '説明', type: 'textarea' },
|
|
22
|
+
{ path: 'link.url', label: 'URL', type: 'text' },
|
|
23
|
+
{ path: 'size.w', label: '幅', type: 'number' },
|
|
24
|
+
{ path: 'size.h', label: '高さ', type: 'number' },
|
|
25
|
+
{ path: 'background.color', label: '背景色', type: 'text' },
|
|
26
|
+
{ path: 'border.color', label: '枠線の色', type: 'text' },
|
|
27
|
+
{ path: 'border.width', label: '枠線の太さ', type: 'number' },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
/** 線の項目。接続位置はノードの中心から見た角度(0=真下・時計回り。90=左 / 180=真上 / 270=右) */
|
|
31
|
+
export const EDGE_FIELDS: readonly DeploymentField[] = [
|
|
32
|
+
{ path: 'id', label: 'ID', type: 'readonly' },
|
|
33
|
+
{ path: 'from.id', label: '接続元のノード', type: 'readonly' },
|
|
34
|
+
{ path: 'from.position', label: '接続元の位置 (角度)', type: 'number' },
|
|
35
|
+
{ path: 'to.id', label: '接続先のノード', type: 'readonly' },
|
|
36
|
+
{ path: 'to.position', label: '接続先の位置 (角度)', type: 'number' },
|
|
37
|
+
{ path: 'stroke.color', label: '線の色', type: 'text' },
|
|
38
|
+
{ path: 'stroke.width', label: '線の太さ', type: 'number' },
|
|
39
|
+
// 始点の矢印(marker.start)は d3.deployment のペインタが描かないので項目にしない
|
|
40
|
+
{ path: 'stroke.marker.end', label: '終点の矢印', type: 'text', options: BOOLEAN_OPTIONS },
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
/** レコードに応じた項目の一覧(線 / ノード) */
|
|
44
|
+
export function fieldsOf(record: DeploymentRecord): DeploymentField[] {
|
|
45
|
+
return isEdge(record) ? [...EDGE_FIELDS] : [...NODE_FIELDS];
|
|
46
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// model.deployment — システム構成図(デプロイメント図。d3.deployment)の「図ごとの知識」。描画はしない(react / d3 に依存しない)。
|
|
2
|
+
//
|
|
3
|
+
// スキーマ: SCHEMAS / NODES_SCHEMA / EDGES_SCHEMA(JSON Schema 2020-12)
|
|
4
|
+
// 整合性ルール: check(data)(id の重複・参照先の存在)/ flatten / findById
|
|
5
|
+
// 既定値の補完: withDefaults(record) / nodeStyle / edgeStyle / degreeOf
|
|
6
|
+
// NODE_TYPES / NODE_STYLE_DEFAULTS / LABEL_DEFAULTS / NODE_DEFAULTS / EDGE_DEFAULTS(d3.deployment が読む)
|
|
7
|
+
// 編集できる項目: fieldsOf(record) / NODE_FIELDS / EDGE_FIELDS
|
|
8
|
+
// patch への変換: movePatch(node, move) / applyPatch(record, values) / mergePatch(record, patch)
|
|
9
|
+
//
|
|
10
|
+
// graph 系(unchi / sitemap / deployment)の 3 つ目。check / flatten / movePatch / mergePatch / getByPath / isColor は
|
|
11
|
+
// model.unchi / model.sitemap と同じ内容の複製で、共通化は別 issue で判断する(#146 の 7)。
|
|
12
|
+
|
|
13
|
+
export type {
|
|
14
|
+
DeploymentId, DeploymentNodeType, DeploymentNodeLabel, DeploymentNodeRecord, DeploymentEdgeEnd, DeploymentEdgeStroke,
|
|
15
|
+
DeploymentEdgeRecord, DeploymentRecord, DeploymentGraph, DeploymentCollection, MergePatch, DeploymentChange,
|
|
16
|
+
DeploymentMove, DeploymentFieldType, DeploymentField, DeploymentValues,
|
|
17
|
+
} from './types.js';
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
NODE_TYPES, NODE_STYLE_DEFAULTS, LABEL_DEFAULTS, NODE_DEFAULTS, EDGE_DEFAULTS,
|
|
21
|
+
isEdge, nodeStyle, edgeStyle, degreeOf, withDefaults,
|
|
22
|
+
} from './defaults.js';
|
|
23
|
+
|
|
24
|
+
export type { DeploymentEdgeStyle } from './defaults.js';
|
|
25
|
+
export { NODES_SCHEMA, EDGES_SCHEMA, SCHEMAS } from './schema.js';
|
|
26
|
+
export { check, flatten, findById } from './check.js';
|
|
27
|
+
export { NODE_FIELDS, EDGE_FIELDS, fieldsOf } from './fields.js';
|
|
28
|
+
export { ModelError, collectionOf, titleOf, getByPath, mergePatch, movePatch, applyPatch, isColor } from './patch.js';
|