@fmsim/board 2.0.0-beta.9 → 2.0.0-beta.92

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 (102) hide show
  1. package/custom-elements.json +7351 -3944
  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-property-editor-backdrop-faces.d.ts +50 -0
  12. package/dist/src/component/restful-property-editor-backdrop-faces.js +524 -0
  13. package/dist/src/component/restful-property-editor-backdrop-faces.js.map +1 -0
  14. package/dist/src/data-storage/board-model-cache.d.ts +13 -0
  15. package/dist/src/data-storage/board-model-cache.js +66 -0
  16. package/dist/src/data-storage/board-model-cache.js.map +1 -0
  17. package/dist/src/index.d.ts +3 -0
  18. package/dist/src/index.js +2 -0
  19. package/dist/src/index.js.map +1 -1
  20. package/dist/src/layers/mcs-mouse-event-handler.js +8 -0
  21. package/dist/src/layers/mcs-mouse-event-handler.js.map +1 -1
  22. package/dist/src/layers/movement-layer.d.ts +4 -4
  23. package/dist/src/layers/movement-layer.js +16 -8
  24. package/dist/src/layers/movement-layer.js.map +1 -1
  25. package/dist/src/modeller/bulk-create-dialog.d.ts +51 -0
  26. package/dist/src/modeller/bulk-create-dialog.js +531 -0
  27. package/dist/src/modeller/bulk-create-dialog.js.map +1 -0
  28. package/dist/src/modeller/bulk-create.d.ts +101 -0
  29. package/dist/src/modeller/bulk-create.js +199 -0
  30. package/dist/src/modeller/bulk-create.js.map +1 -0
  31. package/dist/src/modeller/component-toolbar/component-menu.d.ts +1 -1
  32. package/dist/src/modeller/component-toolbar/component-toolbar.d.ts +1 -1
  33. package/dist/src/modeller/edit-toolbar.d.ts +13 -0
  34. package/dist/src/modeller/edit-toolbar.js +168 -7
  35. package/dist/src/modeller/edit-toolbar.js.map +1 -1
  36. package/dist/src/modeller/property-sidebar/abstract-property.js +10 -2
  37. package/dist/src/modeller/property-sidebar/abstract-property.js.map +1 -1
  38. package/dist/src/modeller/property-sidebar/data-binding/data-binding.d.ts +1 -1
  39. package/dist/src/modeller/property-sidebar/effects/effects.d.ts +10 -1
  40. package/dist/src/modeller/property-sidebar/effects/effects.js +66 -1
  41. package/dist/src/modeller/property-sidebar/effects/effects.js.map +1 -1
  42. package/dist/src/modeller/property-sidebar/effects/event-handler-adapter.d.ts +44 -0
  43. package/dist/src/modeller/property-sidebar/effects/event-handler-adapter.js +70 -0
  44. package/dist/src/modeller/property-sidebar/effects/event-handler-adapter.js.map +1 -0
  45. package/dist/src/modeller/property-sidebar/effects/property-event.d.ts +13 -3
  46. package/dist/src/modeller/property-sidebar/effects/property-event.js +20 -11
  47. package/dist/src/modeller/property-sidebar/effects/property-event.js.map +1 -1
  48. package/dist/src/modeller/property-sidebar/property-sidebar.d.ts +1 -1
  49. package/dist/src/modeller/property-sidebar/shapes/ox-input-entity-id.d.ts +75 -0
  50. package/dist/src/modeller/property-sidebar/shapes/ox-input-entity-id.js +346 -0
  51. package/dist/src/modeller/property-sidebar/shapes/ox-input-entity-id.js.map +1 -0
  52. package/dist/src/modeller/property-sidebar/shapes/shapes.d.ts +22 -1
  53. package/dist/src/modeller/property-sidebar/shapes/shapes.js +94 -2
  54. package/dist/src/modeller/property-sidebar/shapes/shapes.js.map +1 -1
  55. package/dist/src/modeller/property-sidebar/specifics/specifics.d.ts +25 -1
  56. package/dist/src/modeller/property-sidebar/specifics/specifics.js +112 -2
  57. package/dist/src/modeller/property-sidebar/specifics/specifics.js.map +1 -1
  58. package/dist/src/modeller/property-sidebar/styles/styles.d.ts +1 -1
  59. package/dist/src/modeller/scene-viewer/ox-scene-layer.d.ts +4 -3
  60. package/dist/src/modeller/scene-viewer/ox-scene-layer.js +2 -1
  61. package/dist/src/modeller/scene-viewer/ox-scene-layer.js.map +1 -1
  62. package/dist/src/modeller/scene-viewer/ox-scene-viewer.d.ts +2 -1
  63. package/dist/src/modeller/scene-viewer/ox-scene-viewer.js +5 -1
  64. package/dist/src/modeller/scene-viewer/ox-scene-viewer.js.map +1 -1
  65. package/dist/src/ox-board-component-info.d.ts +1 -0
  66. package/dist/src/ox-board-component-info.js +35 -2
  67. package/dist/src/ox-board-component-info.js.map +1 -1
  68. package/dist/src/ox-board-list.js +2 -2
  69. package/dist/src/ox-board-list.js.map +1 -1
  70. package/dist/src/ox-board-modeller.d.ts +6 -3
  71. package/dist/src/ox-board-modeller.js +14 -1
  72. package/dist/src/ox-board-modeller.js.map +1 -1
  73. package/dist/src/ox-board-viewer.d.ts +113 -13
  74. package/dist/src/ox-board-viewer.js +764 -98
  75. package/dist/src/ox-board-viewer.js.map +1 -1
  76. package/dist/src/ox-editor-board-selector.d.ts +3 -0
  77. package/dist/src/ox-editor-board-selector.js +38 -9
  78. package/dist/src/ox-editor-board-selector.js.map +1 -1
  79. package/dist/src/ox-playback-controls.d.ts +157 -0
  80. package/dist/src/ox-playback-controls.js +1089 -0
  81. package/dist/src/ox-playback-controls.js.map +1 -0
  82. package/dist/src/playback/carrier-tracking-store.d.ts +51 -0
  83. package/dist/src/playback/carrier-tracking-store.js +115 -0
  84. package/dist/src/playback/carrier-tracking-store.js.map +1 -0
  85. package/dist/src/playback/playback-engine.d.ts +106 -0
  86. package/dist/src/playback/playback-engine.js +395 -0
  87. package/dist/src/playback/playback-engine.js.map +1 -0
  88. package/dist/src/selector/board-creation-popup.js +2 -2
  89. package/dist/src/selector/board-creation-popup.js.map +1 -1
  90. package/dist/src/selector/board-thumbnail-card.js +6 -6
  91. package/dist/src/selector/board-thumbnail-card.js.map +1 -1
  92. package/dist/src/selector/ox-board-creation-card.js +2 -2
  93. package/dist/src/selector/ox-board-creation-card.js.map +1 -1
  94. package/dist/src/selector/ox-board-selector.js +21 -10
  95. package/dist/src/selector/ox-board-selector.js.map +1 -1
  96. package/dist/tsconfig.tsbuildinfo +1 -1
  97. package/icons/components/text.png +0 -0
  98. package/icons/fab/mode-2d.svg +25 -0
  99. package/icons/fab/mode-3d.svg +25 -0
  100. package/icons/fab/playback-play.svg +49 -0
  101. package/icons/fab/playback-stop.svg +50 -0
  102. package/package.json +8 -4
