@operato/property-panel 10.0.0-beta.7 → 10.0.0

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 (84) hide show
  1. package/CHANGELOG.md +478 -0
  2. package/dist/src/graphql/data-subscription.js +5 -1
  3. package/dist/src/graphql/data-subscription.js.map +1 -1
  4. package/dist/src/index.d.ts +4 -0
  5. package/dist/src/index.js +6 -0
  6. package/dist/src/index.js.map +1 -1
  7. package/dist/src/ox-property-panel.d.ts +17 -0
  8. package/dist/src/ox-property-panel.js +105 -39
  9. package/dist/src/ox-property-panel.js.map +1 -1
  10. package/dist/src/property-panel/abstract-property.js +11 -2
  11. package/dist/src/property-panel/abstract-property.js.map +1 -1
  12. package/dist/src/property-panel/data-binding/data-binding-mapper.js +11 -3
  13. package/dist/src/property-panel/data-binding/data-binding-mapper.js.map +1 -1
  14. package/dist/src/property-panel/data-binding/data-binding-popup.d.ts +63 -0
  15. package/dist/src/property-panel/data-binding/data-binding-popup.js +1414 -0
  16. package/dist/src/property-panel/data-binding/data-binding-popup.js.map +1 -0
  17. package/dist/src/property-panel/data-binding/data-binding.d.ts +1 -0
  18. package/dist/src/property-panel/data-binding/data-binding.js +37 -5
  19. package/dist/src/property-panel/data-binding/data-binding.js.map +1 -1
  20. package/dist/src/property-panel/effects/effects.js +1 -1
  21. package/dist/src/property-panel/effects/effects.js.map +1 -1
  22. package/dist/src/property-panel/effects/property-event-tap.js +21 -1
  23. package/dist/src/property-panel/effects/property-event-tap.js.map +1 -1
  24. package/dist/src/property-panel/effects/property-event.d.ts +15 -7
  25. package/dist/src/property-panel/effects/property-event.js +17 -38
  26. package/dist/src/property-panel/effects/property-event.js.map +1 -1
  27. package/dist/src/property-panel/effects/property-shadow.js +14 -5
  28. package/dist/src/property-panel/effects/property-shadow.js.map +1 -1
  29. package/dist/src/property-panel/event-handlers/event-handlers-help.d.ts +23 -0
  30. package/dist/src/property-panel/event-handlers/event-handlers-help.js +356 -0
  31. package/dist/src/property-panel/event-handlers/event-handlers-help.js.map +1 -0
  32. package/dist/src/property-panel/event-handlers/event-handlers-mapper.d.ts +31 -0
  33. package/dist/src/property-panel/event-handlers/event-handlers-mapper.js +238 -0
  34. package/dist/src/property-panel/event-handlers/event-handlers-mapper.js.map +1 -0
  35. package/dist/src/property-panel/event-handlers/event-handlers-popup.d.ts +42 -0
  36. package/dist/src/property-panel/event-handlers/event-handlers-popup.js +375 -0
  37. package/dist/src/property-panel/event-handlers/event-handlers-popup.js.map +1 -0
  38. package/dist/src/property-panel/event-handlers/event-handlers.d.ts +54 -0
  39. package/dist/src/property-panel/event-handlers/event-handlers.js +410 -0
  40. package/dist/src/property-panel/event-handlers/event-handlers.js.map +1 -0
  41. package/dist/src/property-panel/inspector/inspector-controller.d.ts +66 -0
  42. package/dist/src/property-panel/inspector/inspector-controller.js +288 -0
  43. package/dist/src/property-panel/inspector/inspector-controller.js.map +1 -0
  44. package/dist/src/property-panel/inspector/inspector-group-view.d.ts +15 -0
  45. package/dist/src/property-panel/inspector/inspector-group-view.js +59 -0
  46. package/dist/src/property-panel/inspector/inspector-group-view.js.map +1 -0
  47. package/dist/src/property-panel/inspector/inspector-highlight.d.ts +7 -0
  48. package/dist/src/property-panel/inspector/inspector-highlight.js +25 -0
  49. package/dist/src/property-panel/inspector/inspector-highlight.js.map +1 -0
  50. package/dist/src/property-panel/inspector/inspector-model.d.ts +44 -0
  51. package/dist/src/property-panel/inspector/inspector-model.js +72 -0
  52. package/dist/src/property-panel/inspector/inspector-model.js.map +1 -0
  53. package/dist/src/property-panel/inspector/inspector-tree-view.d.ts +18 -0
  54. package/dist/src/property-panel/inspector/inspector-tree-view.js +61 -0
  55. package/dist/src/property-panel/inspector/inspector-tree-view.js.map +1 -0
  56. package/dist/src/property-panel/inspector/inspector.d.ts +17 -12
  57. package/dist/src/property-panel/inspector/inspector.js +281 -179
  58. package/dist/src/property-panel/inspector/inspector.js.map +1 -1
  59. package/dist/src/property-panel/shapes/ox-placeholder-convert-editor.d.ts +20 -0
  60. package/dist/src/property-panel/shapes/ox-placeholder-convert-editor.js +125 -0
  61. package/dist/src/property-panel/shapes/ox-placeholder-convert-editor.js.map +1 -0
  62. package/dist/src/property-panel/shapes/placeholder-convert.d.ts +34 -0
  63. package/dist/src/property-panel/shapes/placeholder-convert.js +117 -0
  64. package/dist/src/property-panel/shapes/placeholder-convert.js.map +1 -0
  65. package/dist/src/property-panel/shapes/shapes.js +28 -10
  66. package/dist/src/property-panel/shapes/shapes.js.map +1 -1
  67. package/dist/src/property-panel/specifics/specifics.d.ts +1 -0
  68. package/dist/src/property-panel/specifics/specifics.js +2 -1
  69. package/dist/src/property-panel/specifics/specifics.js.map +1 -1
  70. package/dist/src/property-panel/threed/property-material3d.js +8 -1
  71. package/dist/src/property-panel/threed/property-material3d.js.map +1 -1
  72. package/dist/src/property-panel/threed/property-scene3d.d.ts +34 -6
  73. package/dist/src/property-panel/threed/property-scene3d.js +332 -201
  74. package/dist/src/property-panel/threed/property-scene3d.js.map +1 -1
  75. package/dist/src/property-panel/threed/threed.js +8 -0
  76. package/dist/src/property-panel/threed/threed.js.map +1 -1
  77. package/dist/tsconfig.tsbuildinfo +1 -1
  78. package/package.json +8 -7
  79. package/themes/grist-theme.css +1 -1
  80. package/translations/en.json +34 -1
  81. package/translations/ja.json +34 -1
  82. package/translations/ko.json +34 -1
  83. package/translations/ms.json +34 -1
  84. package/translations/zh.json +34 -1
