@fmsim/board 1.0.88 → 2.0.0-beta.101

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 (144) hide show
  1. package/custom-elements.json +7366 -3448
  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/component/etc.js +36 -0
  7. package/dist/src/component/etc.js.map +1 -1
  8. package/dist/src/component/index.d.ts +1 -0
  9. package/dist/src/component/index.js +1 -0
  10. package/dist/src/component/index.js.map +1 -1
  11. package/dist/src/component/restful-attachment-creation-card.js +1 -1
  12. package/dist/src/component/restful-attachment-creation-card.js.map +1 -1
  13. package/dist/src/component/restful-attachment-selector.js +8 -9
  14. package/dist/src/component/restful-attachment-selector.js.map +1 -1
  15. package/dist/src/component/restful-input-attachment-selector.d.ts +1 -1
  16. package/dist/src/component/restful-input-attachment-selector.js +3 -3
  17. package/dist/src/component/restful-input-attachment-selector.js.map +1 -1
  18. package/dist/src/component/restful-input-fill-style.d.ts +13 -2
  19. package/dist/src/component/restful-input-fill-style.js.map +1 -1
  20. package/dist/src/component/restful-property-editor-backdrop-faces.d.ts +50 -0
  21. package/dist/src/component/restful-property-editor-backdrop-faces.js +524 -0
  22. package/dist/src/component/restful-property-editor-backdrop-faces.js.map +1 -0
  23. package/dist/src/index.d.ts +1 -0
  24. package/dist/src/index.js +1 -0
  25. package/dist/src/index.js.map +1 -1
  26. package/dist/src/layers/mcs-mouse-event-handler.js +8 -0
  27. package/dist/src/layers/mcs-mouse-event-handler.js.map +1 -1
  28. package/dist/src/layers/movement-layer.d.ts +7 -6
  29. package/dist/src/layers/movement-layer.js +56 -11
  30. package/dist/src/layers/movement-layer.js.map +1 -1
  31. package/dist/src/modeller/bulk-create-dialog.d.ts +51 -0
  32. package/dist/src/modeller/bulk-create-dialog.js +531 -0
  33. package/dist/src/modeller/bulk-create-dialog.js.map +1 -0
  34. package/dist/src/modeller/bulk-create.d.ts +101 -0
  35. package/dist/src/modeller/bulk-create.js +199 -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 +4 -4
  39. package/dist/src/modeller/component-toolbar/component-toolbar.js.map +1 -1
  40. package/dist/src/modeller/edit-toolbar-style.js +43 -3
  41. package/dist/src/modeller/edit-toolbar-style.js.map +1 -1
  42. package/dist/src/modeller/edit-toolbar.d.ts +21 -14
  43. package/dist/src/modeller/edit-toolbar.js +371 -213
  44. package/dist/src/modeller/edit-toolbar.js.map +1 -1
  45. package/dist/src/modeller/property-sidebar/abstract-property.js +10 -2
  46. package/dist/src/modeller/property-sidebar/abstract-property.js.map +1 -1
  47. package/dist/src/modeller/property-sidebar/data-binding/data-binding-mapper.js +1 -1
  48. package/dist/src/modeller/property-sidebar/data-binding/data-binding-mapper.js.map +1 -1
  49. package/dist/src/modeller/property-sidebar/data-binding/data-binding.d.ts +2 -2
  50. package/dist/src/modeller/property-sidebar/data-binding/data-binding.js +18 -18
  51. package/dist/src/modeller/property-sidebar/data-binding/data-binding.js.map +1 -1
  52. package/dist/src/modeller/property-sidebar/effects/effects.d.ts +10 -1
  53. package/dist/src/modeller/property-sidebar/effects/effects.js +66 -1
  54. package/dist/src/modeller/property-sidebar/effects/effects.js.map +1 -1
  55. package/dist/src/modeller/property-sidebar/effects/event-handler-adapter.d.ts +44 -0
  56. package/dist/src/modeller/property-sidebar/effects/event-handler-adapter.js +70 -0
  57. package/dist/src/modeller/property-sidebar/effects/event-handler-adapter.js.map +1 -0
  58. package/dist/src/modeller/property-sidebar/effects/property-event-hover.js +3 -3
  59. package/dist/src/modeller/property-sidebar/effects/property-event-hover.js.map +1 -1
  60. package/dist/src/modeller/property-sidebar/effects/property-event.d.ts +13 -3
  61. package/dist/src/modeller/property-sidebar/effects/property-event.js +20 -11
  62. package/dist/src/modeller/property-sidebar/effects/property-event.js.map +1 -1
  63. package/dist/src/modeller/property-sidebar/inspector/inspector-controller.d.ts +62 -0
  64. package/dist/src/modeller/property-sidebar/inspector/inspector-controller.js +276 -0
  65. package/dist/src/modeller/property-sidebar/inspector/inspector-controller.js.map +1 -0
  66. package/dist/src/modeller/property-sidebar/inspector/inspector-group-view.d.ts +15 -0
  67. package/dist/src/modeller/property-sidebar/inspector/inspector-group-view.js +56 -0
  68. package/dist/src/modeller/property-sidebar/inspector/inspector-group-view.js.map +1 -0
  69. package/dist/src/modeller/property-sidebar/inspector/inspector-highlight.d.ts +7 -0
  70. package/dist/src/modeller/property-sidebar/inspector/inspector-highlight.js +25 -0
  71. package/dist/src/modeller/property-sidebar/inspector/inspector-highlight.js.map +1 -0
  72. package/dist/src/modeller/property-sidebar/inspector/inspector-model.d.ts +44 -0
  73. package/dist/src/modeller/property-sidebar/inspector/inspector-model.js +72 -0
  74. package/dist/src/modeller/property-sidebar/inspector/inspector-model.js.map +1 -0
  75. package/dist/src/modeller/property-sidebar/inspector/inspector-tree-view.d.ts +18 -0
  76. package/dist/src/modeller/property-sidebar/inspector/inspector-tree-view.js +61 -0
  77. package/dist/src/modeller/property-sidebar/inspector/inspector-tree-view.js.map +1 -0
  78. package/dist/src/modeller/property-sidebar/inspector/inspector.d.ts +17 -12
  79. package/dist/src/modeller/property-sidebar/inspector/inspector.js +281 -154
  80. package/dist/src/modeller/property-sidebar/inspector/inspector.js.map +1 -1
  81. package/dist/src/modeller/property-sidebar/property-sidebar.d.ts +2 -2
  82. package/dist/src/modeller/property-sidebar/property-sidebar.js +11 -24
  83. package/dist/src/modeller/property-sidebar/property-sidebar.js.map +1 -1
  84. package/dist/src/modeller/property-sidebar/shapes/ox-input-entity-id.d.ts +75 -0
  85. package/dist/src/modeller/property-sidebar/shapes/ox-input-entity-id.js +346 -0
  86. package/dist/src/modeller/property-sidebar/shapes/ox-input-entity-id.js.map +1 -0
  87. package/dist/src/modeller/property-sidebar/shapes/shapes.d.ts +23 -2
  88. package/dist/src/modeller/property-sidebar/shapes/shapes.js +96 -4
  89. package/dist/src/modeller/property-sidebar/shapes/shapes.js.map +1 -1
  90. package/dist/src/modeller/property-sidebar/specifics/specifics.d.ts +25 -1
  91. package/dist/src/modeller/property-sidebar/specifics/specifics.js +112 -2
  92. package/dist/src/modeller/property-sidebar/specifics/specifics.js.map +1 -1
  93. package/dist/src/modeller/property-sidebar/styles/styles.d.ts +6 -4
  94. package/dist/src/modeller/property-sidebar/styles/styles.js +140 -437
  95. package/dist/src/modeller/property-sidebar/styles/styles.js.map +1 -1
  96. package/dist/src/modeller/scene-viewer/ox-scene-layer.d.ts +4 -3
  97. package/dist/src/modeller/scene-viewer/ox-scene-layer.js +2 -1
  98. package/dist/src/modeller/scene-viewer/ox-scene-layer.js.map +1 -1
  99. package/dist/src/modeller/scene-viewer/ox-scene-viewer.d.ts +6 -5
  100. package/dist/src/modeller/scene-viewer/ox-scene-viewer.js +7 -3
  101. package/dist/src/modeller/scene-viewer/ox-scene-viewer.js.map +1 -1
  102. package/dist/src/ox-board-component-info.d.ts +3 -3
  103. package/dist/src/ox-board-component-info.js +58 -19
  104. package/dist/src/ox-board-component-info.js.map +1 -1
  105. package/dist/src/ox-board-list.js +12 -12
  106. package/dist/src/ox-board-list.js.map +1 -1
  107. package/dist/src/ox-board-modeller.d.ts +9 -6
  108. package/dist/src/ox-board-modeller.js +32 -33
  109. package/dist/src/ox-board-modeller.js.map +1 -1
  110. package/dist/src/ox-board-viewer.d.ts +129 -17
  111. package/dist/src/ox-board-viewer.js +804 -115
  112. package/dist/src/ox-board-viewer.js.map +1 -1
  113. package/dist/src/ox-editor-board-selector.d.ts +4 -1
  114. package/dist/src/ox-editor-board-selector.js +41 -12
  115. package/dist/src/ox-editor-board-selector.js.map +1 -1
  116. package/dist/src/ox-playback-controls.d.ts +157 -0
  117. package/dist/src/ox-playback-controls.js +1089 -0
  118. package/dist/src/ox-playback-controls.js.map +1 -0
  119. package/dist/src/ox-property-editor-theme.js +2 -2
  120. package/dist/src/ox-property-editor-theme.js.map +1 -1
  121. package/dist/src/playback/carrier-tracking-store.d.ts +51 -0
  122. package/dist/src/playback/carrier-tracking-store.js +115 -0
  123. package/dist/src/playback/carrier-tracking-store.js.map +1 -0
  124. package/dist/src/playback/playback-engine.d.ts +106 -0
  125. package/dist/src/playback/playback-engine.js +395 -0
  126. package/dist/src/playback/playback-engine.js.map +1 -0
  127. package/dist/src/selector/board-creation-popup.d.ts +4 -5
  128. package/dist/src/selector/board-creation-popup.js +16 -19
  129. package/dist/src/selector/board-creation-popup.js.map +1 -1
  130. package/dist/src/selector/board-thumbnail-card.js +8 -8
  131. package/dist/src/selector/board-thumbnail-card.js.map +1 -1
  132. package/dist/src/selector/ox-board-creation-card.d.ts +1 -1
  133. package/dist/src/selector/ox-board-creation-card.js +7 -7
  134. package/dist/src/selector/ox-board-creation-card.js.map +1 -1
  135. package/dist/src/selector/ox-board-selector.js +21 -10
  136. package/dist/src/selector/ox-board-selector.js.map +1 -1
  137. package/dist/tsconfig.tsbuildinfo +1 -1
  138. package/icons/components/text.png +0 -0
  139. package/icons/fab/mode-2d.svg +25 -0
  140. package/icons/fab/mode-3d.svg +25 -0
  141. package/icons/fab/playback-play.svg +49 -0
  142. package/icons/fab/playback-stop.svg +50 -0
  143. package/icons/icon-htoolbar.png +0 -0
  144. package/package.json +22 -21