@@ -8,6 +8,16 @@ import { ScopedElementsMixin } from '@open-wc/scoped-elements';
8
8
  import { PropertySharedStyle } from '../property-shared-style';
9
9
  import { PropertyEventTap } from './property-event-tap';
10
10
  import { convert } from './value-converter';
11
+ import { applyLegacyTapToHandlers, eventHandlersToLegacyTap } from './event-handler-adapter.js';
12
+ /**
13
+ * things-scene v10 의 신규 모델 (`state.eventHandlers: EventHandler[]`) 위에서
14
+ * 기존 단일-tap UI 를 그대로 동작시키는 어댑터.
15
+ *
16
+ * read : eventHandlers 배열에서 trigger='click'/'tap' 인 첫 항목 → legacy tap
17
+ * 객체로 변환 후 PropertyEventTap 에 전달.
18
+ * write: PropertyEventTap 이 emit 한 legacy tap 변경 → click 항목을 update/push
19
+ * 한 새 배열로 외부에 emit. 다른 trigger 의 핸들러는 보존.
20
+ */
11
21
  export class PropertyEvent extends ScopedElementsMixin(LitElement) {
12
22
  firstUpdated() {
13
23
  this.renderRoot.addEventListener('change', this._onValueChange.bind(this));
@@ -18,33 +28,32 @@ export class PropertyEvent extends ScopedElementsMixin(LitElement) {
18
28
  };
19
29
  }
20
30
  render() {
21
- const value = this.value || {};
31
+ const tap = eventHandlersToLegacyTap(this.value);
22
32
  return html `
23
33
  <fieldset>
24
34
  <legend>
25
35
  <ox-i18n msgid="label.tap-event" topic="board-modeller/effects/tap-event"></ox-i18n>
26
36
  </legend>
27
37
 
28
- <property-event-tap value-key="tap" .scene=${this.scene} .value=${value.tap || {}}> </property-event-tap>
38
+ <property-event-tap value-key="tap" .scene=${this.scene} .value=${tap}> </property-event-tap>
29
39
  </fieldset>
30
40
  `;
31
41
  }
32
42
  _onValueChange(e) {
33
- var element = e.target;
34
- var key = element.getAttribute('value-key');
35
- if (!key) {
43
+ const element = e.target;
44
+ const key = element.getAttribute('value-key');
45
+ // 어댑터는 tap 트리거 하나만 처리. PropertyEventTap host 가 retarget 된
46
+ // change 의 value-key 만 받아들이고, 자식 input 의 raw change 는 무시.
47
+ if (key !== 'tap')
36
48
  return;
37
- }
38
- this.value = {
39
- ...this.value,
40
- [key]: convert(element)
41
- };
49
+ const newTap = convert(element);
50
+ this.value = applyLegacyTapToHandlers(this.value, newTap);
42
51
  this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
43
52
  }
44
53
  }
