@yanqirenshi/model.deployment 0.0.0-stage → 0.1.1

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.
@@ -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 / findById / movePatch / mergePatch / getByPath / isColor と
11
+ // 入力の読み取り部品は model.core(#151)のもので、ここでは model.deployment の型を付けて公開している。
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';
package/src/patch.ts ADDED
@@ -0,0 +1,158 @@
1
+ // UI の操作(ドラッグ・インスペクタの適用)を、保存する patch(JSON Merge Patch)に変換する。
2
+ //
3
+ // 戻り値は { collection, id, patch }。利用側(iHub など)は図の種類を知らずにこれを保存すればよい。
4
+ // 入力の検証に失敗したときは ModelError を投げる(メッセージは利用者にそのまま見せられる日本語)。
5
+ // model.sitemap の patch.ts を元にしている(ラベルは label.text、種類は NODE / COMPONENT、線に矢印の有無がある)。
6
+ // 読み取り部品(toColor など)と patch の道具は model.core にある(#151)。
7
+
8
+ import {
9
+ ModelError, changedValues as changedValuesOf, movePatch as movePatchTo, put,
10
+ toBoolean, toColor, toDegree, toLength, toOption, toOptionalText, toWidth,
11
+ } from '@yanqirenshi/model.core';
12
+
13
+ import { NODE_TYPES, isEdge, withDefaults } from './defaults.js';
14
+ import { fieldsOf } from './fields.js';
15
+ import type {
16
+ MergePatch, DeploymentChange, DeploymentCollection, DeploymentEdgeRecord, DeploymentMove, DeploymentNodeRecord,
17
+ DeploymentRecord, DeploymentValues,
18
+ } from './types.js';
19
+
20
+ // model.core のものをそのまま公開する(利用側が model.deployment だけを読めばよいように)
21
+ export { ModelError, getByPath, mergePatch, isColor } from '@yanqirenshi/model.core';
22
+
23
+ /** レコードを置くコレクション */
24
+ export function collectionOf(record: DeploymentRecord): DeploymentCollection {
25
+ return isEdge(record) ? 'edges' : 'nodes';
26
+ }
27
+
28
+ /** インスペクタの見出し。ラベルの文字、無ければ「種類 id」(線は「接続元 → 接続先」) */
29
+ export function titleOf(record: DeploymentRecord): string {
30
+ if (isEdge(record))
31
+ return `${String(record.from.id)} → ${String(record.to.id)}`;
32
+
33
+ const text = record.label && typeof record.label === 'object' ? record.label.text : null;
34
+
35
+ if (typeof text === 'string' && text !== '')
36
+ return text;
37
+
38
+ return `${record.type || ''} ${String(record.id)}`.trim();
39
+ }
40
+
41
+ /**
42
+ * ドラッグで動かしたノードの位置を保存する patch。
43
+ * 保存してある position に移動量を足し、整数に丸める(position が無いときは 0, 0 から)。
44
+ * 子のノード(COMPONENT を含む)の position は親の中での位置なので、親を動かしても子の patch は要らない。
45
+ */
46
+ export function movePatch(node: DeploymentNodeRecord, move: DeploymentMove): DeploymentChange {
47
+ return movePatchTo(node, move, 'nodes');
48
+ }
49
+
50
+ // インスペクタの入力のうち、図に描いている値(withDefaults)から変わった項目だけ
51
+ function changedValues(record: DeploymentRecord, values: DeploymentValues): Map<string, string> {
52
+ return changedValuesOf(withDefaults(record), values, fieldsOf(record));
53
+ }
54
+
55
+ // 幅・高さは 0 以上(0 は「子に合わせる」)。
56
+ // 枠線・線の太さは 0 より大きい数(d3.deployment は 0 を「書いていない」と見て既定に倒すので、0 は入れられない)
57
+ const LENGTH = { hint: '(0 で子に合わせる)' };
58
+
59
+ // ノードの入力 → patch(label.text / type / description / link.url / size / background / border)
60
+ function nodePatch(node: DeploymentNodeRecord, values: DeploymentValues): MergePatch {
61
+ const patch: MergePatch = {};
62
+
63
+ for (const [path, value] of changedValues(node, values)) {
64
+ switch (path) {
65
+ case 'label.text':
66
+ // 名称は空にできない(d3.deployment は名称の無いノードに '????????' を描く)
67
+ if (value === '')
68
+ throw new ModelError('名称を入力してください。');
69
+
70
+ put(patch, path, value);
71
+ break;
72
+ case 'type':
73
+ put(patch, path, toOption(value, NODE_TYPES, '種類'));
74
+ break;
75
+ case 'description':
76
+ put(patch, path, toOptionalText(value));
77
+ break;
78
+ case 'link.url':
79
+ // URL を空にするとリンクごと除く(d3.deployment は url の無い link を描かない)
80
+ if (value === '')
81
+ patch.link = null;
82
+ else
83
+ put(patch, path, value);
84
+ break;
85
+ case 'size.w':
86
+ put(patch, path, toLength(value, '幅', LENGTH));
87
+ break;
88
+ case 'size.h':
89
+ put(patch, path, toLength(value, '高さ', LENGTH));
90
+ break;
91
+ case 'background.color':
92
+ put(patch, path, toColor(value, '背景色'));
93
+ break;
94
+ case 'border.color':
95
+ put(patch, path, toColor(value, '枠線の色'));
96
+ break;
97
+ case 'border.width':
98
+ put(patch, path, toWidth(value, '枠線の太さ'));
99
+ break;
100
+ }
101
+ }
102
+
103
+ return patch;
104
+ }
105
+
106
+ const END_LABELS: Record<string, string> = { from: '接続元', to: '接続先' };
107
+
108
+ // 線の入力 → patch(from / to の角度・stroke・矢印)
109
+ function edgePatch(edge: DeploymentEdgeRecord, values: DeploymentValues): MergePatch {
110
+ const patch: MergePatch = {};
111
+
112
+ for (const [path, value] of changedValues(edge, values)) {
113
+ switch (path) {
114
+ case 'from.position':
115
+ case 'to.position':
116
+ put(patch, path, toDegree(value, `${END_LABELS[path.split('.')[0]]}の位置`));
117
+ break;
118
+ case 'stroke.color':
119
+ put(patch, path, toColor(value, '線の色'));
120
+ break;
121
+ case 'stroke.width':
122
+ put(patch, path, toWidth(value, '線の太さ'));
123
+ break;
124
+ case 'stroke.marker.start':
125
+ put(patch, path, toBoolean(value, '始点の矢印'));
126
+ break;
127
+ case 'stroke.marker.end':
128
+ put(patch, path, toBoolean(value, '終点の矢印'));
129
+ break;
130
+ }
131
+ }
132
+
133
+ // 矢印を片方だけ書くと d3.deployment の Edge.normalize は marker を丸ごと入力で置き換える(無い側は undefined =
134
+ // start は偽、end は truthy 判定で偽)ので、片方を変えたときはもう片方も描いている値で埋める
135
+ const marker = (patch.stroke as MergePatch | undefined)?.marker as MergePatch | undefined;
136
+
137
+ if (marker) {
138
+ const effective = withDefaults(edge).stroke!.marker!;
139
+
140
+ if (marker.start === undefined) marker.start = effective.start;
141
+ if (marker.end === undefined) marker.end = effective.end;
142
+ }
143
+
144
+ return patch;
145
+ }
146
+
147
+ /**
148
+ * インスペクタの「適用」の値を、保存する patch に変換する。
149
+ * 図に描いている値から変えた項目だけを patch に入れる(変えていない既定の値まで書き込まないため)。
150
+ * 変更が無ければ patch は {}。
151
+ */
152
+ export function applyPatch(record: DeploymentRecord, values: DeploymentValues): DeploymentChange {
153
+ return {
154
+ collection: collectionOf(record),
155
+ id: record.id,
156
+ patch: isEdge(record) ? edgePatch(record, values) : nodePatch(record as DeploymentNodeRecord, values),
157
+ };
158
+ }
package/src/schema.ts ADDED
@@ -0,0 +1,100 @@
1
+ // d3.deployment の入力データの JSON Schema(2020-12)。
2
+ //
3
+ // iHub の server/schemas/deployment/{nodes,edges}.schema.json を移したもの。
4
+ // 検査するのは、図の構造にかかわる項目(id・type・size・position・children・link、線の from / to)だけで、
5
+ // 見た目の項目(label・border・background・padding・description)は d3.deployment の入力の形のまま書けるよう
6
+ // additionalProperties を閉じていない(iHub 版は閉じていた)。
7
+ //
8
+ // x-id-start はスキーマの検証には使わない注釈(コレクションが空のときに最初に振る id)。
9
+ // Ajv で使うときは addKeyword({ keyword: 'x-id-start', schemaType: 'number' }) を足す。
10
+
11
+ import { NODE_TYPES } from './defaults.js';
12
+
13
+ const ID = { type: ['integer', 'string'] } as const;
14
+
15
+ const POINT = {
16
+ type: 'object',
17
+ properties: { x: { type: 'number' }, y: { type: 'number' } },
18
+ required: ['x', 'y'],
19
+ } as const;
20
+
21
+ export const NODES_SCHEMA = {
22
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
23
+ title: 'システム構成図のノード',
24
+ description: 'd3.deployment に渡すノード。children に子のノードを入れ子で持つ。id は子のノードも含めて一意にする。position は親の中での位置で、ドラッグで動かすと書き換わる。link を付けると、ノードの左下にリンクを出す。見た目の項目 (label・padding・border・background・description) は d3.deployment の入力の形のまま書ける。',
25
+ 'x-id-start': 1,
26
+ type: 'array',
27
+ items: { $ref: '#/$defs/node' },
28
+ $defs: {
29
+ node: {
30
+ type: 'object',
31
+ properties: {
32
+ id: ID,
33
+ type: { enum: [...NODE_TYPES] },
34
+ label: {
35
+ type: 'object',
36
+ properties: { text: { type: ['string', 'null'] } },
37
+ },
38
+ description: { type: ['string', 'null'] },
39
+ size: {
40
+ type: 'object',
41
+ properties: { w: { type: 'number', minimum: 0 }, h: { type: 'number', minimum: 0 } },
42
+ required: ['w', 'h'],
43
+ },
44
+ position: POINT,
45
+ link: {
46
+ type: ['object', 'null'],
47
+ properties: { url: { type: ['string', 'null'] } },
48
+ },
49
+ children: { type: 'array', items: { $ref: '#/$defs/node' } },
50
+ },
51
+ required: ['id'],
52
+ additionalProperties: true,
53
+ },
54
+ },
55
+ } as const;
56
+
57
+ export const EDGES_SCHEMA = {
58
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
59
+ title: 'システム構成図の線',
60
+ description: 'ノードの間の線。from.id / to.id はノード (子を含む) の id。position は接続位置の角度 (0=真下・時計回り。省略すると 90=左)。stroke.marker は両端の矢印の有無 (既定: 終点だけ)。',
61
+ 'x-id-start': 100,
62
+ type: 'array',
63
+ items: {
64
+ type: 'object',
65
+ properties: {
66
+ id: ID,
67
+ from: { $ref: '#/$defs/end' },
68
+ to: { $ref: '#/$defs/end' },
69
+ stroke: {
70
+ type: 'object',
71
+ properties: {
72
+ color: { type: 'string' },
73
+ width: { type: 'number' },
74
+ marker: {
75
+ type: 'object',
76
+ properties: { start: { type: 'boolean' }, end: { type: 'boolean' } },
77
+ },
78
+ },
79
+ },
80
+ },
81
+ required: ['id', 'from', 'to'],
82
+ additionalProperties: true,
83
+ },
84
+ $defs: {
85
+ end: {
86
+ type: 'object',
87
+ properties: {
88
+ id: ID,
89
+ position: { type: 'number' },
90
+ },
91
+ required: ['id'],
92
+ },
93
+ },
94
+ } as const;
95
+
96
+ /** コレクション名 → スキーマ */
97
+ export const SCHEMAS = {
98
+ nodes: NODES_SCHEMA,
99
+ edges: EDGES_SCHEMA,
100
+ } as const;
package/src/types.ts ADDED
@@ -0,0 +1,94 @@
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
+
7
+ import type { MergePatch, ModelField, ModelFieldType, ModelMove, ModelValues } from '@yanqirenshi/model.core';
8
+ import type { Background, Border, Font, Position, Size, Stroke } from '@yanqirenshi/types';
9
+
10
+ /** レコードの id(nodes / edges とも。ノードは子も含めて一意) */
11
+ export type DeploymentId = number | string;
12
+
13
+ /** ノードの種類。NODE(通常のノード)か COMPONENT */
14
+ export type DeploymentNodeType = 'NODE' | 'COMPONENT';
15
+
16
+ /** ノードのラベル */
17
+ export interface DeploymentNodeLabel {
18
+ text?: string | null;
19
+ position?: Position;
20
+ font?: Font;
21
+ }
22
+
23
+ /** ノードのレコード(入れ子可)。ライブラリが解釈しない項目も持てる(index signature) */
24
+ export interface DeploymentNodeRecord {
25
+ id: DeploymentId;
26
+ type?: DeploymentNodeType | string;
27
+ label?: DeploymentNodeLabel | null;
28
+ /** ホバーのツールチップ(SVG title) */
29
+ description?: string | null;
30
+ size?: Size;
31
+ position?: Position;
32
+ padding?: number;
33
+ border?: Border;
34
+ background?: Background;
35
+ /** ノードの左下に出すリンク */
36
+ link?: { url?: string | null; [key: string]: unknown } | null;
37
+ children?: DeploymentNodeRecord[];
38
+ [key: string]: unknown;
39
+ }
40
+
41
+ /** 線の端点。position はノードの中心から見た角度(0=真下・時計回り。90=左 / 180=真上 / 270=右) */
42
+ export interface DeploymentEdgeEnd {
43
+ id: DeploymentId;
44
+ position?: number;
45
+ }
46
+
47
+ /** 線の見た目。marker は両端の矢印の有無 */
48
+ export interface DeploymentEdgeStroke extends Stroke {
49
+ marker?: { start?: boolean; end?: boolean };
50
+ }
51
+
52
+ /** 線のレコード */
53
+ export interface DeploymentEdgeRecord {
54
+ id: DeploymentId;
55
+ from: DeploymentEdgeEnd;
56
+ to: DeploymentEdgeEnd;
57
+ stroke?: DeploymentEdgeStroke;
58
+ [key: string]: unknown;
59
+ }
60
+
61
+ export type DeploymentRecord = DeploymentNodeRecord | DeploymentEdgeRecord;
62
+
63
+ /** 図全体(d3.deployment の data() に渡すもの) */
64
+ export interface DeploymentGraph {
65
+ nodes?: DeploymentNodeRecord[];
66
+ edges?: DeploymentEdgeRecord[];
67
+ }
68
+
69
+ /** レコードを置くコレクション */
70
+ export type DeploymentCollection = 'nodes' | 'edges';
71
+
72
+ /** JSON Merge Patch(RFC 7386)。null は「その項目を除く」。model.core の MergePatch と同じ(iHub の PATCH /{collection}/{id} が受け取る形) */
73
+ export type { MergePatch };
74
+
75
+ /** 保存する変更。利用側は図の種類を知らずにこれを保存すればよい */
76
+ export interface DeploymentChange {
77
+ collection: DeploymentCollection;
78
+ id: DeploymentId;
79
+ patch: MergePatch;
80
+ }
81
+
82
+ /** ドラッグの移動量(世界座標。d3.deployment の callbacks.node.dragend が渡す move と同じ) */
83
+ export type DeploymentMove = ModelMove;
84
+
85
+ /**
86
+ * 編集できる項目。colonoscope の ColonoscopeField(path / label / type)と同じ形で、
87
+ * 取りうる値の一覧 options だけ足している(型は構造的に互換。colonoscope には依存しない)。
88
+ */
89
+ export type DeploymentFieldType = ModelFieldType;
90
+
91
+ export type DeploymentField = ModelField;
92
+
93
+ /** インスペクタの「適用」が渡す値(path → 入力した文字列) */
94
+ export type DeploymentValues = ModelValues;