@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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018-2026 Satoshi Iwasaki (yanqirenshi@gmail.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,130 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @yanqirenshi/model.deployment
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
システム構成図(デプロイメント図。view = `d3.deployment`)の**図ごとの知識**を、描画から独立させたパッケージです。
|
|
4
|
+
react / d3 に依存しない、純粋なデータと関数だけを持ちます。
|
|
5
|
+
d3.deployment は、このパッケージの既定値(枠線・背景・線の stroke と矢印・端点の角度)を読みます。
|
|
6
|
+
|
|
7
|
+
| 持つもの | 入口 |
|
|
8
|
+
|---|---|
|
|
9
|
+
| 入力データのスキーマ(JSON Schema 2020-12) | `SCHEMAS.nodes` / `SCHEMAS.edges` |
|
|
10
|
+
| コレクションをまたぐ整合性ルール(id の重複・参照先の存在) | `check(data)` |
|
|
11
|
+
| d3.deployment が既定で描いている値 | `withDefaults(record)` / `nodeStyle` / `edgeStyle`、`NODE_STYLE_DEFAULTS` など |
|
|
12
|
+
| 編集できる項目の一覧(インスペクタ用) | `fieldsOf(record)` |
|
|
13
|
+
| UI の操作を保存する patch に変える変換 | `movePatch(node, move)` / `applyPatch(record, values)` |
|
|
14
|
+
|
|
15
|
+
利用側(iHub など)は、モデルが返す `{ collection, id, patch }` を保存するだけで、図の種類を知らずに書けます。
|
|
16
|
+
|
|
17
|
+
## 位置づけ
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
利用側 ──▶ d3.deployment (view) ──▶ model.deployment (既定値を読む)
|
|
21
|
+
──▶ colonoscope (ModelInspector)
|
|
22
|
+
──▶ model.deployment
|
|
23
|
+
──▶ 自前の永続化
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
model.deployment・colonoscope の間に横の依存はありません。組み合わせるのは利用側だけです。
|
|
27
|
+
これは [Foolsgolds/Assholes#138](https://github.com/Foolsgolds/Assholes/issues/138) の派生([#146](https://github.com/Foolsgolds/Assholes/issues/146))で、
|
|
28
|
+
ノードと線の図(graph 系: [model.unchi](../model.unchi) / [model.sitemap](../model.sitemap) / model.deployment)の 3 つ目です。
|
|
29
|
+
`check` / `movePatch` などは同じ内容の複製で、共通化は別イシューで判断します。
|
|
30
|
+
|
|
31
|
+
## レコードの形
|
|
32
|
+
|
|
33
|
+
d3.deployment の `data()` に渡す入力そのもの(`nodes` / `edges` の 1 要素)です。
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
{
|
|
37
|
+
nodes: [{ id, type: 'NODE' | 'COMPONENT', label: { text, position, font }, description,
|
|
38
|
+
size: { w, h }, position: { x, y }, padding, border, background, link: { url },
|
|
39
|
+
children: [ /* 同じ形 */ ] }],
|
|
40
|
+
edges: [{ id, from: { id, position }, to: { id, position },
|
|
41
|
+
stroke: { color, width, marker: { start, end } } }],
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
model.unchi(ポンチ絵)との違い: ノードの種類は NODE / COMPONENT の 2 つ、`description` はツールチップ、線の矢印は `stroke.marker.end`(真偽値。描かれるのは終点だけ)です。
|
|
46
|
+
|
|
47
|
+
## 使い方
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
import D3Deployment, { Rectum } from '@yanqirenshi/d3.deployment';
|
|
51
|
+
import { ModelInspector } from '@yanqirenshi/colonoscope';
|
|
52
|
+
import {
|
|
53
|
+
check, fieldsOf, titleOf, withDefaults, movePatch, applyPatch, mergePatch, findById, ModelError,
|
|
54
|
+
} from '@yanqirenshi/model.deployment';
|
|
55
|
+
|
|
56
|
+
const errors = check({ nodes, edges }); // [] なら問題なし
|
|
57
|
+
|
|
58
|
+
const rectum = new Rectum({
|
|
59
|
+
callbacks: {
|
|
60
|
+
node: {
|
|
61
|
+
// ドラッグの終わり → 位置の patch(core は入力のレコード)
|
|
62
|
+
dragend: (core, node, move) => save(movePatch(core, move)),
|
|
63
|
+
// → { collection: 'nodes', id: 1, patch: { position: { x: 120, y: 40 } } }
|
|
64
|
+
contextmenu: (core) => setTarget({ collection: 'nodes', id: core.id }),
|
|
65
|
+
},
|
|
66
|
+
edge: { contextmenu: (core) => setTarget({ collection: 'edges', id: core.id }) },
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const record = findById(data[target.collection], target.id); // ノードは子も探す
|
|
71
|
+
<ModelInspector
|
|
72
|
+
target={record && withDefaults(record)}
|
|
73
|
+
fields={fieldsOf}
|
|
74
|
+
title={titleOf}
|
|
75
|
+
onApply={(values) => {
|
|
76
|
+
try {
|
|
77
|
+
save(applyPatch(record, values)); // 変えた項目だけが patch に入る
|
|
78
|
+
} catch (e) {
|
|
79
|
+
if (e instanceof ModelError) alert(e.message); // 利用者向けの日本語
|
|
80
|
+
}
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### `applyPatch` の検証
|
|
86
|
+
|
|
87
|
+
| 項目 | 規則 |
|
|
88
|
+
|---|---|
|
|
89
|
+
| 名称(`label.text`) | 空にできない(d3.deployment は名称の無いノードに `????????` を描く) |
|
|
90
|
+
| 種類(`type`) | `NODE` / `COMPONENT` |
|
|
91
|
+
| 説明 | 空にすると項目を除く |
|
|
92
|
+
| URL(`link.url`) | 空にすると `link` ごと除く |
|
|
93
|
+
| 幅・高さ | 0 以上。整数に丸める(0 で子に合わせる) |
|
|
94
|
+
| 色(背景・枠線・線) | ブラウザでは `CSS.supports('color', v)`。CSS の無い環境では `#hex` / 関数表記 / 英字の色名を通す緩い判定 |
|
|
95
|
+
| 枠線・線の太さ | 0 より大きい数(d3.deployment は 0 を「書いていない」と見て既定に倒す) |
|
|
96
|
+
| 接続位置(角度) | 数。0 以上 360 未満の整数に正規化 |
|
|
97
|
+
| 終点の矢印 | `true` / `false`。d3.deployment は `marker` を丸ごと置き換えるので、patch には始点側も描いている値で埋めて入れる |
|
|
98
|
+
|
|
99
|
+
図に描いている値(`withDefaults` の結果)から**変えた項目だけ**を patch に入れます。
|
|
100
|
+
|
|
101
|
+
### 編集できる項目(`fieldsOf`)
|
|
102
|
+
|
|
103
|
+
- ノード: 名称・種類・ID・説明・URL・幅・高さ・背景色・枠線の色 / 太さ
|
|
104
|
+
- 線: ID・接続元 / 接続先のノードと位置(角度)・線の色 / 太さ・終点の矢印
|
|
105
|
+
|
|
106
|
+
`border.type` と始点の矢印(`marker.start`)は d3.deployment が描かないので項目にしません。
|
|
107
|
+
|
|
108
|
+
### 既定値の出どころ
|
|
109
|
+
|
|
110
|
+
d3.deployment は共通基盤 assh0le のデータクラス(`Stroke` / `Background` / `Label` / `Padding` / `Position`)で入力を正規化していました。
|
|
111
|
+
枠線・背景の既定はここに移し(`nodeStyle`)、assh0le のテンプレートと同じ値であることをテストで突き合わせています。
|
|
112
|
+
ラベルの位置・フォント・余白・位置の正規化は assh0le のまま(値はこのパッケージの定数と同じ)。
|
|
113
|
+
|
|
114
|
+
## スコープ外(いまは持たない)
|
|
115
|
+
|
|
116
|
+
- `label.position` / `label.font` / `padding` の編集、ノード・線の追加削除
|
|
117
|
+
- ファイル・HTTP などの永続化(利用側の実装)
|
|
118
|
+
|
|
119
|
+
## 配布形式
|
|
120
|
+
|
|
121
|
+
ESM(`dist/`)に加えて CommonJS 版(`dist/cjs/`)も出します。
|
|
122
|
+
Babel 系の `d3.deployment` を jest(CommonJS)でテストするためで、d3.deployment の jest が ESM を読めるようになったら外せます。
|
|
123
|
+
|
|
124
|
+
## コマンド
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
npm run build -w @yanqirenshi/model.deployment # tsc で dist/(ESM + d.ts)と dist/cjs/(CommonJS)へビルド
|
|
128
|
+
npm run typecheck -w @yanqirenshi/model.deployment
|
|
129
|
+
npm test -w @yanqirenshi/model.deployment # vitest(assh0le のテンプレートとの突き合わせを含む)
|
|
130
|
+
```
|
package/dist/check.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DeploymentGraph, DeploymentId, DeploymentNodeRecord, DeploymentRecord } from './types.js';
|
|
2
|
+
/** 入れ子のノード(children)を平らにする(親 → 子 の順) */
|
|
3
|
+
export declare function flatten(nodes: DeploymentNodeRecord[]): DeploymentNodeRecord[];
|
|
4
|
+
/**
|
|
5
|
+
* 図全体を検査する。
|
|
6
|
+
* - nodes(子を含む)の id が重複していない
|
|
7
|
+
* - edges の id が重複していない
|
|
8
|
+
* - edges の from.id / to.id がノードに存在する
|
|
9
|
+
*
|
|
10
|
+
* @param prefix メッセージのコレクション名の前に付ける文字列(例: 'deployment/')
|
|
11
|
+
*/
|
|
12
|
+
export declare function check(data: DeploymentGraph, prefix?: string): string[];
|
|
13
|
+
/** ノード(子を含む)か線を id で探す(無ければ null) */
|
|
14
|
+
export declare function findById<T extends DeploymentRecord>(list: readonly T[] | undefined, id: DeploymentId): T | null;
|
|
15
|
+
//# sourceMappingURL=check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAExG,wCAAwC;AACxC,wBAAgB,OAAO,CAAC,KAAK,EAAE,oBAAoB,EAAE,GAAG,oBAAoB,EAAE,CAE7E;AAgBD;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,eAAe,EAAE,MAAM,SAAK,GAAG,MAAM,EAAE,CAalE;AAED,qCAAqC;AACrC,wBAAgB,QAAQ,CAAC,CAAC,SAAS,gBAAgB,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,GAAG,SAAS,EAAE,EAAE,EAAE,YAAY,GAAG,CAAC,GAAG,IAAI,CAa/G"}
|
package/dist/check.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
/** 入れ子のノード(children)を平らにする(親 → 子 の順) */
|
|
8
|
+
export function flatten(nodes) {
|
|
9
|
+
return nodes.flatMap(node => [node, ...flatten(node.children || [])]);
|
|
10
|
+
}
|
|
11
|
+
// list の中で id が重複していればエラーにする。id の集合を返す。
|
|
12
|
+
function checkUnique(errors, label, list) {
|
|
13
|
+
const values = new Set();
|
|
14
|
+
for (const d of list) {
|
|
15
|
+
if (values.has(d.id))
|
|
16
|
+
errors.push(`${label}: id が重複しています。(id=${String(d.id)})`);
|
|
17
|
+
values.add(d.id);
|
|
18
|
+
}
|
|
19
|
+
return values;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 図全体を検査する。
|
|
23
|
+
* - nodes(子を含む)の id が重複していない
|
|
24
|
+
* - edges の id が重複していない
|
|
25
|
+
* - edges の from.id / to.id がノードに存在する
|
|
26
|
+
*
|
|
27
|
+
* @param prefix メッセージのコレクション名の前に付ける文字列(例: 'deployment/')
|
|
28
|
+
*/
|
|
29
|
+
export function check(data, prefix = '') {
|
|
30
|
+
const errors = [];
|
|
31
|
+
const node_ids = checkUnique(errors, `${prefix}nodes`, flatten(data.nodes || []));
|
|
32
|
+
checkUnique(errors, `${prefix}edges`, data.edges || []);
|
|
33
|
+
for (const edge of data.edges || [])
|
|
34
|
+
for (const end of ['from', 'to'])
|
|
35
|
+
if (!edge[end] || !node_ids.has(edge[end].id))
|
|
36
|
+
errors.push(`${prefix}edges: ${end}.id のノードが見つかりません。(id=${String(edge.id)}, ${end}.id=${String(edge[end]?.id)})`);
|
|
37
|
+
return errors;
|
|
38
|
+
}
|
|
39
|
+
/** ノード(子を含む)か線を id で探す(無ければ null) */
|
|
40
|
+
export function findById(list, id) {
|
|
41
|
+
for (const d of list || []) {
|
|
42
|
+
if (d.id === id)
|
|
43
|
+
return d;
|
|
44
|
+
const children = d.children;
|
|
45
|
+
const hit = children ? findById(children, id) : null;
|
|
46
|
+
if (hit)
|
|
47
|
+
return hit;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// コレクションをまたぐ整合性の検査(id の重複・参照先の存在)。
|
|
3
|
+
// スキーマ(schema.ts)に合っているデータを渡す。戻り値はエラーのメッセージの配列(空なら問題なし)。
|
|
4
|
+
//
|
|
5
|
+
// iHub の server/validation/rules.mjs の checkGraph('deployment', data) と同じ検査・同じ文言。
|
|
6
|
+
// model.unchi / model.sitemap の check.ts の複製(graph 系の 3 つ目。共通化の判断は #146 の 7 — 別 issue)。
|
|
7
|
+
// メッセージの先頭はコレクション名で、prefix を渡すと 'deployment/nodes: ...' のように付けられる。
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.flatten = flatten;
|
|
10
|
+
exports.check = check;
|
|
11
|
+
exports.findById = findById;
|
|
12
|
+
/** 入れ子のノード(children)を平らにする(親 → 子 の順) */
|
|
13
|
+
function flatten(nodes) {
|
|
14
|
+
return nodes.flatMap(node => [node, ...flatten(node.children || [])]);
|
|
15
|
+
}
|
|
16
|
+
// list の中で id が重複していればエラーにする。id の集合を返す。
|
|
17
|
+
function checkUnique(errors, label, list) {
|
|
18
|
+
const values = new Set();
|
|
19
|
+
for (const d of list) {
|
|
20
|
+
if (values.has(d.id))
|
|
21
|
+
errors.push(`${label}: id が重複しています。(id=${String(d.id)})`);
|
|
22
|
+
values.add(d.id);
|
|
23
|
+
}
|
|
24
|
+
return values;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 図全体を検査する。
|
|
28
|
+
* - nodes(子を含む)の id が重複していない
|
|
29
|
+
* - edges の id が重複していない
|
|
30
|
+
* - edges の from.id / to.id がノードに存在する
|
|
31
|
+
*
|
|
32
|
+
* @param prefix メッセージのコレクション名の前に付ける文字列(例: 'deployment/')
|
|
33
|
+
*/
|
|
34
|
+
function check(data, prefix = '') {
|
|
35
|
+
const errors = [];
|
|
36
|
+
const node_ids = checkUnique(errors, `${prefix}nodes`, flatten(data.nodes || []));
|
|
37
|
+
checkUnique(errors, `${prefix}edges`, data.edges || []);
|
|
38
|
+
for (const edge of data.edges || [])
|
|
39
|
+
for (const end of ['from', 'to'])
|
|
40
|
+
if (!edge[end] || !node_ids.has(edge[end].id))
|
|
41
|
+
errors.push(`${prefix}edges: ${end}.id のノードが見つかりません。(id=${String(edge.id)}, ${end}.id=${String(edge[end]?.id)})`);
|
|
42
|
+
return errors;
|
|
43
|
+
}
|
|
44
|
+
/** ノード(子を含む)か線を id で探す(無ければ null) */
|
|
45
|
+
function findById(list, id) {
|
|
46
|
+
for (const d of list || []) {
|
|
47
|
+
if (d.id === id)
|
|
48
|
+
return d;
|
|
49
|
+
const children = d.children;
|
|
50
|
+
const hit = children ? findById(children, id) : null;
|
|
51
|
+
if (hit)
|
|
52
|
+
return hit;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// d3.deployment が「書いていない項目」に既定で描く値。
|
|
3
|
+
//
|
|
4
|
+
// 値の出どころは、d3.deployment の datamodels/Node.js が使う共通基盤 assh0le のデータクラス
|
|
5
|
+
// (Stroke.template = 枠線、Background.template = 背景、Label.template = ラベル、Padding / Position.template)と、
|
|
6
|
+
// datamodels/Edge.js の dataTemplate(stroke と矢印)、Geometry.getPortLineTo(角度の既定 90)。
|
|
7
|
+
// d3.deployment はこのファイルを読む(#146)ので、ここが正。
|
|
8
|
+
//
|
|
9
|
+
// d3.deployment は border.type を正規化するが描画には使わない(dasharray を付けない)ので、モデルも項目にしない。
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.EDGE_DEFAULTS = exports.NODE_DEFAULTS = exports.LABEL_DEFAULTS = exports.NODE_STYLE_DEFAULTS = exports.NODE_TYPES = void 0;
|
|
12
|
+
exports.isEdge = isEdge;
|
|
13
|
+
exports.nodeStyle = nodeStyle;
|
|
14
|
+
exports.edgeStyle = edgeStyle;
|
|
15
|
+
exports.degreeOf = degreeOf;
|
|
16
|
+
exports.withDefaults = withDefaults;
|
|
17
|
+
/** ノードの種類 */
|
|
18
|
+
exports.NODE_TYPES = ['NODE', 'COMPONENT'];
|
|
19
|
+
/** ノードの枠線・背景の既定(assh0le の Stroke.template / Background.template) */
|
|
20
|
+
exports.NODE_STYLE_DEFAULTS = {
|
|
21
|
+
border: { color: '#afafb0', width: 1, type: 'solid' },
|
|
22
|
+
background: { color: '#fff', opacity: 1.0 },
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* ラベルの既定(assh0le の Label.template)。text が無いとき d3.deployment は '????????' を描く
|
|
26
|
+
* (withDefaults では '' を入れる — 「書いていない」ことをインスペクタで見せるため)。
|
|
27
|
+
*/
|
|
28
|
+
exports.LABEL_DEFAULTS = {
|
|
29
|
+
placeholder: '????????',
|
|
30
|
+
position: { x: 0, y: 0 },
|
|
31
|
+
font: { size: 16, color: '#333333' },
|
|
32
|
+
};
|
|
33
|
+
/** ノードの既定(Padding / Position.template)。size の 0 は「子に合わせる」 */
|
|
34
|
+
exports.NODE_DEFAULTS = {
|
|
35
|
+
type: 'NODE',
|
|
36
|
+
padding: 0,
|
|
37
|
+
position: { x: 0, y: 0 },
|
|
38
|
+
};
|
|
39
|
+
/** 線の既定(Edge.dataTemplate の stroke、端点の角度は Geometry.getPortLineTo の 90=左) */
|
|
40
|
+
exports.EDGE_DEFAULTS = {
|
|
41
|
+
stroke: {
|
|
42
|
+
color: '#333',
|
|
43
|
+
width: 1.5,
|
|
44
|
+
marker: { start: false, end: true },
|
|
45
|
+
},
|
|
46
|
+
position: 90,
|
|
47
|
+
};
|
|
48
|
+
/** 線か(from と to を持つ)。ノードは持たない */
|
|
49
|
+
function isEdge(record) {
|
|
50
|
+
return !!(record && record.from && record.to);
|
|
51
|
+
}
|
|
52
|
+
/** 既定に入力の指定(truthy な項目だけ。assh0le の Stroke.build / Background.build と同じ判定)を重ねる */
|
|
53
|
+
function mergeStyle(defaults, given) {
|
|
54
|
+
const out = { ...defaults };
|
|
55
|
+
for (const key of Object.keys(given || {})) {
|
|
56
|
+
const v = given[key];
|
|
57
|
+
if (v)
|
|
58
|
+
out[key] = v;
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
/** ノードが描かれる枠線・背景(既定に入力を重ねた、実際に描かれる値) */
|
|
63
|
+
function nodeStyle(node) {
|
|
64
|
+
return {
|
|
65
|
+
border: mergeStyle(exports.NODE_STYLE_DEFAULTS.border, node.border),
|
|
66
|
+
background: mergeStyle(exports.NODE_STYLE_DEFAULTS.background, node.background),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 線が描かれる値(Edge.normalize と同じ: stroke の color / width / marker はそれぞれ truthy ならそれ)。
|
|
71
|
+
* marker は入力があれば丸ごと置き換わる(片方だけ書くと、もう片方は偽)。
|
|
72
|
+
* d3.deployment のペインタが描くのは終点の矢印(marker.end)だけで、start は描かない。
|
|
73
|
+
*/
|
|
74
|
+
function edgeStyle(edge) {
|
|
75
|
+
return {
|
|
76
|
+
stroke: {
|
|
77
|
+
color: edge.stroke?.color || exports.EDGE_DEFAULTS.stroke.color,
|
|
78
|
+
width: edge.stroke?.width || exports.EDGE_DEFAULTS.stroke.width,
|
|
79
|
+
marker: edge.stroke?.marker
|
|
80
|
+
? { start: !!edge.stroke.marker.start, end: !!edge.stroke.marker.end }
|
|
81
|
+
: { ...exports.EDGE_DEFAULTS.stroke.marker },
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** 端点の角度(無ければ 90。Geometry.getPortLineTo と同じ: 0 は 0、それ以外の falsy は 90) */
|
|
86
|
+
function degreeOf(position) {
|
|
87
|
+
if (position === 0)
|
|
88
|
+
return 0;
|
|
89
|
+
return position ? position : exports.EDGE_DEFAULTS.position;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* レコードに、d3.deployment が既定で描いている値を重ねて返す(入力は書き換えない)。
|
|
93
|
+
* インスペクタの初期値に使う。children は重ねない(子は右クリックしたときに自分で通す)。
|
|
94
|
+
*
|
|
95
|
+
* 重ねる項目: ノードは type・padding・position・label.text(無ければ '')・border・background、
|
|
96
|
+
* 線は stroke(矢印を含む)・from.position・to.position。size は view が子に合わせて埋めるので触らない。
|
|
97
|
+
*/
|
|
98
|
+
function withDefaults(record) {
|
|
99
|
+
if (isEdge(record)) {
|
|
100
|
+
return {
|
|
101
|
+
...record,
|
|
102
|
+
...edgeStyle(record),
|
|
103
|
+
from: { ...record.from, position: degreeOf(record.from.position) },
|
|
104
|
+
to: { ...record.to, position: degreeOf(record.to.position) },
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const node = record;
|
|
108
|
+
const style = nodeStyle(node);
|
|
109
|
+
const label = node.label && typeof node.label === 'object' ? node.label : {};
|
|
110
|
+
return {
|
|
111
|
+
...node,
|
|
112
|
+
type: node.type || exports.NODE_DEFAULTS.type,
|
|
113
|
+
label: { ...label, text: typeof label.text === 'string' ? label.text : '' },
|
|
114
|
+
padding: node.padding || node.padding === 0 ? node.padding : exports.NODE_DEFAULTS.padding,
|
|
115
|
+
position: { ...exports.NODE_DEFAULTS.position, ...(node.position || {}) },
|
|
116
|
+
border: style.border,
|
|
117
|
+
background: style.background,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// 編集できる項目の一覧。インスペクタ(colonoscope の ModelInspector)に渡す。
|
|
3
|
+
//
|
|
4
|
+
// path はレコードの実際の項目を指す。既定値は withDefaults() で重ねてから渡すので、
|
|
5
|
+
// 書いていない項目(枠線の色など)も、図に描かれている値が初期値として出る。
|
|
6
|
+
// type: 'readonly' は表示だけ。options は取りうる値の一覧(検証にも使う)。
|
|
7
|
+
//
|
|
8
|
+
// iHub の SystemConfigurationDiagram.js の fields(label.text / type / id)から始め、
|
|
9
|
+
// d3.deployment が描く説明(ツールチップ)・リンク・幅・高さ・背景色・枠線の色 / 太さ、線の矢印を足した。
|
|
10
|
+
// border.type は d3.deployment が描かないので出さない。
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.EDGE_FIELDS = exports.NODE_FIELDS = void 0;
|
|
13
|
+
exports.fieldsOf = fieldsOf;
|
|
14
|
+
const defaults_js_1 = require("./defaults.js");
|
|
15
|
+
const BOOLEAN_OPTIONS = ['true', 'false'];
|
|
16
|
+
/** ノードの項目 */
|
|
17
|
+
exports.NODE_FIELDS = [
|
|
18
|
+
{ path: 'label.text', label: '名称', type: 'text' },
|
|
19
|
+
{ path: 'type', label: '種類', type: 'text', options: defaults_js_1.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
|
+
/** 線の項目。接続位置はノードの中心から見た角度(0=真下・時計回り。90=左 / 180=真上 / 270=右) */
|
|
30
|
+
exports.EDGE_FIELDS = [
|
|
31
|
+
{ path: 'id', label: 'ID', type: 'readonly' },
|
|
32
|
+
{ path: 'from.id', label: '接続元のノード', type: 'readonly' },
|
|
33
|
+
{ path: 'from.position', label: '接続元の位置 (角度)', type: 'number' },
|
|
34
|
+
{ path: 'to.id', label: '接続先のノード', type: 'readonly' },
|
|
35
|
+
{ path: 'to.position', label: '接続先の位置 (角度)', type: 'number' },
|
|
36
|
+
{ path: 'stroke.color', label: '線の色', type: 'text' },
|
|
37
|
+
{ path: 'stroke.width', label: '線の太さ', type: 'number' },
|
|
38
|
+
// 始点の矢印(marker.start)は d3.deployment のペインタが描かないので項目にしない
|
|
39
|
+
{ path: 'stroke.marker.end', label: '終点の矢印', type: 'text', options: BOOLEAN_OPTIONS },
|
|
40
|
+
];
|
|
41
|
+
/** レコードに応じた項目の一覧(線 / ノード) */
|
|
42
|
+
function fieldsOf(record) {
|
|
43
|
+
return (0, defaults_js_1.isEdge)(record) ? [...exports.EDGE_FIELDS] : [...exports.NODE_FIELDS];
|
|
44
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// model.deployment — システム構成図(デプロイメント図。d3.deployment)の「図ごとの知識」。描画はしない(react / d3 に依存しない)。
|
|
3
|
+
//
|
|
4
|
+
// スキーマ: SCHEMAS / NODES_SCHEMA / EDGES_SCHEMA(JSON Schema 2020-12)
|
|
5
|
+
// 整合性ルール: check(data)(id の重複・参照先の存在)/ flatten / findById
|
|
6
|
+
// 既定値の補完: withDefaults(record) / nodeStyle / edgeStyle / degreeOf
|
|
7
|
+
// NODE_TYPES / NODE_STYLE_DEFAULTS / LABEL_DEFAULTS / NODE_DEFAULTS / EDGE_DEFAULTS(d3.deployment が読む)
|
|
8
|
+
// 編集できる項目: fieldsOf(record) / NODE_FIELDS / EDGE_FIELDS
|
|
9
|
+
// patch への変換: movePatch(node, move) / applyPatch(record, values) / mergePatch(record, patch)
|
|
10
|
+
//
|
|
11
|
+
// graph 系(unchi / sitemap / deployment)の 3 つ目。check / flatten / movePatch / mergePatch / getByPath / isColor は
|
|
12
|
+
// model.unchi / model.sitemap と同じ内容の複製で、共通化は別 issue で判断する(#146 の 7)。
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.isColor = exports.applyPatch = exports.movePatch = exports.mergePatch = exports.getByPath = exports.titleOf = exports.collectionOf = exports.ModelError = exports.fieldsOf = exports.EDGE_FIELDS = exports.NODE_FIELDS = exports.findById = exports.flatten = exports.check = exports.SCHEMAS = exports.EDGES_SCHEMA = exports.NODES_SCHEMA = exports.withDefaults = exports.degreeOf = exports.edgeStyle = exports.nodeStyle = exports.isEdge = exports.EDGE_DEFAULTS = exports.NODE_DEFAULTS = exports.LABEL_DEFAULTS = exports.NODE_STYLE_DEFAULTS = exports.NODE_TYPES = void 0;
|
|
15
|
+
var defaults_js_1 = require("./defaults.js");
|
|
16
|
+
Object.defineProperty(exports, "NODE_TYPES", { enumerable: true, get: function () { return defaults_js_1.NODE_TYPES; } });
|
|
17
|
+
Object.defineProperty(exports, "NODE_STYLE_DEFAULTS", { enumerable: true, get: function () { return defaults_js_1.NODE_STYLE_DEFAULTS; } });
|
|
18
|
+
Object.defineProperty(exports, "LABEL_DEFAULTS", { enumerable: true, get: function () { return defaults_js_1.LABEL_DEFAULTS; } });
|
|
19
|
+
Object.defineProperty(exports, "NODE_DEFAULTS", { enumerable: true, get: function () { return defaults_js_1.NODE_DEFAULTS; } });
|
|
20
|
+
Object.defineProperty(exports, "EDGE_DEFAULTS", { enumerable: true, get: function () { return defaults_js_1.EDGE_DEFAULTS; } });
|
|
21
|
+
Object.defineProperty(exports, "isEdge", { enumerable: true, get: function () { return defaults_js_1.isEdge; } });
|
|
22
|
+
Object.defineProperty(exports, "nodeStyle", { enumerable: true, get: function () { return defaults_js_1.nodeStyle; } });
|
|
23
|
+
Object.defineProperty(exports, "edgeStyle", { enumerable: true, get: function () { return defaults_js_1.edgeStyle; } });
|
|
24
|
+
Object.defineProperty(exports, "degreeOf", { enumerable: true, get: function () { return defaults_js_1.degreeOf; } });
|
|
25
|
+
Object.defineProperty(exports, "withDefaults", { enumerable: true, get: function () { return defaults_js_1.withDefaults; } });
|
|
26
|
+
var schema_js_1 = require("./schema.js");
|
|
27
|
+
Object.defineProperty(exports, "NODES_SCHEMA", { enumerable: true, get: function () { return schema_js_1.NODES_SCHEMA; } });
|
|
28
|
+
Object.defineProperty(exports, "EDGES_SCHEMA", { enumerable: true, get: function () { return schema_js_1.EDGES_SCHEMA; } });
|
|
29
|
+
Object.defineProperty(exports, "SCHEMAS", { enumerable: true, get: function () { return schema_js_1.SCHEMAS; } });
|
|
30
|
+
var check_js_1 = require("./check.js");
|
|
31
|
+
Object.defineProperty(exports, "check", { enumerable: true, get: function () { return check_js_1.check; } });
|
|
32
|
+
Object.defineProperty(exports, "flatten", { enumerable: true, get: function () { return check_js_1.flatten; } });
|
|
33
|
+
Object.defineProperty(exports, "findById", { enumerable: true, get: function () { return check_js_1.findById; } });
|
|
34
|
+
var fields_js_1 = require("./fields.js");
|
|
35
|
+
Object.defineProperty(exports, "NODE_FIELDS", { enumerable: true, get: function () { return fields_js_1.NODE_FIELDS; } });
|
|
36
|
+
Object.defineProperty(exports, "EDGE_FIELDS", { enumerable: true, get: function () { return fields_js_1.EDGE_FIELDS; } });
|
|
37
|
+
Object.defineProperty(exports, "fieldsOf", { enumerable: true, get: function () { return fields_js_1.fieldsOf; } });
|
|
38
|
+
var patch_js_1 = require("./patch.js");
|
|
39
|
+
Object.defineProperty(exports, "ModelError", { enumerable: true, get: function () { return patch_js_1.ModelError; } });
|
|
40
|
+
Object.defineProperty(exports, "collectionOf", { enumerable: true, get: function () { return patch_js_1.collectionOf; } });
|
|
41
|
+
Object.defineProperty(exports, "titleOf", { enumerable: true, get: function () { return patch_js_1.titleOf; } });
|
|
42
|
+
Object.defineProperty(exports, "getByPath", { enumerable: true, get: function () { return patch_js_1.getByPath; } });
|
|
43
|
+
Object.defineProperty(exports, "mergePatch", { enumerable: true, get: function () { return patch_js_1.mergePatch; } });
|
|
44
|
+
Object.defineProperty(exports, "movePatch", { enumerable: true, get: function () { return patch_js_1.movePatch; } });
|
|
45
|
+
Object.defineProperty(exports, "applyPatch", { enumerable: true, get: function () { return patch_js_1.applyPatch; } });
|
|
46
|
+
Object.defineProperty(exports, "isColor", { enumerable: true, get: function () { return patch_js_1.isColor; } });
|