@@ -0,0 +1,346 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+ import { __decorate } from "tslib";
5
+ import '@operato/i18n/ox-i18n.js';
6
+ import { css, html, LitElement } from 'lit';
7
+ import { property, query, state } from 'lit/decorators.js';
8
+ import { ScrollbarStyles } from '@operato/styles';
9
+ import { searchFigureIds, filterFigureIds } from '@fmsim/api';
10
+ /**
11
+ * 컴포넌트 ID 입력기 — 자유 입력 + 타입별 백엔드(MCS) 검색 선택을 함께 제공.
12
+ *
13
+ * - 기존처럼 자유롭게 텍스트로 ID 를 입력할 수 있다 (commit = blur/Enter).
14
+ * - input 에 *포커스가 가면* 아래로 검색 드롭다운이 펼쳐진다. `entityType`
15
+ * (컴포넌트 타입) 으로 MCS 에 정의된 ID 후보를 조회한다.
16
+ * - machine 타입 → 머신 id 가 1레벨 평면으로 (선택 가능)
17
+ * - unit 타입 → machine 이 그룹 헤더, 그 아래 unit id 가 자식 (선택 가능).
18
+ * 컴포넌트가 부모 machine 을 알면(`machine` prop) 그 머신으로 좁혀 조회.
19
+ *
20
+ * value-key="id" 와 함께 property-grid 안에 두면, 이 host 가 `change`(bubbles+
21
+ * composed) 를 발행하므로 AbstractProperty 제너릭 핸들러가 `element.value` 를
22
+ * 읽어 property-change 로 전파한다.
23
+ *
24
+ * 등록: 패널 자식 커스텀 엘리먼트 컨벤션에 따라 전역 `@customElement` 가 아니라
25
+ * 부모 패널(PropertyShapes)의 `scopedElements` 로 등록한다 (@open-wc/scoped-elements).
26
+ */
27
+ export class OxInputEntityId extends LitElement {
28
+ constructor() {
29
+ super(...arguments);
30
+ /** 현재 ID 값. */
31
+ this.value = '';
32
+ /** 검색 조건 — 선택된 컴포넌트 타입 (예: 'STOCKER', 'AGV', 'Port'). */
33
+ this.entityType = '';
34
+ /** 검색 조건 — 부모 machine id (unit 컴포넌트가 소속 machine 을 알 때). */
35
+ this.machine = '';
36
+ /** machine 필수 타입(예: Port) — machine 없으면 조회하지 않는다 (후보 과다 방지). */
37
+ this.requireMachine = false;
38
+ /** 백엔드 조회 대신 사용할 로컬 후보 리스트 (예: parentObject — 씬의 머신들). null 이면 백엔드 조회. */
39
+ this.localItems = null;
40
+ this.placeholder = '';
41
+ this._open = false;
42
+ /** 서버에서 받은 전체 후보 리스트 (포커스 시 1회 조회, 키워드 미적용). */
43
+ this._all = [];
44
+ this._loading = false;
45
+ /** machine 필수인데 machine 미지정이라 조회를 보류한 상태. */
46
+ this._needMachine = false;
47
+ /** 선택 가능 항목 기준 active index. */
48
+ this._activeIndex = -1;
49
+ this._loadSeq = 0;
50
+ }
51
+ disconnectedCallback() {
52
+ super.disconnectedCallback();
53
+ clearTimeout(this._blurTimer);
54
+ }
55
+ updated(changed) {
56
+ // 열린 상태에서 조회 조건(타입/부모 머신)이 바뀌면 재조회 (예: 부모 머신을 방금 지정).
57
+ if (this._open && (changed.has('entityType') || changed.has('machine') || changed.has('localItems'))) {
58
+ this._load();
59
+ }
60
+ // 화살표 키로 active 항목 이동 시 항상 보이도록 드롭다운을 스크롤.
61
+ if (changed.has('_activeIndex') && this._open && this._activeIndex >= 0) {
62
+ const el = this.renderRoot.querySelector('.item.active');
63
+ el === null || el === void 0 ? void 0 : el.scrollIntoView({ block: 'nearest' });
64
+ }
65
+ }
66
+ /**
67
+ * 드롭다운 표시 항목 — 입력값(초기값 포함)으로 풀텍스트 로컬 필터.
68
+ * 단, 매치가 0이면 전체 후보로 fallback (막다른 "결과 없음" 방지 — 예: committed
69
+ * id 가 후보 형식과 달라 안 맞는 경우). 서버 재조회 없음.
70
+ */
71
+ get _items() {
72
+ if (!this._all.length)
73
+ return [];
74
+ const filtered = filterFigureIds(this._all, this.value || '');
75
+ return filtered.length ? filtered : this._all;
76
+ }
77
+ /** 드롭다운에서 선택 가능한 id 들 (표시 순서). */
78
+ get _selectableIds() {
79
+ return this._items;
80
+ }
81
+ render() {
82
+ return html `
83
+ <input
84
+ .value=${this.value || ''}
85
+ placeholder=${this.placeholder}
86
+ autocomplete="off"
87
+ @focus=${this._onFocus}
88
+ @blur=${this._onBlur}
89
+ @input=${this._onInput}
90
+ @change=${this._onInputCommit}
91
+ @keydown=${this._onKeydown}
92
+ />
93
+ ${this._open ? this._renderDropdown() : ''}
94
+ `;
95
+ }
96
+ _renderDropdown() {
97
+ if (this._needMachine) {
98
+ return html `<div class="dropdown">
99
+ <div class="hint"><ox-i18n msgid="text.need-parent-machine">상위 머신을 먼저 지정하세요</ox-i18n></div>
100
+ </div>`;
101
+ }
102
+ if (this._loading) {
103
+ return html `<div class="dropdown"><div class="hint"><ox-i18n msgid="text.searching">searching…</ox-i18n></div></div>`;
104
+ }
105
+ if (!this._items.length) {
106
+ return html `<div class="dropdown"><div class="hint"><ox-i18n msgid="text.no-results">no results</ox-i18n></div></div>`;
107
+ }
108
+ return html `
109
+ <div class="dropdown">
110
+ ${this._items.map((id, idx) => html `
111
+ <div
112
+ class="item ${idx === this._activeIndex ? 'active' : ''}"
113
+ @mousedown=${(e) => this._select(id, e)}
114
+ >
115
+ ${id}
116
+ </div>
117
+ `)}
118
+ </div>
119
+ `;
120
+ }
121
+ _onFocus() {
122
+ clearTimeout(this._blurTimer);
123
+ this._open = true;
124
+ this._activeIndex = -1;
125
+ this._load();
126
+ }
127
+ _onBlur() {
128
+ // 드롭다운 항목 mousedown 이 먼저 처리되도록 약간 지연 후 닫기
129
+ this._blurTimer = setTimeout(() => {
130
+ this._open = false;
131
+ this._activeIndex = -1;
132
+ }, 150);
133
+ }
134
+ _onInput(e) {
135
+ this.value = e.target.value;
136
+ this._open = true;
137
+ this._activeIndex = -1;
138
+ }
139
+ /** 자유 입력 commit (blur/Enter 의 native change) — 기존 free-text 동작 보존. */
140
+ _onInputCommit(e) {
141
+ this.value = e.target.value;
142
+ this._emitChange();
143
+ }
144
+ _onKeydown(e) {
145
+ const ids = this._selectableIds;
146
+ if (!this._open || !ids.length) {
147
+ if (e.key === 'Escape')
148
+ this._open = false;
149
+ return;
150
+ }
151
+ if (e.key === 'ArrowDown') {
152
+ e.preventDefault();
153
+ this._activeIndex = (this._activeIndex + 1) % ids.length;
154
+ }
155
+ else if (e.key === 'ArrowUp') {
156
+ e.preventDefault();
157
+ this._activeIndex = (this._activeIndex - 1 + ids.length) % ids.length;
158
+ }
159
+ else if (e.key === 'Enter') {
160
+ if (this._activeIndex >= 0) {
161
+ e.preventDefault();
162
+ this._commitValue(ids[this._activeIndex]);
163
+ }
164
+ }
165
+ else if (e.key === 'Escape') {
166
+ this._open = false;
167
+ this._activeIndex = -1;
168
+ }
169
+ }
170
+ _select(id, e) {
171
+ // mousedown 기본동작(blur) 막아 흐름 제어
172
+ e.preventDefault();
173
+ this._commitValue(id);
174
+ }
175
+ _commitValue(id) {
176
+ var _a;
177
+ this.value = id;
178
+ this._open = false;
179
+ this._activeIndex = -1;
180
+ if (this._input)
181
+ this._input.value = id;
182
+ this._emitChange();
183
+ (_a = this._input) === null || _a === void 0 ? void 0 : _a.blur();
184
+ }
185
+ _emitChange() {
186
+ // 같은 값 재발행 skip (예: 드롭다운 선택 직후 blur 의 native change 중복)
187
+ if (this.value === this._lastEmitted)
188
+ return;
189
+ this._lastEmitted = this.value;
190
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
191
+ }
192
+ /**
193
+ * 후보 전체를 1회 조회 (포커스 시). keyword 는 보내지 않으며, 표시 필터는 _items
194
+ * 게터가 로컬에서 처리한다. entityType(+unit 이면 machine) 만 서버로 보낸다.
195
+ */
196
+ async _load() {
197
+ // 로컬 리스트 모드(예: parentObject) — 백엔드 조회 없이 주어진 리스트 사용.
198
+ if (this.localItems != null) {
199
+ this._needMachine = false;
200
+ this._loading = false;
201
+ this._all = this.localItems;
202
+ return;
203
+ }
204
+ if (!this.entityType) {
205
+ this._all = [];
206
+ return;
207
+ }
208
+ // machine 필수 타입(Port 등)인데 부모 머신 미지정 → 조회 보류 (후보 과다 방지).
209
+ if (this.requireMachine && !this.machine) {
210
+ this._all = [];
211
+ this._needMachine = true;
212
+ return;
213
+ }
214
+ this._needMachine = false;
215
+ const seq = ++this._loadSeq;
216
+ this._loading = true;
217
+ try {
218
+ const ids = await searchFigureIds(this.entityType, '', this.machine ? { machine: this.machine } : {});
219
+ if (seq !== this._loadSeq)
220
+ return; // 더 최신 조회가 있으면 폐기
221
+ this._all = ids;
222
+ }
223
+ catch (_a) {
224
+ if (seq === this._loadSeq)
225
+ this._all = [];
226
+ }
227
+ finally {
228
+ if (seq === this._loadSeq)
229
+ this._loading = false;
230
+ }
231
+ }
232
+ }
233
+ OxInputEntityId.styles = [
234
+ ScrollbarStyles,
235
+ css `
236
+ :host {
237
+ position: relative;
238
+ display: flex;
239
+ align-items: stretch;
240
+ width: 100%;
241
+ height: 100%;
242
+ }
243
+
244
+ input {
245
+ flex: 1 1 auto;
246
+ width: 100%;
247
+ height: 100%;
248
+ box-sizing: border-box;
249
+ margin: 0;
250
+ padding: var(--input-padding, 2px 4px);
251
+ font: inherit;
252
+ border: var(--property-sidebar-fieldset-border, 1px solid #ccc);
253
+ }
254
+
255
+ .dropdown {
256
+ position: absolute;
257
+ top: 100%;
258
+ left: 0;
259
+ right: 0;
260
+ z-index: 100;
261
+
262
+ max-height: 220px;
263
+ overflow-y: auto;
264
+
265
+ margin-top: 2px;
266
+ background: var(--md-sys-color-surface, #fff);
267
+ border: 1px solid var(--md-sys-color-outline-variant, #c4c7c5);
268
+ border-radius: 4px;
269
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.18);
270
+ }
271
+
272
+ .group-header {
273
+ padding: 3px 8px;
274
+ font-size: 10px;
275
+ font-weight: 600;
276
+ text-transform: uppercase;
277
+ letter-spacing: 0.04em;
278
+ color: var(--md-sys-color-on-surface-variant, #777);
279
+ background: var(--md-sys-color-surface-variant, #eef0f3);
280
+ position: sticky;
281
+ top: 0;
282
+ }
283
+
284
+ .item {
285
+ padding: 4px 8px;
286
+ cursor: pointer;
287
+ white-space: nowrap;
288
+ overflow: hidden;
289
+ text-overflow: ellipsis;
290
+ font: var(--property-sidebar-fieldset-label, 12px sans-serif);
291
+ }
292
+
293
+ /* unit 항목 (그룹 자식) 은 살짝 들여쓰기 */
294
+ .item.child {
295
+ padding-left: 18px;
296
+ }
297
+
298
+ .item:hover,
299
+ .item.active {
300
+ background: var(--md-sys-color-secondary-container, #d7e3ff);
301
+ }
302
+
303
+ .hint {
304
+ padding: 6px 8px;
305
+ color: var(--md-sys-color-on-surface-variant, #888);
306
+ font-size: 11px;
307
+ }
308
+ `
309
+ ];
310
+ __decorate([
311
+ property({ type: String })
312
+ ], OxInputEntityId.prototype, "value", void 0);
313
+ __decorate([
314
+ property({ type: String })
315
+ ], OxInputEntityId.prototype, "entityType", void 0);
316
+ __decorate([
317
+ property({ type: String })
318
+ ], OxInputEntityId.prototype, "machine", void 0);
319
+ __decorate([
320
+ property({ type: Boolean })
321
+ ], OxInputEntityId.prototype, "requireMachine", void 0);
322
+ __decorate([
323
+ property({ type: Array })
324
+ ], OxInputEntityId.prototype, "localItems", void 0);
325
+ __decorate([
326
+ property({ type: String })
327
+ ], OxInputEntityId.prototype, "placeholder", void 0);
328
+ __decorate([
329
+ state()
330
+ ], OxInputEntityId.prototype, "_open", void 0);
331
+ __decorate([
332
+ state()
333
+ ], OxInputEntityId.prototype, "_all", void 0);
334
+ __decorate([
335
+ state()
336
+ ], OxInputEntityId.prototype, "_loading", void 0);
337
+ __decorate([
338
+ state()
339
+ ], OxInputEntityId.prototype, "_needMachine", void 0);
340
+ __decorate([
341
+ state()
342
+ ], OxInputEntityId.prototype, "_activeIndex", void 0);
343
+ __decorate([
344
+ query('input')
345
+ ], OxInputEntityId.prototype, "_input", void 0);
346
+ //# sourceMappingURL=ox-input-entity-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ox-input-entity-id.js","sourceRoot":"","sources":["../../../../../src/modeller/property-sidebar/shapes/ox-input-entity-id.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, LitElement, PropertyValues } from 'lit'\nimport { property, query, state } from 'lit/decorators.js'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { searchFigureIds, filterFigureIds } from '@fmsim/api'\n\n/**\n * 컴포넌트 ID 입력기 — 자유 입력 + 타입별 백엔드(MCS) 검색 선택을 함께 제공.\n *\n * - 기존처럼 자유롭게 텍스트로 ID 를 입력할 수 있다 (commit = blur/Enter).\n * - input 에 *포커스가 가면* 아래로 검색 드롭다운이 펼쳐진다. `entityType`\n * (컴포넌트 타입) 으로 MCS 에 정의된 ID 후보를 조회한다.\n * - machine 타입 → 머신 id 가 1레벨 평면으로 (선택 가능)\n * - unit 타입 → machine 이 그룹 헤더, 그 아래 unit id 가 자식 (선택 가능).\n * 컴포넌트가 부모 machine 을 알면(`machine` prop) 그 머신으로 좁혀 조회.\n *\n * value-key=\"id\" 와 함께 property-grid 안에 두면, 이 host 가 `change`(bubbles+\n * composed) 를 발행하므로 AbstractProperty 제너릭 핸들러가 `element.value` 를\n * 읽어 property-change 로 전파한다.\n *\n * 등록: 패널 자식 커스텀 엘리먼트 컨벤션에 따라 전역 `@customElement` 가 아니라\n * 부모 패널(PropertyShapes)의 `scopedElements` 로 등록한다 (@open-wc/scoped-elements).\n */\nexport class OxInputEntityId extends LitElement {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n position: relative;\n display: flex;\n align-items: stretch;\n width: 100%;\n height: 100%;\n }\n\n input {\n flex: 1 1 auto;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n margin: 0;\n padding: var(--input-padding, 2px 4px);\n font: inherit;\n border: var(--property-sidebar-fieldset-border, 1px solid #ccc);\n }\n\n .dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 100;\n\n max-height: 220px;\n overflow-y: auto;\n\n margin-top: 2px;\n background: var(--md-sys-color-surface, #fff);\n border: 1px solid var(--md-sys-color-outline-variant, #c4c7c5);\n border-radius: 4px;\n box-shadow: 0 4px 10px rgba(0, 0, 0, 0.18);\n }\n\n .group-header {\n padding: 3px 8px;\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--md-sys-color-on-surface-variant, #777);\n background: var(--md-sys-color-surface-variant, #eef0f3);\n position: sticky;\n top: 0;\n }\n\n .item {\n padding: 4px 8px;\n cursor: pointer;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font: var(--property-sidebar-fieldset-label, 12px sans-serif);\n }\n\n /* unit 항목 (그룹 자식) 은 살짝 들여쓰기 */\n .item.child {\n padding-left: 18px;\n }\n\n .item:hover,\n .item.active {\n background: var(--md-sys-color-secondary-container, #d7e3ff);\n }\n\n .hint {\n padding: 6px 8px;\n color: var(--md-sys-color-on-surface-variant, #888);\n font-size: 11px;\n }\n `\n ]\n\n /** 현재 ID 값. */\n @property({ type: String }) value = ''\n /** 검색 조건 — 선택된 컴포넌트 타입 (예: 'STOCKER', 'AGV', 'Port'). */\n @property({ type: String }) entityType = ''\n /** 검색 조건 — 부모 machine id (unit 컴포넌트가 소속 machine 을 알 때). */\n @property({ type: String }) machine = ''\n /** machine 필수 타입(예: Port) — machine 없으면 조회하지 않는다 (후보 과다 방지). */\n @property({ type: Boolean }) requireMachine = false\n /** 백엔드 조회 대신 사용할 로컬 후보 리스트 (예: parentObject — 씬의 머신들). null 이면 백엔드 조회. */\n @property({ type: Array }) localItems: string[] | null = null\n @property({ type: String }) placeholder = ''\n\n @state() private _open = false\n /** 서버에서 받은 전체 후보 리스트 (포커스 시 1회 조회, 키워드 미적용). */\n @state() private _all: string[] = []\n @state() private _loading = false\n /** machine 필수인데 machine 미지정이라 조회를 보류한 상태. */\n @state() private _needMachine = false\n /** 선택 가능 항목 기준 active index. */\n @state() private _activeIndex = -1\n\n @query('input') private _input!: HTMLInputElement\n\n private _blurTimer?: ReturnType<typeof setTimeout>\n private _loadSeq = 0\n /** 마지막으로 발행한 값 — 같은 값 재발행(undo 중복) 방지. */\n private _lastEmitted?: string\n\n disconnectedCallback() {\n super.disconnectedCallback()\n clearTimeout(this._blurTimer)\n }\n\n updated(changed: PropertyValues) {\n // 열린 상태에서 조회 조건(타입/부모 머신)이 바뀌면 재조회 (예: 부모 머신을 방금 지정).\n if (this._open && (changed.has('entityType') || changed.has('machine') || changed.has('localItems'))) {\n this._load()\n }\n // 화살표 키로 active 항목 이동 시 항상 보이도록 드롭다운을 스크롤.\n if (changed.has('_activeIndex') && this._open && this._activeIndex >= 0) {\n const el = this.renderRoot.querySelector('.item.active') as HTMLElement | null\n el?.scrollIntoView({ block: 'nearest' })\n }\n }\n\n /**\n * 드롭다운 표시 항목 — 입력값(초기값 포함)으로 풀텍스트 로컬 필터.\n * 단, 매치가 0이면 전체 후보로 fallback (막다른 \"결과 없음\" 방지 — 예: committed\n * id 가 후보 형식과 달라 안 맞는 경우). 서버 재조회 없음.\n */\n private get _items(): string[] {\n if (!this._all.length) return []\n const filtered = filterFigureIds(this._all, this.value || '')\n return filtered.length ? filtered : this._all\n }\n\n /** 드롭다운에서 선택 가능한 id 들 (표시 순서). */\n private get _selectableIds(): string[] {\n return this._items\n }\n\n render() {\n return html`\n <input\n .value=${this.value || ''}\n placeholder=${this.placeholder}\n autocomplete=\"off\"\n @focus=${this._onFocus}\n @blur=${this._onBlur}\n @input=${this._onInput}\n @change=${this._onInputCommit}\n @keydown=${this._onKeydown}\n />\n ${this._open ? this._renderDropdown() : ''}\n `\n }\n\n private _renderDropdown() {\n if (this._needMachine) {\n return html`<div class=\"dropdown\">\n <div class=\"hint\"><ox-i18n msgid=\"text.need-parent-machine\">상위 머신을 먼저 지정하세요</ox-i18n></div>\n </div>`\n }\n if (this._loading) {\n return html`<div class=\"dropdown\"><div class=\"hint\"><ox-i18n msgid=\"text.searching\">searching…</ox-i18n></div></div>`\n }\n if (!this._items.length) {\n return html`<div class=\"dropdown\"><div class=\"hint\"><ox-i18n msgid=\"text.no-results\">no results</ox-i18n></div></div>`\n }\n\n return html`\n <div class=\"dropdown\">\n ${this._items.map(\n (id, idx) => html`\n <div\n class=\"item ${idx === this._activeIndex ? 'active' : ''}\"\n @mousedown=${(e: MouseEvent) => this._select(id, e)}\n >\n ${id}\n </div>\n `\n )}\n </div>\n `\n }\n\n private _onFocus() {\n clearTimeout(this._blurTimer)\n this._open = true\n this._activeIndex = -1\n this._load()\n }\n\n private _onBlur() {\n // 드롭다운 항목 mousedown 이 먼저 처리되도록 약간 지연 후 닫기\n this._blurTimer = setTimeout(() => {\n this._open = false\n this._activeIndex = -1\n }, 150)\n }\n\n private _onInput(e: Event) {\n this.value = (e.target as HTMLInputElement).value\n this._open = true\n this._activeIndex = -1\n }\n\n /** 자유 입력 commit (blur/Enter 의 native change) — 기존 free-text 동작 보존. */\n private _onInputCommit(e: Event) {\n this.value = (e.target as HTMLInputElement).value\n this._emitChange()\n }\n\n private _onKeydown(e: KeyboardEvent) {\n const ids = this._selectableIds\n if (!this._open || !ids.length) {\n if (e.key === 'Escape') this._open = false\n return\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n this._activeIndex = (this._activeIndex + 1) % ids.length\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n this._activeIndex = (this._activeIndex - 1 + ids.length) % ids.length\n } else if (e.key === 'Enter') {\n if (this._activeIndex >= 0) {\n e.preventDefault()\n this._commitValue(ids[this._activeIndex])\n }\n } else if (e.key === 'Escape') {\n this._open = false\n this._activeIndex = -1\n }\n }\n\n private _select(id: string, e: MouseEvent) {\n // mousedown 기본동작(blur) 막아 흐름 제어\n e.preventDefault()\n this._commitValue(id)\n }\n\n private _commitValue(id: string) {\n this.value = id\n this._open = false\n this._activeIndex = -1\n if (this._input) this._input.value = id\n this._emitChange()\n this._input?.blur()\n }\n\n private _emitChange() {\n // 같은 값 재발행 skip (예: 드롭다운 선택 직후 blur 의 native change 중복)\n if (this.value === this._lastEmitted) return\n this._lastEmitted = this.value\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n }\n\n /**\n * 후보 전체를 1회 조회 (포커스 시). keyword 는 보내지 않으며, 표시 필터는 _items\n * 게터가 로컬에서 처리한다. entityType(+unit 이면 machine) 만 서버로 보낸다.\n */\n private async _load() {\n // 로컬 리스트 모드(예: parentObject) — 백엔드 조회 없이 주어진 리스트 사용.\n if (this.localItems != null) {\n this._needMachine = false\n this._loading = false\n this._all = this.localItems\n return\n }\n if (!this.entityType) {\n this._all = []\n return\n }\n // machine 필수 타입(Port 등)인데 부모 머신 미지정 → 조회 보류 (후보 과다 방지).\n if (this.requireMachine && !this.machine) {\n this._all = []\n this._needMachine = true\n return\n }\n this._needMachine = false\n const seq = ++this._loadSeq\n this._loading = true\n try {\n const ids = await searchFigureIds(this.entityType, '', this.machine ? { machine: this.machine } : {})\n if (seq !== this._loadSeq) return // 더 최신 조회가 있으면 폐기\n this._all = ids\n } catch {\n if (seq === this._loadSeq) this._all = []\n } finally {\n if (seq === this._loadSeq) this._loading = false\n }\n }\n}\n"]}
@@ -3,11 +3,16 @@
3
3
  */
