@fmsim/board 2.0.0-beta.11 → 2.0.0-beta.111

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.
Files changed (126) hide show
  1. package/custom-elements.json +6502 -2308
  2. package/demo/index-modeller.html +1 -1
  3. package/demo/index-player.html +2 -2
  4. package/demo/index-viewer.html +2 -2
  5. package/demo/index.html +2 -2
  6. package/dist/src/board-i18n.d.ts +37 -0
  7. package/dist/src/board-i18n.js +121 -0
  8. package/dist/src/board-i18n.js.map +1 -0
  9. package/dist/src/component/etc.js +36 -0
  10. package/dist/src/component/etc.js.map +1 -1
  11. package/dist/src/component/index.d.ts +1 -0
  12. package/dist/src/component/index.js +1 -0
  13. package/dist/src/component/index.js.map +1 -1
  14. package/dist/src/component/restful-property-editor-backdrop-faces.d.ts +50 -0
  15. package/dist/src/component/restful-property-editor-backdrop-faces.js +524 -0
  16. package/dist/src/component/restful-property-editor-backdrop-faces.js.map +1 -0
  17. package/dist/src/config/flow-replay.d.ts +14 -0
  18. package/dist/src/config/flow-replay.js +20 -0
  19. package/dist/src/config/flow-replay.js.map +1 -0
  20. package/dist/src/data-storage/board-model-cache.d.ts +13 -0
  21. package/dist/src/data-storage/board-model-cache.js +66 -0
  22. package/dist/src/data-storage/board-model-cache.js.map +1 -0
  23. package/dist/src/index.d.ts +5 -0
  24. package/dist/src/index.js +4 -0
  25. package/dist/src/index.js.map +1 -1
  26. package/dist/src/layers/mcs-mouse-event-handler.js +28 -3
  27. package/dist/src/layers/mcs-mouse-event-handler.js.map +1 -1
  28. package/dist/src/layers/movement-layer.d.ts +9 -6
  29. package/dist/src/layers/movement-layer.js +35 -10
  30. package/dist/src/layers/movement-layer.js.map +1 -1
  31. package/dist/src/modeller/bulk-create-dialog.d.ts +62 -0
  32. package/dist/src/modeller/bulk-create-dialog.js +595 -0
  33. package/dist/src/modeller/bulk-create-dialog.js.map +1 -0
  34. package/dist/src/modeller/bulk-create.d.ts +107 -0
  35. package/dist/src/modeller/bulk-create.js +201 -0
  36. package/dist/src/modeller/bulk-create.js.map +1 -0
  37. package/dist/src/modeller/component-toolbar/component-menu.d.ts +1 -1
  38. package/dist/src/modeller/component-toolbar/component-toolbar.d.ts +7 -1
  39. package/dist/src/modeller/component-toolbar/component-toolbar.js +18 -2
  40. package/dist/src/modeller/component-toolbar/component-toolbar.js.map +1 -1
  41. package/dist/src/modeller/edit-toolbar.d.ts +13 -0
  42. package/dist/src/modeller/edit-toolbar.js +177 -7
  43. package/dist/src/modeller/edit-toolbar.js.map +1 -1
  44. package/dist/src/modeller/property-sidebar/abstract-property.js +10 -2
  45. package/dist/src/modeller/property-sidebar/abstract-property.js.map +1 -1
  46. package/dist/src/modeller/property-sidebar/data-binding/data-binding.d.ts +1 -1
  47. package/dist/src/modeller/property-sidebar/effects/effects.d.ts +10 -1
  48. package/dist/src/modeller/property-sidebar/effects/effects.js +67 -1
  49. package/dist/src/modeller/property-sidebar/effects/effects.js.map +1 -1
  50. package/dist/src/modeller/property-sidebar/effects/event-handler-adapter.d.ts +44 -0
  51. package/dist/src/modeller/property-sidebar/effects/event-handler-adapter.js +70 -0
  52. package/dist/src/modeller/property-sidebar/effects/event-handler-adapter.js.map +1 -0
  53. package/dist/src/modeller/property-sidebar/effects/property-event.d.ts +13 -3
  54. package/dist/src/modeller/property-sidebar/effects/property-event.js +20 -11
  55. package/dist/src/modeller/property-sidebar/effects/property-event.js.map +1 -1
  56. package/dist/src/modeller/property-sidebar/inspector/inspector-controller.d.ts +66 -0
  57. package/dist/src/modeller/property-sidebar/inspector/inspector-controller.js +288 -0
  58. package/dist/src/modeller/property-sidebar/inspector/inspector-controller.js.map +1 -0
  59. package/dist/src/modeller/property-sidebar/inspector/inspector-group-view.d.ts +15 -0
  60. package/dist/src/modeller/property-sidebar/inspector/inspector-group-view.js +67 -0
  61. package/dist/src/modeller/property-sidebar/inspector/inspector-group-view.js.map +1 -0
  62. package/dist/src/modeller/property-sidebar/inspector/inspector-highlight.d.ts +7 -0
  63. package/dist/src/modeller/property-sidebar/inspector/inspector-highlight.js +25 -0
  64. package/dist/src/modeller/property-sidebar/inspector/inspector-highlight.js.map +1 -0
  65. package/dist/src/modeller/property-sidebar/inspector/inspector-model.d.ts +44 -0
  66. package/dist/src/modeller/property-sidebar/inspector/inspector-model.js +72 -0
  67. package/dist/src/modeller/property-sidebar/inspector/inspector-model.js.map +1 -0
  68. package/dist/src/modeller/property-sidebar/inspector/inspector-tree-view.d.ts +18 -0
  69. package/dist/src/modeller/property-sidebar/inspector/inspector-tree-view.js +61 -0
  70. package/dist/src/modeller/property-sidebar/inspector/inspector-tree-view.js.map +1 -0
  71. package/dist/src/modeller/property-sidebar/inspector/inspector.d.ts +17 -12
  72. package/dist/src/modeller/property-sidebar/inspector/inspector.js +287 -151
  73. package/dist/src/modeller/property-sidebar/inspector/inspector.js.map +1 -1
  74. package/dist/src/modeller/property-sidebar/property-sidebar.d.ts +1 -1
  75. package/dist/src/modeller/property-sidebar/shapes/ox-input-entity-id.d.ts +75 -0
  76. package/dist/src/modeller/property-sidebar/shapes/ox-input-entity-id.js +355 -0
  77. package/dist/src/modeller/property-sidebar/shapes/ox-input-entity-id.js.map +1 -0
  78. package/dist/src/modeller/property-sidebar/shapes/shapes.d.ts +22 -1
  79. package/dist/src/modeller/property-sidebar/shapes/shapes.js +110 -2
  80. package/dist/src/modeller/property-sidebar/shapes/shapes.js.map +1 -1
  81. package/dist/src/modeller/property-sidebar/specifics/parent-object.d.ts +29 -0
  82. package/dist/src/modeller/property-sidebar/specifics/parent-object.js +74 -0
  83. package/dist/src/modeller/property-sidebar/specifics/parent-object.js.map +1 -0
  84. package/dist/src/modeller/property-sidebar/specifics/specifics.d.ts +27 -1
  85. package/dist/src/modeller/property-sidebar/specifics/specifics.js +95 -3
  86. package/dist/src/modeller/property-sidebar/specifics/specifics.js.map +1 -1
  87. package/dist/src/modeller/property-sidebar/styles/styles.d.ts +1 -1
  88. package/dist/src/modeller/scene-viewer/ox-scene-layer.d.ts +4 -3
  89. package/dist/src/modeller/scene-viewer/ox-scene-layer.js +2 -1
  90. package/dist/src/modeller/scene-viewer/ox-scene-layer.js.map +1 -1
  91. package/dist/src/modeller/scene-viewer/ox-scene-viewer.d.ts +2 -1
  92. package/dist/src/modeller/scene-viewer/ox-scene-viewer.js +5 -1
  93. package/dist/src/modeller/scene-viewer/ox-scene-viewer.js.map +1 -1
  94. package/dist/src/ox-board-component-info.d.ts +1 -0
  95. package/dist/src/ox-board-component-info.js +42 -2
  96. package/dist/src/ox-board-component-info.js.map +1 -1
  97. package/dist/src/ox-board-modeller.d.ts +6 -3
  98. package/dist/src/ox-board-modeller.js +21 -1
  99. package/dist/src/ox-board-modeller.js.map +1 -1
  100. package/dist/src/ox-board-viewer.d.ts +117 -13
  101. package/dist/src/ox-board-viewer.js +688 -102
  102. package/dist/src/ox-board-viewer.js.map +1 -1
  103. package/dist/src/ox-editor-board-selector.d.ts +3 -0
  104. package/dist/src/ox-editor-board-selector.js +40 -9
  105. package/dist/src/ox-editor-board-selector.js.map +1 -1
  106. package/dist/src/ox-playback-controls.d.ts +115 -18
  107. package/dist/src/ox-playback-controls.js +744 -167
  108. package/dist/src/ox-playback-controls.js.map +1 -1
  109. package/dist/src/playback/carrier-tracking-store.d.ts +51 -0
  110. package/dist/src/playback/carrier-tracking-store.js +115 -0
  111. package/dist/src/playback/carrier-tracking-store.js.map +1 -0
  112. package/dist/src/playback/playback-engine.d.ts +106 -0
  113. package/dist/src/playback/playback-engine.js +395 -0
  114. package/dist/src/playback/playback-engine.js.map +1 -0
  115. package/dist/src/scene-monitor-renderer.d.ts +10 -0
  116. package/dist/src/scene-monitor-renderer.js +226 -0
  117. package/dist/src/scene-monitor-renderer.js.map +1 -0
  118. package/dist/src/selector/ox-board-selector.js +14 -2
  119. package/dist/src/selector/ox-board-selector.js.map +1 -1
  120. package/dist/tsconfig.tsbuildinfo +1 -1
  121. package/icons/components/text.png +0 -0
  122. package/icons/fab/mode-2d.svg +25 -0
  123. package/icons/fab/mode-3d.svg +25 -0
  124. package/icons/fab/playback-play.svg +49 -0
  125. package/icons/fab/playback-stop.svg +50 -0
  126. package/package.json +8 -4