@@ -1 +1 @@
1
- {"version":3,"file":"inspector.js","sourceRoot":"","sources":["../../../../src/property-panel/inspector/inspector.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/input/ox-input-search.js'\n\nimport { css, html, unsafeCSS, LitElement, PropertyValues, TemplateResult } from 'lit'\nimport { property, state } from 'lit/decorators.js'\n\n// TODO test Sortable\n// import Sortable from 'sortablejs'\nimport { i18next } from '@operato/i18n'\n\nimport { Component, Container, Scene } from '@hatiolab/things-scene'\nimport { InspectorStyles } from '@operato/styles/inspector-styles.js'\n\nexport class SceneInspector extends LitElement {\n static styles = [\n InspectorStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n\n color: var(--scene-inspector-color);\n }\n\n ox-input-search {\n --input-search-padding: 3px;\n --input-search-focus-border-bottom: none;\n --input-search-font: normal 16px var(--theme-font);\n\n border-radius: 999px;\n box-sizing: border-box;\n border: 1px solid var(--md-sys-color-on-secondary-container);\n padding: 0 10px;\n margin: 5px;\n }\n\n div[result] {\n flex: 1;\n\n display: flex;\n flex-direction: column;\n\n overflow-y: auto;\n }\n\n .component {\n display: block;\n overflow: hidden;\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n font-size: 14px;\n }\n\n .component[selected] {\n background-color: var(--scene-inspector-selected-background-color);\n border-top: var(--scene-inspector-selected-border);\n border-bottom: var(--scene-inspector-selected-border);\n }\n [selected] .type {\n font-weight: bold;\n }\n\n span,\n i {\n display: inline-block;\n }\n\n span.type {\n text-overflow: ellipses;\n }\n\n span.name {\n background-color: var(--scene-inspector-name-background-color);\n border-radius: var(--border-radius);\n padding: 0 4px;\n color: #fff;\n font-size: 0.8em;\n }\n\n .eye {\n margin: 0 0 0 5px;\n vertical-align: middle;\n opacity: 0.7;\n font-size: 1.1em;\n color: var(--scene-inspector-eye-icon-color);\n }\n\n pre {\n display: inline;\n }\n `\n ]\n\n @property({ type: Object }) scene?: Scene\n\n @state() private searchText: string = ''\n\n private _extendedMap: any\n private show: boolean = false\n private count: number = -1\n\n disconnectScene(scene?: Scene) {\n if (scene) {\n scene.off('selected', undefined, this)\n scene.off('execute', undefined, this)\n scene.off('undo', undefined, this)\n scene.off('redo', undefined, this)\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n\n this.disconnectScene(this.scene)\n delete this._extendedMap\n }\n\n render() {\n this.count = 0\n\n return html`\n <ox-input-search\n .placeholder=${i18next.t('text.search with type, id or tag')}\n autofocus\n @change=${(e: Event) => (this.searchText = ((e.target as HTMLInputElement).value || '').toLowerCase())}\n ></ox-input-search>\n\n <div result>${!this.scene ? html`` : this.renderComponent(this.scene.root, 0)}</div>\n `\n }\n\n firstUpdated() {\n dispatchEvent(new Event('resize'))\n\n this.renderRoot.addEventListener('click', this._onclick.bind(this) as EventListener)\n this.renderRoot.addEventListener('dblclick', this._ondblclick.bind(this) as EventListener)\n }\n\n refresh() {\n let selected = this.scene?.selected || []\n\n selected.forEach((component: Component) => {\n let parent = component.parent\n while (parent && !this.extendedMap.get(parent)) {\n this.extendedMap.set(parent, true)\n parent = parent.parent\n }\n })\n\n this.requestUpdate()\n }\n\n updated(change: PropertyValues<this>) {\n if (change.has('scene')) {\n let oldScene = change.get('scene') as Scene\n\n if (oldScene) {\n this.disconnectScene(oldScene)\n delete this._extendedMap\n }\n\n if (this.scene && this.scene.root) {\n this.scene.on('selected', this.refresh, this)\n this.scene.on('execute', this.refresh, this)\n this.scene.on('undo', this.refresh, this)\n this.scene.on('redo', this.refresh, this)\n\n this.refresh()\n }\n }\n\n // TODO test Sortable\n // this.updateComplete.then(() => {\n // this.renderRoot.querySelectorAll('[sortable]').forEach(sortable => {\n // new Sortable(sortable as HTMLElement, this.sortableConfig)\n // })\n // })\n }\n\n // TODO test Sortable\n // sortableConfig: Sortable.Options = {\n // group: 'inspector',\n // animation: 150,\n // draggable: '.component',\n // swapThreshold: 1,\n // onSort: this.onSort.bind(this)\n // }\n\n // TODO test Sortable\n // onSort(e: Sortable.SortableEvent) {\n // if (!this.scene) return\n\n // var component = (e.item as HTMLElement & { component: Component }).component\n // var to_container = (e.to as HTMLElement & { component: Component }).component as Container\n // var to_index = e.newIndex! - 1\n\n // this.scene.move(component, to_container, to_index)\n\n // this.show = false\n // this.updateComplete.then(() => {\n // this.show = true\n // })\n // }\n\n _onclick(e: MouseEvent) {\n e.stopPropagation()\n\n var targetElement = e.target as HTMLElement & { component: Component }\n var classList = targetElement.classList\n var component\n\n while (!component && targetElement) {\n component = targetElement.component\n\n if (component) break\n\n targetElement = targetElement.parentNode as HTMLElement & { component: Component }\n\n if (!targetElement || targetElement === this.renderRoot) break\n }\n\n if (component) {\n if (classList.contains('eye')) {\n this.toggleHidden(component)\n } else if (classList.contains('extended') || classList.contains('collapsed')) {\n this.toggleExtended(component)\n }\n\n this.selectComponent(component, e.shiftKey)\n } else {\n if (classList.contains('inspector')) {\n this.show = !this.show\n this.style.height = this.show ? '100%' : ''\n }\n }\n\n this.requestUpdate()\n }\n\n _ondblclick(e: MouseEvent) {\n e.stopPropagation()\n\n var targetElement = e.target as HTMLElement & { component: Component }\n var component\n\n while (!component && targetElement) {\n component = targetElement.component\n\n if (component) break\n\n targetElement = targetElement.parentNode as HTMLElement & { component: Component }\n\n if (!targetElement || targetElement === this.renderRoot) break\n }\n\n if (component && component.isContainer()) {\n this.toggleExtended(component)\n }\n\n this.requestUpdate()\n }\n\n get extendedMap() {\n if (!this._extendedMap) {\n this._extendedMap = new WeakMap()\n }\n\n return this._extendedMap\n }\n\n getNodeHandleClass(component: Component) {\n if (component.isContainer() && component.components.length > 0) {\n return !!this.extendedMap.get(component) ? 'extended' : 'collapsed'\n } else {\n return 'collapsespace'\n }\n }\n\n isExtended(component: Component) {\n return !!this.extendedMap.get(component)\n }\n\n toggleExtended(component: Component) {\n var extended = this.isExtended(component)\n\n if (extended) {\n this.extendedMap.delete(component)\n } else {\n this.extendedMap.set(component, !extended)\n }\n\n this.requestUpdate()\n }\n\n toggleHidden(component: Component) {\n component.set('hidden', !component.hidden)\n\n this.requestUpdate()\n }\n\n selectComponent(component: Component, append: boolean = false) {\n const selected = this.scene!.selected\n\n if (append) {\n const idx = selected.findIndex((s: Component) => s === component)\n if (idx != -1) {\n selected.splice(idx, 1)\n this.scene!.selected = [...selected]\n } else {\n this.scene!.selected = [...selected, component]\n }\n } else {\n this.scene!.selected = [component]\n }\n\n component.trigger('reactionreset')\n component.trigger('reaction')\n\n this.requestUpdate()\n }\n\n shouldBeShown(component: Component, counting: boolean = false): boolean {\n const { type, name, id, tag } = component.state\n const found =\n !this.searchText || `${type} ${name || ''} ${id || ''} ${tag || ''}`.toLowerCase().search(this.searchText) > -1\n\n if (counting && found) {\n this.count++\n }\n\n const foundChildren = (component.isContainer() ? component.components : []).filter((child: Component) =>\n this.shouldBeShown(child, counting)\n ).length\n\n return !!(found || foundChildren > 0)\n }\n\n renderComponent(component: Component, depth: number): TemplateResult {\n if (!component) {\n return html``\n }\n\n if (!this.shouldBeShown(component, depth == 0)) {\n return html``\n }\n\n const children = component.isContainer() ? component.components : []\n const extended = this.isExtended(component) ? children.filter((child: Component) => this.shouldBeShown(child)) : []\n const { type, id, tag, class: clazz } = component.state\n\n const name = (id ? `#${id}` : '') + (tag ? `@${tag}` : '') + (clazz ? `.(${clazz})` : '')\n\n return html`\n <div\n class=\"component\"\n ?selected=${(this.scene?.selected || []).indexOf(component) > -1}\n .component=${component}\n ?sortable=${component.isContainer()}\n >\n <span>\n ${depth > 0\n ? html`\n <md-icon class=\"eye\">${component.get('hidden') ? 'visibility_off' : 'visibility'}</md-icon>\n <pre>${' '.repeat(depth)}</pre>\n `\n : html` <pre>${' '.repeat(depth + 2)}</pre> `}\n\n <span class=${this.getNodeHandleClass(component)}> </span>\n\n <span class=\"type\">${depth == 0 ? html`ROOT(count: ${this.count})` : type}</span>\n ${name ? html` <span class=\"name\">${name}</span> ` : html``}\n </span>\n\n ${extended.map((child: Component) => this.renderComponent(child, depth + 1))}\n </div>\n `\n }\n}\n"]}
1
+ {"version":3,"file":"inspector.js","sourceRoot":"","sources":["../../../../src/property-panel/inspector/inspector.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * SceneInspector — 속성패널 inspector 탭 호스트.\n *\n * 얇은 호스트: 검색 상태 + 클릭 위임만 담당하고, scene 상호작용은 InspectorController,\n * 렌더는 뷰 렌더러(현재 트리)로 위임한다. (아키텍처 리팩터 P0 — 동작은 기존과 동일)\n */\nimport '@operato/input/ox-input-search.js'\n\nimport { css, html, LitElement, nothing, PropertyValues, TemplateResult } from 'lit'\nimport { property, state } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { Component, Scene } from '@hatiolab/things-scene'\nimport { InspectorStyles } from '@operato/styles/inspector-styles.js'\n\nimport { parseQuery } from './inspector-model.js'\nimport { InspectorController } from './inspector-controller.js'\nimport { countMatches, renderInspectorTree } from './inspector-tree-view.js'\nimport { renderInspectorGroupView } from './inspector-group-view.js'\n\ntype InspectorViewMode = 'tree' | 'group'\n\n/** 뷰 모드 선호를 세션 간 유지(localStorage). 전역 사용자 선호. */\nconst VIEW_MODE_STORAGE_KEY = 'scene-inspector.view-mode'\n\nfunction readStoredViewMode(): InspectorViewMode {\n try {\n const v = localStorage.getItem(VIEW_MODE_STORAGE_KEY)\n return v === 'group' || v === 'tree' ? v : 'tree'\n } catch {\n return 'tree'\n }\n}\n\nfunction writeStoredViewMode(mode: InspectorViewMode): void {\n try {\n localStorage.setItem(VIEW_MODE_STORAGE_KEY, mode)\n } catch {\n /* localStorage 미가용(SSR/프라이빗 등) — 무시 */\n }\n}\n\nexport class SceneInspector extends LitElement {\n static styles = [\n InspectorStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n\n color: var(--scene-inspector-color);\n }\n\n ox-input-search {\n --input-search-padding: 3px;\n --input-search-focus-border-bottom: none;\n --input-search-font: normal 16px var(--theme-font);\n\n border-radius: 999px;\n box-sizing: border-box;\n border: 1px solid var(--md-sys-color-on-secondary-container);\n padding: 0 10px;\n margin: 5px;\n }\n\n div[result] {\n flex: 1;\n\n display: flex;\n flex-direction: column;\n\n overflow-y: auto;\n }\n\n .component {\n display: block;\n overflow: hidden;\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n font-size: 14px;\n }\n\n .component[selected] {\n background-color: var(--scene-inspector-selected-background-color);\n border-top: var(--scene-inspector-selected-border);\n border-bottom: var(--scene-inspector-selected-border);\n }\n [selected] .type {\n font-weight: bold;\n }\n\n span,\n i {\n display: inline-block;\n }\n\n span.type {\n text-overflow: ellipses;\n }\n\n span.name {\n background-color: var(--scene-inspector-name-background-color);\n border-radius: var(--border-radius);\n padding: 0 4px;\n color: #fff;\n font-size: 0.8em;\n }\n\n .eye {\n margin: 0 0 0 4px;\n vertical-align: middle;\n opacity: 0.7;\n font-size: 1em;\n width: 1em;\n color: var(--scene-inspector-eye-icon-color);\n cursor: pointer;\n }\n\n .lock {\n margin: 0;\n vertical-align: middle;\n opacity: 0.3;\n font-size: 1em;\n width: 1em;\n cursor: pointer;\n }\n .lock[locked] {\n opacity: 0.9;\n color: var(--md-sys-color-error, #d9534f);\n }\n\n pre {\n display: inline;\n /* 트리 인덴트(수직라인까지의 간격) 최소화 — depth 당 좁은 폭 */\n font-size: 0.6em;\n margin: 0;\n }\n\n /* 뷰 모드 툴바 */\n div[toolbar] {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px 4px;\n }\n div[toolbar] .spacer {\n flex: 1;\n }\n div[toolbar] md-icon {\n cursor: pointer;\n opacity: 0.5;\n font-size: 18px;\n border-radius: 4px;\n padding: 2px;\n }\n div[toolbar] md-icon:hover {\n opacity: 0.9;\n }\n div[toolbar] md-icon[selected] {\n opacity: 1;\n color: var(--md-sys-color-primary, #4a9eff);\n background: rgba(0, 0, 0, 0.05);\n }\n\n /* 타입 그룹 뷰 */\n .group-header {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n padding: 2px 0;\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n }\n .group-header:hover {\n background: rgba(0, 0, 0, 0.03);\n }\n .group-count {\n margin-left: auto;\n font-size: 0.72em;\n font-weight: normal;\n background: var(--scene-inspector-name-background-color);\n color: #fff;\n border-radius: 999px;\n padding: 0 6px;\n }\n .group-hidden,\n .group-dim {\n font-size: 1em;\n opacity: 0.35;\n cursor: pointer;\n }\n .group-hidden {\n margin: 0 2px 0 6px;\n }\n .group-dim {\n margin: 0 4px 0 2px;\n }\n .group-hidden:hover,\n .group-dim:hover {\n opacity: 0.8;\n }\n .group-header[hidden-group] .group-hidden,\n .group-header[dimmed] .group-dim {\n opacity: 0.95;\n color: var(--md-sys-color-primary, #4a9eff);\n }\n .group-header[dimmed] .group-type {\n opacity: 0.45;\n }\n .group-member {\n padding-left: 8px;\n }\n .muted {\n opacity: 0.4;\n font-style: italic;\n }\n /* 검색 매치 하이라이트 */\n mark {\n background: var(--md-sys-color-tertiary-container, #ffe082);\n color: inherit;\n border-radius: 2px;\n padding: 0 1px;\n }\n `\n ]\n\n @property({ type: Object }) scene?: Scene\n\n @state() private searchText: string = ''\n @state() private viewMode: InspectorViewMode = readStoredViewMode()\n\n private controller = new InspectorController()\n private show: boolean = false\n /** reveal(스크롤) 판정용 직전 선택. */\n private _lastSelected: Component[] = []\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.controller.unbind()\n }\n\n render() {\n return html`\n <ox-input-search\n .placeholder=${i18next.t('text.search with type, id or tag')}\n autofocus\n @change=${(e: Event) => (this.searchText = ((e.target as HTMLInputElement).value || '').toLowerCase())}\n ></ox-input-search>\n\n <div toolbar>\n <md-icon class=\"view-btn\" ?selected=${this.viewMode === 'tree'} title=\"Tree view\" @click=${() => this._setViewMode('tree')}>account_tree</md-icon>\n <md-icon class=\"view-btn\" ?selected=${this.viewMode === 'group'} title=\"Group by type\" @click=${() => this._setViewMode('group')}>category</md-icon>\n <span class=\"spacer\"></span>\n ${this.viewMode === 'group'\n ? html`\n <md-icon title=\"Expand all groups\" @click=${this._expandGroups}>unfold_more</md-icon>\n <md-icon title=\"Collapse to group level\" @click=${this._collapseGroups}>unfold_less</md-icon>\n `\n : nothing}\n </div>\n\n <div result>${this._renderResult()}</div>\n `\n }\n\n private _renderResult(): TemplateResult {\n if (!this.scene) return html``\n\n const terms = parseQuery(this.searchText)\n\n if (this.viewMode === 'group') {\n return renderInspectorGroupView(this.controller.index, {\n selected: this.controller.selected,\n terms,\n isGroupExpanded: (type: string) => this.controller.isGroupExpanded(type),\n isTypeDimmed: (type: string) => this.controller.isTypeDimmed(type),\n isTypeHidden: (type: string) => this.controller.isTypeHidden(type)\n })\n }\n\n return renderInspectorTree(this.scene.root, {\n selected: this.controller.selected,\n terms,\n count: countMatches(this.scene.root, terms),\n isExtended: (c: Component) => this.controller.isExtended(c),\n nodeHandleClass: (c: Component) => this.controller.nodeHandleClass(c)\n })\n }\n\n private _setViewMode(mode: InspectorViewMode) {\n if (this.viewMode === mode) return\n this.viewMode = mode\n writeStoredViewMode(mode) // 세션 간 유지\n }\n\n private _collapseGroups = () => {\n this.controller.collapseAllGroups()\n this.requestUpdate()\n }\n\n private _expandGroups = () => {\n const index = this.controller.index\n if (index) this.controller.expandAllGroups([...index.byType.keys()])\n this.requestUpdate()\n }\n\n firstUpdated() {\n dispatchEvent(new Event('resize'))\n\n this.renderRoot.addEventListener('click', this._onclick.bind(this) as EventListener)\n this.renderRoot.addEventListener('dblclick', this._ondblclick.bind(this) as EventListener)\n }\n\n /** 외부에서 강제 재렌더용(기존 공개 메쏘드 유지). */\n refresh() {\n this.requestUpdate()\n }\n\n updated(change: PropertyValues<this>) {\n if (change.has('scene')) {\n this.controller.bind(this.scene, () => this.requestUpdate())\n }\n this._maybeReveal()\n }\n\n /**\n * 선택이 바뀌면(캔버스 등) 해당 항목을 리스트에 노출한다 — 그룹 뷰면 그룹을 펼친 뒤 스크롤.\n * scrollIntoView({block:'nearest'}) 라 이미 보이는 경우(인스펙터 클릭 등)엔 사실상 no-op.\n */\n private _maybeReveal() {\n const selected = this.controller.selected\n const changed =\n selected.length !== this._lastSelected.length || selected.some((c, i) => c !== this._lastSelected[i])\n if (!changed) return\n\n if (selected.length === 0) {\n this._lastSelected = selected\n return\n }\n\n if (this.viewMode === 'group') {\n const types = [...new Set(selected.map(c => c.state.type))]\n if (types.some(t => !this.controller.isGroupExpanded(t))) {\n this.controller.expandAllGroups(types)\n return // _lastSelected 미갱신 → 다음 updated 에서 changed 유지 → 그때 스크롤\n }\n }\n\n this._lastSelected = selected\n this.updateComplete.then(() => {\n const el = this.renderRoot.querySelector('.component[selected]') as HTMLElement | null\n el?.scrollIntoView({ block: 'nearest' })\n })\n }\n\n /** 이벤트 타겟에서 상위로 올라가며 `.component` 를 가진 요소를 찾는다. */\n private _resolveComponent(e: MouseEvent): Component | undefined {\n let targetElement = e.target as HTMLElement & { component: Component }\n let component: Component | undefined\n\n while (!component && targetElement) {\n component = targetElement.component\n\n if (component) break\n\n targetElement = targetElement.parentNode as HTMLElement & { component: Component }\n\n if (!targetElement || targetElement === this.renderRoot) break\n }\n\n return component\n }\n\n _onclick(e: MouseEvent) {\n e.stopPropagation()\n\n const target = e.target as HTMLElement\n\n // 그룹 hidden 아이콘 클릭 → 그룹(타입) 전원 hidden 토글 (헤더 펼침보다 먼저 처리)\n if (target.classList.contains('group-hidden')) {\n const type = target.closest?.('[data-group-type]')?.getAttribute('data-group-type')\n if (type != null) this.controller.toggleTypeHidden(type)\n this.requestUpdate()\n return\n }\n\n // 그룹 dim-out 아이콘 클릭 → 그룹(타입) 포커스 dim 토글 (헤더 펼침보다 먼저 처리)\n if (target.classList.contains('group-dim')) {\n const type = target.closest?.('[data-group-type]')?.getAttribute('data-group-type')\n if (type != null) this.controller.toggleTypeDim(type)\n this.requestUpdate()\n return\n }\n\n // 타입 그룹 헤더 클릭 → 그룹 펼침/접힘 (멤버 행은 헤더의 자손이 아니므로 여기 안 걸림)\n const header = target.closest?.('[data-group-type]') as HTMLElement | null\n if (header) {\n const type = header.getAttribute('data-group-type')\n if (type != null) this.controller.toggleGroup(type)\n this.requestUpdate()\n return\n }\n\n const classList = (e.target as HTMLElement).classList\n const component = this._resolveComponent(e)\n\n if (component) {\n if (classList.contains('eye')) {\n this.controller.toggleHidden(component)\n } else if (classList.contains('lock')) {\n this.controller.toggleLocked(component)\n } else if (classList.contains('extended') || classList.contains('collapsed')) {\n this.controller.toggleExtended(component)\n }\n\n // shift = 범위(볼륨) 선택/블록 해제, ctrl/cmd = 개별 토글, 그 외 = 단일 선택\n if (e.shiftKey) {\n this.controller.selectRange(component, parseQuery(this.searchText))\n } else {\n this.controller.select(component, e.metaKey || e.ctrlKey)\n }\n } else {\n if (classList.contains('inspector')) {\n this.show = !this.show\n this.style.height = this.show ? '100%' : ''\n }\n }\n\n this.requestUpdate()\n }\n\n _ondblclick(e: MouseEvent) {\n e.stopPropagation()\n\n const component = this._resolveComponent(e)\n\n if (component) {\n if (component.isContainer()) {\n this.controller.toggleExtended(component)\n }\n // 캔버스를 해당 컴포넌트로 센터 이동(줌).\n this.controller.centerTo(component)\n }\n\n this.requestUpdate()\n }\n}\n"]}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ *
4
+ * placeholder → 도메인 type 변환 property editor.
5
+ *
6
+ * GenericObject.nature.properties 에 `{ type: 'placeholder-convert', name: '_convert' }` 로
7
+ * 선언되어 specific 탭에서 자동 렌더링. 별도 사용처 없음 — nature 선언으로 충분.
8
+ *
9
+ * 변환 흐름:
10
+ * 타입 버튼 클릭 → i-need-selected → specifics.ts undoableChange → convertComponentType
11
+ */
12
+ import { TemplateResult } from 'lit';
13
+ import { OxPropertyEditor } from '@operato/property-editor';
14
+ import type { PropertySpec } from '@operato/property-editor';
15
+ export declare class OxPlaceholderConvertEditor extends OxPropertyEditor {
16
+ static styles: import("lit").CSSResult[];
17
+ private _search;
18
+ editorTemplate(_value: any, _spec: PropertySpec): TemplateResult;
19
+ private _onConvert;
20
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ *
4
+ * placeholder → 도메인 type 변환 property editor.
5
+ *
6
+ * GenericObject.nature.properties 에 `{ type: 'placeholder-convert', name: '_convert' }` 로
7
+ * 선언되어 specific 탭에서 자동 렌더링. 별도 사용처 없음 — nature 선언으로 충분.
8
+ *
9
+ * 변환 흐름:
10
+ * 타입 버튼 클릭 → i-need-selected → specifics.ts undoableChange → convertComponentType
11
+ */
12
+ import { __decorate } from "tslib";
13
+ import { css, html } from 'lit';
14
+ import { customElement, state } from 'lit/decorators.js';
15
+ import { OxPropertyEditor } from '@operato/property-editor';
16
+ import { convertComponentType, getConvertibleTypes } from './placeholder-convert.js';
17
+ OxPropertyEditor.register({ 'placeholder-convert': 'ox-placeholder-convert-editor' });
18
+ let OxPlaceholderConvertEditor = class OxPlaceholderConvertEditor extends OxPropertyEditor {
19
+ constructor() {
20
+ super(...arguments);
21
+ this._search = '';
22
+ }
23
+ editorTemplate(_value, _spec) {
24
+ const all = getConvertibleTypes();
25
+ const q = this._search.trim().toLowerCase();
26
+ const candidates = q ? all.filter(t => t.toLowerCase().includes(q)) : all;
27
+ return html `
28
+ <div class="convert-wrap fullwidth">
29
+ <input
30
+ type="search"
31
+ class="convert-search"
32
+ placeholder="search type to convert to..."
33
+ .value=${this._search}
34
+ @input=${(e) => {
35
+ this._search = e.target.value;
36
+ }}
37
+ />
38
+ <div class="convert-list">
39
+ ${candidates.length === 0
40
+ ? html `<div class="empty">no matching type</div>`
41
+ : candidates.map(type => html `
42
+ <button class="convert-item" @click=${() => this._onConvert(type)} title=${`convert to ${type}`}>
43
+ ${type}
44
+ </button>
45
+ `)}
46
+ </div>
47
+ </div>
48
+ `;
49
+ }
50
+ _onConvert(newType) {
51
+ this.dispatchEvent(new CustomEvent('i-need-selected', {
52
+ bubbles: true,
53
+ composed: true,
54
+ detail: {
55
+ callback: (selected) => {
56
+ if (selected[0])
57
+ convertComponentType(selected[0], newType);
58
+ }
59
+ }
60
+ }));
61
+ this._search = '';
62
+ }
63
+ };
64
+ OxPlaceholderConvertEditor.styles = [
65
+ ...OxPropertyEditor.styles,
66
+ css `
67
+ .convert-wrap {
68
+ display: flex;
69
+ flex-direction: column;
70
+ gap: 4px;
71
+ padding: 4px 0;
72
+ }
73
+ .convert-search {
74
+ font-size: 12px;
75
+ padding: 4px 8px;
76
+ border: 1px solid var(--md-sys-color-outline-variant, #d0d7de);
77
+ border-radius: 4px;
78
+ background: var(--md-sys-color-surface, #fff);
79
+ color: var(--md-sys-color-on-surface, #000);
80
+ outline: none;
81
+ }
82
+ .convert-search:focus {
83
+ border-color: var(--md-sys-color-primary, #2a64d8);
84
+ }
85
+ .convert-list {
86
+ display: flex;
87
+ flex-direction: column;
88
+ gap: 2px;
89
+ max-height: 200px;
90
+ overflow-y: auto;
91
+ background: var(--md-sys-color-surface, #fff);
92
+ border-radius: 4px;
93
+ padding: 2px;
94
+ }
95
+ .convert-list .empty {
96
+ text-align: center;
97
+ font-size: 11px;
98
+ padding: 8px;
99
+ color: var(--md-sys-color-on-secondary-container, #777);
100
+ }
101
+ .convert-item {
102
+ text-align: left;
103
+ font-size: 12px;
104
+ font-family: monospace;
105
+ padding: 4px 8px;
106
+ background: transparent;
107
+ border: 1px solid transparent;
108
+ border-radius: 3px;
109
+ cursor: pointer;
110
+ color: var(--md-sys-color-on-surface, #000);
111
+ }
112
+ .convert-item:hover {
113
+ background: var(--md-sys-color-primary-container, #eaf2ff);
114
+ border-color: var(--md-sys-color-primary, #2a64d8);
115
+ }
116
+ `
117
+ ];
118
+ __decorate([
119
+ state()
120
+ ], OxPlaceholderConvertEditor.prototype, "_search", void 0);
121
+ OxPlaceholderConvertEditor = __decorate([
122
+ customElement('ox-placeholder-convert-editor')
123
+ ], OxPlaceholderConvertEditor);
124
+ export { OxPlaceholderConvertEditor };
125
+ //# sourceMappingURL=ox-placeholder-convert-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ox-placeholder-convert-editor.js","sourceRoot":"","sources":["../../../../src/property-panel/shapes/ox-placeholder-convert-editor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;;AAEH,OAAO,EAAE,GAAG,EAAE,IAAI,EAAkB,MAAM,KAAK,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAGxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAG3D,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAEpF,gBAAgB,CAAC,QAAQ,CAAC,EAAE,qBAAqB,EAAE,+BAA+B,EAAE,CAAC,CAAA;AAG9E,IAAM,0BAA0B,GAAhC,MAAM,0BAA2B,SAAQ,gBAAgB;IAAzD;;QAwDY,YAAO,GAAG,EAAE,CAAA;IA+C/B,CAAC;IA7CC,cAAc,CAAC,MAAW,EAAE,KAAmB;QAC7C,MAAM,GAAG,GAAG,mBAAmB,EAAE,CAAA;QACjC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;QAC3C,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;QAEzE,OAAO,IAAI,CAAA;;;;;;mBAMI,IAAI,CAAC,OAAO;mBACZ,CAAC,CAAQ,EAAE,EAAE;YACpB,IAAI,CAAC,OAAO,GAAI,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAA;QACrD,CAAC;;;YAGC,UAAU,CAAC,MAAM,KAAK,CAAC;YACvB,CAAC,CAAC,IAAI,CAAA,2CAA2C;YACjD,CAAC,CAAC,UAAU,CAAC,GAAG,CACZ,IAAI,CAAC,EAAE,CAAC,IAAI,CAAA;wDAC4B,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,cAAc,IAAI,EAAE;sBAC3F,IAAI;;iBAET,CACF;;;KAGV,CAAA;IACH,CAAC;IAEO,UAAU,CAAC,OAAe;QAChC,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,iBAAiB,EAAE;YACjC,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE;gBACN,QAAQ,EAAE,CAAC,QAAqB,EAAE,EAAE;oBAClC,IAAI,QAAQ,CAAC,CAAC,CAAC;wBAAE,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;gBAC7D,CAAC;aACF;SACF,CAAC,CACH,CAAA;QACD,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;IACnB,CAAC;;AArGM,iCAAM,GAAG;IACd,GAAG,gBAAgB,CAAC,MAAM;IAC1B,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAkDF;CACF,AArDY,CAqDZ;AAEgB;IAAhB,KAAK,EAAE;2DAAqB;AAxDlB,0BAA0B;IADtC,aAAa,CAAC,+BAA+B,CAAC;GAClC,0BAA0B,CAuGtC","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * placeholder → 도메인 type 변환 property editor.\n *\n * GenericObject.nature.properties 에 `{ type: 'placeholder-convert', name: '_convert' }` 로\n * 선언되어 specific 탭에서 자동 렌더링. 별도 사용처 없음 — nature 선언으로 충분.\n *\n * 변환 흐름:\n * 타입 버튼 클릭 → i-need-selected → specifics.ts undoableChange → convertComponentType\n */\n\nimport { css, html, TemplateResult } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\n\nimport type { Component } from '@hatiolab/things-scene'\nimport { OxPropertyEditor } from '@operato/property-editor'\nimport type { PropertySpec } from '@operato/property-editor'\n\nimport { convertComponentType, getConvertibleTypes } from './placeholder-convert.js'\n\nOxPropertyEditor.register({ 'placeholder-convert': 'ox-placeholder-convert-editor' })\n\n@customElement('ox-placeholder-convert-editor')\nexport class OxPlaceholderConvertEditor extends OxPropertyEditor {\n static styles = [\n ...OxPropertyEditor.styles,\n css`\n .convert-wrap {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 4px 0;\n }\n .convert-search {\n font-size: 12px;\n padding: 4px 8px;\n border: 1px solid var(--md-sys-color-outline-variant, #d0d7de);\n border-radius: 4px;\n background: var(--md-sys-color-surface, #fff);\n color: var(--md-sys-color-on-surface, #000);\n outline: none;\n }\n .convert-search:focus {\n border-color: var(--md-sys-color-primary, #2a64d8);\n }\n .convert-list {\n display: flex;\n flex-direction: column;\n gap: 2px;\n max-height: 200px;\n overflow-y: auto;\n background: var(--md-sys-color-surface, #fff);\n border-radius: 4px;\n padding: 2px;\n }\n .convert-list .empty {\n text-align: center;\n font-size: 11px;\n padding: 8px;\n color: var(--md-sys-color-on-secondary-container, #777);\n }\n .convert-item {\n text-align: left;\n font-size: 12px;\n font-family: monospace;\n padding: 4px 8px;\n background: transparent;\n border: 1px solid transparent;\n border-radius: 3px;\n cursor: pointer;\n color: var(--md-sys-color-on-surface, #000);\n }\n .convert-item:hover {\n background: var(--md-sys-color-primary-container, #eaf2ff);\n border-color: var(--md-sys-color-primary, #2a64d8);\n }\n `\n ]\n\n @state() private _search = ''\n\n editorTemplate(_value: any, _spec: PropertySpec): TemplateResult {\n const all = getConvertibleTypes()\n const q = this._search.trim().toLowerCase()\n const candidates = q ? all.filter(t => t.toLowerCase().includes(q)) : all\n\n return html`\n <div class=\"convert-wrap fullwidth\">\n <input\n type=\"search\"\n class=\"convert-search\"\n placeholder=\"search type to convert to...\"\n .value=${this._search}\n @input=${(e: Event) => {\n this._search = (e.target as HTMLInputElement).value\n }}\n />\n <div class=\"convert-list\">\n ${candidates.length === 0\n ? html`<div class=\"empty\">no matching type</div>`\n : candidates.map(\n type => html`\n <button class=\"convert-item\" @click=${() => this._onConvert(type)} title=${`convert to ${type}`}>\n ${type}\n </button>\n `\n )}\n </div>\n </div>\n `\n }\n\n private _onConvert(newType: string) {\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: (selected: Component[]) => {\n if (selected[0]) convertComponentType(selected[0], newType)\n }\n }\n })\n )\n this._search = ''\n }\n}\n"]}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ *
4
+ * Placeholder → 도메인 type 변환 helper.
5
+ *
6
+ * 대상: board-import 가 떨어뜨린 generic-* placeholder 컴포넌트 — 사용자가 모델러에서
7
+ * 구체 type 으로 변환하는 액션. property-panel 의 shapes 섹션이 호출.
8
+ *
9
+ * 정책:
10
+ * - **placeholder type 한정** — 일반 컴포넌트 (Stocker, Forklift 등) 는 대상 X. 안전.
11
+ * - things-scene 의 `Scene.undoableChange` 로 감싸 undo/redo 자연스럽게 통합.
12
+ * - 좌표/크기/회전/id 보존. 새 type 의 default state 는 클래스 생성자가 처리.
13
+ *
14
+ * things-scene 의 컴포넌트 type 변경은 클래스 자체 교체가 필요 (메서드 binding 이라).
15
+ * 단순 `state.type` 갱신으론 부족 → 기존 컴포넌트 제거 + 새 type 으로 compile + 같은 위치에 추가.
16
+ */
17
+ import { Component } from '@hatiolab/things-scene';
18
+ /**
19
+ * 변환 대상 placeholder 인지 판정. property-panel 의 conditional 렌더 가드.
20
+ */
21
+ export declare function isPlaceholderComponent(component: Component | undefined | null): boolean;
22
+ export declare function getConvertibleTypes(): string[];
23
+ /**
24
+ * placeholder 를 새 type 으로 변환. 좌표/크기/회전/id 보존.
25
+ *
26
+ * 동작:
27
+ * 1) component.hierarchy 로 모델 snapshot
28
+ * 2) 새 모델 = snapshot + { type: newType }
29
+ * 3) Scene.undoableChange 안에서: removeSelf → compile(newModel) → parent.insertComponentAt 같은 index
30
+ * 4) snapshot-commander 가 자동으로 undoable snapshot 캡처
31
+ *
32
+ * 실패 (newType 미등록 / 컴포넌트 placeholder 아님 등) 시 throw.
33
+ */
34
+ export declare function convertComponentType(component: Component, newType: string): void;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ *
4
+ * Placeholder → 도메인 type 변환 helper.
5
+ *
6
+ * 대상: board-import 가 떨어뜨린 generic-* placeholder 컴포넌트 — 사용자가 모델러에서
7
+ * 구체 type 으로 변환하는 액션. property-panel 의 shapes 섹션이 호출.
8
+ *
9
+ * 정책:
10
+ * - **placeholder type 한정** — 일반 컴포넌트 (Stocker, Forklift 등) 는 대상 X. 안전.
11
+ * - things-scene 의 `Scene.undoableChange` 로 감싸 undo/redo 자연스럽게 통합.
12
+ * - 좌표/크기/회전/id 보존. 새 type 의 default state 는 클래스 생성자가 처리.
13
+ *
14
+ * things-scene 의 컴포넌트 type 변경은 클래스 자체 교체가 필요 (메서드 binding 이라).
15
+ * 단순 `state.type` 갱신으론 부족 → 기존 컴포넌트 제거 + 새 type 으로 compile + 같은 위치에 추가.
16
+ */
17
+ import { Component, Model } from '@hatiolab/things-scene';
18
+ /** 변환 가능한 placeholder type 모음. board-import 의 CORE_PLACEHOLDER_TYPES 와 동기화.
19
+ * 도메인 generic 추상 (GenericTransport, GenericFacility 등) 은 GLB 바디 교체 전용 —
20
+ * 여기에 포함하지 않는다 (변환 아닌 GLB attach 가 별도 UI).
21
+ */
22
+ const PLACEHOLDER_TYPES = new Set([
23
+ 'placeholder',
24
+ 'GenericObject' // 구버전 PascalCase 호환
25
+ ]);
26
+ /**
27
+ * 변환 대상 placeholder 인지 판정. property-panel 의 conditional 렌더 가드.
28
+ */
29
+ export function isPlaceholderComponent(component) {
30
+ var _a;
31
+ if (!component)
32
+ return false;
33
+ return PLACEHOLDER_TYPES.has((_a = component.state) === null || _a === void 0 ? void 0 : _a.type);
34
+ }
35
+ /**
36
+ * 변환 후보 type 목록 — 등록된 모든 component type 중 placeholder 자기 자신 + 시스템 type
37
+ * (root container, layer 등) 제외. property-panel dropdown 후보에 사용.
38
+ */
39
+ const SYSTEM_TYPES = new Set([
40
+ 'root',
41
+ 'root-container',
42
+ 'group',
43
+ 'layer',
44
+ 'model-layer',
45
+ 'tool-layer',
46
+ 'modeler-layer'
47
+ ]);
48
+ export function getConvertibleTypes() {
49
+ var _a, _b;
50
+ const all = (_b = (_a = Component).types) === null || _b === void 0 ? void 0 : _b.call(_a);
51
+ if (!all)
52
+ return [];
53
+ return all
54
+ .filter(t => !PLACEHOLDER_TYPES.has(t))
55
+ .filter(t => !SYSTEM_TYPES.has(t))
56
+ .sort();
57
+ }
58
+ /**
59
+ * placeholder 를 새 type 으로 변환. 좌표/크기/회전/id 보존.
60
+ *
61
+ * 동작:
62
+ * 1) component.hierarchy 로 모델 snapshot
63
+ * 2) 새 모델 = snapshot + { type: newType }
64
+ * 3) Scene.undoableChange 안에서: removeSelf → compile(newModel) → parent.insertComponentAt 같은 index
65
+ * 4) snapshot-commander 가 자동으로 undoable snapshot 캡처
66
+ *
67
+ * 실패 (newType 미등록 / 컴포넌트 placeholder 아님 등) 시 throw.
68
+ */
69
+ export function convertComponentType(component, newType) {
70
+ if (!isPlaceholderComponent(component)) {
71
+ throw new Error(`Component (type=${component.state.type}) is not a placeholder — convert restricted to placeholders only.`);
72
+ }
73
+ const clazz = Component.register(newType);
74
+ if (!clazz) {
75
+ throw new Error(`Unknown component type: ${newType}`);
76
+ }
77
+ if (PLACEHOLDER_TYPES.has(newType)) {
78
+ throw new Error(`Cannot convert to another placeholder type: ${newType}`);
79
+ }
80
+ const app = component.app;
81
+ if (!(app === null || app === void 0 ? void 0 : app.commander))
82
+ throw new Error('Component is not attached to an app/scene.');
83
+ const oldHierarchy = JSON.parse(JSON.stringify(component.hierarchy));
84
+ const newModel = {
85
+ ...oldHierarchy,
86
+ type: newType
87
+ };
88
+ performTypeSwap(component, newModel);
89
+ }
90
+ /**
91
+ * 공통 swap 로직 — Scene.undoableChange 안에서 component 를 새 모델로 교체.
92
+ * snapshot-commander 가 자동으로 undoable 처리.
93
+ */
94
+ function performTypeSwap(component, newModel) {
95
+ var _a, _b, _c, _d;
96
+ const app = component.app;
97
+ const parent = component.parent;
98
+ if (!parent)
99
+ throw new Error('Component has no parent — cannot swap.');
100
+ // children 인덱스 (insertComponentAt 으로 같은 위치 보존)
101
+ const siblings = parent.components;
102
+ const index = siblings ? siblings.indexOf(component) : -1;
103
+ (_b = (_a = component).removeSelf) === null || _b === void 0 ? void 0 : _b.call(_a, false);
104
+ const newComp = Model.compile(newModel, app);
105
+ if (!newComp) {
106
+ throw new Error(`Failed to compile new component type: ${newModel.type}`);
107
+ }
108
+ if (index >= 0 && parent.insertComponentAt) {
109
+ ;
110
+ parent.insertComponentAt(newComp, index);
111
+ }
112
+ else {
113
+ ;
114
+ (_d = (_c = parent).addComponent) === null || _d === void 0 ? void 0 : _d.call(_c, newComp);
115
+ }
116
+ }
117
+ //# sourceMappingURL=placeholder-convert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placeholder-convert.js","sourceRoot":"","sources":["../../../../src/property-panel/shapes/placeholder-convert.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * Placeholder → 도메인 type 변환 helper.\n *\n * 대상: board-import 가 떨어뜨린 generic-* placeholder 컴포넌트 — 사용자가 모델러에서\n * 구체 type 으로 변환하는 액션. property-panel 의 shapes 섹션이 호출.\n *\n * 정책:\n * - **placeholder type 한정** — 일반 컴포넌트 (Stocker, Forklift 등) 는 대상 X. 안전.\n * - things-scene 의 `Scene.undoableChange` 로 감싸 undo/redo 자연스럽게 통합.\n * - 좌표/크기/회전/id 보존. 새 type 의 default state 는 클래스 생성자가 처리.\n *\n * things-scene 의 컴포넌트 type 변경은 클래스 자체 교체가 필요 (메서드 binding 이라).\n * 단순 `state.type` 갱신으론 부족 → 기존 컴포넌트 제거 + 새 type 으로 compile + 같은 위치에 추가.\n */\n\nimport { Component, Model } from '@hatiolab/things-scene'\n\n/** 변환 가능한 placeholder type 모음. board-import 의 CORE_PLACEHOLDER_TYPES 와 동기화.\n * 도메인 generic 추상 (GenericTransport, GenericFacility 등) 은 GLB 바디 교체 전용 —\n * 여기에 포함하지 않는다 (변환 아닌 GLB attach 가 별도 UI).\n */\nconst PLACEHOLDER_TYPES = new Set([\n 'placeholder',\n 'GenericObject' // 구버전 PascalCase 호환\n])\n\n/**\n * 변환 대상 placeholder 인지 판정. property-panel 의 conditional 렌더 가드.\n */\nexport function isPlaceholderComponent(component: Component | undefined | null): boolean {\n if (!component) return false\n return PLACEHOLDER_TYPES.has(component.state?.type as string)\n}\n\n/**\n * 변환 후보 type 목록 — 등록된 모든 component type 중 placeholder 자기 자신 + 시스템 type\n * (root container, layer 등) 제외. property-panel dropdown 후보에 사용.\n */\nconst SYSTEM_TYPES = new Set([\n 'root',\n 'root-container',\n 'group',\n 'layer',\n 'model-layer',\n 'tool-layer',\n 'modeler-layer'\n])\n\nexport function getConvertibleTypes(): string[] {\n const all = (Component as any).types?.() as string[] | undefined\n if (!all) return []\n return all\n .filter(t => !PLACEHOLDER_TYPES.has(t))\n .filter(t => !SYSTEM_TYPES.has(t))\n .sort()\n}\n\n/**\n * placeholder 를 새 type 으로 변환. 좌표/크기/회전/id 보존.\n *\n * 동작:\n * 1) component.hierarchy 로 모델 snapshot\n * 2) 새 모델 = snapshot + { type: newType }\n * 3) Scene.undoableChange 안에서: removeSelf → compile(newModel) → parent.insertComponentAt 같은 index\n * 4) snapshot-commander 가 자동으로 undoable snapshot 캡처\n *\n * 실패 (newType 미등록 / 컴포넌트 placeholder 아님 등) 시 throw.\n */\nexport function convertComponentType(component: Component, newType: string): void {\n if (!isPlaceholderComponent(component)) {\n throw new Error(\n `Component (type=${component.state.type}) is not a placeholder — convert restricted to placeholders only.`\n )\n }\n const clazz = (Component as any).register(newType)\n if (!clazz) {\n throw new Error(`Unknown component type: ${newType}`)\n }\n if (PLACEHOLDER_TYPES.has(newType)) {\n throw new Error(`Cannot convert to another placeholder type: ${newType}`)\n }\n\n const app = (component as any).app\n if (!app?.commander) throw new Error('Component is not attached to an app/scene.')\n\n const oldHierarchy = JSON.parse(JSON.stringify((component as any).hierarchy))\n const newModel: Record<string, any> = {\n ...oldHierarchy,\n type: newType\n }\n\n performTypeSwap(component, newModel)\n}\n\n/**\n * 공통 swap 로직 — Scene.undoableChange 안에서 component 를 새 모델로 교체.\n * snapshot-commander 가 자동으로 undoable 처리.\n */\nfunction performTypeSwap(component: Component, newModel: Record<string, any>): void {\n const app = (component as any).app\n const parent = (component as any).parent\n if (!parent) throw new Error('Component has no parent — cannot swap.')\n\n // children 인덱스 (insertComponentAt 으로 같은 위치 보존)\n const siblings = (parent as any).components as Component[] | undefined\n const index = siblings ? siblings.indexOf(component) : -1\n\n // 호출자(specifics.ts의 i-need-selected → undoableChange)가 이미 감싸므로 직접 실행.\n ;(component as any).removeSelf?.(false)\n const newComp = (Model as any).compile(newModel as any, app)\n if (!newComp) {\n throw new Error(`Failed to compile new component type: ${newModel.type}`)\n }\n if (index >= 0 && (parent as any).insertComponentAt) {\n ;(parent as any).insertComponentAt(newComp, index)\n } else {\n ;(parent as any).addComponent?.(newComp)\n }\n}\n"]}
@@ -7,7 +7,7 @@ import '@operato/input/ox-buttons-radio.js';
7
7
  import '@material/web/icon/icon.js';
8
8
  import '@operato/i18n/ox-i18n.js';
9
9
  import '@operato/help/ox-title-with-help.js';
10
- import { html } from 'lit';
10
+ import { css, html } from 'lit';
11
11
  import { property, state } from 'lit/decorators.js';
12
12
  import { keyed } from 'lit/directives/keyed.js';
13
13
  import { PropertyGridStyles } from '@operato/styles/property-grid-styles.js';
@@ -83,7 +83,10 @@ export class PropertyShapes extends AbstractProperty {
83
83
  <label> <md-icon>height</md-icon><ox-i18n msgid="label.height">height</ox-i18n> </label>
84
84
  <input type="number" value-key="bounds.height" .value=${String(height)} />
85
85
 
86
- ${this.selected.length && this.selected[0].isRootModel()
86
+ ${this.selected.length &&
87
+ this.selected[0] &&
88
+ typeof this.selected[0].isRootModel === 'function' &&
89
+ this.selected[0].isRootModel()
87
90
  ? html ``
88
91
  : html `
89
92
  <label class="rotate">
@@ -118,6 +121,7 @@ export class PropertyShapes extends AbstractProperty {
118
121
  <option value="absolute">absolute</option>
119
122
  <option value="fixed">fixed</option>
120
123
  <option value="sticky">sticky</option>
124
+ <option value="popup">popup (template)</option>
121
125
  </select>
122
126
 
123
127
  <label>Origin</label>
@@ -218,7 +222,10 @@ export class PropertyShapes extends AbstractProperty {
218
222
  </fieldset>
219
223
  `
220
224
  : html ``}
221
- ${this.selected.length && this.selected[0].isRootModel()
225
+ ${this.selected.length &&
226
+ this.selected[0] &&
227
+ typeof this.selected[0].isRootModel === 'function' &&
228
+ this.selected[0].isRootModel()
222
229
  ? html `
223
230
  <fieldset class="icon-label">
224
231
  <legend>
@@ -284,38 +291,49 @@ export class PropertyShapes extends AbstractProperty {
284
291
  }
285
292
  _hasTextProperty(selected) {
286
293
  for (let i = 0; i < selected.length; i++) {
287
- if (!selected[i].hasTextProperty)
294
+ if (!selected[i] || !selected[i].hasTextProperty)
288
295
  return false;
289
296
  }
290
297
  return true;
291
298
  }
292
299
  _hasProperties(selected) {
293
- if (!selected || selected.length == 0 || (selected[0] && selected[0].isLayer && selected[0].isLayer()))
300
+ if (!selected ||
301
+ selected.length == 0 ||
302
+ (selected[0] && typeof selected[0].isLayer === 'function' && selected[0].isLayer()))
294
303
  return false;
295
304
  return true;
296
305
  }
297
306
  _isIdentifiable(selected) {
298
- if (!selected || selected.length == 0 || selected.length > 1 || (selected[0].isLayer && selected[0].isLayer()))
307
+ if (!selected ||
308
+ selected.length == 0 ||
309
+ selected.length > 1 ||
310
+ !selected[0] ||
311
+ (typeof selected[0].isLayer === 'function' && selected[0].isLayer()))
299
312
  return false;
300
313
  return true;
301
314
  }
302
315
  _isClassIdentifiable(selected) {
303
- if (!selected || (selected[0] && selected[0].isLayer()))
316
+ if (!selected || (selected[0] && typeof selected[0].isLayer === 'function' && selected[0].isLayer()))
304
317
  return false;
305
318
  return true;
306
319
  }
307
320
  _isLine(selected) {
308
- if (!selected || !(selected[0] && selected[0].isLine()))
321
+ if (!selected || !(selected[0] && typeof selected[0].isLine === 'function' && selected[0].isLine()))
309
322
  return false;
310
323
  return true;
311
324
  }
312
325
  _isPositionable(selected) {
313
- if (!selected || !(selected[0] && selected[0].isPositionable()))
326
+ if (!selected || !(selected[0] && typeof selected[0].isPositionable === 'function' && selected[0].isPositionable()))
314
327
  return false;
315
328
  return true;
316
329
  }
317
330
  }
318
- PropertyShapes.styles = [PropertyGridStyles, BoxPaddingEditorStyles];
331
+ PropertyShapes.styles = [
332
+ PropertyGridStyles,
333
+ BoxPaddingEditorStyles,
334
+ css `
335
+ `
336
+ ];
319
337
  __decorate([
320
338
  property({ type: Object })
321
339
  ], PropertyShapes.prototype, "value", void 0);