@yanqirenshi/model.tm 0.1.0 → 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.
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +2 -11
- package/dist/cjs/check.js +7 -16
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/patch.js +27 -127
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/patch.d.ts +2 -12
- package/dist/patch.d.ts.map +1 -1
- package/dist/patch.js +11 -111
- package/dist/types.d.ts +7 -18
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/check.ts +4 -15
- package/src/index.ts +1 -0
- package/src/patch.ts +15 -147
- package/src/types.ts +8 -17
package/dist/check.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAkB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAO1E;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,SAAK,GAAG,MAAM,EAAE,CAuF1D;AAED,wCAAwC;AACxC,wBAAgB,QAAQ,CAAC,CAAC,SAAS,QAAQ,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,GAAG,SAAS,EAAE,EAAE,EAAE,IAAI,GAAG,CAAC,GAAG,IAAI,CAE/F"}
|
package/dist/check.js
CHANGED
|
@@ -6,17 +6,8 @@
|
|
|
6
6
|
// Entity.entityTypeContents)も検査する。描く前に分かるほうがよい知識で、d3.ter 側の例外は残る。
|
|
7
7
|
//
|
|
8
8
|
// メッセージの先頭はコレクション名で、prefix を渡すと 'tm/entities: ...' のように付けられる。
|
|
9
|
+
import { checkUnique, findById as findInList } from '@yanqirenshi/model.core';
|
|
9
10
|
import { MAPPING_TYPES, isEntityType } from './defaults.js';
|
|
10
|
-
// list の中で id が重複していればエラーにする。id の集合を返す。
|
|
11
|
-
function checkUnique(errors, label, list) {
|
|
12
|
-
const values = new Set();
|
|
13
|
-
for (const d of list) {
|
|
14
|
-
if (values.has(d.id))
|
|
15
|
-
errors.push(`${label}: id が重複しています。(id=${String(d.id)})`);
|
|
16
|
-
values.add(d.id);
|
|
17
|
-
}
|
|
18
|
-
return values;
|
|
19
|
-
}
|
|
20
11
|
// エンティティが持つ識別子のマスタの集合(CLASS の親判定に使う)
|
|
21
12
|
function identifierMasters(entity) {
|
|
22
13
|
return new Set((entity.identifiers || []).map(d => d.identifier));
|
|
@@ -103,5 +94,5 @@ export function check(data, prefix = '') {
|
|
|
103
94
|
}
|
|
104
95
|
/** コレクションの中からレコードを id で探す(無ければ null) */
|
|
105
96
|
export function findById(list, id) {
|
|
106
|
-
return (list
|
|
97
|
+
return findInList(list, id);
|
|
107
98
|
}
|
package/dist/cjs/check.js
CHANGED
|
@@ -10,17 +10,8 @@
|
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.check = check;
|
|
12
12
|
exports.findById = findById;
|
|
13
|
+
const model_core_1 = require("@yanqirenshi/model.core");
|
|
13
14
|
const defaults_js_1 = require("./defaults.js");
|
|
14
|
-
// list の中で id が重複していればエラーにする。id の集合を返す。
|
|
15
|
-
function checkUnique(errors, label, list) {
|
|
16
|
-
const values = new Set();
|
|
17
|
-
for (const d of list) {
|
|
18
|
-
if (values.has(d.id))
|
|
19
|
-
errors.push(`${label}: id が重複しています。(id=${String(d.id)})`);
|
|
20
|
-
values.add(d.id);
|
|
21
|
-
}
|
|
22
|
-
return values;
|
|
23
|
-
}
|
|
24
15
|
// エンティティが持つ識別子のマスタの集合(CLASS の親判定に使う)
|
|
25
16
|
function identifierMasters(entity) {
|
|
26
17
|
return new Set((entity.identifiers || []).map(d => d.identifier));
|
|
@@ -44,12 +35,12 @@ function check(data, prefix = '') {
|
|
|
44
35
|
const attributes = data.attributes || [];
|
|
45
36
|
const entities = data.entities || [];
|
|
46
37
|
const relationships = data.relationships || [];
|
|
47
|
-
const identifier_ids = checkUnique(errors, `${prefix}identifiers`, identifiers);
|
|
48
|
-
const attribute_ids = checkUnique(errors, `${prefix}attributes`, attributes);
|
|
49
|
-
checkUnique(errors, `${prefix}entities`, entities);
|
|
50
|
-
checkUnique(errors, `${prefix}relationships`, relationships);
|
|
38
|
+
const identifier_ids = (0, model_core_1.checkUnique)(errors, `${prefix}identifiers`, identifiers);
|
|
39
|
+
const attribute_ids = (0, model_core_1.checkUnique)(errors, `${prefix}attributes`, attributes);
|
|
40
|
+
(0, model_core_1.checkUnique)(errors, `${prefix}entities`, entities);
|
|
41
|
+
(0, model_core_1.checkUnique)(errors, `${prefix}relationships`, relationships);
|
|
51
42
|
// インスタンスの id は、エンティティをまたいで一意にする(d3.ter はインスタンスを id だけで探すため)
|
|
52
|
-
checkUnique(errors, `${prefix}entities の identifiers・attributes`, entities.flatMap(e => [...(e.identifiers || []), ...(e.attributes || [])]));
|
|
43
|
+
(0, model_core_1.checkUnique)(errors, `${prefix}entities の identifiers・attributes`, entities.flatMap(e => [...(e.identifiers || []), ...(e.attributes || [])]));
|
|
53
44
|
for (const e of entities) {
|
|
54
45
|
if (!(0, defaults_js_1.isEntityType)(e.type))
|
|
55
46
|
errors.push(`${prefix}entities: type が種別のどれでもありません。(id=${String(e.id)}, type=${String(e.type)})`);
|
|
@@ -107,5 +98,5 @@ function check(data, prefix = '') {
|
|
|
107
98
|
}
|
|
108
99
|
/** コレクションの中からレコードを id で探す(無ければ null) */
|
|
109
100
|
function findById(list, id) {
|
|
110
|
-
return (
|
|
101
|
+
return (0, model_core_1.findById)(list, id);
|
|
111
102
|
}
|
package/dist/cjs/index.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// patch への変換: movePatch(entity, move) / applyPatch(record, values, collection?) / mergePatch(record, patch)
|
|
10
10
|
//
|
|
11
11
|
// view(d3.ter)はこのパッケージの既定値(種別バッジ・配色・角度)を読む。インスペクタ(colonoscope)との間に横の依存は無い。
|
|
12
|
+
// ModelError / mergePatch / getByPath / movePatch と入力の読み取り部品は model.core(#151)のもの。
|
|
12
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
14
|
exports.applyPatch = exports.movePatch = exports.mergePatch = exports.getByPath = exports.titleOf = exports.collectionOf = exports.ModelError = exports.fieldsOf = exports.nameFields = exports.MASTER_FIELDS = exports.RELATIONSHIP_FIELDS = exports.ENTITY_FIELDS = exports.findById = exports.check = exports.RELATIONSHIPS_SCHEMA = exports.ENTITIES_SCHEMA = exports.ATTRIBUTES_SCHEMA = exports.IDENTIFIERS_SCHEMA = exports.SCHEMAS = exports.withDefaults = exports.relationshipWithDefaults = exports.entityWithDefaults = exports.endWithDefaults = exports.nameOf = exports.kindOf = exports.isEntity = exports.isRelationship = exports.isEntityType = exports.RELATIONSHIP_DEFAULTS = exports.ENTITY_DEFAULTS = exports.OPTIONALITIES = exports.CARDINALITIES = exports.SUBSET_KINDS = exports.MAPPING_TYPES = exports.TYPE_COLORS = exports.TYPE_BADGES = exports.ENTITY_TYPES = void 0;
|
|
14
15
|
var defaults_js_1 = require("./defaults.js");
|
package/dist/cjs/patch.js
CHANGED
|
@@ -4,24 +4,21 @@
|
|
|
4
4
|
// 戻り値は { collection, id, patch }。利用側(iHub など)は図の種類を知らずにこれを保存すればよい。
|
|
5
5
|
// 入力の検証に失敗したときは ModelError を投げる(メッセージは利用者にそのまま見せられる日本語)。
|
|
6
6
|
// 使い方の誤り(マスタなのに collection を渡していない)は ModelError ではなく Error。
|
|
7
|
+
// 読み取り部品(toLength など)と patch の道具は model.core にある(#151)。
|
|
7
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.ModelError = void 0;
|
|
9
|
+
exports.mergePatch = exports.getByPath = exports.ModelError = void 0;
|
|
9
10
|
exports.collectionOf = collectionOf;
|
|
10
11
|
exports.titleOf = titleOf;
|
|
11
|
-
exports.getByPath = getByPath;
|
|
12
|
-
exports.mergePatch = mergePatch;
|
|
13
12
|
exports.movePatch = movePatch;
|
|
14
13
|
exports.applyPatch = applyPatch;
|
|
14
|
+
const model_core_1 = require("@yanqirenshi/model.core");
|
|
15
15
|
const defaults_js_1 = require("./defaults.js");
|
|
16
16
|
const fields_js_1 = require("./fields.js");
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
exports.ModelError = ModelError;
|
|
17
|
+
// model.core のものをそのまま公開する(利用側が model.tm だけを読めばよいように)
|
|
18
|
+
var model_core_2 = require("@yanqirenshi/model.core");
|
|
19
|
+
Object.defineProperty(exports, "ModelError", { enumerable: true, get: function () { return model_core_2.ModelError; } });
|
|
20
|
+
Object.defineProperty(exports, "getByPath", { enumerable: true, get: function () { return model_core_2.getByPath; } });
|
|
21
|
+
Object.defineProperty(exports, "mergePatch", { enumerable: true, get: function () { return model_core_2.mergePatch; } });
|
|
25
22
|
/**
|
|
26
23
|
* レコードを置くコレクション。関係・エンティティは形から決まる。
|
|
27
24
|
* マスタは identifiers / attributes を形から判別できないので collection が要る。
|
|
@@ -45,122 +42,25 @@ function titleOf(record) {
|
|
|
45
42
|
const name = (0, defaults_js_1.nameOf)(record);
|
|
46
43
|
return name !== '' ? name : String(record.id);
|
|
47
44
|
}
|
|
48
|
-
/** ドット区切りの path で値を取り出す */
|
|
49
|
-
function getByPath(obj, path) {
|
|
50
|
-
return path.split('.').reduce((acc, key) => (acc == null ? acc : acc[key]), obj);
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* JSON Merge Patch をレコードに重ねたものを返す(入力は書き換えない)。
|
|
54
|
-
* null の項目は除く。保存したあと、表示を先に更新するときに使う。
|
|
55
|
-
*/
|
|
56
|
-
function mergePatch(record, patch) {
|
|
57
|
-
const out = { ...record };
|
|
58
|
-
for (const [key, value] of Object.entries(patch)) {
|
|
59
|
-
if (value === null) {
|
|
60
|
-
delete out[key];
|
|
61
|
-
}
|
|
62
|
-
else if (typeof value === 'object' && !Array.isArray(value)) {
|
|
63
|
-
const base = out[key];
|
|
64
|
-
out[key] = mergePatch(base && typeof base === 'object' && !Array.isArray(base) ? base : {}, value);
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
out[key] = value;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return out;
|
|
71
|
-
}
|
|
72
45
|
/**
|
|
73
46
|
* ドラッグで動かしたエンティティの位置を保存する patch。
|
|
74
47
|
* 保存してある position に移動量を足し、整数に丸める(position が無いときは 0, 0 から)。z は触らない。
|
|
75
48
|
*/
|
|
76
49
|
function movePatch(entity, move) {
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
collection: 'entities',
|
|
80
|
-
id: entity.id,
|
|
81
|
-
patch: {
|
|
82
|
-
position: {
|
|
83
|
-
x: Math.round(base.x + move.dx),
|
|
84
|
-
y: Math.round(base.y + move.dy),
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
// ---- 入力の読み取り ------------------------------------------------------------
|
|
90
|
-
// 入力した文字列を数にする。数でないときは null。
|
|
91
|
-
function toNumber(value) {
|
|
92
|
-
const n = Number(value);
|
|
93
|
-
return value !== '' && Number.isFinite(n) ? n : null;
|
|
94
|
-
}
|
|
95
|
-
// 0 以上の整数(幅・高さ。0 は「内容に合わせる」)
|
|
96
|
-
function toLength(value, label) {
|
|
97
|
-
const n = toNumber(value);
|
|
98
|
-
if (n === null || n < 0)
|
|
99
|
-
throw new ModelError(`${label}は 0 以上の数で入力してください。(0 で内容に合わせる)`);
|
|
100
|
-
return Math.round(n);
|
|
50
|
+
return (0, model_core_1.movePatch)(entity, move, 'entities');
|
|
101
51
|
}
|
|
102
|
-
//
|
|
103
|
-
function toDegree(value, label) {
|
|
104
|
-
const n = toNumber(value);
|
|
105
|
-
if (n === null)
|
|
106
|
-
throw new ModelError(`${label}は、角度 (数) で入力してください。`);
|
|
107
|
-
return ((Math.round(n) % 360) + 360) % 360;
|
|
108
|
-
}
|
|
109
|
-
// 候補の中の数、または空(null = 項目を除く)
|
|
110
|
-
function toOptionNumberOrNull(value, options, label) {
|
|
111
|
-
if (value === '')
|
|
112
|
-
return null;
|
|
113
|
-
const n = toNumber(value);
|
|
114
|
-
if (n === null || !options.includes(n))
|
|
115
|
-
throw new ModelError(`${label}は、${options.join(' / ')} のどれか (または空) で入力してください。`);
|
|
116
|
-
return n;
|
|
117
|
-
}
|
|
118
|
-
// 候補の中の文字列
|
|
119
|
-
function toOption(value, options, label) {
|
|
120
|
-
if (!options.includes(value))
|
|
121
|
-
throw new ModelError(`${label}は、${options.join(' / ')} のどれかで入力してください。`);
|
|
122
|
-
return value;
|
|
123
|
-
}
|
|
124
|
-
// 空にできる文字列。空なら null(JSON Merge Patch で項目を除く)
|
|
125
|
-
function toOptionalText(value) {
|
|
126
|
-
return value === '' ? null : value;
|
|
127
|
-
}
|
|
128
|
-
// patch の入れ子に値を置く(patch.from.position = v のように)
|
|
129
|
-
function put(patch, path, value) {
|
|
130
|
-
const keys = path.split('.');
|
|
131
|
-
let cur = patch;
|
|
132
|
-
for (const key of keys.slice(0, -1)) {
|
|
133
|
-
if (!cur[key] || typeof cur[key] !== 'object')
|
|
134
|
-
cur[key] = {};
|
|
135
|
-
cur = cur[key];
|
|
136
|
-
}
|
|
137
|
-
cur[keys[keys.length - 1]] = value;
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* インスペクタの入力のうち、図に描いている値(withDefaults で埋めた値)から変わった項目だけを返す。
|
|
141
|
-
* 前後の空白は除く。読み取り専用の項目と、values に無い項目は含めない。
|
|
142
|
-
*/
|
|
52
|
+
// インスペクタの入力のうち、図に描いている値(withDefaults)から変わった項目だけ
|
|
143
53
|
function changedValues(record, values, collection) {
|
|
144
|
-
|
|
145
|
-
const out = new Map();
|
|
146
|
-
for (const field of (0, fields_js_1.fieldsOf)(record, collection)) {
|
|
147
|
-
if (field.type === 'readonly')
|
|
148
|
-
continue;
|
|
149
|
-
const value = values[field.path];
|
|
150
|
-
if (value === undefined)
|
|
151
|
-
continue;
|
|
152
|
-
const current = getByPath(effective, field.path);
|
|
153
|
-
if (value.trim() !== String(current ?? ''))
|
|
154
|
-
out.set(field.path, value.trim());
|
|
155
|
-
}
|
|
156
|
-
return out;
|
|
54
|
+
return (0, model_core_1.changedValues)((0, defaults_js_1.withDefaults)(record), values, (0, fields_js_1.fieldsOf)(record, collection));
|
|
157
55
|
}
|
|
56
|
+
// 幅・高さは 0 以上(0 は「内容に合わせる」)
|
|
57
|
+
const LENGTH = { hint: '(0 で内容に合わせる)' };
|
|
158
58
|
// 名前(name / name.logical / name.physical)。required なら空にできない
|
|
159
59
|
function namePatch(patch, path, value, required) {
|
|
160
60
|
// name と name.logical は空にできない。name.physical は空にできる(論理名だけのモデル)
|
|
161
61
|
if (value === '' && required)
|
|
162
|
-
throw new ModelError('名称を入力してください。');
|
|
163
|
-
put(patch, path, value);
|
|
62
|
+
throw new model_core_1.ModelError('名称を入力してください。');
|
|
63
|
+
(0, model_core_1.put)(patch, path, value);
|
|
164
64
|
}
|
|
165
65
|
// エンティティの入力 → patch(name / type / description / size)
|
|
166
66
|
function entityPatch(entity, values) {
|
|
@@ -175,16 +75,16 @@ function entityPatch(entity, values) {
|
|
|
175
75
|
namePatch(patch, path, value, false);
|
|
176
76
|
break;
|
|
177
77
|
case 'type':
|
|
178
|
-
put(patch, path, toOption(value, defaults_js_1.ENTITY_TYPES, '種別'));
|
|
78
|
+
(0, model_core_1.put)(patch, path, (0, model_core_1.toOption)(value, defaults_js_1.ENTITY_TYPES, '種別'));
|
|
179
79
|
break;
|
|
180
80
|
case 'size.w':
|
|
181
|
-
put(patch, path, toLength(value, '幅'));
|
|
81
|
+
(0, model_core_1.put)(patch, path, (0, model_core_1.toLength)(value, '幅', LENGTH));
|
|
182
82
|
break;
|
|
183
83
|
case 'size.h':
|
|
184
|
-
put(patch, path, toLength(value, '高さ'));
|
|
84
|
+
(0, model_core_1.put)(patch, path, (0, model_core_1.toLength)(value, '高さ', LENGTH));
|
|
185
85
|
break;
|
|
186
86
|
case 'description':
|
|
187
|
-
put(patch, path, toOptionalText(value));
|
|
87
|
+
(0, model_core_1.put)(patch, path, (0, model_core_1.toOptionalText)(value));
|
|
188
88
|
break;
|
|
189
89
|
}
|
|
190
90
|
}
|
|
@@ -200,32 +100,32 @@ function relationshipPatch(relationship, values) {
|
|
|
200
100
|
const label = END_LABELS[head];
|
|
201
101
|
switch (key) {
|
|
202
102
|
case 'position':
|
|
203
|
-
put(patch, path, toDegree(value, `${label}の位置`));
|
|
103
|
+
(0, model_core_1.put)(patch, path, (0, model_core_1.toDegree)(value, `${label}の位置`));
|
|
204
104
|
break;
|
|
205
105
|
case 'cardinality':
|
|
206
|
-
put(patch, path, toOptionNumberOrNull(value, defaults_js_1.CARDINALITIES, `${label}のカーディナリティ`));
|
|
106
|
+
(0, model_core_1.put)(patch, path, (0, model_core_1.toOptionNumberOrNull)(value, defaults_js_1.CARDINALITIES, `${label}のカーディナリティ`));
|
|
207
107
|
break;
|
|
208
108
|
case 'optionality':
|
|
209
|
-
put(patch, path, toOptionNumberOrNull(value, defaults_js_1.OPTIONALITIES, `${label}のオプショナリティ`));
|
|
109
|
+
(0, model_core_1.put)(patch, path, (0, model_core_1.toOptionNumberOrNull)(value, defaults_js_1.OPTIONALITIES, `${label}のオプショナリティ`));
|
|
210
110
|
break;
|
|
211
111
|
}
|
|
212
112
|
continue;
|
|
213
113
|
}
|
|
214
114
|
switch (path) {
|
|
215
115
|
case 'subset.kind':
|
|
216
|
-
put(patch, path, toOption(value, defaults_js_1.SUBSET_KINDS, 'サブセットの区分'));
|
|
116
|
+
(0, model_core_1.put)(patch, path, (0, model_core_1.toOption)(value, defaults_js_1.SUBSET_KINDS, 'サブセットの区分'));
|
|
217
117
|
break;
|
|
218
118
|
case 'subset.code':
|
|
219
119
|
// 区分コードを空にするとサブセットの表記を消す(subset ごと除く)
|
|
220
120
|
if (value === '')
|
|
221
121
|
patch.subset = null;
|
|
222
122
|
else
|
|
223
|
-
put(patch, path, value);
|
|
123
|
+
(0, model_core_1.put)(patch, path, value);
|
|
224
124
|
break;
|
|
225
125
|
case 'type':
|
|
226
126
|
case 'label':
|
|
227
127
|
case 'description':
|
|
228
|
-
put(patch, path, toOptionalText(value));
|
|
128
|
+
(0, model_core_1.put)(patch, path, (0, model_core_1.toOptionalText)(value));
|
|
229
129
|
break;
|
|
230
130
|
}
|
|
231
131
|
}
|
|
@@ -235,7 +135,7 @@ function relationshipPatch(relationship, values) {
|
|
|
235
135
|
// kind だけを新しく書いて code が無い subset は作らない(d3.ter は code が無ければ描かない)
|
|
236
136
|
const subset = patch.subset;
|
|
237
137
|
if (subset && subset.kind !== undefined && subset.code === undefined && !relationship.subset?.code)
|
|
238
|
-
throw new ModelError('サブセットの区分コードを入力してください。');
|
|
138
|
+
throw new model_core_1.ModelError('サブセットの区分コードを入力してください。');
|
|
239
139
|
return patch;
|
|
240
140
|
}
|
|
241
141
|
// マスタの入力 → patch(name / description)
|
|
@@ -252,7 +152,7 @@ function masterPatch(master, values, collection) {
|
|
|
252
152
|
namePatch(patch, path, value, false);
|
|
253
153
|
break;
|
|
254
154
|
case 'description':
|
|
255
|
-
put(patch, path, toOptionalText(value));
|
|
155
|
+
(0, model_core_1.put)(patch, path, (0, model_core_1.toOptionalText)(value));
|
|
256
156
|
break;
|
|
257
157
|
}
|
|
258
158
|
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,YAAY,EACV,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAC3F,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,QAAQ,EAAE,oBAAoB,EACrG,QAAQ,EAAE,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,EAC3D,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,GAC7D,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,EACL,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EACjG,eAAe,EAAE,qBAAqB,EACtC,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,YAAY,GACpI,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACpH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACtG,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// patch への変換: movePatch(entity, move) / applyPatch(record, values, collection?) / mergePatch(record, patch)
|
|
9
9
|
//
|
|
10
10
|
// view(d3.ter)はこのパッケージの既定値(種別バッジ・配色・角度)を読む。インスペクタ(colonoscope)との間に横の依存は無い。
|
|
11
|
+
// ModelError / mergePatch / getByPath / movePatch と入力の読み取り部品は model.core(#151)のもの。
|
|
11
12
|
export { ENTITY_TYPES, TYPE_BADGES, TYPE_COLORS, MAPPING_TYPES, SUBSET_KINDS, CARDINALITIES, OPTIONALITIES, ENTITY_DEFAULTS, RELATIONSHIP_DEFAULTS, isEntityType, isRelationship, isEntity, kindOf, nameOf, endWithDefaults, entityWithDefaults, relationshipWithDefaults, withDefaults, } from './defaults.js';
|
|
12
13
|
export { SCHEMAS, IDENTIFIERS_SCHEMA, ATTRIBUTES_SCHEMA, ENTITIES_SCHEMA, RELATIONSHIPS_SCHEMA } from './schema.js';
|
|
13
14
|
export { check, findById } from './check.js';
|
package/dist/patch.d.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
export declare class ModelError extends Error {
|
|
4
|
-
constructor(message: string);
|
|
5
|
-
}
|
|
1
|
+
import type { TmChange, TmCollection, TmEntityRecord, TmMove, TmRecord, TmValues } from './types.js';
|
|
2
|
+
export { ModelError, getByPath, mergePatch } from '@yanqirenshi/model.core';
|
|
6
3
|
/**
|
|
7
4
|
* レコードを置くコレクション。関係・エンティティは形から決まる。
|
|
8
5
|
* マスタは identifiers / attributes を形から判別できないので collection が要る。
|
|
@@ -10,13 +7,6 @@ export declare class ModelError extends Error {
|
|
|
10
7
|
export declare function collectionOf(record: TmRecord, collection?: TmCollection): TmCollection;
|
|
11
8
|
/** インスペクタの見出し。名前、無ければ id(関係はラベル、無ければ「種類 id」) */
|
|
12
9
|
export declare function titleOf(record: TmRecord): string;
|
|
13
|
-
/** ドット区切りの path で値を取り出す */
|
|
14
|
-
export declare function getByPath(obj: unknown, path: string): unknown;
|
|
15
|
-
/**
|
|
16
|
-
* JSON Merge Patch をレコードに重ねたものを返す(入力は書き換えない)。
|
|
17
|
-
* null の項目は除く。保存したあと、表示を先に更新するときに使う。
|
|
18
|
-
*/
|
|
19
|
-
export declare function mergePatch<T extends object>(record: T, patch: MergePatch): T;
|
|
20
10
|
/**
|
|
21
11
|
* ドラッグで動かしたエンティティの位置を保存する patch。
|
|
22
12
|
* 保存してある position に移動量を足し、整数に丸める(position が無いときは 0, 0 から)。z は触らない。
|
package/dist/patch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAkB,MAAM,EAAE,QAAQ,EAAwB,QAAQ,EACrH,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE5E;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,YAAY,GAAG,YAAY,CAWtF;AAED,gDAAgD;AAChD,wBAAgB,OAAO,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAWhD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,CAExE;AAmID;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,YAAY,GAAG,QAAQ,CAUlG"}
|
package/dist/patch.js
CHANGED
|
@@ -3,15 +3,12 @@
|
|
|
3
3
|
// 戻り値は { collection, id, patch }。利用側(iHub など)は図の種類を知らずにこれを保存すればよい。
|
|
4
4
|
// 入力の検証に失敗したときは ModelError を投げる(メッセージは利用者にそのまま見せられる日本語)。
|
|
5
5
|
// 使い方の誤り(マスタなのに collection を渡していない)は ModelError ではなく Error。
|
|
6
|
+
// 読み取り部品(toLength など)と patch の道具は model.core にある(#151)。
|
|
7
|
+
import { ModelError, changedValues as changedValuesOf, movePatch as movePatchTo, put, toDegree, toLength, toOption, toOptionNumberOrNull, toOptionalText, } from '@yanqirenshi/model.core';
|
|
6
8
|
import { CARDINALITIES, ENTITY_TYPES, OPTIONALITIES, SUBSET_KINDS, isEntity, isRelationship, kindOf, nameOf, withDefaults, } from './defaults.js';
|
|
7
9
|
import { fieldsOf } from './fields.js';
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
constructor(message) {
|
|
11
|
-
super(message);
|
|
12
|
-
this.name = 'ModelError';
|
|
13
|
-
}
|
|
14
|
-
}
|
|
10
|
+
// model.core のものをそのまま公開する(利用側が model.tm だけを読めばよいように)
|
|
11
|
+
export { ModelError, getByPath, mergePatch } from '@yanqirenshi/model.core';
|
|
15
12
|
/**
|
|
16
13
|
* レコードを置くコレクション。関係・エンティティは形から決まる。
|
|
17
14
|
* マスタは identifiers / attributes を形から判別できないので collection が要る。
|
|
@@ -35,116 +32,19 @@ export function titleOf(record) {
|
|
|
35
32
|
const name = nameOf(record);
|
|
36
33
|
return name !== '' ? name : String(record.id);
|
|
37
34
|
}
|
|
38
|
-
/** ドット区切りの path で値を取り出す */
|
|
39
|
-
export function getByPath(obj, path) {
|
|
40
|
-
return path.split('.').reduce((acc, key) => (acc == null ? acc : acc[key]), obj);
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* JSON Merge Patch をレコードに重ねたものを返す(入力は書き換えない)。
|
|
44
|
-
* null の項目は除く。保存したあと、表示を先に更新するときに使う。
|
|
45
|
-
*/
|
|
46
|
-
export function mergePatch(record, patch) {
|
|
47
|
-
const out = { ...record };
|
|
48
|
-
for (const [key, value] of Object.entries(patch)) {
|
|
49
|
-
if (value === null) {
|
|
50
|
-
delete out[key];
|
|
51
|
-
}
|
|
52
|
-
else if (typeof value === 'object' && !Array.isArray(value)) {
|
|
53
|
-
const base = out[key];
|
|
54
|
-
out[key] = mergePatch(base && typeof base === 'object' && !Array.isArray(base) ? base : {}, value);
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
out[key] = value;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return out;
|
|
61
|
-
}
|
|
62
35
|
/**
|
|
63
36
|
* ドラッグで動かしたエンティティの位置を保存する patch。
|
|
64
37
|
* 保存してある position に移動量を足し、整数に丸める(position が無いときは 0, 0 から)。z は触らない。
|
|
65
38
|
*/
|
|
66
39
|
export function movePatch(entity, move) {
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
collection: 'entities',
|
|
70
|
-
id: entity.id,
|
|
71
|
-
patch: {
|
|
72
|
-
position: {
|
|
73
|
-
x: Math.round(base.x + move.dx),
|
|
74
|
-
y: Math.round(base.y + move.dy),
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
// ---- 入力の読み取り ------------------------------------------------------------
|
|
80
|
-
// 入力した文字列を数にする。数でないときは null。
|
|
81
|
-
function toNumber(value) {
|
|
82
|
-
const n = Number(value);
|
|
83
|
-
return value !== '' && Number.isFinite(n) ? n : null;
|
|
84
|
-
}
|
|
85
|
-
// 0 以上の整数(幅・高さ。0 は「内容に合わせる」)
|
|
86
|
-
function toLength(value, label) {
|
|
87
|
-
const n = toNumber(value);
|
|
88
|
-
if (n === null || n < 0)
|
|
89
|
-
throw new ModelError(`${label}は 0 以上の数で入力してください。(0 で内容に合わせる)`);
|
|
90
|
-
return Math.round(n);
|
|
40
|
+
return movePatchTo(entity, move, 'entities');
|
|
91
41
|
}
|
|
92
|
-
//
|
|
93
|
-
function toDegree(value, label) {
|
|
94
|
-
const n = toNumber(value);
|
|
95
|
-
if (n === null)
|
|
96
|
-
throw new ModelError(`${label}は、角度 (数) で入力してください。`);
|
|
97
|
-
return ((Math.round(n) % 360) + 360) % 360;
|
|
98
|
-
}
|
|
99
|
-
// 候補の中の数、または空(null = 項目を除く)
|
|
100
|
-
function toOptionNumberOrNull(value, options, label) {
|
|
101
|
-
if (value === '')
|
|
102
|
-
return null;
|
|
103
|
-
const n = toNumber(value);
|
|
104
|
-
if (n === null || !options.includes(n))
|
|
105
|
-
throw new ModelError(`${label}は、${options.join(' / ')} のどれか (または空) で入力してください。`);
|
|
106
|
-
return n;
|
|
107
|
-
}
|
|
108
|
-
// 候補の中の文字列
|
|
109
|
-
function toOption(value, options, label) {
|
|
110
|
-
if (!options.includes(value))
|
|
111
|
-
throw new ModelError(`${label}は、${options.join(' / ')} のどれかで入力してください。`);
|
|
112
|
-
return value;
|
|
113
|
-
}
|
|
114
|
-
// 空にできる文字列。空なら null(JSON Merge Patch で項目を除く)
|
|
115
|
-
function toOptionalText(value) {
|
|
116
|
-
return value === '' ? null : value;
|
|
117
|
-
}
|
|
118
|
-
// patch の入れ子に値を置く(patch.from.position = v のように)
|
|
119
|
-
function put(patch, path, value) {
|
|
120
|
-
const keys = path.split('.');
|
|
121
|
-
let cur = patch;
|
|
122
|
-
for (const key of keys.slice(0, -1)) {
|
|
123
|
-
if (!cur[key] || typeof cur[key] !== 'object')
|
|
124
|
-
cur[key] = {};
|
|
125
|
-
cur = cur[key];
|
|
126
|
-
}
|
|
127
|
-
cur[keys[keys.length - 1]] = value;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* インスペクタの入力のうち、図に描いている値(withDefaults で埋めた値)から変わった項目だけを返す。
|
|
131
|
-
* 前後の空白は除く。読み取り専用の項目と、values に無い項目は含めない。
|
|
132
|
-
*/
|
|
42
|
+
// インスペクタの入力のうち、図に描いている値(withDefaults)から変わった項目だけ
|
|
133
43
|
function changedValues(record, values, collection) {
|
|
134
|
-
|
|
135
|
-
const out = new Map();
|
|
136
|
-
for (const field of fieldsOf(record, collection)) {
|
|
137
|
-
if (field.type === 'readonly')
|
|
138
|
-
continue;
|
|
139
|
-
const value = values[field.path];
|
|
140
|
-
if (value === undefined)
|
|
141
|
-
continue;
|
|
142
|
-
const current = getByPath(effective, field.path);
|
|
143
|
-
if (value.trim() !== String(current ?? ''))
|
|
144
|
-
out.set(field.path, value.trim());
|
|
145
|
-
}
|
|
146
|
-
return out;
|
|
44
|
+
return changedValuesOf(withDefaults(record), values, fieldsOf(record, collection));
|
|
147
45
|
}
|
|
46
|
+
// 幅・高さは 0 以上(0 は「内容に合わせる」)
|
|
47
|
+
const LENGTH = { hint: '(0 で内容に合わせる)' };
|
|
148
48
|
// 名前(name / name.logical / name.physical)。required なら空にできない
|
|
149
49
|
function namePatch(patch, path, value, required) {
|
|
150
50
|
// name と name.logical は空にできない。name.physical は空にできる(論理名だけのモデル)
|
|
@@ -168,10 +68,10 @@ function entityPatch(entity, values) {
|
|
|
168
68
|
put(patch, path, toOption(value, ENTITY_TYPES, '種別'));
|
|
169
69
|
break;
|
|
170
70
|
case 'size.w':
|
|
171
|
-
put(patch, path, toLength(value, '幅'));
|
|
71
|
+
put(patch, path, toLength(value, '幅', LENGTH));
|
|
172
72
|
break;
|
|
173
73
|
case 'size.h':
|
|
174
|
-
put(patch, path, toLength(value, '高さ'));
|
|
74
|
+
put(patch, path, toLength(value, '高さ', LENGTH));
|
|
175
75
|
break;
|
|
176
76
|
case 'description':
|
|
177
77
|
put(patch, path, toOptionalText(value));
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { MergePatch, ModelField, ModelFieldType, ModelMove, ModelValues } from '@yanqirenshi/model.core';
|
|
1
2
|
import type { Name, Position, Size } from '@yanqirenshi/types';
|
|
2
3
|
/** レコードの id(コレクションの中で一意。インスタンス(identifiers / attributes の行)の id は全エンティティを通して一意) */
|
|
3
4
|
export type TmId = number | string;
|
|
@@ -107,10 +108,8 @@ export type TmMasterCollection = 'identifiers' | 'attributes';
|
|
|
107
108
|
export type TmCollection = TmMasterCollection | 'entities' | 'relationships';
|
|
108
109
|
/** レコードの種類(どの形か) */
|
|
109
110
|
export type TmKind = 'master' | 'entity' | 'relationship';
|
|
110
|
-
/** JSON Merge Patch(RFC 7386)。null
|
|
111
|
-
export type MergePatch
|
|
112
|
-
[key: string]: unknown;
|
|
113
|
-
};
|
|
111
|
+
/** JSON Merge Patch(RFC 7386)。null は「その項目を除く」。model.core の MergePatch と同じ(iHub の PATCH /{collection}/{id} が受け取る形) */
|
|
112
|
+
export type { MergePatch };
|
|
114
113
|
/** 保存する変更。利用側は図の種類を知らずにこれを保存すればよい */
|
|
115
114
|
export interface TmChange {
|
|
116
115
|
collection: TmCollection;
|
|
@@ -118,23 +117,13 @@ export interface TmChange {
|
|
|
118
117
|
patch: MergePatch;
|
|
119
118
|
}
|
|
120
119
|
/** ドラッグの移動量(世界座標) */
|
|
121
|
-
export
|
|
122
|
-
dx: number;
|
|
123
|
-
dy: number;
|
|
124
|
-
}
|
|
120
|
+
export type TmMove = ModelMove;
|
|
125
121
|
/**
|
|
126
122
|
* 編集できる項目。colonoscope の ColonoscopeField(path / label / type)と同じ形で、
|
|
127
123
|
* 取りうる値の一覧 options だけ足している(型は構造的に互換。colonoscope には依存しない)。
|
|
128
124
|
*/
|
|
129
|
-
export type TmFieldType =
|
|
130
|
-
export
|
|
131
|
-
/** レコードの項目のパス(ドット区切り。例: 'from.position')。写し先ではなく実際の項目を指す */
|
|
132
|
-
path: string;
|
|
133
|
-
label: string;
|
|
134
|
-
type: TmFieldType;
|
|
135
|
-
/** 取りうる値の一覧(種別・カーディナリティなど。文字列で持つ) */
|
|
136
|
-
options?: readonly string[];
|
|
137
|
-
}
|
|
125
|
+
export type TmFieldType = ModelFieldType;
|
|
126
|
+
export type TmField = ModelField;
|
|
138
127
|
/** インスペクタの「適用」が渡す値(path → 入力した文字列) */
|
|
139
|
-
export type TmValues =
|
|
128
|
+
export type TmValues = ModelValues;
|
|
140
129
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yanqirenshi/model.tm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Diagram model for TM (T-shaped ER method) diagrams (d3.ter) — JSON Schema, consistency rules, drawing defaults, editable fields and patch conversion. Pure data and functions: no React / d3 dependency",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
+
"@yanqirenshi/model.core": "^0.1.0",
|
|
40
41
|
"@yanqirenshi/types": "^0.1.2"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
package/src/check.ts
CHANGED
|
@@ -7,22 +7,11 @@
|
|
|
7
7
|
//
|
|
8
8
|
// メッセージの先頭はコレクション名で、prefix を渡すと 'tm/entities: ...' のように付けられる。
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import type { TmEntityRecord, TmGraph, TmId, TmRecord } from './types.js';
|
|
12
|
-
|
|
13
|
-
// list の中で id が重複していればエラーにする。id の集合を返す。
|
|
14
|
-
function checkUnique(errors: string[], label: string, list: readonly { id: TmId }[]): Set<TmId> {
|
|
15
|
-
const values = new Set<TmId>();
|
|
16
|
-
|
|
17
|
-
for (const d of list) {
|
|
18
|
-
if (values.has(d.id))
|
|
19
|
-
errors.push(`${label}: id が重複しています。(id=${String(d.id)})`);
|
|
10
|
+
import { checkUnique, findById as findInList } from '@yanqirenshi/model.core';
|
|
20
11
|
|
|
21
|
-
|
|
22
|
-
}
|
|
12
|
+
import { MAPPING_TYPES, isEntityType } from './defaults.js';
|
|
23
13
|
|
|
24
|
-
|
|
25
|
-
}
|
|
14
|
+
import type { TmEntityRecord, TmGraph, TmId, TmRecord } from './types.js';
|
|
26
15
|
|
|
27
16
|
// エンティティが持つ識別子のマスタの集合(CLASS の親判定に使う)
|
|
28
17
|
function identifierMasters(entity: TmEntityRecord): Set<TmId> {
|
|
@@ -133,5 +122,5 @@ export function check(data: TmGraph, prefix = ''): string[] {
|
|
|
133
122
|
|
|
134
123
|
/** コレクションの中からレコードを id で探す(無ければ null) */
|
|
135
124
|
export function findById<T extends TmRecord>(list: readonly T[] | undefined, id: TmId): T | null {
|
|
136
|
-
return (list
|
|
125
|
+
return findInList(list, id);
|
|
137
126
|
}
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// patch への変換: movePatch(entity, move) / applyPatch(record, values, collection?) / mergePatch(record, patch)
|
|
9
9
|
//
|
|
10
10
|
// view(d3.ter)はこのパッケージの既定値(種別バッジ・配色・角度)を読む。インスペクタ(colonoscope)との間に横の依存は無い。
|
|
11
|
+
// ModelError / mergePatch / getByPath / movePatch と入力の読み取り部品は model.core(#151)のもの。
|
|
11
12
|
|
|
12
13
|
export type {
|
|
13
14
|
TmId, TmName, TmEntityType, TmMasterRecord, TmIdentifierRef, TmAttributeRef, TmEntityRecord,
|
package/src/patch.ts
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
// 戻り値は { collection, id, patch }。利用側(iHub など)は図の種類を知らずにこれを保存すればよい。
|
|
4
4
|
// 入力の検証に失敗したときは ModelError を投げる(メッセージは利用者にそのまま見せられる日本語)。
|
|
5
5
|
// 使い方の誤り(マスタなのに collection を渡していない)は ModelError ではなく Error。
|
|
6
|
+
// 読み取り部品(toLength など)と patch の道具は model.core にある(#151)。
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
ModelError, changedValues as changedValuesOf, movePatch as movePatchTo, put, toDegree, toLength, toOption, toOptionNumberOrNull, toOptionalText,
|
|
10
|
+
} from '@yanqirenshi/model.core';
|
|
6
11
|
|
|
7
12
|
import {
|
|
8
13
|
CARDINALITIES, ENTITY_TYPES, OPTIONALITIES, SUBSET_KINDS, isEntity, isRelationship, kindOf, nameOf, withDefaults,
|
|
@@ -12,13 +17,8 @@ import type {
|
|
|
12
17
|
MergePatch, TmChange, TmCollection, TmEntityRecord, TmMasterRecord, TmMove, TmRecord, TmRelationshipRecord, TmValues,
|
|
13
18
|
} from './types.js';
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
export
|
|
17
|
-
constructor(message: string) {
|
|
18
|
-
super(message);
|
|
19
|
-
this.name = 'ModelError';
|
|
20
|
-
}
|
|
21
|
-
}
|
|
20
|
+
// model.core のものをそのまま公開する(利用側が model.tm だけを読めばよいように)
|
|
21
|
+
export { ModelError, getByPath, mergePatch } from '@yanqirenshi/model.core';
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* レコードを置くコレクション。関係・エンティティは形から決まる。
|
|
@@ -51,154 +51,22 @@ export function titleOf(record: TmRecord): string {
|
|
|
51
51
|
return name !== '' ? name : String(record.id);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
/** ドット区切りの path で値を取り出す */
|
|
55
|
-
export function getByPath(obj: unknown, path: string): unknown {
|
|
56
|
-
return path.split('.').reduce<unknown>(
|
|
57
|
-
(acc, key) => (acc == null ? acc : (acc as Record<string, unknown>)[key]),
|
|
58
|
-
obj,
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* JSON Merge Patch をレコードに重ねたものを返す(入力は書き換えない)。
|
|
64
|
-
* null の項目は除く。保存したあと、表示を先に更新するときに使う。
|
|
65
|
-
*/
|
|
66
|
-
export function mergePatch<T extends object>(record: T, patch: MergePatch): T {
|
|
67
|
-
const out: Record<string, unknown> = { ...(record as Record<string, unknown>) };
|
|
68
|
-
|
|
69
|
-
for (const [key, value] of Object.entries(patch)) {
|
|
70
|
-
if (value === null) {
|
|
71
|
-
delete out[key];
|
|
72
|
-
} else if (typeof value === 'object' && !Array.isArray(value)) {
|
|
73
|
-
const base = out[key];
|
|
74
|
-
|
|
75
|
-
out[key] = mergePatch(
|
|
76
|
-
base && typeof base === 'object' && !Array.isArray(base) ? (base as object) : {},
|
|
77
|
-
value as MergePatch,
|
|
78
|
-
);
|
|
79
|
-
} else {
|
|
80
|
-
out[key] = value;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return out as T;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
54
|
/**
|
|
88
55
|
* ドラッグで動かしたエンティティの位置を保存する patch。
|
|
89
56
|
* 保存してある position に移動量を足し、整数に丸める(position が無いときは 0, 0 から)。z は触らない。
|
|
90
57
|
*/
|
|
91
58
|
export function movePatch(entity: TmEntityRecord, move: TmMove): TmChange {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
collection: 'entities',
|
|
96
|
-
id: entity.id,
|
|
97
|
-
patch: {
|
|
98
|
-
position: {
|
|
99
|
-
x: Math.round(base.x + move.dx),
|
|
100
|
-
y: Math.round(base.y + move.dy),
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
-
};
|
|
59
|
+
return movePatchTo(entity, move, 'entities');
|
|
104
60
|
}
|
|
105
61
|
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
// 入力した文字列を数にする。数でないときは null。
|
|
109
|
-
function toNumber(value: string): number | null {
|
|
110
|
-
const n = Number(value);
|
|
111
|
-
|
|
112
|
-
return value !== '' && Number.isFinite(n) ? n : null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// 0 以上の整数(幅・高さ。0 は「内容に合わせる」)
|
|
116
|
-
function toLength(value: string, label: string): number {
|
|
117
|
-
const n = toNumber(value);
|
|
118
|
-
|
|
119
|
-
if (n === null || n < 0)
|
|
120
|
-
throw new ModelError(`${label}は 0 以上の数で入力してください。(0 で内容に合わせる)`);
|
|
121
|
-
|
|
122
|
-
return Math.round(n);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// 角度(0 以上 360 未満の整数に正規化)
|
|
126
|
-
function toDegree(value: string, label: string): number {
|
|
127
|
-
const n = toNumber(value);
|
|
128
|
-
|
|
129
|
-
if (n === null)
|
|
130
|
-
throw new ModelError(`${label}は、角度 (数) で入力してください。`);
|
|
131
|
-
|
|
132
|
-
return ((Math.round(n) % 360) + 360) % 360;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// 候補の中の数、または空(null = 項目を除く)
|
|
136
|
-
function toOptionNumberOrNull(value: string, options: readonly number[], label: string): number | null {
|
|
137
|
-
if (value === '')
|
|
138
|
-
return null;
|
|
139
|
-
|
|
140
|
-
const n = toNumber(value);
|
|
141
|
-
|
|
142
|
-
if (n === null || !options.includes(n))
|
|
143
|
-
throw new ModelError(`${label}は、${options.join(' / ')} のどれか (または空) で入力してください。`);
|
|
144
|
-
|
|
145
|
-
return n;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// 候補の中の文字列
|
|
149
|
-
function toOption(value: string, options: readonly string[], label: string): string {
|
|
150
|
-
if (!options.includes(value))
|
|
151
|
-
throw new ModelError(`${label}は、${options.join(' / ')} のどれかで入力してください。`);
|
|
152
|
-
|
|
153
|
-
return value;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// 空にできる文字列。空なら null(JSON Merge Patch で項目を除く)
|
|
157
|
-
function toOptionalText(value: string): string | null {
|
|
158
|
-
return value === '' ? null : value;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// patch の入れ子に値を置く(patch.from.position = v のように)
|
|
162
|
-
function put(patch: MergePatch, path: string, value: unknown): void {
|
|
163
|
-
const keys = path.split('.');
|
|
164
|
-
let cur = patch;
|
|
165
|
-
|
|
166
|
-
for (const key of keys.slice(0, -1)) {
|
|
167
|
-
if (!cur[key] || typeof cur[key] !== 'object')
|
|
168
|
-
cur[key] = {};
|
|
169
|
-
|
|
170
|
-
cur = cur[key] as MergePatch;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
cur[keys[keys.length - 1]] = value;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* インスペクタの入力のうち、図に描いている値(withDefaults で埋めた値)から変わった項目だけを返す。
|
|
178
|
-
* 前後の空白は除く。読み取り専用の項目と、values に無い項目は含めない。
|
|
179
|
-
*/
|
|
62
|
+
// インスペクタの入力のうち、図に描いている値(withDefaults)から変わった項目だけ
|
|
180
63
|
function changedValues(record: TmRecord, values: TmValues, collection?: TmCollection): Map<string, string> {
|
|
181
|
-
|
|
182
|
-
const out = new Map<string, string>();
|
|
183
|
-
|
|
184
|
-
for (const field of fieldsOf(record, collection)) {
|
|
185
|
-
if (field.type === 'readonly')
|
|
186
|
-
continue;
|
|
187
|
-
|
|
188
|
-
const value = values[field.path];
|
|
189
|
-
|
|
190
|
-
if (value === undefined)
|
|
191
|
-
continue;
|
|
192
|
-
|
|
193
|
-
const current = getByPath(effective, field.path);
|
|
194
|
-
|
|
195
|
-
if (value.trim() !== String(current ?? ''))
|
|
196
|
-
out.set(field.path, value.trim());
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return out;
|
|
64
|
+
return changedValuesOf(withDefaults(record), values, fieldsOf(record, collection));
|
|
200
65
|
}
|
|
201
66
|
|
|
67
|
+
// 幅・高さは 0 以上(0 は「内容に合わせる」)
|
|
68
|
+
const LENGTH = { hint: '(0 で内容に合わせる)' };
|
|
69
|
+
|
|
202
70
|
// 名前(name / name.logical / name.physical)。required なら空にできない
|
|
203
71
|
function namePatch(patch: MergePatch, path: string, value: string, required: boolean): void {
|
|
204
72
|
// name と name.logical は空にできない。name.physical は空にできる(論理名だけのモデル)
|
|
@@ -225,10 +93,10 @@ function entityPatch(entity: TmEntityRecord, values: TmValues): MergePatch {
|
|
|
225
93
|
put(patch, path, toOption(value, ENTITY_TYPES, '種別'));
|
|
226
94
|
break;
|
|
227
95
|
case 'size.w':
|
|
228
|
-
put(patch, path, toLength(value, '幅'));
|
|
96
|
+
put(patch, path, toLength(value, '幅', LENGTH));
|
|
229
97
|
break;
|
|
230
98
|
case 'size.h':
|
|
231
|
-
put(patch, path, toLength(value, '高さ'));
|
|
99
|
+
put(patch, path, toLength(value, '高さ', LENGTH));
|
|
232
100
|
break;
|
|
233
101
|
case 'description':
|
|
234
102
|
put(patch, path, toOptionalText(value));
|
package/src/types.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// 形は model.er(ER 図)と同じ階層形式で、TM 固有の項目(エンティティの type、関係の subset / mapping / label)が足されている。
|
|
6
6
|
// d3.ter が正規化したあとの datum(_id / _core を持つ Atman)ではない。
|
|
7
7
|
|
|
8
|
+
import type { MergePatch, ModelField, ModelFieldType, ModelMove, ModelValues } from '@yanqirenshi/model.core';
|
|
8
9
|
import type { Name, Position, Size } from '@yanqirenshi/types';
|
|
9
10
|
|
|
10
11
|
/** レコードの id(コレクションの中で一意。インスタンス(identifiers / attributes の行)の id は全エンティティを通して一意) */
|
|
@@ -130,8 +131,8 @@ export type TmCollection = TmMasterCollection | 'entities' | 'relationships';
|
|
|
130
131
|
/** レコードの種類(どの形か) */
|
|
131
132
|
export type TmKind = 'master' | 'entity' | 'relationship';
|
|
132
133
|
|
|
133
|
-
/** JSON Merge Patch(RFC 7386)。null
|
|
134
|
-
export type
|
|
134
|
+
/** JSON Merge Patch(RFC 7386)。null は「その項目を除く」。model.core の MergePatch と同じ(iHub の PATCH /{collection}/{id} が受け取る形) */
|
|
135
|
+
export type { MergePatch };
|
|
135
136
|
|
|
136
137
|
/** 保存する変更。利用側は図の種類を知らずにこれを保存すればよい */
|
|
137
138
|
export interface TmChange {
|
|
@@ -141,25 +142,15 @@ export interface TmChange {
|
|
|
141
142
|
}
|
|
142
143
|
|
|
143
144
|
/** ドラッグの移動量(世界座標) */
|
|
144
|
-
export
|
|
145
|
-
dx: number;
|
|
146
|
-
dy: number;
|
|
147
|
-
}
|
|
145
|
+
export type TmMove = ModelMove;
|
|
148
146
|
|
|
149
147
|
/**
|
|
150
148
|
* 編集できる項目。colonoscope の ColonoscopeField(path / label / type)と同じ形で、
|
|
151
149
|
* 取りうる値の一覧 options だけ足している(型は構造的に互換。colonoscope には依存しない)。
|
|
152
150
|
*/
|
|
153
|
-
export type TmFieldType =
|
|
154
|
-
|
|
155
|
-
export
|
|
156
|
-
/** レコードの項目のパス(ドット区切り。例: 'from.position')。写し先ではなく実際の項目を指す */
|
|
157
|
-
path: string;
|
|
158
|
-
label: string;
|
|
159
|
-
type: TmFieldType;
|
|
160
|
-
/** 取りうる値の一覧(種別・カーディナリティなど。文字列で持つ) */
|
|
161
|
-
options?: readonly string[];
|
|
162
|
-
}
|
|
151
|
+
export type TmFieldType = ModelFieldType;
|
|
152
|
+
|
|
153
|
+
export type TmField = ModelField;
|
|
163
154
|
|
|
164
155
|
/** インスペクタの「適用」が渡す値(path → 入力した文字列) */
|
|
165
|
-
export type TmValues =
|
|
156
|
+
export type TmValues = ModelValues;
|