@operato/slot-layout-editor 10.29.52 → 10.29.57
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/CHANGELOG.md +24 -0
- package/dist/src/draft.d.ts +53 -6
- package/dist/src/draft.js +132 -23
- package/dist/src/draft.js.map +1 -1
- package/dist/src/issue-text.d.ts +13 -0
- package/dist/src/issue-text.js +78 -7
- package/dist/src/issue-text.js.map +1 -1
- package/dist/src/ox-property-editor-slot-layout.d.ts +1 -0
- package/dist/src/ox-property-editor-slot-layout.js +2 -0
- package/dist/src/ox-property-editor-slot-layout.js.map +1 -1
- package/dist/src/ox-slot-layout-editor.d.ts +73 -15
- package/dist/src/ox-slot-layout-editor.js +784 -239
- package/dist/src/ox-slot-layout-editor.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/translations/en.json +77 -35
- package/translations/es.json +85 -43
- package/translations/ja.json +91 -49
- package/translations/ko.json +90 -48
- package/translations/ms.json +91 -49
- package/translations/zh.json +91 -49
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,30 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
### [10.29.57](https://github.com/hatiolab/operato/compare/v10.29.56...v10.29.57) (2026-10-27)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### :rocket: New Features
|
|
10
|
+
|
|
11
|
+
* **slot-layout-editor:** start with no numbers to type ([fe55219](https://github.com/hatiolab/operato/commit/fe552194b9a45ea7a3fe2b16427b8a3e9805eb62))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### :bug: Bug Fix
|
|
15
|
+
|
|
16
|
+
* **slot-layout-editor:** lengths are shown to three decimals ([66bd3af](https://github.com/hatiolab/operato/commit/66bd3af6e5df6860e89f3e706938081370607992))
|
|
17
|
+
* **slot-layout-editor:** words for ACCEPTS_EMPTY, things-scene ^10.1.102 ([ca42efb](https://github.com/hatiolab/operato/commit/ca42efb8b2feb6cc917febd5a0597f6a1360aef9))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### [10.29.56](https://github.com/hatiolab/operato/compare/v10.29.55...v10.29.56) (2026-10-26)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### :rocket: New Features
|
|
25
|
+
|
|
26
|
+
* **slot-layout-editor:** anchor face picture first, live editing, fit to face, type chooser ([9e0c183](https://github.com/hatiolab/operato/commit/9e0c1833cf07699ae052a53d6ed5b65d7f6ee7ae))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
6
30
|
### [10.29.52](https://github.com/hatiolab/operato/compare/v10.29.51...v10.29.52) (2026-10-25)
|
|
7
31
|
|
|
8
32
|
**Note:** Version bump only for package @operato/slot-layout-editor
|
package/dist/src/draft.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 자리 레이아웃 편집 동작. 입력 레이아웃을 바꾸지 않고 새 레이아웃을 돌려준다.
|
|
5
5
|
* 레이아웃에 호스트가 덧붙인 키(예: 실시간 주소 읽기 설정)는 그대로 보존한다.
|
|
6
6
|
*/
|
|
7
|
-
import { type GridOrder, type LayoutIssue, type SlotLayout, type SlotLayoutTier, type SlotPattern, type SlotSpec } from '@hatiolab/things-scene';
|
|
7
|
+
import { type FitReason, type FitVerdict, type GridOrder, type ItemDims, type LayoutIssue, type SlotLayout, type SlotLayoutTier, type SlotPattern, type SlotSpec } from '@hatiolab/things-scene';
|
|
8
8
|
/** 프리셋이 받는 크기. slotHeight 는 자리 허용 높이 */
|
|
9
9
|
export interface PresetSize {
|
|
10
10
|
width: number;
|
|
@@ -84,15 +84,62 @@ export interface AddressImpact {
|
|
|
84
84
|
* 어느 쪽이든 쓸 수 없는 값이면 빈 결과.
|
|
85
85
|
*/
|
|
86
86
|
export declare function addressImpact(before: SlotLayout | undefined, after: SlotLayout | undefined): AddressImpact;
|
|
87
|
-
|
|
87
|
+
/** 앵커 면 위의 자리 하나. 위치·회전은 앵커 기준(화면 위가 -z), 크기는 돌리기 전 허용 폭·깊이 */
|
|
88
|
+
export interface FaceSlot {
|
|
89
|
+
tier: number;
|
|
88
90
|
slotNo: number;
|
|
91
|
+
id: string;
|
|
89
92
|
name?: string;
|
|
90
|
-
/** 자리 중심 (Tier 로컬). 화면 위가 -z */
|
|
91
93
|
x: number;
|
|
92
94
|
z: number;
|
|
93
|
-
/**
|
|
95
|
+
/** 앵커 기준 회전(도). Tier 회전 + 자리 회전 */
|
|
96
|
+
rotation: number;
|
|
94
97
|
width: number;
|
|
95
98
|
depth: number;
|
|
96
99
|
}
|
|
97
|
-
/**
|
|
98
|
-
export declare function
|
|
100
|
+
/** 한 앵커 위에 선 모든 Tier 의 자리 (앵커 기준). 자리를 만들 수 없는 Tier 는 건너뛴다 */
|
|
101
|
+
export declare function anchorFaceSlots(layout: SlotLayout | undefined, anchorId: string): FaceSlot[];
|
|
102
|
+
/**
|
|
103
|
+
* ROW Tier 의 끝 자리를 앵커 면의 한 점으로 끌었을 때의 간격. 자리들은 Tier 원점을 가운데로 늘어서므로 끝 자리 중심은
|
|
104
|
+
* 원점에서 (개수 − 1) ÷ 2 × 간격 떨어져 있다. 자리가 둘보다 적거나 ROW 가 아니면 null
|
|
105
|
+
*/
|
|
106
|
+
export declare function pitchToPoint(layout: SlotLayout, tierNo: number, point: {
|
|
107
|
+
x: number;
|
|
108
|
+
z: number;
|
|
109
|
+
}): number | null;
|
|
110
|
+
/** 면에 맞출 수 없는 까닭 */
|
|
111
|
+
export type FitToFaceReason = 'FACE_UNKNOWN' | 'PATTERN_UNSUPPORTED' | 'ROTATION_UNSUPPORTED' | 'SLOT_WIDER_THAN_FACE' | 'SLOTS_DO_NOT_FIT';
|
|
112
|
+
/**
|
|
113
|
+
* 면에 맞춤: Tier 를 면 가운데에 두고, 같은 폭의 자리들을 면 끝에서 끝까지 고르게 놓는 간격을 한 번 계산해 숫자로 적는다.
|
|
114
|
+
* 면 폭 W, 자리 폭 w(자리 회전을 반영한 외접 폭), 개수 n 이면 간격 = (W − w) ÷ (n − 1). 자리 하나면 가운데에만 둔다.
|
|
115
|
+
* GRID 는 X(열)·Z(행)에 각각 같은 식을 쓴다. 크기가 바뀌어도 따라가는 규칙이 아니다(ADR-0051 4.1: 크기에 따른 간격은 미결).
|
|
116
|
+
* Tier·자리 회전이 90° 단위가 아니거나 CUSTOM 이면 맞추지 않는다. 자리가 면에 다 들어가지 않으면 까닭을 돌려준다.
|
|
117
|
+
*/
|
|
118
|
+
export declare function fitTierToFace<L extends SlotLayout>(layout: L, tierNo: number, face: {
|
|
119
|
+
width: number;
|
|
120
|
+
depth: number;
|
|
121
|
+
} | undefined): {
|
|
122
|
+
layout: L;
|
|
123
|
+
} | {
|
|
124
|
+
reason: FitToFaceReason;
|
|
125
|
+
};
|
|
126
|
+
/** 호스트가 주는 실린 아이템 (읽기 전용). type 은 판정용 아이템 타입(표에 없으면 null), code 는 데이터가 준 값 */
|
|
127
|
+
export interface HeldItem {
|
|
128
|
+
id: string;
|
|
129
|
+
slot?: string;
|
|
130
|
+
code?: string;
|
|
131
|
+
type?: string | null;
|
|
132
|
+
dims?: ItemDims | null;
|
|
133
|
+
}
|
|
134
|
+
/** 편집 중인 레이아웃에서 실린 아이템이 어떻게 되는지. 판정만 하고 아이템을 옮기거나 지우지 않는다 */
|
|
135
|
+
export interface HeldItemStatus {
|
|
136
|
+
item: HeldItem;
|
|
137
|
+
/** 그 자리가 이 레이아웃에 있는가. 없으면 미배치로 남는다 */
|
|
138
|
+
placed: boolean;
|
|
139
|
+
/** 아이템 타입 표에 없는 코드 */
|
|
140
|
+
unregistered: boolean;
|
|
141
|
+
verdict: FitVerdict;
|
|
142
|
+
reasons: FitReason[];
|
|
143
|
+
}
|
|
144
|
+
/** 같은 자리의 앞 아이템을 깔고 공통 판정(judgeItemFit)으로 본다. 모양을 자리에 맞춰 그리는 것은 판정이 아니다 */
|
|
145
|
+
export declare function heldItemStatuses(layout: SlotLayout | undefined, items?: readonly HeldItem[]): HeldItemStatus[];
|
package/dist/src/draft.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 자리 레이아웃 편집 동작. 입력 레이아웃을 바꾸지 않고 새 레이아웃을 돌려준다.
|
|
5
5
|
* 레이아웃에 호스트가 덧붙인 키(예: 실시간 주소 읽기 설정)는 그대로 보존한다.
|
|
6
6
|
*/
|
|
7
|
-
import { resolveSlot, rotatedFootprint, slotAddresses, slotCount, slotFrame, slotIdOf, slotNumbers } from '@hatiolab/things-scene';
|
|
7
|
+
import { addressOfSlotId, judgeItemFit, resolveSlot, rotatedFootprint, slotAddresses, slotCount, slotFrame, slotIdOf, slotNumbers } from '@hatiolab/things-scene';
|
|
8
8
|
// JSON 복사는 NaN·Infinity 를 null 로 바꿔 잘못된 값을 숨기므로 쓰지 않는다
|
|
9
9
|
const clone = (v) => structuredClone(v);
|
|
10
10
|
/** 크기·값을 검사하고 프리셋으로 레이아웃을 만든다. 모자라거나 잘못되면 layout 없이 문제 목록 */
|
|
@@ -204,32 +204,141 @@ export function addressImpact(before, after) {
|
|
|
204
204
|
}
|
|
205
205
|
return impact;
|
|
206
206
|
}
|
|
207
|
-
/**
|
|
208
|
-
export function
|
|
207
|
+
/** 한 앵커 위에 선 모든 Tier 의 자리 (앵커 기준). 자리를 만들 수 없는 Tier 는 건너뛴다 */
|
|
208
|
+
export function anchorFaceSlots(layout, anchorId) {
|
|
209
209
|
var _a;
|
|
210
|
-
const tier = (_a = layout === null || layout === void 0 ? void 0 : layout.tiers) === null || _a === void 0 ? void 0 : _a.find(t => (t === null || t === void 0 ? void 0 : t.tierNo) === tierNo);
|
|
211
|
-
if (!layout || !tier)
|
|
212
|
-
return [];
|
|
213
|
-
let numbers = [];
|
|
214
|
-
try {
|
|
215
|
-
const count = slotCount(tier.layout);
|
|
216
|
-
if (!Number.isSafeInteger(count) || count <= 0 || count > 10000)
|
|
217
|
-
return [];
|
|
218
|
-
numbers = slotNumbers(tier.layout);
|
|
219
|
-
}
|
|
220
|
-
catch (_b) {
|
|
221
|
-
return [];
|
|
222
|
-
}
|
|
223
210
|
const out = [];
|
|
224
|
-
for (const
|
|
225
|
-
|
|
226
|
-
if (!s)
|
|
211
|
+
for (const tier of (_a = layout === null || layout === void 0 ? void 0 : layout.tiers) !== null && _a !== void 0 ? _a : []) {
|
|
212
|
+
if ((tier === null || tier === void 0 ? void 0 : tier.anchorId) !== anchorId)
|
|
227
213
|
continue;
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
214
|
+
let numbers;
|
|
215
|
+
try {
|
|
216
|
+
const count = slotCount(tier.layout);
|
|
217
|
+
if (!Number.isSafeInteger(count) || count <= 0 || count > 10000)
|
|
218
|
+
continue;
|
|
219
|
+
numbers = slotNumbers(tier.layout);
|
|
220
|
+
}
|
|
221
|
+
catch (_b) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
const slots = [];
|
|
225
|
+
for (const n of numbers) {
|
|
226
|
+
const s = resolveSlot(layout, { tier: tier.tierNo, slot: n });
|
|
227
|
+
if (!s)
|
|
228
|
+
continue;
|
|
229
|
+
const f = slotFrame(s);
|
|
230
|
+
const one = { tier: tier.tierNo, slotNo: n, id: slotIdOf({ tier: tier.tierNo, slot: n }), ...(s.name ? { name: s.name } : {}), x: f.position.x, z: f.position.z, rotation: f.rotation, width: s.allowed.width, depth: s.allowed.depth };
|
|
231
|
+
if (![one.x, one.z, one.rotation, one.width, one.depth].every(Number.isFinite)) {
|
|
232
|
+
slots.length = 0;
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
slots.push(one);
|
|
236
|
+
}
|
|
237
|
+
out.push(...slots);
|
|
232
238
|
}
|
|
233
239
|
return out;
|
|
234
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* ROW Tier 의 끝 자리를 앵커 면의 한 점으로 끌었을 때의 간격. 자리들은 Tier 원점을 가운데로 늘어서므로 끝 자리 중심은
|
|
243
|
+
* 원점에서 (개수 − 1) ÷ 2 × 간격 떨어져 있다. 자리가 둘보다 적거나 ROW 가 아니면 null
|
|
244
|
+
*/
|
|
245
|
+
export function pitchToPoint(layout, tierNo, point) {
|
|
246
|
+
const tier = layout.tiers.find(t => t.tierNo === tierNo);
|
|
247
|
+
if (!tier || tier.layout.pattern !== 'ROW' || tier.layout.count < 2)
|
|
248
|
+
return null;
|
|
249
|
+
const first = resolveSlot(layout, { tier: tierNo, slot: 1 });
|
|
250
|
+
const last = resolveSlot(layout, { tier: tierNo, slot: tier.layout.count });
|
|
251
|
+
if (!first || !last)
|
|
252
|
+
return null;
|
|
253
|
+
const a = slotFrame(first).position;
|
|
254
|
+
const b = slotFrame(last).position;
|
|
255
|
+
const length = Math.hypot(b.x - a.x, b.z - a.z);
|
|
256
|
+
if (!(length > 0))
|
|
257
|
+
return null;
|
|
258
|
+
const u = { x: (b.x - a.x) / length, z: (b.z - a.z) / length };
|
|
259
|
+
const along = (point.x - tier.position.x) * u.x + (point.z - tier.position.z) * u.z;
|
|
260
|
+
return (2 * along) / (tier.layout.count - 1);
|
|
261
|
+
}
|
|
262
|
+
const round = (v) => Math.round(v * 10) / 10;
|
|
263
|
+
/**
|
|
264
|
+
* 면에 맞춤: Tier 를 면 가운데에 두고, 같은 폭의 자리들을 면 끝에서 끝까지 고르게 놓는 간격을 한 번 계산해 숫자로 적는다.
|
|
265
|
+
* 면 폭 W, 자리 폭 w(자리 회전을 반영한 외접 폭), 개수 n 이면 간격 = (W − w) ÷ (n − 1). 자리 하나면 가운데에만 둔다.
|
|
266
|
+
* GRID 는 X(열)·Z(행)에 각각 같은 식을 쓴다. 크기가 바뀌어도 따라가는 규칙이 아니다(ADR-0051 4.1: 크기에 따른 간격은 미결).
|
|
267
|
+
* Tier·자리 회전이 90° 단위가 아니거나 CUSTOM 이면 맞추지 않는다. 자리가 면에 다 들어가지 않으면 까닭을 돌려준다.
|
|
268
|
+
*/
|
|
269
|
+
export function fitTierToFace(layout, tierNo, face) {
|
|
270
|
+
var _a, _b, _c;
|
|
271
|
+
const tier = layout.tiers.find(t => t.tierNo === tierNo);
|
|
272
|
+
if (!tier)
|
|
273
|
+
return { reason: 'PATTERN_UNSUPPORTED' };
|
|
274
|
+
if (!face || !(face.width > 0) || !(face.depth > 0))
|
|
275
|
+
return { reason: 'FACE_UNKNOWN' };
|
|
276
|
+
const l = tier.layout;
|
|
277
|
+
if (l.pattern === 'CUSTOM')
|
|
278
|
+
return { reason: 'PATTERN_UNSUPPORTED' };
|
|
279
|
+
const quarter = (v) => (((v % 360) + 360) % 360) / 90;
|
|
280
|
+
const tierQuarter = quarter((_a = tier.rotation) !== null && _a !== void 0 ? _a : 0);
|
|
281
|
+
if (!Number.isInteger(tierQuarter) || !Number.isInteger(quarter((_b = l.slot.rotation) !== null && _b !== void 0 ? _b : 0)))
|
|
282
|
+
return { reason: 'ROTATION_UNSUPPORTED' };
|
|
283
|
+
const turned = tierQuarter % 2 === 1;
|
|
284
|
+
// 면의 크기를 Tier 축으로: Tier 가 90° 돌면 Tier X 가 앵커 Z 를 따라간다
|
|
285
|
+
const span = { x: turned ? face.depth : face.width, z: turned ? face.width : face.depth };
|
|
286
|
+
const fp = rotatedFootprint(l.slot.allowed, (_c = l.slot.rotation) !== null && _c !== void 0 ? _c : 0);
|
|
287
|
+
const pitchFor = (room, width, n) => {
|
|
288
|
+
if (width > room)
|
|
289
|
+
return 'SLOT_WIDER_THAN_FACE';
|
|
290
|
+
if (n <= 1)
|
|
291
|
+
return NaN;
|
|
292
|
+
const pitch = (room - width) / (n - 1);
|
|
293
|
+
return pitch < width ? 'SLOTS_DO_NOT_FIT' : round(pitch);
|
|
294
|
+
};
|
|
295
|
+
const next = clone(layout);
|
|
296
|
+
const t = next.tiers.find(x => x.tierNo === tierNo);
|
|
297
|
+
t.position = { ...t.position, x: 0, z: 0 };
|
|
298
|
+
const nl = t.layout;
|
|
299
|
+
if (nl.pattern === 'SINGLE') {
|
|
300
|
+
if (fp.width > span.x || fp.depth > span.z)
|
|
301
|
+
return { reason: 'SLOT_WIDER_THAN_FACE' };
|
|
302
|
+
}
|
|
303
|
+
else if (nl.pattern === 'ROW') {
|
|
304
|
+
const alongX = nl.axis === 'X';
|
|
305
|
+
const cross = alongX ? fp.depth > span.z : fp.width > span.x;
|
|
306
|
+
if (cross)
|
|
307
|
+
return { reason: 'SLOT_WIDER_THAN_FACE' };
|
|
308
|
+
const pitch = pitchFor(alongX ? span.x : span.z, alongX ? fp.width : fp.depth, nl.count);
|
|
309
|
+
if (typeof pitch === 'string')
|
|
310
|
+
return { reason: pitch };
|
|
311
|
+
if (!Number.isNaN(pitch))
|
|
312
|
+
nl.pitch = pitch;
|
|
313
|
+
}
|
|
314
|
+
else if (nl.pattern === 'GRID') {
|
|
315
|
+
const px = pitchFor(span.x, fp.width, nl.cols);
|
|
316
|
+
const pz = pitchFor(span.z, fp.depth, nl.rows);
|
|
317
|
+
if (typeof px === 'string')
|
|
318
|
+
return { reason: px };
|
|
319
|
+
if (typeof pz === 'string')
|
|
320
|
+
return { reason: pz };
|
|
321
|
+
nl.pitch = { x: Number.isNaN(px) ? nl.pitch.x : px, z: Number.isNaN(pz) ? nl.pitch.z : pz };
|
|
322
|
+
}
|
|
323
|
+
return { layout: next };
|
|
324
|
+
}
|
|
325
|
+
/** 같은 자리의 앞 아이템을 깔고 공통 판정(judgeItemFit)으로 본다. 모양을 자리에 맞춰 그리는 것은 판정이 아니다 */
|
|
326
|
+
export function heldItemStatuses(layout, items = []) {
|
|
327
|
+
const below = new Map();
|
|
328
|
+
return items.map(item => {
|
|
329
|
+
var _a, _b, _c, _d, _e;
|
|
330
|
+
const address = addressOfSlotId(item.slot);
|
|
331
|
+
let slot = null;
|
|
332
|
+
try {
|
|
333
|
+
slot = address && layout ? resolveSlot(layout, address) : null;
|
|
334
|
+
}
|
|
335
|
+
catch (_f) {
|
|
336
|
+
slot = null;
|
|
337
|
+
}
|
|
338
|
+
const occupants = (_b = below.get((_a = item.slot) !== null && _a !== void 0 ? _a : '')) !== null && _b !== void 0 ? _b : [];
|
|
339
|
+
const fit = judgeItemFit(slot, { type: (_c = item.type) !== null && _c !== void 0 ? _c : undefined, dims: (_d = item.dims) !== null && _d !== void 0 ? _d : null }, occupants.map(o => { var _a, _b; return ({ type: (_a = o.type) !== null && _a !== void 0 ? _a : undefined, dims: (_b = o.dims) !== null && _b !== void 0 ? _b : null }); }));
|
|
340
|
+
below.set((_e = item.slot) !== null && _e !== void 0 ? _e : '', [...occupants, item]);
|
|
341
|
+
return { item, placed: !!slot, unregistered: item.type === null && !!item.code, verdict: fit.verdict, reasons: fit.reasons };
|
|
342
|
+
});
|
|
343
|
+
}
|
|
235
344
|
//# sourceMappingURL=draft.js.map
|
package/dist/src/draft.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draft.js","sourceRoot":"","sources":["../../src/draft.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * 자리 레이아웃 편집 동작. 입력 레이아웃을 바꾸지 않고 새 레이아웃을 돌려준다.\n * 레이아웃에 호스트가 덧붙인 키(예: 실시간 주소 읽기 설정)는 그대로 보존한다.\n */\n\nimport {\n resolveSlot,\n rotatedFootprint,\n slotAddresses,\n slotCount,\n slotFrame,\n slotIdOf,\n slotNumbers,\n type GridOrder,\n type LayoutIssue,\n type PatternSlot,\n type SlotLayout,\n type SlotLayoutTier,\n type SlotPattern,\n type SlotSpec\n} from '@hatiolab/things-scene'\n\n// JSON 복사는 NaN·Infinity 를 null 로 바꿔 잘못된 값을 숨기므로 쓰지 않는다\nconst clone = <T>(v: T): T => structuredClone(v)\n\n// ── 프리셋 ──────────────────────────────────────────────────────────────────\n\n/** 프리셋이 받는 크기. slotHeight 는 자리 허용 높이 */\nexport interface PresetSize {\n width: number\n depth: number\n slotHeight: number\n}\n\n/** 프리셋이 크기 외에 더 받는 값. 이름은 편집기가 키로 번역한다(label.slot-layout.preset-param.{key}) */\nexport interface PresetParam {\n key: string\n /** 양의 정수만 받는다 */\n integer?: boolean\n}\n\n/**\n * 호스트가 넘기는 프리셋. 이름은 편집기가 키로 번역한다(label.slot-layout.preset.{key}) — 기종 프리셋(figure-model)을 쓰는\n * 호스트마다 같은 이름을 따로 번역해 두지 않는다\n */\nexport interface SlotLayoutPreset<L extends SlotLayout = SlotLayout> {\n key: string\n params?: PresetParam[]\n build(size: PresetSize, params: Record<string, number>): L\n}\n\n/** 크기·값을 검사하고 프리셋으로 레이아웃을 만든다. 모자라거나 잘못되면 layout 없이 문제 목록 */\nexport function applyPreset<L extends SlotLayout>(\n preset: SlotLayoutPreset<L>,\n size: Partial<PresetSize>,\n params: Record<string, unknown> = {}\n): { layout?: L; issues: LayoutIssue[] } {\n const issues: LayoutIssue[] = []\n const positive = (v: unknown) => typeof v === 'number' && Number.isFinite(v) && v > 0\n const positiveInt = (v: unknown) => Number.isSafeInteger(v) && (v as number) > 0\n for (const k of ['width', 'depth', 'slotHeight'] as const) if (!positive(size[k])) issues.push({ code: 'POSITIVE_NUMBER_REQUIRED', path: k })\n for (const p of preset.params ?? [])\n if (!(p.integer ? positiveInt(params[p.key]) : positive(params[p.key])))\n issues.push({ code: p.integer ? 'POSITIVE_INTEGER_REQUIRED' : 'POSITIVE_NUMBER_REQUIRED', path: p.key })\n if (issues.length) return { issues }\n return { layout: preset.build(size as PresetSize, params as Record<string, number>), issues }\n}\n\n// ── Tier ───────────────────────────────────────────────────────────────────\n// Tier 번호는 홀더 안의 식별 번호다. 지우거나 정렬해도 다시 매기지 않는다 (번호가 바뀌면 놓인 Carrier 의 주소가 바뀐다).\n\n/** Tier 설정을 복사해 새 Tier 로 붙인다. 번호는 지금 가장 큰 번호 + 1, 배치는 placement 로 바꾼다. 점유는 복사하지 않는다 */\nexport function copyTier<L extends SlotLayout>(layout: L, tierNo: number, placement: TierPatch = {}): L {\n const next = clone(layout)\n const source = next.tiers.find(t => t.tierNo === tierNo)\n if (!source) return next\n const newNo = Math.max(...next.tiers.map(t => t.tierNo)) + 1\n next.tiers.push({ ...clone(source), ...clone(placement), tierNo: newNo })\n return next\n}\n\n/** Tier 를 지운다. 남은 Tier 의 번호는 그대로다. 마지막 남은 Tier 는 지우지 않는다 */\nexport function removeTier<L extends SlotLayout>(layout: L, tierNo: number): L {\n const next = clone(layout)\n if (next.tiers.length <= 1) return next\n next.tiers = next.tiers.filter(t => t.tierNo !== tierNo)\n return next\n}\n\n/** Tier 가 선 Anchor, 적재 방식, Anchor 기준 배치 */\nexport type TierPatch = Partial<Pick<SlotLayoutTier, 'anchorId' | 'mount' | 'position' | 'rotation'>>\n\nexport function updateTier<L extends SlotLayout>(layout: L, tierNo: number, patch: TierPatch): L {\n const next = clone(layout)\n const tier = next.tiers.find(t => t.tierNo === tierNo)\n if (tier) Object.assign(tier, clone(patch))\n return next\n}\n\n// ── 패턴 ───────────────────────────────────────────────────────────────────\n\nconst specOf = (l: SlotPattern): SlotSpec => {\n if (l.pattern === 'CUSTOM') {\n const first = [...l.slots].sort((a, b) => a.slotNo - b.slotNo)[0]\n const { allowed, accepts, stackLimit, rotation } = first ?? { allowed: { width: 1, depth: 1, height: 1 } }\n return clone({ allowed, accepts, stackLimit, rotation })\n }\n return clone(l.slot)\n}\n\n/**\n * 층의 패턴을 바꾼다. 자리 규격은 그대로 두고 자리 수·간격은 지금 배치를 이어받는다.\n * CUSTOM 으로 바꾸면 지금 자리들의 위치·규격을 그대로 적어 둔다.\n */\nexport function setTierPattern<L extends SlotLayout>(layout: L, tierNo: number, pattern: SlotPattern['pattern']): L {\n const next = clone(layout)\n const tier = next.tiers.find(t => t.tierNo === tierNo)\n if (!tier || tier.layout.pattern === pattern) return next\n const cur = tier.layout\n const spec = specOf(cur)\n const count = slotCount(cur)\n\n switch (pattern) {\n case 'SINGLE':\n tier.layout = { pattern, slot: spec }\n break\n case 'ROW':\n tier.layout = {\n pattern,\n count: Math.max(count, 1),\n pitch: cur.pattern === 'ROW' ? cur.pitch : cur.pattern === 'GRID' ? cur.pitch.x : spec.allowed.width,\n axis: 'X',\n origin: 'MIN',\n slot: spec\n }\n break\n case 'GRID':\n tier.layout = {\n pattern,\n rows: cur.pattern === 'GRID' ? cur.rows : 1,\n cols: cur.pattern === 'GRID' ? cur.cols : Math.max(count, 1),\n pitch: { x: cur.pattern === 'ROW' ? cur.pitch : spec.allowed.width, z: spec.allowed.depth },\n order: 'ROW_MAJOR',\n origin: { x: 'MIN', z: 'MIN' },\n slot: spec\n }\n break\n case 'CUSTOM': {\n const slots: PatternSlot[] = []\n for (const n of slotNumbers(cur)) {\n const r = resolveSlot(layout, { tier: tierNo, slot: n })!\n slots.push({\n slotNo: n,\n offset: { ...r.offset },\n allowed: { ...r.allowed },\n ...(r.accepts ? { accepts: [...r.accepts] } : {}),\n ...(r.stackLimit !== 1 ? { stackLimit: r.stackLimit } : {}),\n ...(r.rotation ? { rotation: r.rotation } : {})\n })\n }\n tier.layout = { pattern, slots }\n break\n }\n }\n return next\n}\n\nexport interface PatternPatch {\n count?: number\n pitch?: number | { x: number; z: number }\n axis?: 'X' | 'Z'\n origin?: 'MIN' | 'MAX' | { x: 'MIN' | 'MAX'; z: 'MIN' | 'MAX' }\n rows?: number\n cols?: number\n order?: GridOrder\n}\n\n/** 패턴 값(자리 수·간격·축·번호 규칙)을 고친다. 그 패턴에 없는 키는 무시한다 */\nexport function updatePattern<L extends SlotLayout>(layout: L, tierNo: number, patch: PatternPatch): L {\n const next = clone(layout)\n const tier = next.tiers.find(t => t.tierNo === tierNo)\n if (!tier) return next\n const allowedKeys: Record<string, string[]> = {\n SINGLE: [],\n ROW: ['count', 'pitch', 'axis', 'origin'],\n GRID: ['rows', 'cols', 'pitch', 'order', 'origin'],\n CUSTOM: []\n }\n for (const [k, v] of Object.entries(patch))\n if (allowedKeys[tier.layout.pattern].includes(k)) (tier.layout as unknown as Record<string, unknown>)[k] = clone(v)\n return next\n}\n\nexport type SlotSpecPatch = Partial<SlotSpec> & { offset?: { x: number; z: number }; name?: string }\n\n/** SINGLE·ROW·GRID 의 공통 자리 규격, CUSTOM 이면 slotNo 자리를 고친다. undefined 값은 키를 지운다 */\nexport function updateSlotSpec<L extends SlotLayout>(layout: L, tierNo: number, patch: SlotSpecPatch, slotNo?: number): L {\n const next = clone(layout)\n const tier = next.tiers.find(t => t.tierNo === tierNo)\n if (!tier) return next\n const target = (tier.layout.pattern === 'CUSTOM' ? tier.layout.slots.find(s => s.slotNo === slotNo) : tier.layout.slot) as\n | Record<string, unknown>\n | undefined\n if (!target) return next\n for (const [k, v] of Object.entries(patch)) {\n if (v === undefined) delete target[k]\n else target[k] = clone(v)\n }\n if (tier.layout.pattern !== 'CUSTOM') for (const k of ['offset', 'name']) delete target[k]\n return next\n}\n\n/** CUSTOM 층에 자리를 하나 더한다 (마지막 자리 규격 복사, 번호는 마지막 + 1) */\nexport function addCustomSlot<L extends SlotLayout>(layout: L, tierNo: number): L {\n const next = clone(layout)\n const tier = next.tiers.find(t => t.tierNo === tierNo)\n if (!tier || tier.layout.pattern !== 'CUSTOM') return next\n const last = [...tier.layout.slots].sort((a, b) => a.slotNo - b.slotNo).pop()\n if (last) {\n const { name: _name, ...rest } = clone(last)\n tier.layout.slots.push({ ...rest, slotNo: last.slotNo + 1 })\n } else tier.layout.slots.push({ slotNo: 1, offset: { x: 0, z: 0 }, allowed: { width: 1, depth: 1, height: 1 } })\n return next\n}\n\n/** CUSTOM Tier 의 자리를 지운다. 남은 자리의 번호는 그대로다. 마지막 남은 자리는 지우지 않는다 */\nexport function removeCustomSlot<L extends SlotLayout>(layout: L, tierNo: number, slotNo: number): L {\n const next = clone(layout)\n const tier = next.tiers.find(t => t.tierNo === tierNo)\n if (!tier || tier.layout.pattern !== 'CUSTOM' || tier.layout.slots.length <= 1) return next\n tier.layout.slots = tier.layout.slots.filter(s => s.slotNo !== slotNo)\n return next\n}\n\n// ── 주소 영향 ──────────────────────────────────────────────────────────────\n\n/** 바꾼 값을 적용하면 뜻이 바뀌는 주소: 위치가 바뀌는 자리, 없어지는 자리 (자리 id) */\nexport interface AddressImpact {\n moved: string[]\n gone: string[]\n}\n\nconst sameFrame = (a: ReturnType<typeof slotFrame>, b: ReturnType<typeof slotFrame>) =>\n a.rotation === b.rotation && a.position.x === b.position.x && a.position.y === b.position.y && a.position.z === b.position.z\n\n/**\n * 앞 레이아웃의 주소마다 새 레이아웃에서 같은 주소가 같은 Anchor 의 같은 위치인지 본다. 놓인 Carrier 는 주소로\n * 다시 읽히므로 (빈 자리로 옮겨 채우지 않는다) 여기 나온 주소의 Carrier 는 다른 곳에 그려지거나 예외가 된다.\n * 어느 쪽이든 쓸 수 없는 값이면 빈 결과.\n */\nexport function addressImpact(before: SlotLayout | undefined, after: SlotLayout | undefined): AddressImpact {\n const impact: AddressImpact = { moved: [], gone: [] }\n if (!Array.isArray(before?.tiers) || !Array.isArray(after?.tiers)) return impact\n try {\n for (const address of slotAddresses(before!)) {\n const was = resolveSlot(before!, address)\n const now = resolveSlot(after!, address)\n if (!was) continue\n const id = slotIdOf(address)\n if (!now) impact.gone.push(id)\n else if (was.anchorId !== now.anchorId || !sameFrame(slotFrame(was), slotFrame(now))) impact.moved.push(id)\n }\n } catch {\n return { moved: [], gone: [] }\n }\n return impact\n}\n\n// ── 미리보기 ───────────────────────────────────────────────────────────────\n\nexport interface PreviewSlot {\n slotNo: number\n name?: string\n /** 자리 중심 (Tier 로컬). 화면 위가 -z */\n x: number\n z: number\n /** 회전한 허용 공간의 외접 폭·깊이 */\n width: number\n depth: number\n}\n\n/** Tier 하나의 자리 위치와 크기 (Tier 로컬). 자리를 만들 수 없는 값이면 빈 목록 */\nexport function previewTier(layout: SlotLayout | undefined, tierNo: number): PreviewSlot[] {\n const tier = layout?.tiers?.find(t => t?.tierNo === tierNo)\n if (!layout || !tier) return []\n let numbers: number[] = []\n try {\n const count = slotCount(tier.layout)\n if (!Number.isSafeInteger(count) || count <= 0 || count > 10000) return []\n numbers = slotNumbers(tier.layout)\n } catch {\n return []\n }\n const out: PreviewSlot[] = []\n for (const n of numbers) {\n const s = resolveSlot(layout, { tier: tierNo, slot: n })\n if (!s) continue\n const fp = rotatedFootprint({ width: s.allowed.width, depth: s.allowed.depth, height: s.allowed.height }, s.rotation)\n if (![s.offset.x, s.offset.z, fp.width, fp.depth].every(Number.isFinite)) return []\n out.push({ slotNo: n, name: s.name, x: s.offset.x, z: s.offset.z, width: fp.width, depth: fp.depth })\n }\n return out\n}\n"]}
|
|
1
|
+
{"version":3,"file":"draft.js","sourceRoot":"","sources":["../../src/draft.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * 자리 레이아웃 편집 동작. 입력 레이아웃을 바꾸지 않고 새 레이아웃을 돌려준다.\n * 레이아웃에 호스트가 덧붙인 키(예: 실시간 주소 읽기 설정)는 그대로 보존한다.\n */\n\nimport {\n addressOfSlotId,\n judgeItemFit,\n resolveSlot,\n rotatedFootprint,\n slotAddresses,\n slotCount,\n slotFrame,\n slotIdOf,\n slotNumbers,\n type FitReason,\n type FitVerdict,\n type GridOrder,\n type ItemDims,\n type LayoutIssue,\n type PatternSlot,\n type SlotLayout,\n type SlotLayoutTier,\n type SlotPattern,\n type SlotSpec\n} from '@hatiolab/things-scene'\n\n// JSON 복사는 NaN·Infinity 를 null 로 바꿔 잘못된 값을 숨기므로 쓰지 않는다\nconst clone = <T>(v: T): T => structuredClone(v)\n\n// ── 프리셋 ──────────────────────────────────────────────────────────────────\n\n/** 프리셋이 받는 크기. slotHeight 는 자리 허용 높이 */\nexport interface PresetSize {\n width: number\n depth: number\n slotHeight: number\n}\n\n/** 프리셋이 크기 외에 더 받는 값. 이름은 편집기가 키로 번역한다(label.slot-layout.preset-param.{key}) */\nexport interface PresetParam {\n key: string\n /** 양의 정수만 받는다 */\n integer?: boolean\n}\n\n/**\n * 호스트가 넘기는 프리셋. 이름은 편집기가 키로 번역한다(label.slot-layout.preset.{key}) — 기종 프리셋(figure-model)을 쓰는\n * 호스트마다 같은 이름을 따로 번역해 두지 않는다\n */\nexport interface SlotLayoutPreset<L extends SlotLayout = SlotLayout> {\n key: string\n params?: PresetParam[]\n build(size: PresetSize, params: Record<string, number>): L\n}\n\n/** 크기·값을 검사하고 프리셋으로 레이아웃을 만든다. 모자라거나 잘못되면 layout 없이 문제 목록 */\nexport function applyPreset<L extends SlotLayout>(\n preset: SlotLayoutPreset<L>,\n size: Partial<PresetSize>,\n params: Record<string, unknown> = {}\n): { layout?: L; issues: LayoutIssue[] } {\n const issues: LayoutIssue[] = []\n const positive = (v: unknown) => typeof v === 'number' && Number.isFinite(v) && v > 0\n const positiveInt = (v: unknown) => Number.isSafeInteger(v) && (v as number) > 0\n for (const k of ['width', 'depth', 'slotHeight'] as const) if (!positive(size[k])) issues.push({ code: 'POSITIVE_NUMBER_REQUIRED', path: k })\n for (const p of preset.params ?? [])\n if (!(p.integer ? positiveInt(params[p.key]) : positive(params[p.key])))\n issues.push({ code: p.integer ? 'POSITIVE_INTEGER_REQUIRED' : 'POSITIVE_NUMBER_REQUIRED', path: p.key })\n if (issues.length) return { issues }\n return { layout: preset.build(size as PresetSize, params as Record<string, number>), issues }\n}\n\n// ── Tier ───────────────────────────────────────────────────────────────────\n// Tier 번호는 홀더 안의 식별 번호다. 지우거나 정렬해도 다시 매기지 않는다 (번호가 바뀌면 놓인 Carrier 의 주소가 바뀐다).\n\n/** Tier 설정을 복사해 새 Tier 로 붙인다. 번호는 지금 가장 큰 번호 + 1, 배치는 placement 로 바꾼다. 점유는 복사하지 않는다 */\nexport function copyTier<L extends SlotLayout>(layout: L, tierNo: number, placement: TierPatch = {}): L {\n const next = clone(layout)\n const source = next.tiers.find(t => t.tierNo === tierNo)\n if (!source) return next\n const newNo = Math.max(...next.tiers.map(t => t.tierNo)) + 1\n next.tiers.push({ ...clone(source), ...clone(placement), tierNo: newNo })\n return next\n}\n\n/** Tier 를 지운다. 남은 Tier 의 번호는 그대로다. 마지막 남은 Tier 는 지우지 않는다 */\nexport function removeTier<L extends SlotLayout>(layout: L, tierNo: number): L {\n const next = clone(layout)\n if (next.tiers.length <= 1) return next\n next.tiers = next.tiers.filter(t => t.tierNo !== tierNo)\n return next\n}\n\n/** Tier 가 선 Anchor, 적재 방식, Anchor 기준 배치 */\nexport type TierPatch = Partial<Pick<SlotLayoutTier, 'anchorId' | 'mount' | 'position' | 'rotation'>>\n\nexport function updateTier<L extends SlotLayout>(layout: L, tierNo: number, patch: TierPatch): L {\n const next = clone(layout)\n const tier = next.tiers.find(t => t.tierNo === tierNo)\n if (tier) Object.assign(tier, clone(patch))\n return next\n}\n\n// ── 패턴 ───────────────────────────────────────────────────────────────────\n\nconst specOf = (l: SlotPattern): SlotSpec => {\n if (l.pattern === 'CUSTOM') {\n const first = [...l.slots].sort((a, b) => a.slotNo - b.slotNo)[0]\n const { allowed, accepts, stackLimit, rotation } = first ?? { allowed: { width: 1, depth: 1, height: 1 } }\n return clone({ allowed, accepts, stackLimit, rotation })\n }\n return clone(l.slot)\n}\n\n/**\n * 층의 패턴을 바꾼다. 자리 규격은 그대로 두고 자리 수·간격은 지금 배치를 이어받는다.\n * CUSTOM 으로 바꾸면 지금 자리들의 위치·규격을 그대로 적어 둔다.\n */\nexport function setTierPattern<L extends SlotLayout>(layout: L, tierNo: number, pattern: SlotPattern['pattern']): L {\n const next = clone(layout)\n const tier = next.tiers.find(t => t.tierNo === tierNo)\n if (!tier || tier.layout.pattern === pattern) return next\n const cur = tier.layout\n const spec = specOf(cur)\n const count = slotCount(cur)\n\n switch (pattern) {\n case 'SINGLE':\n tier.layout = { pattern, slot: spec }\n break\n case 'ROW':\n tier.layout = {\n pattern,\n count: Math.max(count, 1),\n pitch: cur.pattern === 'ROW' ? cur.pitch : cur.pattern === 'GRID' ? cur.pitch.x : spec.allowed.width,\n axis: 'X',\n origin: 'MIN',\n slot: spec\n }\n break\n case 'GRID':\n tier.layout = {\n pattern,\n rows: cur.pattern === 'GRID' ? cur.rows : 1,\n cols: cur.pattern === 'GRID' ? cur.cols : Math.max(count, 1),\n pitch: { x: cur.pattern === 'ROW' ? cur.pitch : spec.allowed.width, z: spec.allowed.depth },\n order: 'ROW_MAJOR',\n origin: { x: 'MIN', z: 'MIN' },\n slot: spec\n }\n break\n case 'CUSTOM': {\n const slots: PatternSlot[] = []\n for (const n of slotNumbers(cur)) {\n const r = resolveSlot(layout, { tier: tierNo, slot: n })!\n slots.push({\n slotNo: n,\n offset: { ...r.offset },\n allowed: { ...r.allowed },\n ...(r.accepts ? { accepts: [...r.accepts] } : {}),\n ...(r.stackLimit !== 1 ? { stackLimit: r.stackLimit } : {}),\n ...(r.rotation ? { rotation: r.rotation } : {})\n })\n }\n tier.layout = { pattern, slots }\n break\n }\n }\n return next\n}\n\nexport interface PatternPatch {\n count?: number\n pitch?: number | { x: number; z: number }\n axis?: 'X' | 'Z'\n origin?: 'MIN' | 'MAX' | { x: 'MIN' | 'MAX'; z: 'MIN' | 'MAX' }\n rows?: number\n cols?: number\n order?: GridOrder\n}\n\n/** 패턴 값(자리 수·간격·축·번호 규칙)을 고친다. 그 패턴에 없는 키는 무시한다 */\nexport function updatePattern<L extends SlotLayout>(layout: L, tierNo: number, patch: PatternPatch): L {\n const next = clone(layout)\n const tier = next.tiers.find(t => t.tierNo === tierNo)\n if (!tier) return next\n const allowedKeys: Record<string, string[]> = {\n SINGLE: [],\n ROW: ['count', 'pitch', 'axis', 'origin'],\n GRID: ['rows', 'cols', 'pitch', 'order', 'origin'],\n CUSTOM: []\n }\n for (const [k, v] of Object.entries(patch))\n if (allowedKeys[tier.layout.pattern].includes(k)) (tier.layout as unknown as Record<string, unknown>)[k] = clone(v)\n return next\n}\n\nexport type SlotSpecPatch = Partial<SlotSpec> & { offset?: { x: number; z: number }; name?: string }\n\n/** SINGLE·ROW·GRID 의 공통 자리 규격, CUSTOM 이면 slotNo 자리를 고친다. undefined 값은 키를 지운다 */\nexport function updateSlotSpec<L extends SlotLayout>(layout: L, tierNo: number, patch: SlotSpecPatch, slotNo?: number): L {\n const next = clone(layout)\n const tier = next.tiers.find(t => t.tierNo === tierNo)\n if (!tier) return next\n const target = (tier.layout.pattern === 'CUSTOM' ? tier.layout.slots.find(s => s.slotNo === slotNo) : tier.layout.slot) as\n | Record<string, unknown>\n | undefined\n if (!target) return next\n for (const [k, v] of Object.entries(patch)) {\n if (v === undefined) delete target[k]\n else target[k] = clone(v)\n }\n if (tier.layout.pattern !== 'CUSTOM') for (const k of ['offset', 'name']) delete target[k]\n return next\n}\n\n/** CUSTOM 층에 자리를 하나 더한다 (마지막 자리 규격 복사, 번호는 마지막 + 1) */\nexport function addCustomSlot<L extends SlotLayout>(layout: L, tierNo: number): L {\n const next = clone(layout)\n const tier = next.tiers.find(t => t.tierNo === tierNo)\n if (!tier || tier.layout.pattern !== 'CUSTOM') return next\n const last = [...tier.layout.slots].sort((a, b) => a.slotNo - b.slotNo).pop()\n if (last) {\n const { name: _name, ...rest } = clone(last)\n tier.layout.slots.push({ ...rest, slotNo: last.slotNo + 1 })\n } else tier.layout.slots.push({ slotNo: 1, offset: { x: 0, z: 0 }, allowed: { width: 1, depth: 1, height: 1 } })\n return next\n}\n\n/** CUSTOM Tier 의 자리를 지운다. 남은 자리의 번호는 그대로다. 마지막 남은 자리는 지우지 않는다 */\nexport function removeCustomSlot<L extends SlotLayout>(layout: L, tierNo: number, slotNo: number): L {\n const next = clone(layout)\n const tier = next.tiers.find(t => t.tierNo === tierNo)\n if (!tier || tier.layout.pattern !== 'CUSTOM' || tier.layout.slots.length <= 1) return next\n tier.layout.slots = tier.layout.slots.filter(s => s.slotNo !== slotNo)\n return next\n}\n\n// ── 주소 영향 ──────────────────────────────────────────────────────────────\n\n/** 바꾼 값을 적용하면 뜻이 바뀌는 주소: 위치가 바뀌는 자리, 없어지는 자리 (자리 id) */\nexport interface AddressImpact {\n moved: string[]\n gone: string[]\n}\n\nconst sameFrame = (a: ReturnType<typeof slotFrame>, b: ReturnType<typeof slotFrame>) =>\n a.rotation === b.rotation && a.position.x === b.position.x && a.position.y === b.position.y && a.position.z === b.position.z\n\n/**\n * 앞 레이아웃의 주소마다 새 레이아웃에서 같은 주소가 같은 Anchor 의 같은 위치인지 본다. 놓인 Carrier 는 주소로\n * 다시 읽히므로 (빈 자리로 옮겨 채우지 않는다) 여기 나온 주소의 Carrier 는 다른 곳에 그려지거나 예외가 된다.\n * 어느 쪽이든 쓸 수 없는 값이면 빈 결과.\n */\nexport function addressImpact(before: SlotLayout | undefined, after: SlotLayout | undefined): AddressImpact {\n const impact: AddressImpact = { moved: [], gone: [] }\n if (!Array.isArray(before?.tiers) || !Array.isArray(after?.tiers)) return impact\n try {\n for (const address of slotAddresses(before!)) {\n const was = resolveSlot(before!, address)\n const now = resolveSlot(after!, address)\n if (!was) continue\n const id = slotIdOf(address)\n if (!now) impact.gone.push(id)\n else if (was.anchorId !== now.anchorId || !sameFrame(slotFrame(was), slotFrame(now))) impact.moved.push(id)\n }\n } catch {\n return { moved: [], gone: [] }\n }\n return impact\n}\n\n// ── 앵커 면 위의 그림 ──────────────────────────────────────────────────────\n\n/** 앵커 면 위의 자리 하나. 위치·회전은 앵커 기준(화면 위가 -z), 크기는 돌리기 전 허용 폭·깊이 */\nexport interface FaceSlot {\n tier: number\n slotNo: number\n id: string\n name?: string\n x: number\n z: number\n /** 앵커 기준 회전(도). Tier 회전 + 자리 회전 */\n rotation: number\n width: number\n depth: number\n}\n\n/** 한 앵커 위에 선 모든 Tier 의 자리 (앵커 기준). 자리를 만들 수 없는 Tier 는 건너뛴다 */\nexport function anchorFaceSlots(layout: SlotLayout | undefined, anchorId: string): FaceSlot[] {\n const out: FaceSlot[] = []\n for (const tier of layout?.tiers ?? []) {\n if (tier?.anchorId !== anchorId) continue\n let numbers: number[]\n try {\n const count = slotCount(tier.layout)\n if (!Number.isSafeInteger(count) || count <= 0 || count > 10000) continue\n numbers = slotNumbers(tier.layout)\n } catch {\n continue\n }\n const slots: FaceSlot[] = []\n for (const n of numbers) {\n const s = resolveSlot(layout!, { tier: tier.tierNo, slot: n })\n if (!s) continue\n const f = slotFrame(s)\n const one = { tier: tier.tierNo, slotNo: n, id: slotIdOf({ tier: tier.tierNo, slot: n }), ...(s.name ? { name: s.name } : {}), x: f.position.x, z: f.position.z, rotation: f.rotation, width: s.allowed.width, depth: s.allowed.depth }\n if (![one.x, one.z, one.rotation, one.width, one.depth].every(Number.isFinite)) {\n slots.length = 0\n break\n }\n slots.push(one)\n }\n out.push(...slots)\n }\n return out\n}\n\n/**\n * ROW Tier 의 끝 자리를 앵커 면의 한 점으로 끌었을 때의 간격. 자리들은 Tier 원점을 가운데로 늘어서므로 끝 자리 중심은\n * 원점에서 (개수 − 1) ÷ 2 × 간격 떨어져 있다. 자리가 둘보다 적거나 ROW 가 아니면 null\n */\nexport function pitchToPoint(layout: SlotLayout, tierNo: number, point: { x: number; z: number }): number | null {\n const tier = layout.tiers.find(t => t.tierNo === tierNo)\n if (!tier || tier.layout.pattern !== 'ROW' || tier.layout.count < 2) return null\n const first = resolveSlot(layout, { tier: tierNo, slot: 1 })\n const last = resolveSlot(layout, { tier: tierNo, slot: tier.layout.count })\n if (!first || !last) return null\n const a = slotFrame(first).position\n const b = slotFrame(last).position\n const length = Math.hypot(b.x - a.x, b.z - a.z)\n if (!(length > 0)) return null\n const u = { x: (b.x - a.x) / length, z: (b.z - a.z) / length }\n const along = (point.x - tier.position.x) * u.x + (point.z - tier.position.z) * u.z\n return (2 * along) / (tier.layout.count - 1)\n}\n\n/** 면에 맞출 수 없는 까닭 */\nexport type FitToFaceReason = 'FACE_UNKNOWN' | 'PATTERN_UNSUPPORTED' | 'ROTATION_UNSUPPORTED' | 'SLOT_WIDER_THAN_FACE' | 'SLOTS_DO_NOT_FIT'\n\nconst round = (v: number) => Math.round(v * 10) / 10\n\n/**\n * 면에 맞춤: Tier 를 면 가운데에 두고, 같은 폭의 자리들을 면 끝에서 끝까지 고르게 놓는 간격을 한 번 계산해 숫자로 적는다.\n * 면 폭 W, 자리 폭 w(자리 회전을 반영한 외접 폭), 개수 n 이면 간격 = (W − w) ÷ (n − 1). 자리 하나면 가운데에만 둔다.\n * GRID 는 X(열)·Z(행)에 각각 같은 식을 쓴다. 크기가 바뀌어도 따라가는 규칙이 아니다(ADR-0051 4.1: 크기에 따른 간격은 미결).\n * Tier·자리 회전이 90° 단위가 아니거나 CUSTOM 이면 맞추지 않는다. 자리가 면에 다 들어가지 않으면 까닭을 돌려준다.\n */\nexport function fitTierToFace<L extends SlotLayout>(\n layout: L,\n tierNo: number,\n face: { width: number; depth: number } | undefined\n): { layout: L } | { reason: FitToFaceReason } {\n const tier = layout.tiers.find(t => t.tierNo === tierNo)\n if (!tier) return { reason: 'PATTERN_UNSUPPORTED' }\n if (!face || !(face.width > 0) || !(face.depth > 0)) return { reason: 'FACE_UNKNOWN' }\n const l = tier.layout\n if (l.pattern === 'CUSTOM') return { reason: 'PATTERN_UNSUPPORTED' }\n const quarter = (v: number) => (((v % 360) + 360) % 360) / 90\n const tierQuarter = quarter(tier.rotation ?? 0)\n if (!Number.isInteger(tierQuarter) || !Number.isInteger(quarter(l.slot.rotation ?? 0))) return { reason: 'ROTATION_UNSUPPORTED' }\n const turned = tierQuarter % 2 === 1\n // 면의 크기를 Tier 축으로: Tier 가 90° 돌면 Tier X 가 앵커 Z 를 따라간다\n const span = { x: turned ? face.depth : face.width, z: turned ? face.width : face.depth }\n const fp = rotatedFootprint(l.slot.allowed, l.slot.rotation ?? 0)\n const pitchFor = (room: number, width: number, n: number): number | FitToFaceReason => {\n if (width > room) return 'SLOT_WIDER_THAN_FACE'\n if (n <= 1) return NaN\n const pitch = (room - width) / (n - 1)\n return pitch < width ? 'SLOTS_DO_NOT_FIT' : round(pitch)\n }\n const next = clone(layout)\n const t = next.tiers.find(x => x.tierNo === tierNo)!\n t.position = { ...t.position, x: 0, z: 0 }\n const nl = t.layout\n if (nl.pattern === 'SINGLE') {\n if (fp.width > span.x || fp.depth > span.z) return { reason: 'SLOT_WIDER_THAN_FACE' }\n } else if (nl.pattern === 'ROW') {\n const alongX = nl.axis === 'X'\n const cross = alongX ? fp.depth > span.z : fp.width > span.x\n if (cross) return { reason: 'SLOT_WIDER_THAN_FACE' }\n const pitch = pitchFor(alongX ? span.x : span.z, alongX ? fp.width : fp.depth, nl.count)\n if (typeof pitch === 'string') return { reason: pitch }\n if (!Number.isNaN(pitch)) nl.pitch = pitch\n } else if (nl.pattern === 'GRID') {\n const px = pitchFor(span.x, fp.width, nl.cols)\n const pz = pitchFor(span.z, fp.depth, nl.rows)\n if (typeof px === 'string') return { reason: px }\n if (typeof pz === 'string') return { reason: pz }\n nl.pitch = { x: Number.isNaN(px) ? nl.pitch.x : px, z: Number.isNaN(pz) ? nl.pitch.z : pz }\n }\n return { layout: next }\n}\n\n// ── 실린 아이템 ────────────────────────────────────────────────────────────\n\n/** 호스트가 주는 실린 아이템 (읽기 전용). type 은 판정용 아이템 타입(표에 없으면 null), code 는 데이터가 준 값 */\nexport interface HeldItem {\n id: string\n slot?: string\n code?: string\n type?: string | null\n dims?: ItemDims | null\n}\n\n/** 편집 중인 레이아웃에서 실린 아이템이 어떻게 되는지. 판정만 하고 아이템을 옮기거나 지우지 않는다 */\nexport interface HeldItemStatus {\n item: HeldItem\n /** 그 자리가 이 레이아웃에 있는가. 없으면 미배치로 남는다 */\n placed: boolean\n /** 아이템 타입 표에 없는 코드 */\n unregistered: boolean\n verdict: FitVerdict\n reasons: FitReason[]\n}\n\n/** 같은 자리의 앞 아이템을 깔고 공통 판정(judgeItemFit)으로 본다. 모양을 자리에 맞춰 그리는 것은 판정이 아니다 */\nexport function heldItemStatuses(layout: SlotLayout | undefined, items: readonly HeldItem[] = []): HeldItemStatus[] {\n const below = new Map<string, HeldItem[]>()\n return items.map(item => {\n const address = addressOfSlotId(item.slot)\n let slot = null\n try {\n slot = address && layout ? resolveSlot(layout, address) : null\n } catch {\n slot = null\n }\n const occupants = below.get(item.slot ?? '') ?? []\n const fit = judgeItemFit(\n slot,\n { type: item.type ?? undefined, dims: item.dims ?? null },\n occupants.map(o => ({ type: o.type ?? undefined, dims: o.dims ?? null }))\n )\n below.set(item.slot ?? '', [...occupants, item])\n return { item, placed: !!slot, unregistered: item.type === null && !!item.code, verdict: fit.verdict, reasons: fit.reasons }\n })\n}\n"]}
|
package/dist/src/issue-text.d.ts
CHANGED
|
@@ -19,3 +19,16 @@ export declare const SPACE_TEXT: Record<SlotSpaceCode, string>;
|
|
|
19
19
|
export declare function spaceWarningText(warning: EditorIssue): string;
|
|
20
20
|
/** 문제 하나를 화면 문구로. 앞에 값의 위치를 붙인다 */
|
|
21
21
|
export declare function issueText(issue: EditorIssue): string;
|
|
22
|
+
/** 문제 하나를 화면 문구로, 위치 없이. 칸 옆에 보일 때 쓴다 */
|
|
23
|
+
export declare function issueMessage(issue: EditorIssue): string;
|
|
24
|
+
/** 문제가 어느 Tier(목록 순서)의 어느 칸인지. Tier 밖이거나 칸이 없으면 field 없음 */
|
|
25
|
+
export declare function issueField(issue: EditorIssue): {
|
|
26
|
+
tierIndex?: number;
|
|
27
|
+
field?: string;
|
|
28
|
+
};
|
|
29
|
+
/** 실린 아이템 판정 까닭의 기본 문구. 번역 키는 label.slot-layout.fit.{까닭} */
|
|
30
|
+
export declare const FIT_TEXT: Record<string, string>;
|
|
31
|
+
export declare function fitText(reason: string): string;
|
|
32
|
+
/** 면에 맞출 수 없는 까닭의 기본 문구. 번역 키는 label.slot-layout.fit-face.{까닭} */
|
|
33
|
+
export declare const FIT_FACE_TEXT: Record<string, string>;
|
|
34
|
+
export declare function fitFaceText(reason: string): string;
|
package/dist/src/issue-text.js
CHANGED
|
@@ -8,9 +8,9 @@ import { i18next } from '@operato/i18n';
|
|
|
8
8
|
/** things-scene 검증 코드의 기본 문구. 자리표시자가 중괄호 하나라서 문구에 중괄호 글자를 쓰지 않는다 */
|
|
9
9
|
export const ISSUE_TEXT = {
|
|
10
10
|
TIERS_REQUIRED: 'At least one tier is required',
|
|
11
|
-
MOUNT_INVALID: 'Must be
|
|
12
|
-
ANCHOR_REQUIRED: 'Choose
|
|
13
|
-
ANCHOR_UNKNOWN: 'This holder has no
|
|
11
|
+
MOUNT_INVALID: 'Must be on, in or hang',
|
|
12
|
+
ANCHOR_REQUIRED: 'Choose a face',
|
|
13
|
+
ANCHOR_UNKNOWN: 'This holder has no face {anchorId}',
|
|
14
14
|
TIER_NUMBER_DUPLICATE: 'Tier {tierNo} is used twice',
|
|
15
15
|
FINITE_NUMBER_REQUIRED: 'A number is required',
|
|
16
16
|
POSITIVE_NUMBER_REQUIRED: 'A number greater than 0 is required',
|
|
@@ -18,18 +18,18 @@ export const ISSUE_TEXT = {
|
|
|
18
18
|
ARRAY_REQUIRED: 'A list is required',
|
|
19
19
|
OBJECT_REQUIRED: 'The value is not a layout',
|
|
20
20
|
AXIS_INVALID: 'Must be X or Z',
|
|
21
|
-
PATTERN_INVALID: 'Must be
|
|
21
|
+
PATTERN_INVALID: 'Must be one, row, grid or listed',
|
|
22
22
|
SLOTS_REQUIRED: 'At least one slot is required',
|
|
23
23
|
SLOT_NUMBER_DUPLICATE: 'Slot {slotNo} is used twice',
|
|
24
24
|
NOT_PERSISTABLE: 'Cannot be saved ({value})',
|
|
25
|
-
|
|
25
|
+
ACCEPTS_EMPTY: 'The list of types it takes is empty; choose 「Any type」 to take any item'
|
|
26
26
|
};
|
|
27
27
|
export const t = (key, fallback, opts = {}) => i18next.t(`label.slot-layout.${key}`, { defaultValue: fallback, ...opts });
|
|
28
28
|
/** 자리 공간 경고의 기본 문구. 번역 키는 label.slot-layout.space.{코드} */
|
|
29
29
|
export const SPACE_TEXT = {
|
|
30
30
|
SLOT_OVERLAP: 'Tier {tier} slot {slot} overlaps tier {otherTier} slot {otherSlot}',
|
|
31
|
-
SLOT_OUTSIDE_ANCHOR: 'Tier {tier} slot {slot} reaches past its
|
|
32
|
-
ANCHOR_SPACE_UNKNOWN: '
|
|
31
|
+
SLOT_OUTSIDE_ANCHOR: 'Tier {tier} slot {slot} reaches past its face',
|
|
32
|
+
ANCHOR_SPACE_UNKNOWN: 'Face {anchorId} has no size: whether slots stay on it is not checked'
|
|
33
33
|
};
|
|
34
34
|
export function spaceWarningText(warning) {
|
|
35
35
|
var _a;
|
|
@@ -43,4 +43,75 @@ export function issueText(issue) {
|
|
|
43
43
|
const text = t(`issue.${issue.code}`, fallback, { ...issue.params, interpolation: { escapeValue: false } });
|
|
44
44
|
return issue.path ? `${issue.path}: ${text}` : text;
|
|
45
45
|
}
|
|
46
|
+
/** 문제 하나를 화면 문구로, 위치 없이. 칸 옆에 보일 때 쓴다 */
|
|
47
|
+
export function issueMessage(issue) {
|
|
48
|
+
var _a;
|
|
49
|
+
const fallback = (_a = ISSUE_TEXT[issue.code]) !== null && _a !== void 0 ? _a : issue.code;
|
|
50
|
+
return t(`issue.${issue.code}`, fallback, { ...issue.params, interpolation: { escapeValue: false } });
|
|
51
|
+
}
|
|
52
|
+
/** Tier 안의 경로 → 편집기 칸(data-field). 칸이 없는 경로(CUSTOM 자리 표 등)는 목록에 보인다 */
|
|
53
|
+
const FIELD_OF_PATH = {
|
|
54
|
+
anchorId: 'anchor',
|
|
55
|
+
mount: 'mount',
|
|
56
|
+
position: 'position-x',
|
|
57
|
+
'position.x': 'position-x',
|
|
58
|
+
'position.y': 'position-y',
|
|
59
|
+
'position.z': 'position-z',
|
|
60
|
+
rotation: 'tier-rotation',
|
|
61
|
+
layout: 'pattern',
|
|
62
|
+
'layout.pattern': 'pattern',
|
|
63
|
+
'layout.count': 'count',
|
|
64
|
+
'layout.pitch': 'pitch',
|
|
65
|
+
'layout.pitch.x': 'pitch-x',
|
|
66
|
+
'layout.pitch.z': 'pitch-z',
|
|
67
|
+
'layout.axis': 'axis',
|
|
68
|
+
'layout.origin': 'origin',
|
|
69
|
+
'layout.origin.x': 'origin-x',
|
|
70
|
+
'layout.origin.z': 'origin-z',
|
|
71
|
+
'layout.rows': 'rows',
|
|
72
|
+
'layout.cols': 'cols',
|
|
73
|
+
'layout.order': 'order',
|
|
74
|
+
'layout.slot': 'allowed-width',
|
|
75
|
+
'layout.slot.allowed': 'allowed-width',
|
|
76
|
+
'layout.slot.allowed.width': 'allowed-width',
|
|
77
|
+
'layout.slot.allowed.depth': 'allowed-depth',
|
|
78
|
+
'layout.slot.allowed.height': 'allowed-height',
|
|
79
|
+
'layout.slot.stackLimit': 'stack-limit',
|
|
80
|
+
'layout.slot.rotation': 'rotation',
|
|
81
|
+
'layout.slot.accepts': 'accepts'
|
|
82
|
+
};
|
|
83
|
+
/** 문제가 어느 Tier(목록 순서)의 어느 칸인지. Tier 밖이거나 칸이 없으면 field 없음 */
|
|
84
|
+
export function issueField(issue) {
|
|
85
|
+
var _a;
|
|
86
|
+
const m = /^tiers\[(\d+)\](?:\.(.+))?$/.exec((_a = issue.path) !== null && _a !== void 0 ? _a : '');
|
|
87
|
+
if (!m)
|
|
88
|
+
return {};
|
|
89
|
+
return { tierIndex: Number(m[1]), field: m[2] ? FIELD_OF_PATH[m[2]] : undefined };
|
|
90
|
+
}
|
|
91
|
+
/** 실린 아이템 판정 까닭의 기본 문구. 번역 키는 label.slot-layout.fit.{까닭} */
|
|
92
|
+
export const FIT_TEXT = {
|
|
93
|
+
SLOT_NOT_FOUND: 'Slot gone: kept, unplaced',
|
|
94
|
+
TYPE_NOT_ACCEPTED: 'Type not taken here',
|
|
95
|
+
WIDTH_EXCEEDED: 'Wider than the slot',
|
|
96
|
+
DEPTH_EXCEEDED: 'Deeper than the slot',
|
|
97
|
+
HEIGHT_EXCEEDED: 'Taller than the slot',
|
|
98
|
+
STACK_LIMIT_EXCEEDED: 'Over the stack limit',
|
|
99
|
+
SPEC_UNKNOWN: 'Size unknown: not judged'
|
|
100
|
+
};
|
|
101
|
+
export function fitText(reason) {
|
|
102
|
+
var _a;
|
|
103
|
+
return t(`fit.${reason}`, (_a = FIT_TEXT[reason]) !== null && _a !== void 0 ? _a : reason);
|
|
104
|
+
}
|
|
105
|
+
/** 면에 맞출 수 없는 까닭의 기본 문구. 번역 키는 label.slot-layout.fit-face.{까닭} */
|
|
106
|
+
export const FIT_FACE_TEXT = {
|
|
107
|
+
FACE_UNKNOWN: 'The face size is not known',
|
|
108
|
+
PATTERN_UNSUPPORTED: 'Not for listed slots',
|
|
109
|
+
ROTATION_UNSUPPORTED: 'Only for turns of 90°',
|
|
110
|
+
SLOT_WIDER_THAN_FACE: 'A slot is larger than the face',
|
|
111
|
+
SLOTS_DO_NOT_FIT: 'The slots do not fit on the face'
|
|
112
|
+
};
|
|
113
|
+
export function fitFaceText(reason) {
|
|
114
|
+
var _a;
|
|
115
|
+
return t(`fit-face.${reason}`, (_a = FIT_FACE_TEXT[reason]) !== null && _a !== void 0 ? _a : reason);
|
|
116
|
+
}
|
|
46
117
|
//# sourceMappingURL=issue-text.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"issue-text.js","sourceRoot":"","sources":["../../src/issue-text.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAUvC,oEAAoE;AACpE,MAAM,CAAC,MAAM,UAAU,GAAoC;IACzD,cAAc,EAAE,+BAA+B;IAC/C,aAAa,EAAE,wBAAwB;IACvC,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"issue-text.js","sourceRoot":"","sources":["../../src/issue-text.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * 검증 문제(코드·경로·매개변수)를 화면 문구로. 번역 키는 label.slot-layout.issue.{코드} 이고,\n * 번역이 없으면 아래 영어 기본 문구를 쓴다. 호스트 고유 코드는 호스트가 번역을 넣거나 issueText 옵션으로 바꾼다.\n */\n\nimport { i18next } from '@operato/i18n'\nimport type { LayoutIssueCode, SlotSpaceCode } from '@hatiolab/things-scene'\n\n/** 문제 하나. 호스트 고유 코드도 같은 모양으로 온다 */\nexport interface EditorIssue {\n code: string\n path: string\n params?: Record<string, string | number>\n}\n\n/** things-scene 검증 코드의 기본 문구. 자리표시자가 중괄호 하나라서 문구에 중괄호 글자를 쓰지 않는다 */\nexport const ISSUE_TEXT: Record<LayoutIssueCode, string> = {\n TIERS_REQUIRED: 'At least one tier is required',\n MOUNT_INVALID: 'Must be on, in or hang',\n ANCHOR_REQUIRED: 'Choose a face',\n ANCHOR_UNKNOWN: 'This holder has no face {anchorId}',\n TIER_NUMBER_DUPLICATE: 'Tier {tierNo} is used twice',\n FINITE_NUMBER_REQUIRED: 'A number is required',\n POSITIVE_NUMBER_REQUIRED: 'A number greater than 0 is required',\n POSITIVE_INTEGER_REQUIRED: 'A whole number of 1 or more is required',\n ARRAY_REQUIRED: 'A list is required',\n OBJECT_REQUIRED: 'The value is not a layout',\n AXIS_INVALID: 'Must be X or Z',\n PATTERN_INVALID: 'Must be one, row, grid or listed',\n SLOTS_REQUIRED: 'At least one slot is required',\n SLOT_NUMBER_DUPLICATE: 'Slot {slotNo} is used twice',\n NOT_PERSISTABLE: 'Cannot be saved ({value})',\n ACCEPTS_EMPTY: 'The list of types it takes is empty; choose 「Any type」 to take any item'\n}\n\nexport const t = (key: string, fallback: string, opts: Record<string, unknown> = {}) =>\n i18next.t(`label.slot-layout.${key}`, { defaultValue: fallback, ...opts })\n\n/** 자리 공간 경고의 기본 문구. 번역 키는 label.slot-layout.space.{코드} */\nexport const SPACE_TEXT: Record<SlotSpaceCode, string> = {\n SLOT_OVERLAP: 'Tier {tier} slot {slot} overlaps tier {otherTier} slot {otherSlot}',\n SLOT_OUTSIDE_ANCHOR: 'Tier {tier} slot {slot} reaches past its face',\n ANCHOR_SPACE_UNKNOWN: 'Face {anchorId} has no size: whether slots stay on it is not checked'\n}\n\nexport function spaceWarningText(warning: EditorIssue): string {\n const fallback = (SPACE_TEXT as Record<string, string>)[warning.code] ?? warning.code\n return t(`space.${warning.code}`, fallback, { ...warning.params, interpolation: { escapeValue: false } })\n}\n\n/** 문제 하나를 화면 문구로. 앞에 값의 위치를 붙인다 */\nexport function issueText(issue: EditorIssue): string {\n const fallback = (ISSUE_TEXT as Record<string, string>)[issue.code] ?? issue.code\n const text = t(`issue.${issue.code}`, fallback, { ...issue.params, interpolation: { escapeValue: false } })\n return issue.path ? `${issue.path}: ${text}` : text\n}\n\n/** 문제 하나를 화면 문구로, 위치 없이. 칸 옆에 보일 때 쓴다 */\nexport function issueMessage(issue: EditorIssue): string {\n const fallback = (ISSUE_TEXT as Record<string, string>)[issue.code] ?? issue.code\n return t(`issue.${issue.code}`, fallback, { ...issue.params, interpolation: { escapeValue: false } })\n}\n\n/** Tier 안의 경로 → 편집기 칸(data-field). 칸이 없는 경로(CUSTOM 자리 표 등)는 목록에 보인다 */\nconst FIELD_OF_PATH: Record<string, string> = {\n anchorId: 'anchor',\n mount: 'mount',\n position: 'position-x',\n 'position.x': 'position-x',\n 'position.y': 'position-y',\n 'position.z': 'position-z',\n rotation: 'tier-rotation',\n layout: 'pattern',\n 'layout.pattern': 'pattern',\n 'layout.count': 'count',\n 'layout.pitch': 'pitch',\n 'layout.pitch.x': 'pitch-x',\n 'layout.pitch.z': 'pitch-z',\n 'layout.axis': 'axis',\n 'layout.origin': 'origin',\n 'layout.origin.x': 'origin-x',\n 'layout.origin.z': 'origin-z',\n 'layout.rows': 'rows',\n 'layout.cols': 'cols',\n 'layout.order': 'order',\n 'layout.slot': 'allowed-width',\n 'layout.slot.allowed': 'allowed-width',\n 'layout.slot.allowed.width': 'allowed-width',\n 'layout.slot.allowed.depth': 'allowed-depth',\n 'layout.slot.allowed.height': 'allowed-height',\n 'layout.slot.stackLimit': 'stack-limit',\n 'layout.slot.rotation': 'rotation',\n 'layout.slot.accepts': 'accepts'\n}\n\n/** 문제가 어느 Tier(목록 순서)의 어느 칸인지. Tier 밖이거나 칸이 없으면 field 없음 */\nexport function issueField(issue: EditorIssue): { tierIndex?: number; field?: string } {\n const m = /^tiers\\[(\\d+)\\](?:\\.(.+))?$/.exec(issue.path ?? '')\n if (!m) return {}\n return { tierIndex: Number(m[1]), field: m[2] ? FIELD_OF_PATH[m[2]] : undefined }\n}\n\n/** 실린 아이템 판정 까닭의 기본 문구. 번역 키는 label.slot-layout.fit.{까닭} */\nexport const FIT_TEXT: Record<string, string> = {\n SLOT_NOT_FOUND: 'Slot gone: kept, unplaced',\n TYPE_NOT_ACCEPTED: 'Type not taken here',\n WIDTH_EXCEEDED: 'Wider than the slot',\n DEPTH_EXCEEDED: 'Deeper than the slot',\n HEIGHT_EXCEEDED: 'Taller than the slot',\n STACK_LIMIT_EXCEEDED: 'Over the stack limit',\n SPEC_UNKNOWN: 'Size unknown: not judged'\n}\n\nexport function fitText(reason: string): string {\n return t(`fit.${reason}`, FIT_TEXT[reason] ?? reason)\n}\n\n/** 면에 맞출 수 없는 까닭의 기본 문구. 번역 키는 label.slot-layout.fit-face.{까닭} */\nexport const FIT_FACE_TEXT: Record<string, string> = {\n FACE_UNKNOWN: 'The face size is not known',\n PATTERN_UNSUPPORTED: 'Not for listed slots',\n ROTATION_UNSUPPORTED: 'Only for turns of 90°',\n SLOT_WIDER_THAN_FACE: 'A slot is larger than the face',\n SLOTS_DO_NOT_FIT: 'The slots do not fit on the face'\n}\n\nexport function fitFaceText(reason: string): string {\n return t(`fit-face.${reason}`, FIT_FACE_TEXT[reason] ?? reason)\n}\n"]}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* 속성 패널용 자리 레이아웃 편집기 (편집기 타입 'slot-layout').
|
|
5
5
|
*
|
|
6
6
|
* 기존 속성 편집기 계약을 따른다: 값은 this.value, 바뀌면 change 를 보내고 호스트(속성 패널)가 컴포넌트에 반영한다.
|
|
7
|
+
* 편집기는 그림과 여러 구역을 가지므로 라벨 줄 아래 탭 전체 폭을 쓴다(fullwidth).
|
|
7
8
|
* 선택 컴포넌트가 필요한 옵션(값의 출처, 기본값, 상태 문구)은 'i-need-selected' 로 선택 목록을 받아
|
|
8
9
|
* 속성 정의의 property.options(selected) 가 만든다. 편집기는 컴포넌트를 직접 바꾸지 않는다.
|
|
9
10
|
*
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* 속성 패널용 자리 레이아웃 편집기 (편집기 타입 'slot-layout').
|
|
5
5
|
*
|
|
6
6
|
* 기존 속성 편집기 계약을 따른다: 값은 this.value, 바뀌면 change 를 보내고 호스트(속성 패널)가 컴포넌트에 반영한다.
|
|
7
|
+
* 편집기는 그림과 여러 구역을 가지므로 라벨 줄 아래 탭 전체 폭을 쓴다(fullwidth).
|
|
7
8
|
* 선택 컴포넌트가 필요한 옵션(값의 출처, 기본값, 상태 문구)은 'i-need-selected' 로 선택 목록을 받아
|
|
8
9
|
* 속성 정의의 property.options(selected) 가 만든다. 편집기는 컴포넌트를 직접 바꾸지 않는다.
|
|
9
10
|
*
|
|
@@ -36,6 +37,7 @@ let OxPropertyEditorSlotLayout = class OxPropertyEditorSlotLayout extends OxProp
|
|
|
36
37
|
return html `
|
|
37
38
|
<ox-slot-layout-editor
|
|
38
39
|
id="editor"
|
|
40
|
+
fullwidth
|
|
39
41
|
.value=${value}
|
|
40
42
|
.options=${this._options}
|
|
41
43
|
@slot-layout-use-default=${this._onUseDefault}
|