@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
@@ -1 +1 @@
1
- {"version":3,"file":"effects.js","sourceRoot":"","sources":["../../../../../src/modeller/property-sidebar/effects/effects.ts"],"names":[],"mappings":"AAAA;;GAEG;;AAEH,OAAO,0BAA0B,CAAA;AAEjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAG5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAErD,MAAM,OAAO,eAAgB,SAAQ,mBAAmB,CAAC,gBAAgB,CAAC;IAA1E;;QAK6B,aAAQ,GAAgB,EAAE,CAAA;IA8BvD,CAAC;IA5BC,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,iBAAiB,EAAE,cAAc;YACjC,gBAAgB,EAAE,aAAa;SAChC,CAAA;IACH,CAAC;IAED,MAAM;QACJ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAA;QAE9B,OAAO,IAAI,CAAA;;;;;;qDAMsC,KAAK,CAAC,MAAM,IAAI,EAAE;;;QAG/D,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,CAAC;YACzB,CAAC,CAAC,IAAI,CAAA,6CAA6C,IAAI,CAAC,KAAK,WAAW,KAAK,CAAC,KAAK,IAAI,EAAE,sBAAsB;YAC/G,CAAC,CAAC,OAAO;KACZ,CAAA;IACH,CAAC;;AAjCM,sBAAM,GAAG,CAAC,mBAAmB,CAAC,AAAxB,CAAwB;AAET;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;8CAAmB;AAClB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;8CAAc;AACd;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;iDAA2B","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport '@operato/i18n/ox-i18n.js'\n\nimport { html, nothing } from 'lit'\nimport { property } from 'lit/decorators.js'\n\nimport { Component, Properties, Scene } from '@hatiolab/things-scene'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\n\nimport { AbstractProperty } from '../abstract-property.js'\nimport { PropertySharedStyle } from '../property-shared-style.js'\nimport { PropertyEvent } from './property-event.js'\nimport { PropertyShadow } from './property-shadow.js'\n\nexport class PropertyEffects extends ScopedElementsMixin(AbstractProperty) {\n static styles = [PropertySharedStyle]\n\n @property({ type: Object }) value?: Properties\n @property({ type: Object }) scene?: Scene\n @property({ type: Array }) selected: Component[] = []\n\n firstUpdated() {\n this.renderRoot.addEventListener('change', this._onValueChange.bind(this))\n }\n\n static get scopedElements() {\n return {\n 'property-shadow': PropertyShadow,\n 'property-event': PropertyEvent\n }\n }\n\n render() {\n const value = this.value || {}\n\n return html`\n <fieldset>\n <legend>\n <ox-i18n topic=\"board-modeller/effects/shadow\" msgid=\"label.shadow\">shadow</ox-i18n>\n </legend>\n\n <property-shadow value-key=\"shadow\" .value=${value.shadow || {}}> </property-shadow>\n </fieldset>\n\n ${this.selected.length == 1\n ? html` <property-event value-key=\"event\" .scene=${this.scene} .value=${value.event || {}}> </property-event> `\n : nothing}\n `\n }\n}\n"]}
1
+ {"version":3,"file":"effects.js","sourceRoot":"","sources":["../../../../../src/modeller/property-sidebar/effects/effects.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 { html, nothing } from 'lit'\nimport { property } from 'lit/decorators.js'\n\nimport { Component, Properties, Scene, isThreedEnabled } from '@hatiolab/things-scene'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\n\nimport { AbstractProperty } from '../abstract-property.js'\nimport { PropertySharedStyle } from '../property-shared-style.js'\nimport { PropertyEvent } from './property-event.js'\nimport { PropertyShadow } from './property-shadow.js'\n\nexport class PropertyEffects extends ScopedElementsMixin(AbstractProperty) {\n static styles = [PropertySharedStyle]\n\n @property({ type: Object }) value?: Properties\n @property({ type: Object }) scene?: Scene\n @property({ type: Array }) selected: Component[] = []\n\n firstUpdated() {\n this.renderRoot.addEventListener('change', this._onValueChange.bind(this))\n }\n\n static get scopedElements() {\n return {\n 'property-shadow': PropertyShadow,\n 'property-event': PropertyEvent\n }\n }\n\n render() {\n const value = this.value || {}\n\n // 3D 그림자 편집은 단일 선택 + 3D 표현 가능한 컴포넌트에서만 노출.\n // (material3d 는 컴포넌트별 객체라 다중 선택 일괄 병합은 위험 — 단일로 제한.)\n const target = this.selected.length == 1 ? (this.selected[0] as any) : null\n const supports3d = !!target?.is3dish?.()\n const material3d = (value.material3d as any) || {}\n const castShadow = material3d.castShadow ?? true\n const receiveShadow = material3d.receiveShadow ?? false\n\n return html`\n <fieldset>\n <legend>\n <ox-i18n topic=\"board-modeller/effects/shadow\" msgid=\"label.shadow\">shadow</ox-i18n>\n </legend>\n\n <property-shadow value-key=\"shadow\" .value=${value.shadow || {}}> </property-shadow>\n </fieldset>\n\n ${supports3d && isThreedEnabled()\n ? html`\n <fieldset>\n <legend>\n <ox-i18n topic=\"board-modeller/effects/shadow-3d\" msgid=\"label.shadow-3d\">3D shadow</ox-i18n>\n </legend>\n\n <div>\n <input\n id=\"cb-cast-shadow\"\n type=\"checkbox\"\n .checked=${castShadow}\n @change=${(e: Event) => this._onShadow3dChange('castShadow', e)}\n />\n <label for=\"cb-cast-shadow\">\n <ox-i18n msgid=\"label.cast-shadow\">cast shadow</ox-i18n>\n </label>\n\n <input\n id=\"cb-receive-shadow\"\n type=\"checkbox\"\n .checked=${receiveShadow}\n @change=${(e: Event) => this._onShadow3dChange('receiveShadow', e)}\n />\n <label for=\"cb-receive-shadow\">\n <ox-i18n msgid=\"label.receive-shadow\">receive shadow</ox-i18n>\n </label>\n </div>\n </fieldset>\n `\n : nothing}\n\n ${this.selected.length == 1\n ? html`\n <property-event\n value-key=\"eventHandlers\"\n .scene=${this.scene}\n .value=${value.eventHandlers || []}\n >\n </property-event>\n `\n : nothing}\n `\n }\n\n /**\n * 3D 그림자 토글 — material3d.castShadow / receiveShadow 만 갱신.\n *\n * material3d 는 단일 객체 속성이므로 기존 material3d 전체를 보존한 채 해당\n * 키만 덮어써 dispatch 한다 ({ castShadow } 만 보내면 set() 의 top-level shallow\n * merge 로 material3d 전체가 사라짐). value-key 를 두지 않아 AbstractProperty\n * 의 제너릭 change 핸들러는 이 입력을 무시한다.\n */\n private _onShadow3dChange(key: 'castShadow' | 'receiveShadow', e: Event) {\n const checked = (e.target as HTMLInputElement).checked\n const current = (this.value?.material3d as any) || {}\n\n this.dispatchEvent(\n new CustomEvent('property-change', {\n bubbles: true,\n composed: true,\n detail: { material3d: { ...current, [key]: checked } }\n })\n )\n }\n}\n"]}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ *
4
+ * 기존 PropertyEvent UI 는 legacy `state.event = { tap: {...} }` 단일 객체
5
+ * 모델 위에 만들어졌으나, things-scene v10 의 신규 모델은
6
+ * `state.eventHandlers: EventHandler[]` 다중 핸들러 배열. dispatcher 가
7
+ * legacy fallback 도 지원하지만, UI 가 신규 모델로 직접 read/write 하도록
8
+ * 정합화한다.
9
+ *
10
+ * 정책 — 사용자 의도에 따른 *최소 호환*:
11
+ * - 신규 모델의 multi-trigger / multi-handler 는 미지원
12
+ * - 기존 UI 그대로 — tap (click) 단일 핸들러만
13
+ * - 다른 trigger 의 핸들러가 이미 배열에 있으면 *건드리지 않는다*
14
+ * (UI 가 모르는 항목은 보존)
15
+ */
16
+ /**
17
+ * EventHandler — things-scene v10 의 신규 모델 spec 미러.
18
+ * things-scene root export 에 아직 포함되지 않아 inline 정의.
19
+ * (참조: things-scene/src/interfaces/event-handler.ts:29)
20
+ */
21
+ export interface EventHandler {
22
+ trigger: string;
23
+ action?: string;
24
+ code?: string;
25
+ target?: string;
26
+ value?: any;
27
+ options?: Record<string, any>;
28
+ disabled?: boolean;
29
+ }
30
+ /** legacy tap 객체 형태 — PropertyEventTap UI 가 다루는 모양. */
31
+ export interface LegacyTap {
32
+ action?: string;
33
+ target?: string;
34
+ value?: any;
35
+ pressed?: boolean;
36
+ options?: Record<string, any>;
37
+ }
38
+ /** trigger 가 click/tap 인 첫 핸들러를 legacy tap 객체로 변환. */
39
+ export declare function eventHandlersToLegacyTap(handlers: EventHandler[] | undefined): LegacyTap;
40
+ /**
41
+ * 기존 배열에서 click/tap 항목을 새 값으로 교체 (또는 push, 또는 제거).
42
+ * 다른 trigger 항목은 그대로 보존.
43
+ */
44
+ export declare function applyLegacyTapToHandlers(handlers: EventHandler[] | undefined, newTap: LegacyTap | undefined): EventHandler[];
@@ -0,0 +1,70 @@
1
+ /** trigger 가 click/tap 인 첫 핸들러를 legacy tap 객체로 변환. */
2
+ export function eventHandlersToLegacyTap(handlers) {
3
+ var _a, _b;
4
+ if (!Array.isArray(handlers))
5
+ return {};
6
+ const h = handlers.find(h => h.trigger === 'click' || h.trigger === 'tap');
7
+ if (!h)
8
+ return {};
9
+ // action='script' 인 경우 code 를 value 자리로 노출 — legacy UI 가 value
10
+ // 텍스트 영역에 script 를 넣어 쓰던 관습 보존.
11
+ if (h.action === 'script') {
12
+ return { action: 'script', target: h.target, value: (_a = h.code) !== null && _a !== void 0 ? _a : '' };
13
+ }
14
+ return {
15
+ action: h.action,
16
+ target: h.target,
17
+ value: h.value,
18
+ pressed: (_b = h.options) === null || _b === void 0 ? void 0 : _b.pressed,
19
+ options: h.options
20
+ };
21
+ }
22
+ /** legacy tap 객체 → EventHandler. action 없으면 null (= 삭제 의미). */
23
+ function legacyTapToEventHandler(tap) {
24
+ var _a;
25
+ if (!tap)
26
+ return null;
27
+ // action 이 비어있고 target/value 도 없으면 의미 있는 데이터 없음 → 제거
28
+ const hasContent = !!(tap.action || tap.target || tap.value);
29
+ if (!hasContent)
30
+ return null;
31
+ if (tap.action === 'script') {
32
+ return {
33
+ trigger: 'click',
34
+ action: 'script',
35
+ code: typeof tap.value === 'string' ? tap.value : '',
36
+ target: tap.target
37
+ };
38
+ }
39
+ // pressed 는 legacy 의 *최상위 키* 였지만 신규 모델에선 options 의 일부.
40
+ const options = { ...((_a = tap.options) !== null && _a !== void 0 ? _a : {}) };
41
+ if (tap.pressed !== undefined)
42
+ options.pressed = tap.pressed;
43
+ return {
44
+ trigger: 'click',
45
+ action: tap.action,
46
+ target: tap.target,
47
+ value: tap.value,
48
+ ...(Object.keys(options).length > 0 ? { options } : {})
49
+ };
50
+ }
51
+ /**
52
+ * 기존 배열에서 click/tap 항목을 새 값으로 교체 (또는 push, 또는 제거).
53
+ * 다른 trigger 항목은 그대로 보존.
54
+ */
55
+ export function applyLegacyTapToHandlers(handlers, newTap) {
56
+ const out = Array.isArray(handlers) ? [...handlers] : [];
57
+ const idx = out.findIndex(h => h.trigger === 'click' || h.trigger === 'tap');
58
+ const newHandler = legacyTapToEventHandler(newTap);
59
+ if (newHandler) {
60
+ if (idx >= 0)
61
+ out[idx] = newHandler;
62
+ else
63
+ out.push(newHandler);
64
+ }
65
+ else if (idx >= 0) {
66
+ out.splice(idx, 1);
67
+ }
68
+ return out;
69
+ }
70
+ //# sourceMappingURL=event-handler-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-handler-adapter.js","sourceRoot":"","sources":["../../../../../src/modeller/property-sidebar/effects/event-handler-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * 기존 PropertyEvent UI 는 legacy `state.event = { tap: {...} }` 단일 객체\n * 모델 위에 만들어졌으나, things-scene v10 의 신규 모델은\n * `state.eventHandlers: EventHandler[]` 다중 핸들러 배열. dispatcher 가\n * legacy fallback 도 지원하지만, UI 가 신규 모델로 직접 read/write 하도록\n * 정합화한다.\n *\n * 정책 — 사용자 의도에 따른 *최소 호환*:\n * - 신규 모델의 multi-trigger / multi-handler 는 미지원\n * - 기존 UI 그대로 — tap (click) 단일 핸들러만\n * - 다른 trigger 의 핸들러가 이미 배열에 있으면 *건드리지 않는다*\n * (UI 가 모르는 항목은 보존)\n */\n/**\n * EventHandler — things-scene v10 의 신규 모델 spec 미러.\n * things-scene root export 에 아직 포함되지 않아 inline 정의.\n * (참조: things-scene/src/interfaces/event-handler.ts:29)\n */\nexport interface EventHandler {\n trigger: string\n action?: string\n code?: string\n target?: string\n value?: any\n options?: Record<string, any>\n disabled?: boolean\n}\n\n/** legacy tap 객체 형태 — PropertyEventTap UI 가 다루는 모양. */\nexport interface LegacyTap {\n action?: string\n target?: string\n value?: any\n pressed?: boolean\n options?: Record<string, any>\n}\n\n/** trigger 가 click/tap 인 첫 핸들러를 legacy tap 객체로 변환. */\nexport function eventHandlersToLegacyTap(handlers: EventHandler[] | undefined): LegacyTap {\n if (!Array.isArray(handlers)) return {}\n const h = handlers.find(h => h.trigger === 'click' || h.trigger === 'tap')\n if (!h) return {}\n\n // action='script' 인 경우 code 를 value 자리로 노출 — legacy UI 가 value\n // 텍스트 영역에 script 를 넣어 쓰던 관습 보존.\n if (h.action === 'script') {\n return { action: 'script', target: h.target, value: h.code ?? '' }\n }\n\n return {\n action: h.action,\n target: h.target,\n value: h.value,\n pressed: h.options?.pressed,\n options: h.options\n }\n}\n\n/** legacy tap 객체 → EventHandler. action 없으면 null (= 삭제 의미). */\nfunction legacyTapToEventHandler(tap: LegacyTap | undefined): EventHandler | null {\n if (!tap) return null\n // action 이 비어있고 target/value 도 없으면 의미 있는 데이터 없음 → 제거\n const hasContent = !!(tap.action || tap.target || tap.value)\n if (!hasContent) return null\n\n if (tap.action === 'script') {\n return {\n trigger: 'click',\n action: 'script',\n code: typeof tap.value === 'string' ? tap.value : '',\n target: tap.target\n }\n }\n\n // pressed 는 legacy 의 *최상위 키* 였지만 신규 모델에선 options 의 일부.\n const options: Record<string, any> = { ...(tap.options ?? {}) }\n if (tap.pressed !== undefined) options.pressed = tap.pressed\n\n return {\n trigger: 'click',\n action: tap.action,\n target: tap.target,\n value: tap.value,\n ...(Object.keys(options).length > 0 ? { options } : {})\n }\n}\n\n/**\n * 기존 배열에서 click/tap 항목을 새 값으로 교체 (또는 push, 또는 제거).\n * 다른 trigger 항목은 그대로 보존.\n */\nexport function applyLegacyTapToHandlers(\n handlers: EventHandler[] | undefined,\n newTap: LegacyTap | undefined\n): EventHandler[] {\n const out: EventHandler[] = Array.isArray(handlers) ? [...handlers] : []\n const idx = out.findIndex(h => h.trigger === 'click' || h.trigger === 'tap')\n const newHandler = legacyTapToEventHandler(newTap)\n\n if (newHandler) {\n if (idx >= 0) out[idx] = newHandler\n else out.push(newHandler)\n } else if (idx >= 0) {\n out.splice(idx, 1)\n }\n return out\n}\n"]}
@@ -2,12 +2,22 @@
2
2
  * @license Copyright © HatioLab Inc. All rights reserved.