45
54
  PropertyEvent.styles = [PropertySharedStyle];
46
55
  __decorate([
47
- property({ type: Object })
56
+ property({ type: Array })
48
57
  ], PropertyEvent.prototype, "value", void 0);
49
58
  __decorate([
50
59
  property({ type: Object })
@@ -1 +1 @@
1
- {"version":3,"file":"property-event.js","sourceRoot":"","sources":["../../../../../src/modeller/property-sidebar/effects/property-event.ts"],"names":[],"mappings":"AAAA;;GAEG;;AAEH,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAG5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE3C,MAAM,OAAO,aAAc,SAAQ,mBAAmB,CAAC,UAAU,CAAC;IAMhE,YAAY;QACV,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAC5E,CAAC;IAED,MAAM,KAAK,cAAc;QACvB,OAAO;YACL,oBAAoB,EAAE,gBAAgB;SACvC,CAAA;IACH,CAAC;IAED,MAAM;QACJ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAA;QAE9B,OAAO,IAAI,CAAA;;;;;;qDAMsC,IAAI,CAAC,KAAK,WAAW,KAAK,CAAC,GAAG,IAAI,EAAE;;KAEpF,CAAA;IACH,CAAC;IAED,cAAc,CAAC,CAAQ;QACrB,IAAI,OAAO,GAAG,CAAC,CAAC,MAAqB,CAAA;QACrC,IAAI,GAAG,GAAG,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,CAAA;QAE3C,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAM;QACR,CAAC;QAED,IAAI,CAAC,KAAK,GAAG;YACX,GAAG,IAAI,CAAC,KAAK;YACb,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC;SACxB,CAAA;QAED,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IAClF,CAAC;;AA3CM,oBAAM,GAAG,CAAC,mBAAmB,CAAC,CAAA;AAET;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;4CAAmB;AAClB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;4CAAc","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport { html, LitElement } from 'lit'\nimport { property } from 'lit/decorators.js'\n\nimport { Properties, Scene } from '@hatiolab/things-scene'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\n\nimport { PropertySharedStyle } from '../property-shared-style'\nimport { PropertyEventTap } from './property-event-tap'\nimport { convert } from './value-converter'\n\nexport class PropertyEvent extends ScopedElementsMixin(LitElement) {\n static styles = [PropertySharedStyle]\n\n @property({ type: Object }) value?: Properties\n @property({ type: Object }) scene?: Scene\n\n firstUpdated() {\n this.renderRoot.addEventListener('change', this._onValueChange.bind(this))\n }\n\n static get scopedElements() {\n return {\n 'property-event-tap': PropertyEventTap\n }\n }\n\n render() {\n const value = this.value || {}\n\n return html`\n <fieldset>\n <legend>\n <ox-i18n msgid=\"label.tap-event\" topic=\"board-modeller/effects/tap-event\"></ox-i18n>\n </legend>\n\n <property-event-tap value-key=\"tap\" .scene=${this.scene} .value=${value.tap || {}}> </property-event-tap>\n </fieldset>\n `\n }\n\n _onValueChange(e: Event) {\n var element = e.target as HTMLElement\n var key = element.getAttribute('value-key')\n\n if (!key) {\n return\n }\n\n this.value = {\n ...this.value,\n [key]: convert(element)\n }\n\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n }\n}\n"]}
1
+ {"version":3,"file":"property-event.js","sourceRoot":"","sources":["../../../../../src/modeller/property-sidebar/effects/property-event.ts"],"names":[],"mappings":"AAAA;;GAEG;;AAEH,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAG5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAC3C,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EAGzB,MAAM,4BAA4B,CAAA;AAEnC;;;;;;;;GAQG;AACH,MAAM,OAAO,aAAc,SAAQ,mBAAmB,CAAC,UAAU,CAAC;IAMhE,YAAY;QACV,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAC5E,CAAC;IAED,MAAM,KAAK,cAAc;QACvB,OAAO;YACL,oBAAoB,EAAE,gBAAgB;SACvC,CAAA;IACH,CAAC;IAED,MAAM;QACJ,MAAM,GAAG,GAAG,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAEhD,OAAO,IAAI,CAAA;;;;;;qDAMsC,IAAI,CAAC,KAAK,WAAW,GAAG;;KAExE,CAAA;IACH,CAAC;IAED,cAAc,CAAC,CAAQ;QACrB,MAAM,OAAO,GAAG,CAAC,CAAC,MAAqB,CAAA;QACvC,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,CAAA;QAE7C,0DAA0D;QAC1D,0DAA0D;QAC1D,IAAI,GAAG,KAAK,KAAK;YAAE,OAAM;QAEzB,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAc,CAAA;QAC5C,IAAI,CAAC,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QAEzD,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IAClF,CAAC;;AAzCM,oBAAM,GAAG,CAAC,mBAAmB,CAAC,CAAA;AAEV;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;4CAAuB;AACrB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;4CAAc","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport { html, LitElement } from 'lit'\nimport { property } from 'lit/decorators.js'\n\nimport { Scene } from '@hatiolab/things-scene'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\n\nimport { PropertySharedStyle } from '../property-shared-style'\nimport { PropertyEventTap } from './property-event-tap'\nimport { convert } from './value-converter'\nimport {\n applyLegacyTapToHandlers,\n eventHandlersToLegacyTap,\n EventHandler,\n LegacyTap\n} from './event-handler-adapter.js'\n\n/**\n * things-scene v10 의 신규 모델 (`state.eventHandlers: EventHandler[]`) 위에서\n * 기존 단일-tap UI 를 그대로 동작시키는 어댑터.\n *\n * read : eventHandlers 배열에서 trigger='click'/'tap' 인 첫 항목 → legacy tap\n * 객체로 변환 후 PropertyEventTap 에 전달.\n * write: PropertyEventTap 이 emit 한 legacy tap 변경 → click 항목을 update/push\n * 한 새 배열로 외부에 emit. 다른 trigger 의 핸들러는 보존.\n */\nexport class PropertyEvent extends ScopedElementsMixin(LitElement) {\n static styles = [PropertySharedStyle]\n\n @property({ type: Array }) value?: EventHandler[]\n @property({ type: Object }) scene?: Scene\n\n firstUpdated() {\n this.renderRoot.addEventListener('change', this._onValueChange.bind(this))\n }\n\n static get scopedElements() {\n return {\n 'property-event-tap': PropertyEventTap\n }\n }\n\n render() {\n const tap = eventHandlersToLegacyTap(this.value)\n\n return html`\n <fieldset>\n <legend>\n <ox-i18n msgid=\"label.tap-event\" topic=\"board-modeller/effects/tap-event\"></ox-i18n>\n </legend>\n\n <property-event-tap value-key=\"tap\" .scene=${this.scene} .value=${tap}> </property-event-tap>\n </fieldset>\n `\n }\n\n _onValueChange(e: Event) {\n const element = e.target as HTMLElement\n const key = element.getAttribute('value-key')\n\n // 어댑터는 tap 트리거 하나만 처리. PropertyEventTap host 가 retarget 된\n // change 의 value-key 만 받아들이고, 자식 input 의 raw change 는 무시.\n if (key !== 'tap') return\n\n const newTap = convert(element) as LegacyTap\n this.value = applyLegacyTapToHandlers(this.value, newTap)\n\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n }\n}\n"]}
@@ -10,7 +10,7 @@ import { SceneInspector } from './inspector/inspector';
10
10
  import { PropertyShapes } from './shapes/shapes.js';