4
4
  import '@operato/input/ox-input-angle.js';
5
5
  import '@operato/input/ox-buttons-radio.js';
6
- import '@material/mwc-icon';
6
+ import '@material/web/icon/icon.js';
7
7
  import '@operato/i18n/ox-i18n.js';
8
8
  import { BOUNDS, Component, Properties } from '@hatiolab/things-scene';
9
9
  import { AbstractProperty } from '../abstract-property';
10
- export declare class PropertyShapes extends AbstractProperty {
10
+ import { OxInputEntityId } from './ox-input-entity-id';
11
+ declare const PropertyShapes_base: typeof AbstractProperty & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/types.js").ScopedElementsHost>;
12
+ export declare class PropertyShapes extends PropertyShapes_base {
13
+ static get scopedElements(): {
14
+ 'ox-input-entity-id': typeof OxInputEntityId;
15
+ };
11
16
  static styles: import("lit").CSSResult[];
12
17
  value?: Properties;
13
18
  bounds: BOUNDS | null;
@@ -18,7 +23,23 @@ export declare class PropertyShapes extends AbstractProperty {
18
23
  _hasTextProperty(selected: Component[]): boolean;
19
24
  _hasProperties(selected: Component[]): boolean;
20
25
  _isIdentifiable(selected: Component[]): boolean;
26
+ /**
27
+ * 단일 선택 컴포넌트의 부모 machine id (unit 의 ID 검색 시 조회조건으로 전달).
28
+ * 부모가 루트 모델/레이어가 아니고 id 를 가지면 그 id 를 반환 — unit 이 자신의
29
+ * 소속 machine 을 특정할 수 있는 경우에 해당. 그 외엔 빈 문자열.
30
+ */
31
+ _parentMachineId(selected: Component[]): string;
32
+ /**
33
+ * 선택 컴포넌트의 id 조회용 타입명 (capability `idLookupType`). 값이 있으면 그 타입으로
34
+ * 검색 에디터를 띄우고, 없으면(일반 도형·Carrier 등) plain input 으로 두어 불필요한 id
35
+ * 리스트 요청을 보내지 않는다. 기본은 자기 타입이며 컴포넌트가 다른 타입으로 override 가능.
36
+ * (머신/유닛 + Node 가 opt-in. isLVComponent 은 Carrier 포함·Node 제외라 부적합.)
37
+ */
38
+ _idLookupType(selected: Component[]): string | undefined;
39
+ /** Board 루트 속성 state.idLookup 으로 id 룩업 전체 ON/OFF (기본 ON). */
40
+ _idLookupEnabled(selected: Component[]): boolean;
21
41
  _isClassIdentifiable(selected: Component[]): boolean;
22
42
  _isLine(selected: Component[]): boolean;
23
43
  _is3dish(selected: Component[]): boolean;
24
44
  }
45
+ export {};
@@ -4,24 +4,32 @@
4
4
  import { __decorate } from "tslib";
5
5
  import '@operato/input/ox-input-angle.js';
6
6
  import '@operato/input/ox-buttons-radio.js';
7
- import '@material/mwc-icon';
7
+ import '@material/web/icon/icon.js';
8
8
  import '@operato/i18n/ox-i18n.js';
9
9
  import { css, html } from 'lit';
10
10
  import { property } from 'lit/decorators.js';
11
11
  import { keyed } from 'lit/directives/keyed.js';
12
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements';
12
13
  import { AbstractProperty } from '../abstract-property';
13
14
  import { PropertySharedStyle } from '../property-shared-style';
14
15
  import { BoxPaddingEditorStyles } from './box-padding-editor-styles';
15
- export class PropertyShapes extends AbstractProperty {
16
+ import { OxInputEntityId } from './ox-input-entity-id';
17
+ export class PropertyShapes extends ScopedElementsMixin(AbstractProperty) {
16
18
  constructor() {
17
19
  super(...arguments);
18
20
  this.bounds = null;
19
21
  this.selected = [];
20
22
  }
23
+ static get scopedElements() {
24
+ return {
25
+ 'ox-input-entity-id': OxInputEntityId
26
+ };
27
+ }
21
28
  firstUpdated() {
22
29
  this.renderRoot.addEventListener('change', this._onValueChange.bind(this));
23
30
  }
24
31
  render() {
32
+ var _a, _b;
25
33
  const value = this.value || {};
26
34
  const { width, height } = this.bounds || {};
27
35
  return html `
@@ -30,7 +38,18 @@ export class PropertyShapes extends AbstractProperty {
30
38
  ${this._isIdentifiable(this.selected)
31
39
  ? html `
32
40
  <label> <ox-i18n msgid="label.id">ID</ox-i18n> </label>
33
- <input value-key="id" .value=${value.id || ''} />
41
+ ${this._idLookupType(this.selected)
42
+ ? html `
43
+ <ox-input-entity-id
44
+ custom-editor
45
+ value-key="id"
46
+ .value=${value.id || ''}
47
+ .entityType=${this._idLookupType(this.selected)}
48
+ .machine=${this._parentMachineId(this.selected)}
49
+ .requireMachine=${!!this.selected[0].idLookupRequiresMachine}
50
+ ></ox-input-entity-id>
51
+ `
52
+ : html `<input value-key="id" .value=${value.id || ''} />`}
34
53
  `
35
54
  : html ``}
36
55
  ${this._isClassIdentifiable(this.selected)
@@ -85,6 +104,12 @@ export class PropertyShapes extends AbstractProperty {
85
104
  ${this.selected.length && this.selected[0].isRootModel()
86
105
  ? html ``
87
106
  : html `
107
+ <label class="depth"> <ox-i18n msgid="label.depth">depth</ox-i18n> </label>
108
+ <input type="number" value-key="depth" .value=${String((_a = value.depth) !== null && _a !== void 0 ? _a : '')} placeholder="auto" />
109
+
110
+ <label class="zPos"> <ox-i18n msgid="label.zPos">zPos</ox-i18n> </label>
111
+ <input type="number" value-key="zPos" .value=${String((_b = value.zPos) !== null && _b !== void 0 ? _b : '')} placeholder="auto" />
112
+
88
113
  <label class="rotate"> <ox-i18n msgid="label.rotate">rotate</ox-i18n> </label>
89
114
  <ox-input-angle value-key="rotation" .value=${value.rotation}> </ox-input-angle>
90
115
  `}
@@ -176,6 +201,31 @@ export class PropertyShapes extends AbstractProperty {
176
201
  <option value="width">width</option>
177
202
  <option value="height">height</option>
178
203
  </select>
204
+
205
+ <div class="checkbox-row">
206
+ <input
207
+ id="cb-threed"
208
+ type="checkbox"
209
+ value-key="threed"
210
+ .checked=${!!value.threed}
211
+ />
212
+ <label for="cb-threed"><ox-i18n msgid="label.start-in-3d">Start in 3D</ox-i18n></label>
213
+ </div>
214
+
215
+ <div class="checkbox-row">
216
+ <input
217
+ id="cb-floor-constraint"
218
+ type="checkbox"
219
+ value-key="cameraFloorConstraint"
220
+ .checked=${!!value.cameraFloorConstraint}
221
+ />
222
+ <label for="cb-floor-constraint"><ox-i18n msgid="label.keep-above-ground">Keep Above Ground</ox-i18n></label>
223
+ </div>
224
+
225
+ <div class="checkbox-row">
226
+ <input id="cb-id-lookup" type="checkbox" value-key="idLookup" .checked=${value.idLookup !== false} />
227
+ <label for="cb-id-lookup"><ox-i18n msgid="label.id-lookup">ID Lookup</ox-i18n></label>
228
+ </div>
179
229
  </div>
180
230
  </fieldset>
181
231
  `
@@ -225,6 +275,44 @@ export class PropertyShapes extends AbstractProperty {
225
275
  return false;
226
276
  return true;
227
277
  }
278
+ /**
279
+ * 단일 선택 컴포넌트의 부모 machine id (unit 의 ID 검색 시 조회조건으로 전달).
280
+ * 부모가 루트 모델/레이어가 아니고 id 를 가지면 그 id 를 반환 — unit 이 자신의
281
+ * 소속 machine 을 특정할 수 있는 경우에 해당. 그 외엔 빈 문자열.
282
+ */
283
+ _parentMachineId(selected) {
284
+ var _a, _b, _c;
285
+ const target = (selected === null || selected === void 0 ? void 0 : selected.length) === 1 ? selected[0] : null;
286
+ if (!target)
287
+ return '';
288
+ // 명시적 parentObjectId(= 부모 머신 id) 우선, 없으면 실제 scene 부모의 id.
289
+ const pid = (_a = target.state) === null || _a === void 0 ? void 0 : _a.parentObjectId;
290
+ if (pid)
291
+ return String(pid);
292
+ const parent = target.parent;
293
+ if (!parent || ((_b = parent.isRootModel) === null || _b === void 0 ? void 0 : _b.call(parent)) || (parent.isLayer && parent.isLayer()))
294
+ return '';
295
+ return ((_c = parent.state) === null || _c === void 0 ? void 0 : _c.id) || '';
296
+ }
297
+ /**
298
+ * 선택 컴포넌트의 id 조회용 타입명 (capability `idLookupType`). 값이 있으면 그 타입으로
299
+ * 검색 에디터를 띄우고, 없으면(일반 도형·Carrier 등) plain input 으로 두어 불필요한 id
300
+ * 리스트 요청을 보내지 않는다. 기본은 자기 타입이며 컴포넌트가 다른 타입으로 override 가능.
301
+ * (머신/유닛 + Node 가 opt-in. isLVComponent 은 Carrier 포함·Node 제외라 부적합.)
302
+ */
303
+ _idLookupType(selected) {
304
+ if ((selected === null || selected === void 0 ? void 0 : selected.length) !== 1)
305
+ return undefined;
306
+ if (!this._idLookupEnabled(selected))
307
+ return undefined;
308
+ return selected[0].idLookupType;
309
+ }
310
+ /** Board 루트 속성 state.idLookup 으로 id 룩업 전체 ON/OFF (기본 ON). */
311
+ _idLookupEnabled(selected) {
312
+ var _a, _b;
313
+ const root = (_a = selected === null || selected === void 0 ? void 0 : selected[0]) === null || _a === void 0 ? void 0 : _a.root;
314
+ return ((_b = root === null || root === void 0 ? void 0 : root.state) === null || _b === void 0 ? void 0 : _b.idLookup) !== false;
315
+ }
228
316
  _isClassIdentifiable(selected) {
229
317
  if (!selected || (selected[0] && selected[0].isLayer()))
230
318
  return false;
@@ -250,7 +338,7 @@ PropertyShapes.styles = [
250
338
  width: 100%;
251
339
  }
252
340
 
253
- fieldset[collapsable] legend mwc-icon {
341
+ fieldset[collapsable] legend md-icon {
254
342
  float: right;
255
343
  font-size: medium;
256
344
  margin: 0;
@@ -266,6 +354,10 @@ PropertyShapes.styles = [
266
354
  background: var(--url-icon-properties-label) 7px -900px no-repeat;
267
355
  }
268
356
 
357
+ .icon-label .checkbox-row label {
358
+ background: none;
359
+ }
360
+
269
361
  .icon-label label.width {
270
362
  background-position: 7px -96px;
271
363
  }