3
3
  */
4
4
  import { LitElement } from 'lit';
5
- import { Properties, Scene } from '@hatiolab/things-scene';
5
+ import { Scene } from '@hatiolab/things-scene';
6
6
  import { PropertyEventTap } from './property-event-tap';
7
- declare const PropertyEvent_base: typeof LitElement & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/types/src/types").ScopedElementsHost>;
7
+ import { EventHandler } from './event-handler-adapter.js';
8
+ declare const PropertyEvent_base: typeof LitElement & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/types.js").ScopedElementsHost>;
9
+ /**
10
+ * things-scene v10 의 신규 모델 (`state.eventHandlers: EventHandler[]`) 위에서
11
+ * 기존 단일-tap UI 를 그대로 동작시키는 어댑터.
12
+ *
13
+ * read : eventHandlers 배열에서 trigger='click'/'tap' 인 첫 항목 → legacy tap
14
+ * 객체로 변환 후 PropertyEventTap 에 전달.
15
+ * write: PropertyEventTap 이 emit 한 legacy tap 변경 → click 항목을 update/push
16
+ * 한 새 배열로 외부에 emit. 다른 trigger 의 핸들러는 보존.
17
+ */
8
18
  export declare class PropertyEvent extends PropertyEvent_base {
9
19
  static styles: import("lit").CSSResult[];
10
- value?: Properties;
20
+ value?: EventHandler[];
11
21
  scene?: Scene;
12
22
  firstUpdated(): void;
13
23
  static get scopedElements(): {
@@ -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"]}
@@ -0,0 +1,66 @@
1
+ import { Component, Scene } from '@hatiolab/things-scene';
2
+ import { InspectorIndex, SearchTerm } from './inspector-model.js';
3
+ export declare class InspectorController {
4
+ private _scene?;
5
+ private _extendedMap;
6
+ /** 타입 그룹 뷰의 펼침 상태 — 그룹 키(type)는 안정적이라 Set 으로 관리. 기본 전부 접힘. */
7
+ private _expandedGroups;
8
+ /**
9
+ * 모델링 포커스 dim 처리된 그룹(타입) 집합.
10
+ * scene 에 보존(scene._inspectorDimmedTypes)하여 인스펙터 탭을 벗어났다 돌아와도 유지된다.
11
+ * (보드를 다시 열면 새 scene 이라 자연 초기화 — transient.) bind 에서 scene 것으로 재취득.
12
+ */
13
+ private _dimmedTypes;
14
+ /** shift+click 범위(볼륨) 선택의 기준점 — 마지막 단일/토글 클릭. */
15
+ private _anchor?;
16
+ private _index?;
17
+ private _onChange?;
18
+ /** scene 에 바인딩 — 이전 바인딩은 해제한다. onChange 는 재렌더 요청 콜백. */
19
+ bind(scene: Scene | undefined, onChange: () => void): void;
20
+ unbind(): void;
21
+ get scene(): Scene | undefined;
22
+ get selected(): Component[];
23
+ /** 캐시된 모델 인덱스 — 없으면 build. execute/undo/redo 시 무효화. */
24
+ get index(): InspectorIndex | undefined;
25
+ private _invalidateAndRefresh;
26
+ /** 선택된 컴포넌트의 조상을 펼치고 재렌더 요청. */
27
+ private _refresh;
28
+ isExtended(component: Component): boolean;
29
+ toggleExtended(component: Component): void;
30
+ /** 트리 노드 핸들(펼침/접힘/공백) 클래스. */
31
+ nodeHandleClass(component: Component): string;
32
+ isGroupExpanded(type: string): boolean;
33
+ toggleGroup(type: string): void;
34
+ /** 모든 그룹 접기 — "그룹 레벨까지만 펼침"(헤더만 표시). */
35
+ collapseAllGroups(): void;
36
+ /** 모든 그룹 펼치기. */
37
+ expandAllGroups(types: string[]): void;
38
+ isTypeDimmed(type: string): boolean;
39
+ /** 그룹(타입) dim-out 토글 → scene.focusFilter 재설정(편집 모드에서 캔버스 dim + pick 제외). */
40
+ toggleTypeDim(type: string): void;
41
+ private _applyFocusFilter;
42
+ /** 그룹 전원이 hidden 이면 true(집계). 멤버/트리 eye 규약과 동일한 아이콘 판정에 사용. */
43
+ isTypeHidden(type: string): boolean;
44
+ /** 그룹(타입) hidden 토글 → 하나라도 보이면 전원 숨김, 전원 숨김이면 전원 표시. */
45
+ toggleTypeHidden(type: string): void;
46
+ /** 개별 컴포넌트 hidden(영구 모델 속성) 토글 — 기존 eye 동작. */
47
+ toggleHidden(component: Component): void;
48
+ /** 개별 컴포넌트 locked(영구 모델 속성) 토글. */
49
+ toggleLocked(component: Component): void;
50
+ /** 캔버스를 해당 컴포넌트로 센터 이동. */
51
+ centerTo(component: Component): void;
52
+ /** 선택 — append(ctrl/cmd) 시 토글식 다중 선택. anchor 를 갱신한다. */
53
+ select(component: Component, append?: boolean): void;
54
+ /**
55
+ * shift+click 범위(볼륨) 선택 — anchor↔target 사이의 **보이는 트리 행**을 블록 선택한다.
56
+ * target 이 이미 선택돼 있으면 그 범위를 블록 **해제**(additive/subtractive). anchor 는 유지.
57
+ */
58
+ selectRange(target: Component, terms: SearchTerm[]): void;
59
+ /** fmsim 선택 강조 — deco-border (operato 는 reaction). */
60
+ private _decorate;
61
+ /**
62
+ * 현재 트리 뷰에 보이는 컴포넌트를 렌더 순서대로 반환 — 범위 선택의 기준.
63
+ * renderNode 와 동일 규칙(shouldBeShown = 자신/자손 매치, isExtended 자식만 전개).
64
+ */
65
+ private _visibleOrder;
66
+ }
@@ -0,0 +1,288 @@
1
+ import { buildInspectorIndex, matchesQuery } from './inspector-model.js';
2
+ export class InspectorController {
3
+ constructor() {
4
+ this._extendedMap = new WeakMap();
5
+ /** 타입 그룹 뷰의 펼침 상태 — 그룹 키(type)는 안정적이라 Set 으로 관리. 기본 전부 접힘. */
6
+ this._expandedGroups = new Set();
7
+ /**
8
+ * 모델링 포커스 dim 처리된 그룹(타입) 집합.
9
+ * scene 에 보존(scene._inspectorDimmedTypes)하여 인스펙터 탭을 벗어났다 돌아와도 유지된다.
10
+ * (보드를 다시 열면 새 scene 이라 자연 초기화 — transient.) bind 에서 scene 것으로 재취득.
11
+ */
12
+ this._dimmedTypes = new Set();
13
+ this._invalidateAndRefresh = () => {
14
+ this._index = undefined;
15
+ this._refresh();
16
+ };
17
+ /** 선택된 컴포넌트의 조상을 펼치고 재렌더 요청. */
18
+ this._refresh = () => {
19
+ var _a, _b;
20
+ const selected = ((_a = this._scene) === null || _a === void 0 ? void 0 : _a.selected) || [];
21
+ selected.forEach((component) => {
22
+ let parent = component.parent;
23
+ while (parent && !this._extendedMap.get(parent)) {
24
+ this._extendedMap.set(parent, true);
25
+ parent = parent.parent;
26
+ }
27
+ });
28
+ (_b = this._onChange) === null || _b === void 0 ? void 0 : _b.call(this);
29
+ };
30
+ }
31
+ /** scene 에 바인딩 — 이전 바인딩은 해제한다. onChange 는 재렌더 요청 콜백. */
32
+ bind(scene, onChange) {
33
+ this.unbind();
34
+ this._scene = scene;
35
+ this._onChange = onChange;
36
+ this._index = undefined;
37
+ if (scene && scene.root) {
38
+ // dim 상태는 scene 에 보존 — 인스펙터 탭을 벗어났다 돌아와도(재마운트) 유지된다.
39
+ const anyScene = scene;
40
+ if (!anyScene._inspectorDimmedTypes)
41
+ anyScene._inspectorDimmedTypes = new Set();
42
+ this._dimmedTypes = anyScene._inspectorDimmedTypes;
43
+ this._applyFocusFilter(); // 보존된 dim 을 캔버스에 재적용 (idempotent)
44
+ scene.on('selected', this._refresh, this);
45
+ scene.on('execute', this._invalidateAndRefresh, this);
46
+ scene.on('undo', this._invalidateAndRefresh, this);
47
+ scene.on('redo', this._invalidateAndRefresh, this);
48
+ this._refresh();
49
+ }
50
+ }
51
+ unbind() {
52
+ const scene = this._scene;
53
+ if (scene) {
54
+ // dim(포커스 필터)은 편집 세션 동안 유지 — 인스펙터 탭을 벗어나도 해제하지 않는다.
55
+ // scene.focusFilter 와 scene._inspectorDimmedTypes 는 그대로 두고, 이벤트 구독만 해제.
56
+ // (보드를 다시 열면 새 scene 이라 자연 초기화된다.)
57
+ scene.off('selected', undefined, this);
58
+ scene.off('execute', undefined, this);
59
+ scene.off('undo', undefined, this);
60
+ scene.off('redo', undefined, this);
61
+ }
62
+ this._scene = undefined;
63
+ this._index = undefined;
64
+ this._extendedMap = new WeakMap();
65
+ // _dimmedTypes 는 scene 에 보존되므로 지우지 않는다. 로컬 참조만 분리(다음 bind 에서 재취득).
66
+ this._dimmedTypes = new Set();
67
+ }
68
+ get scene() {
69
+ return this._scene;
70
+ }
71
+ get selected() {
72
+ var _a;
73
+ return ((_a = this._scene) === null || _a === void 0 ? void 0 : _a.selected) || [];
74
+ }
75
+ /** 캐시된 모델 인덱스 — 없으면 build. execute/undo/redo 시 무효화. */
76
+ get index() {
77
+ var _a;
78
+ if (!((_a = this._scene) === null || _a === void 0 ? void 0 : _a.root))
79
+ return undefined;
80
+ if (!this._index)
81
+ this._index = buildInspectorIndex(this._scene.root);
82
+ return this._index;
83
+ }
84
+ isExtended(component) {
85
+ return !!this._extendedMap.get(component);
86
+ }
87
+ toggleExtended(component) {
88
+ var _a;
89
+ if (this.isExtended(component)) {
90
+ this._extendedMap.delete(component);
91
+ }
92
+ else {
93
+ this._extendedMap.set(component, true);
94
+ }
95
+ (_a = this._onChange) === null || _a === void 0 ? void 0 : _a.call(this);
96
+ }
97
+ /** 트리 노드 핸들(펼침/접힘/공백) 클래스. */
98
+ nodeHandleClass(component) {
99
+ if (component.isContainer() && component.components.length > 0) {
100
+ return this.isExtended(component) ? 'extended' : 'collapsed';
101
+ }
102
+ return 'collapsespace';
103
+ }
104
+ // ── 타입 그룹 뷰 확장 상태 ──
105
+ isGroupExpanded(type) {
106
+ return this._expandedGroups.has(type);
107
+ }
108
+ toggleGroup(type) {
109
+ var _a;
110
+ if (this._expandedGroups.has(type)) {
111
+ this._expandedGroups.delete(type);
112
+ }
113
+ else {
114
+ this._expandedGroups.add(type);
115
+ }
116
+ (_a = this._onChange) === null || _a === void 0 ? void 0 : _a.call(this);
117
+ }
118
+ /** 모든 그룹 접기 — "그룹 레벨까지만 펼침"(헤더만 표시). */
119
+ collapseAllGroups() {
120
+ var _a;
121
+ if (this._expandedGroups.size === 0)
122
+ return;
123
+ this._expandedGroups.clear();
124
+ (_a = this._onChange) === null || _a === void 0 ? void 0 : _a.call(this);
125
+ }
126
+ /** 모든 그룹 펼치기. */
127
+ expandAllGroups(types) {
128
+ var _a;
129
+ types.forEach(t => this._expandedGroups.add(t));
130
+ (_a = this._onChange) === null || _a === void 0 ? void 0 : _a.call(this);
131
+ }
132
+ // ── 모델링 포커스 dim (그룹 단위, transient) ──
133
+ isTypeDimmed(type) {
134
+ return this._dimmedTypes.has(type);
135
+ }
136
+ /** 그룹(타입) dim-out 토글 → scene.focusFilter 재설정(편집 모드에서 캔버스 dim + pick 제외). */
137
+ toggleTypeDim(type) {
138
+ var _a;
139
+ if (this._dimmedTypes.has(type))
140
+ this._dimmedTypes.delete(type);
141
+ else
142
+ this._dimmedTypes.add(type);
143
+ this._applyFocusFilter();
144
+ (_a = this._onChange) === null || _a === void 0 ? void 0 : _a.call(this);
145
+ }
146
+ _applyFocusFilter() {
147
+ if (!this._scene)
148
+ return;
149
+ if (this._dimmedTypes.size === 0) {
150
+ this._scene.focusFilter = undefined;
151
+ }
152
+ else {
153
+ const dimmed = this._dimmedTypes;
154
+ // focus(정상) = dim 집합에 없는 타입. 불일치(=dim 그룹)만 캔버스에서 흐림 + pick 제외.
155
+ this._scene.focusFilter = (component) => !dimmed.has(component.state.type);
156
+ }
157
+ }
158
+ // ── 그룹(타입) 단위 hidden 토글 (개별 hidden 의 집계 — dim 과 달리 영구 모델 속성) ──
159
+ /** 그룹 전원이 hidden 이면 true(집계). 멤버/트리 eye 규약과 동일한 아이콘 판정에 사용. */
160
+ isTypeHidden(type) {
161
+ var _a;
162
+ const entries = (_a = this._index) === null || _a === void 0 ? void 0 : _a.byType.get(type);
163
+ return !!entries && entries.length > 0 && entries.every(e => !!e.component.get('hidden'));
164
+ }
165
+ /** 그룹(타입) hidden 토글 → 하나라도 보이면 전원 숨김, 전원 숨김이면 전원 표시. */
166
+ toggleTypeHidden(type) {
167
+ var _a, _b;
168
+ const entries = (_a = this._index) === null || _a === void 0 ? void 0 : _a.byType.get(type);
169
+ if (!entries || entries.length === 0)
170
+ return;
171
+ const target = !entries.every(e => !!e.component.get('hidden'));
172
+ for (const e of entries)
173
+ e.component.set('hidden', target);
174
+ (_b = this._onChange) === null || _b === void 0 ? void 0 : _b.call(this);
175
+ }
176
+ /** 개별 컴포넌트 hidden(영구 모델 속성) 토글 — 기존 eye 동작. */
177
+ toggleHidden(component) {
178
+ var _a;
179
+ component.set('hidden', !component.get('hidden'));
180
+ (_a = this._onChange) === null || _a === void 0 ? void 0 : _a.call(this);
181
+ }
182
+ /** 개별 컴포넌트 locked(영구 모델 속성) 토글. */
183
+ toggleLocked(component) {
184
+ var _a;
185
+ component.set('locked', !component.get('locked'));
186
+ (_a = this._onChange) === null || _a === void 0 ? void 0 : _a.call(this);
187
+ }
188
+ /** 캔버스를 해당 컴포넌트로 센터 이동. */
189
+ centerTo(component) {
190
+ var _a, _b;
191
+ ;
192
+ (_b = (_a = this._scene) === null || _a === void 0 ? void 0 : _a.centerTo) === null || _b === void 0 ? void 0 : _b.call(_a, component, true);
193
+ }
194
+ /** 선택 — append(ctrl/cmd) 시 토글식 다중 선택. anchor 를 갱신한다. */
195
+ select(component, append = false) {
196
+ var _a;
197
+ const scene = this._scene;
198
+ if (!scene)
199
+ return;
200
+ const selected = scene.selected;
201
+ if (append) {
202
+ const idx = selected.findIndex((s) => s === component);
203
+ if (idx != -1) {
204
+ selected.splice(idx, 1);
205
+ scene.selected = [...selected];
206
+ }
207
+ else {
208
+ scene.selected = [...selected, component];
209
+ }
210
+ }
211
+ else {
212
+ scene.selected = [component];
213
+ }
214
+ this._anchor = component;
215
+ this._decorate(component);
216
+ (_a = this._onChange) === null || _a === void 0 ? void 0 : _a.call(this);
217
+ }
218
+ /**
219
+ * shift+click 범위(볼륨) 선택 — anchor↔target 사이의 **보이는 트리 행**을 블록 선택한다.
220
+ * target 이 이미 선택돼 있으면 그 범위를 블록 **해제**(additive/subtractive). anchor 는 유지.
221
+ */
222
+ selectRange(target, terms) {
223
+ var _a;
224
+ const scene = this._scene;
225
+ if (!scene)
226
+ return;
227
+ const order = this._visibleOrder(terms);
228
+ const ti = order.indexOf(target);
229
+ if (ti < 0)
230
+ return;
231
+ const ai = this._anchor ? order.indexOf(this._anchor) : -1;
232
+ if (ai < 0) {
233
+ // anchor 없음/보이지 않음 → 단일 토글로 폴백(+ anchor 설정)
234
+ this.select(target, true);
235
+ return;
236
+ }
237
+ const [lo, hi] = ai <= ti ? [ai, ti] : [ti, ai];
238
+ const range = order.slice(lo, hi + 1);
239
+ const rangeSet = new Set(range);
240
+ // target 이 이미 선택 상태면 블록 해제, 아니면 블록 선택.
241
+ if (scene.selected.indexOf(target) > -1) {
242
+ scene.selected = scene.selected.filter((c) => !rangeSet.has(c));
243
+ range.forEach(c => c.trigger('deco-border-reset'));
244
+ }
245
+ else {
246
+ const cur = new Set(scene.selected);
247
+ const next = [...scene.selected];
248
+ range.forEach(c => {
249
+ if (!cur.has(c))
250
+ next.push(c);
251
+ this._decorate(c);
252
+ });
253
+ scene.selected = next;
254
+ }
255
+ // anchor 는 유지 — 연속 shift+click 으로 범위 확장/축소.
256
+ (_a = this._onChange) === null || _a === void 0 ? void 0 : _a.call(this);
257
+ }
258
+ /** fmsim 선택 강조 — deco-border (operato 는 reaction). */
259
+ _decorate(component) {
260
+ component.trigger('deco-border-reset');
261
+ component.trigger('deco-border', { distance: 8, lineWidth: 8, color: '#f007' });
262
+ }
263
+ /**
264
+ * 현재 트리 뷰에 보이는 컴포넌트를 렌더 순서대로 반환 — 범위 선택의 기준.
265
+ * renderNode 와 동일 규칙(shouldBeShown = 자신/자손 매치, isExtended 자식만 전개).
266
+ */
267
+ _visibleOrder(terms) {
268
+ var _a;
269
+ const root = (_a = this._scene) === null || _a === void 0 ? void 0 : _a.root;
270
+ if (!root)
271
+ return [];
272
+ const shown = (c) => matchesQuery(c, terms) ||
273
+ (c.isContainer() && c.components.some((child) => shown(child)));
274
+ const out = [];
275
+ const walk = (c) => {
276
+ if (!shown(c))
277
+ return;
278
+ out.push(c);
279
+ if (this.isExtended(c) && c.isContainer()) {
280
+ for (const child of c.components)
281
+ walk(child);
282
+ }
283
+ };
284
+ walk(root);
285
+ return out;
286
+ }
287
+ }
288
+ //# sourceMappingURL=inspector-controller.js.map