11
11
  import { PropertySpecific } from './specifics/specifics.js';
12
12
  import { PropertyStyles } from './styles/styles.js';
13
- declare const PropertySidebar_base: typeof LitElement & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/types/src/types.js").ScopedElementsHost>;
13
+ declare const PropertySidebar_base: typeof LitElement & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/types.js").ScopedElementsHost>;
14
14
  export declare class PropertySidebar extends PropertySidebar_base {
15
15
  static styles: import("lit").CSSResult[];
16
16
  scene: Scene | null;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+ import '@operato/i18n/ox-i18n.js';
5
+ import { LitElement, PropertyValues } from 'lit';
6
+ /**
7
+ * 컴포넌트 ID 입력기 — 자유 입력 + 타입별 백엔드(MCS) 검색 선택을 함께 제공.
8
+ *
9
+ * - 기존처럼 자유롭게 텍스트로 ID 를 입력할 수 있다 (commit = blur/Enter).
10
+ * - input 에 *포커스가 가면* 아래로 검색 드롭다운이 펼쳐진다. `entityType`
11
+ * (컴포넌트 타입) 으로 MCS 에 정의된 ID 후보를 조회한다.
12
+ * - machine 타입 → 머신 id 가 1레벨 평면으로 (선택 가능)
13
+ * - unit 타입 → machine 이 그룹 헤더, 그 아래 unit id 가 자식 (선택 가능).
14
+ * 컴포넌트가 부모 machine 을 알면(`machine` prop) 그 머신으로 좁혀 조회.
15
+ *
16
+ * value-key="id" 와 함께 property-grid 안에 두면, 이 host 가 `change`(bubbles+
17
+ * composed) 를 발행하므로 AbstractProperty 제너릭 핸들러가 `element.value` 를
18
+ * 읽어 property-change 로 전파한다.
19
+ *
20
+ * 등록: 패널 자식 커스텀 엘리먼트 컨벤션에 따라 전역 `@customElement` 가 아니라
21
+ * 부모 패널(PropertyShapes)의 `scopedElements` 로 등록한다 (@open-wc/scoped-elements).
22
+ */
23
+ export declare class OxInputEntityId extends LitElement {
24
+ static styles: import("lit").CSSResult[];
25
+ /** 현재 ID 값. */
26
+ value: string;
27
+ /** 검색 조건 — 선택된 컴포넌트 타입 (예: 'STOCKER', 'AGV', 'Port'). */
28
+ entityType: string;
29
+ /** 검색 조건 — 부모 machine id (unit 컴포넌트가 소속 machine 을 알 때). */
30
+ machine: string;
31
+ /** machine 필수 타입(예: Port) — machine 없으면 조회하지 않는다 (후보 과다 방지). */
32
+ requireMachine: boolean;
33
+ /** 백엔드 조회 대신 사용할 로컬 후보 리스트 (예: parentObject — 씬의 머신들). null 이면 백엔드 조회. */
34
+ localItems: string[] | null;
35
+ placeholder: string;
36
+ private _open;
37
+ /** 서버에서 받은 전체 후보 리스트 (포커스 시 1회 조회, 키워드 미적용). */
38
+ private _all;
39
+ private _loading;
40
+ /** machine 필수인데 machine 미지정이라 조회를 보류한 상태. */
41
+ private _needMachine;
42
+ /** 선택 가능 항목 기준 active index. */
43
+ private _activeIndex;
44
+ private _input;
45
+ private _blurTimer?;
46
+ private _loadSeq;
47
+ /** 마지막으로 발행한 값 — 같은 값 재발행(undo 중복) 방지. */
48
+ private _lastEmitted?;
49
+ disconnectedCallback(): void;
50
+ updated(changed: PropertyValues): void;
51
+ /**
52
+ * 드롭다운 표시 항목 — 입력값(초기값 포함)으로 풀텍스트 로컬 필터.
53
+ * 단, 매치가 0이면 전체 후보로 fallback (막다른 "결과 없음" 방지 — 예: committed
54
+ * id 가 후보 형식과 달라 안 맞는 경우). 서버 재조회 없음.
55
+ */
56
+ private get _items();
57
+ /** 드롭다운에서 선택 가능한 id 들 (표시 순서). */
58
+ private get _selectableIds();
59
+ render(): import("lit-html").TemplateResult<1>;
60
+ private _renderDropdown;
61
+ private _onFocus;
62
+ private _onBlur;
63
+ private _onInput;
64
+ /** 자유 입력 commit (blur/Enter 의 native change) — 기존 free-text 동작 보존. */
65
+ private _onInputCommit;
66
+ private _onKeydown;
67
+ private _select;
68
+ private _commitValue;
69
+ private _emitChange;
70
+ /**
71
+ * 후보 전체를 1회 조회 (포커스 시). keyword 는 보내지 않으며, 표시 필터는 _items
72
+ * 게터가 로컬에서 처리한다. entityType(+unit 이면 machine) 만 서버로 보낸다.
73
+ */
74
+ private _load;
75
+ }
@@ -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 { searchMachineUnits, filterMachineUnitIds } 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 = filterMachineUnitIds(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 searchMachineUnits(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 { searchMachineUnits, filterMachineUnitIds } 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 = filterMachineUnitIds(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 searchMachineUnits(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"]}
@@ -7,7 +7,12 @@ 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 {};