@@ -0,0 +1,595 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ *
4
+ * 멀티 컴포넌트 생성 dialog — 템플릿 + 영역 + 옵션 입력 → OK 시 콜백 호출.
5
+ */
6
+ import { __decorate } from "tslib";
7
+ import { LitElement, css, html } from 'lit';
8
+ import { customElement, property, query, state } from 'lit/decorators.js';
9
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements';
10
+ import { OxInputEntityId } from './property-sidebar/shapes/ox-input-entity-id';
11
+ import { generateBulkComponents, findIdConflicts, inferIdPattern, nextAvailableStart, deriveGridShape } from './bulk-create.js';
12
+ let BulkCreateDialog = class BulkCreateDialog extends ScopedElementsMixin(LitElement) {
13
+ constructor() {
14
+ super(...arguments);
15
+ this.existingIds = new Set();
16
+ /** parentObject 필드 노출 여부 (template 이 ParentObjectMixin 컴포넌트일 때). */
17
+ this.hasParentObject = false;
18
+ /** parentObject 후보 id 리스트 (template 타입 기준, toolbar 에서 계산해 주입). */
19
+ this.parentObjectItems = [];
20
+ this.pattern = 'grid';
21
+ this.parentObjectId = '';
22
+ this.count = 12;
23
+ this.rows = undefined;
24
+ this.cols = undefined;
25
+ this.idPattern = '{i}';
26
+ this.idStart = 1;
27
+ this.idPadding = 0;
28
+ this.conflictPolicy = 'skip';
29
+ this.onOk = () => {
30
+ const models = this.effectiveModels;
31
+ if (models.length === 0)
32
+ return;
33
+ this.onConfirm(models);
34
+ };
35
+ this.onCancelClick = () => {
36
+ var _a;
37
+ (_a = this.onCancel) === null || _a === void 0 ? void 0 : _a.call(this);
38
+ };
39
+ }
40
+ static get scopedElements() {
41
+ return {
42
+ 'ox-input-entity-id': OxInputEntityId
43
+ };
44
+ }
45
+ connectedCallback() {
46
+ var _a, _b, _c;
47
+ super.connectedCallback();
48
+ // OxPopup wrapper (ox-popup) 의 흰색 배경 + box-shadow 가 dialog 라운드 코너
49
+ // 밖으로 비쳐서 4 꼭지점에 흰색 사각형이 보임. 부모를 투명/그림자 없이 만들어
50
+ // dialog 자체의 라운드 + 그림자만 보이게.
51
+ const parent = this.parentElement;
52
+ if (parent) {
53
+ parent.style.background = 'transparent';
54
+ parent.style.boxShadow = 'none';
55
+ parent.style.border = 'none';
56
+ }
57
+ this.area = { ...this.initialArea };
58
+ // 1) 영역 종횡비로 레이아웃 기본 선택:
59
+ // 너비 ≫ 높이 → line-h (가로), 높이 ≫ 너비 → line-v (세로), 비슷하면 grid.
60
+ const w = Math.max(this.area.width, 1);
61
+ const h = Math.max(this.area.height, 1);
62
+ const aspect = w / h;
63
+ if (aspect >= 2.5)
64
+ this.pattern = 'line-h';
65
+ else if (aspect <= 0.4)
66
+ this.pattern = 'line-v';
67
+ else
68
+ this.pattern = 'grid';
69
+ // 2) 템플릿 id 로부터 ID 패턴 / padding 자동 추론.
70
+ const inferred = inferIdPattern((_a = this.template) === null || _a === void 0 ? void 0 : _a.id);
71
+ this.idPattern = inferred.pattern;
72
+ this.idPadding = inferred.padding;
73
+ // 3) 시작 번호 = existingIds 에 패턴 매칭되는 *최대 번호 + 1*.
74
+ // 반복 bulk-paste 시 이전 batch 의 끝 다음으로 자동 advance.
75
+ // 매칭 ID 가 없으면 inferIdPattern 의 fallback (= template num+1) 사용.
76
+ this.idStart = nextAvailableStart(inferred.pattern, this.existingIds, inferred.start);
77
+ // 4) 복사 대상(template)이 이미 parentObjectId 를 가지면 그것을 초기값으로.
78
+ this.parentObjectId = String((_c = (_b = this.template) === null || _b === void 0 ? void 0 : _b.parentObjectId) !== null && _c !== void 0 ? _c : '');
79
+ }
80
+ /** rows/cols 가 미입력 (undefined) 이면 영역 종횡비 + count 로 자동 계산해 표시. */
81
+ get displayRows() {
82
+ if (this.rows != null)
83
+ return this.rows;
84
+ return deriveGridShape(this.count, this.area, undefined, this.cols).rows;
85
+ }
86
+ get displayCols() {
87
+ if (this.cols != null)
88
+ return this.cols;
89
+ return deriveGridShape(this.count, this.area, this.rows, undefined).cols;
90
+ }
91
+ firstUpdated() {
92
+ var _a, _b;
93
+ (_a = this.countInput) === null || _a === void 0 ? void 0 : _a.focus();
94
+ (_b = this.countInput) === null || _b === void 0 ? void 0 : _b.select();
95
+ }
96
+ updateArea(field, value) {
97
+ this.area = { ...this.area, [field]: value };
98
+ }
99
+ get previewModels() {
100
+ var _a, _b;
101
+ if (!this.template)
102
+ return [];
103
+ return generateBulkComponents({
104
+ template: this.template,
105
+ area: this.area,
106
+ pattern: this.pattern,
107
+ count: this.count,
108
+ rows: this.rows,
109
+ cols: this.cols,
110
+ idPattern: this.idPattern.replace(/\{type\}/g, String((_b = (_a = this.template) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : 'C')),
111
+ idStart: this.idStart,
112
+ idPadding: this.idPadding,
113
+ // 선택 시 각 생성 컴포넌트에 parentObjectId 를 병합 (도메인 특수값은 여기서만).
114
+ extraState: this.parentObjectId ? { parentObjectId: this.parentObjectId } : undefined
115
+ });
116
+ }
117
+ get conflicts() {
118
+ return findIdConflicts(this.previewModels, this.existingIds);
119
+ }
120
+ /** 충돌 정책 적용 후 실제 생성될 model 배열. */
121
+ get effectiveModels() {
122
+ const all = this.previewModels;
123
+ if (this.conflictPolicy === 'skip' && this.existingIds.size > 0) {
124
+ return all.filter(m => !this.existingIds.has(m.id));
125
+ }
126
+ return all;
127
+ }
128
+ render() {
129
+ var _a, _b, _c, _d;
130
+ const tplType = (_b = (_a = this.template) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : '?';
131
+ const tplId = (_d = (_c = this.template) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : '?';
132
+ const models = this.previewModels;
133
+ return html `
134
+ <h3>Bulk Paste — ${tplType}</h3>
135
+
136
+ <div class="row">
137
+ <label>템플릿</label>
138
+ <span style="color:#b6d4fe;">${tplId} <span style="color:rgba(255,255,255,0.4);">(${tplType})</span></span>
139
+ </div>
140
+
141
+ <div class="row">
142
+ <label>레이아웃</label>
143
+ <div class="pattern-picker">
144
+ <button
145
+ type="button"
146
+ class="pattern-btn ${this.pattern === 'grid' ? 'active' : ''}"
147
+ @click=${() => (this.pattern = 'grid')}
148
+ title="격자"
149
+ >
150
+ <svg viewBox="0 0 24 24">
151
+ <circle cx="6" cy="6" r="1.6" /><circle cx="12" cy="6" r="1.6" /><circle cx="18" cy="6" r="1.6" />
152
+ <circle cx="6" cy="12" r="1.6" /><circle cx="12" cy="12" r="1.6" /><circle cx="18" cy="12" r="1.6" />
153
+ <circle cx="6" cy="18" r="1.6" /><circle cx="12" cy="18" r="1.6" /><circle cx="18" cy="18" r="1.6" />
154
+ </svg>
155
+ <span>격자</span>
156
+ </button>
157
+ <button
158
+ type="button"
159
+ class="pattern-btn ${this.pattern === 'line-h' ? 'active' : ''}"
160
+ @click=${() => (this.pattern = 'line-h')}
161
+ title="가로 1줄"
162
+ >
163
+ <svg viewBox="0 0 24 24">
164
+ <circle cx="3" cy="12" r="1.6" /><circle cx="8" cy="12" r="1.6" /><circle cx="13" cy="12" r="1.6" />
165
+ <circle cx="18" cy="12" r="1.6" /><circle cx="22" cy="12" r="1.4" />
166
+ </svg>
167
+ <span>가로</span>
168
+ </button>
169
+ <button
170
+ type="button"
171
+ class="pattern-btn ${this.pattern === 'line-v' ? 'active' : ''}"
172
+ @click=${() => (this.pattern = 'line-v')}
173
+ title="세로 1줄"
174
+ >
175
+ <svg viewBox="0 0 24 24">
176
+ <circle cx="12" cy="3" r="1.6" /><circle cx="12" cy="8" r="1.6" /><circle cx="12" cy="13" r="1.6" />
177
+ <circle cx="12" cy="18" r="1.6" /><circle cx="12" cy="22" r="1.4" />
178
+ </svg>
179
+ <span>세로</span>
180
+ </button>
181
+ </div>
182
+ </div>
183
+
184
+ <div class="row">
185
+ <label>개수</label>
186
+ <input
187
+ id="count-input"
188
+ type="number"
189
+ min="1"
190
+ .value=${String(this.count)}
191
+ @input=${(e) => (this.count = Math.max(1, +e.target.value))}
192
+ />
193
+ </div>
194
+
195
+ ${this.pattern === 'grid'
196
+ ? html `
197
+ <div class="row">
198
+ <label>행 × 열</label>
199
+ <div class="grid-shape">
200
+ <input
201
+ type="number"
202
+ min="1"
203
+ .value=${String(this.displayRows)}
204
+ @input=${(e) => {
205
+ const v = e.target.value;
206
+ this.rows = v ? Math.max(1, +v) : undefined;
207
+ }}
208
+ />
209
+ <span>×</span>
210
+ <input
211
+ type="number"
212
+ min="1"
213
+ .value=${String(this.displayCols)}
214
+ @input=${(e) => {
215
+ const v = e.target.value;
216
+ this.cols = v ? Math.max(1, +v) : undefined;
217
+ }}
218
+ />
219
+ </div>
220
+ </div>
221
+ `
222
+ : ''}
223
+
224
+ <div class="row">
225
+ <label>ID 패턴</label>
226
+ <input
227
+ type="text"
228
+ .value=${this.idPattern}
229
+ @input=${(e) => (this.idPattern = e.target.value)}
230
+ />
231
+ </div>
232
+ <div class="row">
233
+ <span></span>
234
+ <div class="hint">placeholder: <code>{i}</code> 일련번호, <code>{r}</code> 행, <code>{c}</code> 열 (grid)</div>
235
+ </div>
236
+
237
+ <div class="row">
238
+ <label>시작 번호</label>
239
+ <input
240
+ type="number"
241
+ .value=${String(this.idStart)}
242
+ @input=${(e) => (this.idStart = +e.target.value)}
243
+ />
244
+ </div>
245
+
246
+ <div class="row">
247
+ <label>Zero-pad 자릿수</label>
248
+ <input
249
+ type="number"
250
+ min="0"
251
+ .value=${String(this.idPadding)}
252
+ @input=${(e) => (this.idPadding = Math.max(0, +e.target.value))}
253
+ />
254
+ </div>
255
+
256
+ ${this.hasParentObject
257
+ ? html `
258
+ <div class="row">
259
+ <label>상위 객체</label>
260
+ <ox-input-entity-id
261
+ .localItems=${this.parentObjectItems}
262
+ .value=${this.parentObjectId}
263
+ placeholder="상위 객체 id (검색)"
264
+ @change=${(e) => (this.parentObjectId = e.target.value)}
265
+ ></ox-input-entity-id>
266
+ </div>
267
+ `
268
+ : ''}
269
+
270
+ <div class="preview">
271
+ ${models.length > 0
272
+ ? html `총 ${models.length}개 — 예: <code>${models[0].id}</code> ... <code>${models[models.length - 1].id}</code>`
273
+ : html `<span style="color:#ff8080;">생성될 컴포넌트 없음</span>`}
274
+ </div>
275
+
276
+ ${this.conflicts.length > 0
277
+ ? html `
278
+ <div class="conflict">
279
+ <strong>ID 충돌 ${this.conflicts.length}개</strong>
280
+ <code>${this.conflicts.slice(0, 5).join(', ')}${this.conflicts.length > 5 ? ', ...' : ''}</code>
281
+ <div class="conflict-hint" style="margin-top:6px;">
282
+ <div class="policy-radios">
283
+ <label>
284
+ <input
285
+ type="radio"
286
+ name="conflict-policy"
287
+ value="skip"
288
+ .checked=${this.conflictPolicy === 'skip'}
289
+ @change=${() => (this.conflictPolicy = 'skip')}
290
+ />
291
+ 충돌 ID 스킵 (기본)
292
+ </label>
293
+ <label>
294
+ <input
295
+ type="radio"
296
+ name="conflict-policy"
297
+ value="duplicate"
298
+ .checked=${this.conflictPolicy === 'duplicate'}
299
+ @change=${() => (this.conflictPolicy = 'duplicate')}
300
+ />
301
+ 중복 그대로 생성
302
+ </label>
303
+ </div>
304
+ </div>
305
+ </div>
306
+ `
307
+ : ''}
308
+
309
+ <div class="actions">
310
+ <button @click=${this.onCancelClick}>취소</button>
311
+ <button
312
+ class="primary"
313
+ @click=${this.onOk}
314
+ ?disabled=${this.effectiveModels.length === 0}
315
+ >
316
+ 생성 (${this.effectiveModels.length}${this.conflicts.length > 0 && this.conflictPolicy === 'skip'
317
+ ? html `<span style="opacity:0.6;"> / ${models.length}</span>`
318
+ : ''})
319
+ </button>
320
+ </div>
321
+ `;
322
+ }
323
+ };
324
+ BulkCreateDialog.styles = css `
325
+ :host {
326
+ display: block;
327
+ padding: 16px 20px;
328
+ min-width: 340px;
329
+ max-width: 480px;
330
+ background: rgba(30, 30, 30, 0.94);
331
+ border: 1px solid rgba(255, 255, 255, 0.08);
332
+ border-radius: 10px;
333
+ backdrop-filter: blur(8px);
334
+ -webkit-backdrop-filter: blur(8px);
335
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
336
+ color: rgba(255, 255, 255, 0.85);
337
+ font: 12px/1.6 -apple-system, BlinkMacSystemFont, sans-serif;
338
+ }
339
+ h3 {
340
+ margin: 0 0 12px;
341
+ font-size: 13px;
342
+ font-weight: 600;
343
+ color: #4a9eff;
344
+ }
345
+ .row {
346
+ display: grid;
347
+ grid-template-columns: 100px 1fr;
348
+ align-items: center;
349
+ gap: 8px;
350
+ margin: 6px 0;
351
+ }
352
+ label {
353
+ font-size: 12px;
354
+ color: rgba(255, 255, 255, 0.55);
355
+ }
356
+ input,
357
+ select {
358
+ padding: 4px 6px;
359
+ font: inherit;
360
+ font-size: 12px;
361
+ background: rgba(255, 255, 255, 0.06);
362
+ border: 1px solid rgba(255, 255, 255, 0.12);
363
+ border-radius: 3px;
364
+ width: 100%;
365
+ box-sizing: border-box;
366
+ color: rgba(255, 255, 255, 0.9);
367
+ outline: none;
368
+ }
369
+ input:focus,
370
+ select:focus {
371
+ border-color: #4a9eff;
372
+ background: rgba(74, 158, 255, 0.08);
373
+ }
374
+ /* parentObject 입력(ox-input-entity-id)의 내부 input 을 dialog 의 다른 input 과 통일. */
375
+ ox-input-entity-id::part(input) {
376
+ padding: 4px 6px;
377
+ font: inherit;
378
+ font-size: 12px;
379
+ background: rgba(255, 255, 255, 0.06);
380
+ border: 1px solid rgba(255, 255, 255, 0.12);
381
+ border-radius: 3px;
382
+ color: rgba(255, 255, 255, 0.9);
383
+ outline: none;
384
+ }
385
+ ox-input-entity-id::part(input):focus {
386
+ border-color: #4a9eff;
387
+ background: rgba(74, 158, 255, 0.08);
388
+ }
389
+ /* 검색 드롭다운(shadow 내부)은 md-sys-color 변수 + 상속 color 로 다크 테마 통일.
390
+ (변수는 shadow 경계를 관통하고, .item 은 color 를 host 로부터 상속받는다.) */
391
+ ox-input-entity-id {
392
+ color: rgba(255, 255, 255, 0.9);
393
+ --md-sys-color-surface: rgb(30, 30, 30);
394
+ --md-sys-color-surface-variant: rgb(45, 45, 45);
395
+ --md-sys-color-outline-variant: rgba(255, 255, 255, 0.15);
396
+ --md-sys-color-secondary-container: rgba(74, 158, 255, 0.25);
397
+ --md-sys-color-on-surface-variant: rgba(255, 255, 255, 0.55);
398
+ }
399
+ select option {
400
+ background: rgb(30, 30, 30);
401
+ color: rgba(255, 255, 255, 0.9);
402
+ }
403
+ .grid-shape {
404
+ display: grid;
405
+ grid-template-columns: 1fr 20px 1fr;
406
+ gap: 4px;
407
+ align-items: center;
408
+ }
409
+ .grid-shape > span {
410
+ text-align: center;
411
+ color: rgba(255, 255, 255, 0.35);
412
+ }
413
+ .pattern-picker {
414
+ display: flex;
415
+ gap: 6px;
416
+ }
417
+ .pattern-btn {
418
+ flex: 1;
419
+ display: flex;
420
+ flex-direction: column;
421
+ align-items: center;
422
+ gap: 2px;
423
+ padding: 6px 4px;
424
+ background: rgba(255, 255, 255, 0.04);
425
+ border: 1px solid rgba(255, 255, 255, 0.1);
426
+ border-radius: 4px;
427
+ cursor: pointer;
428
+ color: rgba(255, 255, 255, 0.6);
429
+ transition: background 0.1s, border-color 0.1s;
430
+ }
431
+ .pattern-btn:hover {
432
+ background: rgba(255, 255, 255, 0.08);
433
+ }
434
+ .pattern-btn.active {
435
+ border-color: #4a9eff;
436
+ background: rgba(74, 158, 255, 0.15);
437
+ color: rgba(255, 255, 255, 0.95);
438
+ }
439
+ .pattern-btn svg {
440
+ width: 28px;
441
+ height: 28px;
442
+ }
443
+ .pattern-btn svg circle {
444
+ fill: currentColor;
445
+ }
446
+ .pattern-btn span {
447
+ font-size: 10.5px;
448
+ }
449
+ .policy-radios {
450
+ display: flex;
451
+ gap: 12px;
452
+ align-items: center;
453
+ }
454
+ .policy-radios label {
455
+ display: flex;
456
+ align-items: center;
457
+ gap: 4px;
458
+ cursor: pointer;
459
+ color: rgba(255, 255, 255, 0.75);
460
+ }
461
+ .policy-radios input[type='radio'] {
462
+ width: auto;
463
+ accent-color: #4a9eff;
464
+ margin: 0;
465
+ }
466
+ code {
467
+ font-family: ui-monospace, SFMono-Regular, monospace;
468
+ background: rgba(255, 255, 255, 0.08);
469
+ padding: 1px 4px;
470
+ border-radius: 3px;
471
+ color: #b6d4fe;
472
+ font-size: 11px;
473
+ }
474
+ .hint {
475
+ font-size: 11px;
476
+ color: rgba(255, 255, 255, 0.4);
477
+ margin-top: 2px;
478
+ grid-column: 2;
479
+ }
480
+ .preview {
481
+ font-size: 11.5px;
482
+ color: rgba(255, 255, 255, 0.65);
483
+ margin-top: 10px;
484
+ padding: 6px 8px;
485
+ background: rgba(255, 255, 255, 0.04);
486
+ border-radius: 4px;
487
+ }
488
+ .conflict {
489
+ margin-top: 8px;
490
+ padding: 8px 10px;
491
+ background: rgba(255, 100, 100, 0.12);
492
+ border: 1px solid rgba(255, 100, 100, 0.3);
493
+ border-radius: 4px;
494
+ color: #ffb3b3;
495
+ font-size: 11.5px;
496
+ }
497
+ .conflict strong {
498
+ color: #ff8080;
499
+ }
500
+ .conflict-hint {
501
+ margin-top: 4px;
502
+ color: rgba(255, 255, 255, 0.45);
503
+ font-size: 11px;
504
+ }
505
+ .actions {
506
+ margin-top: 16px;
507
+ display: flex;
508
+ justify-content: flex-end;
509
+ gap: 8px;
510
+ }
511
+ button {
512
+ padding: 5px 14px;
513
+ font: inherit;
514
+ font-size: 12px;
515
+ border: 1px solid rgba(255, 255, 255, 0.15);
516
+ background: rgba(255, 255, 255, 0.06);
517
+ color: rgba(255, 255, 255, 0.85);
518
+ cursor: pointer;
519
+ border-radius: 4px;
520
+ }
521
+ button:hover:not(:disabled) {
522
+ background: rgba(255, 255, 255, 0.1);
523
+ }
524
+ button:disabled {
525
+ opacity: 0.4;
526
+ cursor: not-allowed;
527
+ }
528
+ button.primary {
529
+ background: #4a9eff;
530
+ color: white;
531
+ border-color: #4a9eff;
532
+ }
533
+ button.primary:hover:not(:disabled) {
534
+ background: #6db1ff;
535
+ }
536
+ `;
537
+ __decorate([
538
+ property({ type: Object })
539
+ ], BulkCreateDialog.prototype, "template", void 0);
540
+ __decorate([
541
+ property({ type: Object })
542
+ ], BulkCreateDialog.prototype, "initialArea", void 0);
543
+ __decorate([
544
+ property({ attribute: false })
545
+ ], BulkCreateDialog.prototype, "existingIds", void 0);
546
+ __decorate([
547
+ property({ attribute: false })
548
+ ], BulkCreateDialog.prototype, "onConfirm", void 0);
549
+ __decorate([
550
+ property({ attribute: false })
551
+ ], BulkCreateDialog.prototype, "onCancel", void 0);
552
+ __decorate([
553
+ property({ attribute: false })
554
+ ], BulkCreateDialog.prototype, "hasParentObject", void 0);
555
+ __decorate([
556
+ property({ attribute: false })
557
+ ], BulkCreateDialog.prototype, "parentObjectItems", void 0);
558
+ __decorate([
559
+ state()
560
+ ], BulkCreateDialog.prototype, "pattern", void 0);
561
+ __decorate([
562
+ state()
563
+ ], BulkCreateDialog.prototype, "parentObjectId", void 0);
564
+ __decorate([
565
+ state()
566
+ ], BulkCreateDialog.prototype, "count", void 0);
567
+ __decorate([
568
+ state()
569
+ ], BulkCreateDialog.prototype, "rows", void 0);
570
+ __decorate([
571
+ state()
572
+ ], BulkCreateDialog.prototype, "cols", void 0);
573
+ __decorate([
574
+ state()
575
+ ], BulkCreateDialog.prototype, "idPattern", void 0);
576
+ __decorate([
577
+ state()
578
+ ], BulkCreateDialog.prototype, "idStart", void 0);
579
+ __decorate([
580
+ state()
581
+ ], BulkCreateDialog.prototype, "idPadding", void 0);
582
+ __decorate([
583
+ state()
584
+ ], BulkCreateDialog.prototype, "area", void 0);
585
+ __decorate([
586
+ state()
587
+ ], BulkCreateDialog.prototype, "conflictPolicy", void 0);
588
+ __decorate([
589
+ query('#count-input')
590
+ ], BulkCreateDialog.prototype, "countInput", void 0);
591
+ BulkCreateDialog = __decorate([
592
+ customElement('bulk-create-dialog')
593
+ ], BulkCreateDialog);
594
+ export { BulkCreateDialog };
595
+ //# sourceMappingURL=bulk-create-dialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bulk-create-dialog.js","sourceRoot":"","sources":["../../../src/modeller/bulk-create-dialog.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * 멀티 컴포넌트 생성 dialog — 템플릿 + 영역 + 옵션 입력 → OK 시 콜백 호출.\n */\n\nimport { LitElement, css, html } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\n\nimport { OxInputEntityId } from './property-sidebar/shapes/ox-input-entity-id'\n\nimport {\n generateBulkComponents,\n findIdConflicts,\n inferIdPattern,\n nextAvailableStart,\n deriveGridShape,\n type BulkCreateOptions,\n type Pattern,\n type Area\n} from './bulk-create.js'\n\nexport type ConflictPolicy = 'skip' | 'duplicate'\n\nexport interface BulkCreateDialogProps {\n /** 템플릿 컴포넌트 state (read-only 표시용). */\n template: Record<string, any>\n /** 초기 영역 (보드 좌표). 사용자가 dialog 내에서 조정 가능. */\n initialArea: Area\n /** 기존 scene 의 모든 ID — 충돌 검사. */\n existingIds?: Set<string>\n /** OK 클릭 시 콜백 — 생성할 model 배열 전달. */\n onConfirm: (models: ReturnType<typeof generateBulkComponents>) => void\n /** 취소 / 닫기 콜백. */\n onCancel?: () => void\n}\n\n@customElement('bulk-create-dialog')\nexport class BulkCreateDialog extends ScopedElementsMixin(LitElement) {\n static get scopedElements() {\n return {\n 'ox-input-entity-id': OxInputEntityId\n }\n }\n\n static styles = css`\n :host {\n display: block;\n padding: 16px 20px;\n min-width: 340px;\n max-width: 480px;\n background: rgba(30, 30, 30, 0.94);\n border: 1px solid rgba(255, 255, 255, 0.08);\n border-radius: 10px;\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);\n color: rgba(255, 255, 255, 0.85);\n font: 12px/1.6 -apple-system, BlinkMacSystemFont, sans-serif;\n }\n h3 {\n margin: 0 0 12px;\n font-size: 13px;\n font-weight: 600;\n color: #4a9eff;\n }\n .row {\n display: grid;\n grid-template-columns: 100px 1fr;\n align-items: center;\n gap: 8px;\n margin: 6px 0;\n }\n label {\n font-size: 12px;\n color: rgba(255, 255, 255, 0.55);\n }\n input,\n select {\n padding: 4px 6px;\n font: inherit;\n font-size: 12px;\n background: rgba(255, 255, 255, 0.06);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 3px;\n width: 100%;\n box-sizing: border-box;\n color: rgba(255, 255, 255, 0.9);\n outline: none;\n }\n input:focus,\n select:focus {\n border-color: #4a9eff;\n background: rgba(74, 158, 255, 0.08);\n }\n /* parentObject 입력(ox-input-entity-id)의 내부 input 을 dialog 의 다른 input 과 통일. */\n ox-input-entity-id::part(input) {\n padding: 4px 6px;\n font: inherit;\n font-size: 12px;\n background: rgba(255, 255, 255, 0.06);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 3px;\n color: rgba(255, 255, 255, 0.9);\n outline: none;\n }\n ox-input-entity-id::part(input):focus {\n border-color: #4a9eff;\n background: rgba(74, 158, 255, 0.08);\n }\n /* 검색 드롭다운(shadow 내부)은 md-sys-color 변수 + 상속 color 로 다크 테마 통일.\n (변수는 shadow 경계를 관통하고, .item 은 color 를 host 로부터 상속받는다.) */\n ox-input-entity-id {\n color: rgba(255, 255, 255, 0.9);\n --md-sys-color-surface: rgb(30, 30, 30);\n --md-sys-color-surface-variant: rgb(45, 45, 45);\n --md-sys-color-outline-variant: rgba(255, 255, 255, 0.15);\n --md-sys-color-secondary-container: rgba(74, 158, 255, 0.25);\n --md-sys-color-on-surface-variant: rgba(255, 255, 255, 0.55);\n }\n select option {\n background: rgb(30, 30, 30);\n color: rgba(255, 255, 255, 0.9);\n }\n .grid-shape {\n display: grid;\n grid-template-columns: 1fr 20px 1fr;\n gap: 4px;\n align-items: center;\n }\n .grid-shape > span {\n text-align: center;\n color: rgba(255, 255, 255, 0.35);\n }\n .pattern-picker {\n display: flex;\n gap: 6px;\n }\n .pattern-btn {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n padding: 6px 4px;\n background: rgba(255, 255, 255, 0.04);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 4px;\n cursor: pointer;\n color: rgba(255, 255, 255, 0.6);\n transition: background 0.1s, border-color 0.1s;\n }\n .pattern-btn:hover {\n background: rgba(255, 255, 255, 0.08);\n }\n .pattern-btn.active {\n border-color: #4a9eff;\n background: rgba(74, 158, 255, 0.15);\n color: rgba(255, 255, 255, 0.95);\n }\n .pattern-btn svg {\n width: 28px;\n height: 28px;\n }\n .pattern-btn svg circle {\n fill: currentColor;\n }\n .pattern-btn span {\n font-size: 10.5px;\n }\n .policy-radios {\n display: flex;\n gap: 12px;\n align-items: center;\n }\n .policy-radios label {\n display: flex;\n align-items: center;\n gap: 4px;\n cursor: pointer;\n color: rgba(255, 255, 255, 0.75);\n }\n .policy-radios input[type='radio'] {\n width: auto;\n accent-color: #4a9eff;\n margin: 0;\n }\n code {\n font-family: ui-monospace, SFMono-Regular, monospace;\n background: rgba(255, 255, 255, 0.08);\n padding: 1px 4px;\n border-radius: 3px;\n color: #b6d4fe;\n font-size: 11px;\n }\n .hint {\n font-size: 11px;\n color: rgba(255, 255, 255, 0.4);\n margin-top: 2px;\n grid-column: 2;\n }\n .preview {\n font-size: 11.5px;\n color: rgba(255, 255, 255, 0.65);\n margin-top: 10px;\n padding: 6px 8px;\n background: rgba(255, 255, 255, 0.04);\n border-radius: 4px;\n }\n .conflict {\n margin-top: 8px;\n padding: 8px 10px;\n background: rgba(255, 100, 100, 0.12);\n border: 1px solid rgba(255, 100, 100, 0.3);\n border-radius: 4px;\n color: #ffb3b3;\n font-size: 11.5px;\n }\n .conflict strong {\n color: #ff8080;\n }\n .conflict-hint {\n margin-top: 4px;\n color: rgba(255, 255, 255, 0.45);\n font-size: 11px;\n }\n .actions {\n margin-top: 16px;\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n }\n button {\n padding: 5px 14px;\n font: inherit;\n font-size: 12px;\n border: 1px solid rgba(255, 255, 255, 0.15);\n background: rgba(255, 255, 255, 0.06);\n color: rgba(255, 255, 255, 0.85);\n cursor: pointer;\n border-radius: 4px;\n }\n button:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.1);\n }\n button:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n }\n button.primary {\n background: #4a9eff;\n color: white;\n border-color: #4a9eff;\n }\n button.primary:hover:not(:disabled) {\n background: #6db1ff;\n }\n `\n\n @property({ type: Object }) template!: Record<string, any>\n @property({ type: Object }) initialArea!: Area\n @property({ attribute: false }) existingIds: Set<string> = new Set()\n @property({ attribute: false }) onConfirm!: BulkCreateDialogProps['onConfirm']\n @property({ attribute: false }) onCancel?: BulkCreateDialogProps['onCancel']\n /** parentObject 필드 노출 여부 (template 이 ParentObjectMixin 컴포넌트일 때). */\n @property({ attribute: false }) hasParentObject = false\n /** parentObject 후보 id 리스트 (template 타입 기준, toolbar 에서 계산해 주입). */\n @property({ attribute: false }) parentObjectItems: string[] = []\n\n @state() private pattern: Pattern = 'grid'\n @state() private parentObjectId = ''\n @state() private count = 12\n @state() private rows: number | undefined = undefined\n @state() private cols: number | undefined = undefined\n @state() private idPattern = '{i}'\n @state() private idStart = 1\n @state() private idPadding = 0\n @state() private area!: Area\n @state() private conflictPolicy: ConflictPolicy = 'skip'\n\n @query('#count-input') countInput!: HTMLInputElement\n\n connectedCallback() {\n super.connectedCallback()\n // OxPopup wrapper (ox-popup) 의 흰색 배경 + box-shadow 가 dialog 라운드 코너\n // 밖으로 비쳐서 4 꼭지점에 흰색 사각형이 보임. 부모를 투명/그림자 없이 만들어\n // dialog 자체의 라운드 + 그림자만 보이게.\n const parent = this.parentElement as HTMLElement | null\n if (parent) {\n parent.style.background = 'transparent'\n parent.style.boxShadow = 'none'\n parent.style.border = 'none'\n }\n this.area = { ...this.initialArea }\n\n // 1) 영역 종횡비로 레이아웃 기본 선택:\n // 너비 ≫ 높이 → line-h (가로), 높이 ≫ 너비 → line-v (세로), 비슷하면 grid.\n const w = Math.max(this.area.width, 1)\n const h = Math.max(this.area.height, 1)\n const aspect = w / h\n if (aspect >= 2.5) this.pattern = 'line-h'\n else if (aspect <= 0.4) this.pattern = 'line-v'\n else this.pattern = 'grid'\n\n // 2) 템플릿 id 로부터 ID 패턴 / padding 자동 추론.\n const inferred = inferIdPattern(this.template?.id)\n this.idPattern = inferred.pattern\n this.idPadding = inferred.padding\n\n // 3) 시작 번호 = existingIds 에 패턴 매칭되는 *최대 번호 + 1*.\n // 반복 bulk-paste 시 이전 batch 의 끝 다음으로 자동 advance.\n // 매칭 ID 가 없으면 inferIdPattern 의 fallback (= template num+1) 사용.\n this.idStart = nextAvailableStart(inferred.pattern, this.existingIds, inferred.start)\n\n // 4) 복사 대상(template)이 이미 parentObjectId 를 가지면 그것을 초기값으로.\n this.parentObjectId = String(this.template?.parentObjectId ?? '')\n }\n\n /** rows/cols 가 미입력 (undefined) 이면 영역 종횡비 + count 로 자동 계산해 표시. */\n private get displayRows(): number {\n if (this.rows != null) return this.rows\n return deriveGridShape(this.count, this.area, undefined, this.cols).rows\n }\n\n private get displayCols(): number {\n if (this.cols != null) return this.cols\n return deriveGridShape(this.count, this.area, this.rows, undefined).cols\n }\n\n firstUpdated() {\n this.countInput?.focus()\n this.countInput?.select()\n }\n\n private updateArea(field: keyof Area, value: number) {\n this.area = { ...this.area, [field]: value }\n }\n\n private get previewModels() {\n if (!this.template) return []\n return generateBulkComponents({\n template: this.template,\n area: this.area,\n pattern: this.pattern,\n count: this.count,\n rows: this.rows,\n cols: this.cols,\n idPattern: this.idPattern.replace(/\\{type\\}/g, String(this.template?.type ?? 'C')),\n idStart: this.idStart,\n idPadding: this.idPadding,\n // 선택 시 각 생성 컴포넌트에 parentObjectId 를 병합 (도메인 특수값은 여기서만).\n extraState: this.parentObjectId ? { parentObjectId: this.parentObjectId } : undefined\n })\n }\n\n private get conflicts(): string[] {\n return findIdConflicts(this.previewModels, this.existingIds)\n }\n\n /** 충돌 정책 적용 후 실제 생성될 model 배열. */\n private get effectiveModels() {\n const all = this.previewModels\n if (this.conflictPolicy === 'skip' && this.existingIds.size > 0) {\n return all.filter(m => !this.existingIds.has(m.id))\n }\n return all\n }\n\n private onOk = () => {\n const models = this.effectiveModels\n if (models.length === 0) return\n this.onConfirm(models)\n }\n\n private onCancelClick = () => {\n this.onCancel?.()\n }\n\n render() {\n const tplType = this.template?.type ?? '?'\n const tplId = this.template?.id ?? '?'\n const models = this.previewModels\n\n return html`\n <h3>Bulk Paste — ${tplType}</h3>\n\n <div class=\"row\">\n <label>템플릿</label>\n <span style=\"color:#b6d4fe;\">${tplId} <span style=\"color:rgba(255,255,255,0.4);\">(${tplType})</span></span>\n </div>\n\n <div class=\"row\">\n <label>레이아웃</label>\n <div class=\"pattern-picker\">\n <button\n type=\"button\"\n class=\"pattern-btn ${this.pattern === 'grid' ? 'active' : ''}\"\n @click=${() => (this.pattern = 'grid')}\n title=\"격자\"\n >\n <svg viewBox=\"0 0 24 24\">\n <circle cx=\"6\" cy=\"6\" r=\"1.6\" /><circle cx=\"12\" cy=\"6\" r=\"1.6\" /><circle cx=\"18\" cy=\"6\" r=\"1.6\" />\n <circle cx=\"6\" cy=\"12\" r=\"1.6\" /><circle cx=\"12\" cy=\"12\" r=\"1.6\" /><circle cx=\"18\" cy=\"12\" r=\"1.6\" />\n <circle cx=\"6\" cy=\"18\" r=\"1.6\" /><circle cx=\"12\" cy=\"18\" r=\"1.6\" /><circle cx=\"18\" cy=\"18\" r=\"1.6\" />\n </svg>\n <span>격자</span>\n </button>\n <button\n type=\"button\"\n class=\"pattern-btn ${this.pattern === 'line-h' ? 'active' : ''}\"\n @click=${() => (this.pattern = 'line-h')}\n title=\"가로 1줄\"\n >\n <svg viewBox=\"0 0 24 24\">\n <circle cx=\"3\" cy=\"12\" r=\"1.6\" /><circle cx=\"8\" cy=\"12\" r=\"1.6\" /><circle cx=\"13\" cy=\"12\" r=\"1.6\" />\n <circle cx=\"18\" cy=\"12\" r=\"1.6\" /><circle cx=\"22\" cy=\"12\" r=\"1.4\" />\n </svg>\n <span>가로</span>\n </button>\n <button\n type=\"button\"\n class=\"pattern-btn ${this.pattern === 'line-v' ? 'active' : ''}\"\n @click=${() => (this.pattern = 'line-v')}\n title=\"세로 1줄\"\n >\n <svg viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"3\" r=\"1.6\" /><circle cx=\"12\" cy=\"8\" r=\"1.6\" /><circle cx=\"12\" cy=\"13\" r=\"1.6\" />\n <circle cx=\"12\" cy=\"18\" r=\"1.6\" /><circle cx=\"12\" cy=\"22\" r=\"1.4\" />\n </svg>\n <span>세로</span>\n </button>\n </div>\n </div>\n\n <div class=\"row\">\n <label>개수</label>\n <input\n id=\"count-input\"\n type=\"number\"\n min=\"1\"\n .value=${String(this.count)}\n @input=${(e: Event) => (this.count = Math.max(1, +(e.target as HTMLInputElement).value))}\n />\n </div>\n\n ${this.pattern === 'grid'\n ? html`\n <div class=\"row\">\n <label>행 × 열</label>\n <div class=\"grid-shape\">\n <input\n type=\"number\"\n min=\"1\"\n .value=${String(this.displayRows)}\n @input=${(e: Event) => {\n const v = (e.target as HTMLInputElement).value\n this.rows = v ? Math.max(1, +v) : undefined\n }}\n />\n <span>×</span>\n <input\n type=\"number\"\n min=\"1\"\n .value=${String(this.displayCols)}\n @input=${(e: Event) => {\n const v = (e.target as HTMLInputElement).value\n this.cols = v ? Math.max(1, +v) : undefined\n }}\n />\n </div>\n </div>\n `\n : ''}\n\n <div class=\"row\">\n <label>ID 패턴</label>\n <input\n type=\"text\"\n .value=${this.idPattern}\n @input=${(e: Event) => (this.idPattern = (e.target as HTMLInputElement).value)}\n />\n </div>\n <div class=\"row\">\n <span></span>\n <div class=\"hint\">placeholder: <code>{i}</code> 일련번호, <code>{r}</code> 행, <code>{c}</code> 열 (grid)</div>\n </div>\n\n <div class=\"row\">\n <label>시작 번호</label>\n <input\n type=\"number\"\n .value=${String(this.idStart)}\n @input=${(e: Event) => (this.idStart = +(e.target as HTMLInputElement).value)}\n />\n </div>\n\n <div class=\"row\">\n <label>Zero-pad 자릿수</label>\n <input\n type=\"number\"\n min=\"0\"\n .value=${String(this.idPadding)}\n @input=${(e: Event) => (this.idPadding = Math.max(0, +(e.target as HTMLInputElement).value))}\n />\n </div>\n\n ${this.hasParentObject\n ? html`\n <div class=\"row\">\n <label>상위 객체</label>\n <ox-input-entity-id\n .localItems=${this.parentObjectItems}\n .value=${this.parentObjectId}\n placeholder=\"상위 객체 id (검색)\"\n @change=${(e: Event) => (this.parentObjectId = (e.target as OxInputEntityId).value)}\n ></ox-input-entity-id>\n </div>\n `\n : ''}\n\n <div class=\"preview\">\n ${models.length > 0\n ? html`총 ${models.length}개 — 예: <code>${models[0].id}</code> ... <code>${models[models.length - 1].id}</code>`\n : html`<span style=\"color:#ff8080;\">생성될 컴포넌트 없음</span>`}\n </div>\n\n ${this.conflicts.length > 0\n ? html`\n <div class=\"conflict\">\n <strong>ID 충돌 ${this.conflicts.length}개</strong>\n <code>${this.conflicts.slice(0, 5).join(', ')}${this.conflicts.length > 5 ? ', ...' : ''}</code>\n <div class=\"conflict-hint\" style=\"margin-top:6px;\">\n <div class=\"policy-radios\">\n <label>\n <input\n type=\"radio\"\n name=\"conflict-policy\"\n value=\"skip\"\n .checked=${this.conflictPolicy === 'skip'}\n @change=${() => (this.conflictPolicy = 'skip')}\n />\n 충돌 ID 스킵 (기본)\n </label>\n <label>\n <input\n type=\"radio\"\n name=\"conflict-policy\"\n value=\"duplicate\"\n .checked=${this.conflictPolicy === 'duplicate'}\n @change=${() => (this.conflictPolicy = 'duplicate')}\n />\n 중복 그대로 생성\n </label>\n </div>\n </div>\n </div>\n `\n : ''}\n\n <div class=\"actions\">\n <button @click=${this.onCancelClick}>취소</button>\n <button\n class=\"primary\"\n @click=${this.onOk}\n ?disabled=${this.effectiveModels.length === 0}\n >\n 생성 (${this.effectiveModels.length}${\n this.conflicts.length > 0 && this.conflictPolicy === 'skip'\n ? html`<span style=\"opacity:0.6;\"> / ${models.length}</span>`\n : ''\n })\n </button>\n </div>\n `\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'bulk-create-dialog': BulkCreateDialog\n }\n}\n"]}