@fmsim/board 2.0.0-beta.93 → 2.0.0-beta.94

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.
@@ -10,7 +10,7 @@ export declare class RestfulInputAttachmentSelector extends LitElement {
10
10
  properties: any;
11
11
  private popup?;
12
12
  render(): import("lit-html").TemplateResult<1>;
13
- getIconByCategory(): "attachment" | "image" | "library_music" | "video_library";
13
+ getIconByCategory(): "image" | "library_music" | "video_library" | "attachment";
14
14
  _onInputChanged(e: KeyboardEvent): void;
15
15
  openSelector(): void;
16
16
  }
@@ -6,7 +6,7 @@ import '@operato/i18n/ox-i18n.js';
6
6
  import { css, html, LitElement } from 'lit';
7
7
  import { property, query, state } from 'lit/decorators.js';
8
8
  import { ScrollbarStyles } from '@operato/styles';
9
- import { searchMachineUnits, filterMachineUnitIds } from '@fmsim/api';
9
+ import { searchFigureIds, filterFigureIds } from '@fmsim/api';
10
10
  /**
11
11
  * 컴포넌트 ID 입력기 — 자유 입력 + 타입별 백엔드(MCS) 검색 선택을 함께 제공.
12
12
  *
@@ -71,7 +71,7 @@ export class OxInputEntityId extends LitElement {
71
71
  get _items() {
72
72
  if (!this._all.length)
73
73
  return [];
74
- const filtered = filterMachineUnitIds(this._all, this.value || '');
74
+ const filtered = filterFigureIds(this._all, this.value || '');
75
75
  return filtered.length ? filtered : this._all;
76
76
  }
77
77
  /** 드롭다운에서 선택 가능한 id 들 (표시 순서). */
@@ -215,7 +215,7 @@ export class OxInputEntityId extends LitElement {
215
215
  const seq = ++this._loadSeq;
216
216
  this._loading = true;
217
217
  try {
218
- const ids = await searchMachineUnits(this.entityType, '', this.machine ? { machine: this.machine } : {});
218
+ const ids = await searchFigureIds(this.entityType, '', this.machine ? { machine: this.machine } : {});
219
219
  if (seq !== this._loadSeq)
220
220
  return; // 더 최신 조회가 있으면 폐기
221
221
  this._all = ids;
@@ -1 +1 @@
1
- {"version":3,"file":"ox-input-entity-id.js","sourceRoot":"","sources":["../../../../../src/modeller/property-sidebar/shapes/ox-input-entity-id.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, LitElement, PropertyValues } from 'lit'\nimport { property, query, state } from 'lit/decorators.js'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { searchMachineUnits, filterMachineUnitIds } from '@fmsim/api'\n\n/**\n * 컴포넌트 ID 입력기 — 자유 입력 + 타입별 백엔드(MCS) 검색 선택을 함께 제공.\n *\n * - 기존처럼 자유롭게 텍스트로 ID 를 입력할 수 있다 (commit = blur/Enter).\n * - input 에 *포커스가 가면* 아래로 검색 드롭다운이 펼쳐진다. `entityType`\n * (컴포넌트 타입) 으로 MCS 에 정의된 ID 후보를 조회한다.\n * - machine 타입 → 머신 id 가 1레벨 평면으로 (선택 가능)\n * - unit 타입 → machine 이 그룹 헤더, 그 아래 unit id 가 자식 (선택 가능).\n * 컴포넌트가 부모 machine 을 알면(`machine` prop) 그 머신으로 좁혀 조회.\n *\n * value-key=\"id\" 와 함께 property-grid 안에 두면, 이 host 가 `change`(bubbles+\n * composed) 를 발행하므로 AbstractProperty 제너릭 핸들러가 `element.value` 를\n * 읽어 property-change 로 전파한다.\n *\n * 등록: 패널 자식 커스텀 엘리먼트 컨벤션에 따라 전역 `@customElement` 가 아니라\n * 부모 패널(PropertyShapes)의 `scopedElements` 로 등록한다 (@open-wc/scoped-elements).\n */\nexport class OxInputEntityId extends LitElement {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n position: relative;\n display: flex;\n align-items: stretch;\n width: 100%;\n height: 100%;\n }\n\n input {\n flex: 1 1 auto;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n margin: 0;\n padding: var(--input-padding, 2px 4px);\n font: inherit;\n border: var(--property-sidebar-fieldset-border, 1px solid #ccc);\n }\n\n .dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 100;\n\n max-height: 220px;\n overflow-y: auto;\n\n margin-top: 2px;\n background: var(--md-sys-color-surface, #fff);\n border: 1px solid var(--md-sys-color-outline-variant, #c4c7c5);\n border-radius: 4px;\n box-shadow: 0 4px 10px rgba(0, 0, 0, 0.18);\n }\n\n .group-header {\n padding: 3px 8px;\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--md-sys-color-on-surface-variant, #777);\n background: var(--md-sys-color-surface-variant, #eef0f3);\n position: sticky;\n top: 0;\n }\n\n .item {\n padding: 4px 8px;\n cursor: pointer;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font: var(--property-sidebar-fieldset-label, 12px sans-serif);\n }\n\n /* unit 항목 (그룹 자식) 은 살짝 들여쓰기 */\n .item.child {\n padding-left: 18px;\n }\n\n .item:hover,\n .item.active {\n background: var(--md-sys-color-secondary-container, #d7e3ff);\n }\n\n .hint {\n padding: 6px 8px;\n color: var(--md-sys-color-on-surface-variant, #888);\n font-size: 11px;\n }\n `\n ]\n\n /** 현재 ID 값. */\n @property({ type: String }) value = ''\n /** 검색 조건 — 선택된 컴포넌트 타입 (예: 'STOCKER', 'AGV', 'Port'). */\n @property({ type: String }) entityType = ''\n /** 검색 조건 — 부모 machine id (unit 컴포넌트가 소속 machine 을 알 때). */\n @property({ type: String }) machine = ''\n /** machine 필수 타입(예: Port) — machine 없으면 조회하지 않는다 (후보 과다 방지). */\n @property({ type: Boolean }) requireMachine = false\n /** 백엔드 조회 대신 사용할 로컬 후보 리스트 (예: parentObject — 씬의 머신들). null 이면 백엔드 조회. */\n @property({ type: Array }) localItems: string[] | null = null\n @property({ type: String }) placeholder = ''\n\n @state() private _open = false\n /** 서버에서 받은 전체 후보 리스트 (포커스 시 1회 조회, 키워드 미적용). */\n @state() private _all: string[] = []\n @state() private _loading = false\n /** machine 필수인데 machine 미지정이라 조회를 보류한 상태. */\n @state() private _needMachine = false\n /** 선택 가능 항목 기준 active index. */\n @state() private _activeIndex = -1\n\n @query('input') private _input!: HTMLInputElement\n\n private _blurTimer?: ReturnType<typeof setTimeout>\n private _loadSeq = 0\n /** 마지막으로 발행한 값 — 같은 값 재발행(undo 중복) 방지. */\n private _lastEmitted?: string\n\n disconnectedCallback() {\n super.disconnectedCallback()\n clearTimeout(this._blurTimer)\n }\n\n updated(changed: PropertyValues) {\n // 열린 상태에서 조회 조건(타입/부모 머신)이 바뀌면 재조회 (예: 부모 머신을 방금 지정).\n if (this._open && (changed.has('entityType') || changed.has('machine') || changed.has('localItems'))) {\n this._load()\n }\n // 화살표 키로 active 항목 이동 시 항상 보이도록 드롭다운을 스크롤.\n if (changed.has('_activeIndex') && this._open && this._activeIndex >= 0) {\n const el = this.renderRoot.querySelector('.item.active') as HTMLElement | null\n el?.scrollIntoView({ block: 'nearest' })\n }\n }\n\n /**\n * 드롭다운 표시 항목 — 입력값(초기값 포함)으로 풀텍스트 로컬 필터.\n * 단, 매치가 0이면 전체 후보로 fallback (막다른 \"결과 없음\" 방지 — 예: committed\n * id 가 후보 형식과 달라 안 맞는 경우). 서버 재조회 없음.\n */\n private get _items(): string[] {\n if (!this._all.length) return []\n const filtered = filterMachineUnitIds(this._all, this.value || '')\n return filtered.length ? filtered : this._all\n }\n\n /** 드롭다운에서 선택 가능한 id 들 (표시 순서). */\n private get _selectableIds(): string[] {\n return this._items\n }\n\n render() {\n return html`\n <input\n .value=${this.value || ''}\n placeholder=${this.placeholder}\n autocomplete=\"off\"\n @focus=${this._onFocus}\n @blur=${this._onBlur}\n @input=${this._onInput}\n @change=${this._onInputCommit}\n @keydown=${this._onKeydown}\n />\n ${this._open ? this._renderDropdown() : ''}\n `\n }\n\n private _renderDropdown() {\n if (this._needMachine) {\n return html`<div class=\"dropdown\">\n <div class=\"hint\"><ox-i18n msgid=\"text.need-parent-machine\">상위 머신을 먼저 지정하세요</ox-i18n></div>\n </div>`\n }\n if (this._loading) {\n return html`<div class=\"dropdown\"><div class=\"hint\"><ox-i18n msgid=\"text.searching\">searching…</ox-i18n></div></div>`\n }\n if (!this._items.length) {\n return html`<div class=\"dropdown\"><div class=\"hint\"><ox-i18n msgid=\"text.no-results\">no results</ox-i18n></div></div>`\n }\n\n return html`\n <div class=\"dropdown\">\n ${this._items.map(\n (id, idx) => html`\n <div\n class=\"item ${idx === this._activeIndex ? 'active' : ''}\"\n @mousedown=${(e: MouseEvent) => this._select(id, e)}\n >\n ${id}\n </div>\n `\n )}\n </div>\n `\n }\n\n private _onFocus() {\n clearTimeout(this._blurTimer)\n this._open = true\n this._activeIndex = -1\n this._load()\n }\n\n private _onBlur() {\n // 드롭다운 항목 mousedown 이 먼저 처리되도록 약간 지연 후 닫기\n this._blurTimer = setTimeout(() => {\n this._open = false\n this._activeIndex = -1\n }, 150)\n }\n\n private _onInput(e: Event) {\n this.value = (e.target as HTMLInputElement).value\n this._open = true\n this._activeIndex = -1\n }\n\n /** 자유 입력 commit (blur/Enter 의 native change) — 기존 free-text 동작 보존. */\n private _onInputCommit(e: Event) {\n this.value = (e.target as HTMLInputElement).value\n this._emitChange()\n }\n\n private _onKeydown(e: KeyboardEvent) {\n const ids = this._selectableIds\n if (!this._open || !ids.length) {\n if (e.key === 'Escape') this._open = false\n return\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n this._activeIndex = (this._activeIndex + 1) % ids.length\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n this._activeIndex = (this._activeIndex - 1 + ids.length) % ids.length\n } else if (e.key === 'Enter') {\n if (this._activeIndex >= 0) {\n e.preventDefault()\n this._commitValue(ids[this._activeIndex])\n }\n } else if (e.key === 'Escape') {\n this._open = false\n this._activeIndex = -1\n }\n }\n\n private _select(id: string, e: MouseEvent) {\n // mousedown 기본동작(blur) 막아 흐름 제어\n e.preventDefault()\n this._commitValue(id)\n }\n\n private _commitValue(id: string) {\n this.value = id\n this._open = false\n this._activeIndex = -1\n if (this._input) this._input.value = id\n this._emitChange()\n this._input?.blur()\n }\n\n private _emitChange() {\n // 같은 값 재발행 skip (예: 드롭다운 선택 직후 blur 의 native change 중복)\n if (this.value === this._lastEmitted) return\n this._lastEmitted = this.value\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n }\n\n /**\n * 후보 전체를 1회 조회 (포커스 시). keyword 는 보내지 않으며, 표시 필터는 _items\n * 게터가 로컬에서 처리한다. entityType(+unit 이면 machine) 만 서버로 보낸다.\n */\n private async _load() {\n // 로컬 리스트 모드(예: parentObject) — 백엔드 조회 없이 주어진 리스트 사용.\n if (this.localItems != null) {\n this._needMachine = false\n this._loading = false\n this._all = this.localItems\n return\n }\n if (!this.entityType) {\n this._all = []\n return\n }\n // machine 필수 타입(Port 등)인데 부모 머신 미지정 → 조회 보류 (후보 과다 방지).\n if (this.requireMachine && !this.machine) {\n this._all = []\n this._needMachine = true\n return\n }\n this._needMachine = false\n const seq = ++this._loadSeq\n this._loading = true\n try {\n const ids = await searchMachineUnits(this.entityType, '', this.machine ? { machine: this.machine } : {})\n if (seq !== this._loadSeq) return // 더 최신 조회가 있으면 폐기\n this._all = ids\n } catch {\n if (seq === this._loadSeq) this._all = []\n } finally {\n if (seq === this._loadSeq) this._loading = false\n }\n }\n}\n"]}
1
+ {"version":3,"file":"ox-input-entity-id.js","sourceRoot":"","sources":["../../../../../src/modeller/property-sidebar/shapes/ox-input-entity-id.ts"],"names":[],"mappings":"AAAA;;GAEG;;AAEH,OAAO,0BAA0B,CAAA;AAEjC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAkB,MAAM,KAAK,CAAA;AAC3D,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE7D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,eAAgB,SAAQ,UAAU;IAA/C;;QA+EE,eAAe;QACa,UAAK,GAAG,EAAE,CAAA;QACtC,yDAAyD;QAC7B,eAAU,GAAG,EAAE,CAAA;QAC3C,2DAA2D;QAC/B,YAAO,GAAG,EAAE,CAAA;QACxC,gEAAgE;QACnC,mBAAc,GAAG,KAAK,CAAA;QACnD,0EAA0E;QAC/C,eAAU,GAAoB,IAAI,CAAA;QACjC,gBAAW,GAAG,EAAE,CAAA;QAE3B,UAAK,GAAG,KAAK,CAAA;QAC9B,gDAAgD;QAC/B,SAAI,GAAa,EAAE,CAAA;QACnB,aAAQ,GAAG,KAAK,CAAA;QACjC,6CAA6C;QAC5B,iBAAY,GAAG,KAAK,CAAA;QACrC,gCAAgC;QACf,iBAAY,GAAG,CAAC,CAAC,CAAA;QAK1B,aAAQ,GAAG,CAAC,CAAA;IA6LtB,CAAC;IAzLC,oBAAoB;QAClB,KAAK,CAAC,oBAAoB,EAAE,CAAA;QAC5B,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IAC/B,CAAC;IAED,OAAO,CAAC,OAAuB;QAC7B,sDAAsD;QACtD,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;YACrG,IAAI,CAAC,KAAK,EAAE,CAAA;QACd,CAAC;QACD,2CAA2C;QAC3C,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,EAAE,CAAC;YACxE,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,cAAc,CAAuB,CAAA;YAC9E,EAAE,aAAF,EAAE,uBAAF,EAAE,CAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;QAC1C,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,IAAY,MAAM;QAChB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,CAAA;QAChC,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAA;QAC7D,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAA;IAC/C,CAAC;IAED,kCAAkC;IAClC,IAAY,cAAc;QACxB,OAAO,IAAI,CAAC,MAAM,CAAA;IACpB,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAA;;iBAEE,IAAI,CAAC,KAAK,IAAI,EAAE;sBACX,IAAI,CAAC,WAAW;;iBAErB,IAAI,CAAC,QAAQ;gBACd,IAAI,CAAC,OAAO;iBACX,IAAI,CAAC,QAAQ;kBACZ,IAAI,CAAC,cAAc;mBAClB,IAAI,CAAC,UAAU;;QAE1B,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE;KAC3C,CAAA;IACH,CAAC;IAEO,eAAe;QACrB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,OAAO,IAAI,CAAA;;aAEJ,CAAA;QACT,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,OAAO,IAAI,CAAA,0GAA0G,CAAA;QACvH,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACxB,OAAO,IAAI,CAAA,2GAA2G,CAAA;QACxH,CAAC;QAED,OAAO,IAAI,CAAA;;UAEL,IAAI,CAAC,MAAM,CAAC,GAAG,CACf,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAA;;4BAEC,GAAG,KAAK,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;2BAC1C,CAAC,CAAa,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;;gBAEjD,EAAE;;WAEP,CACF;;KAEJ,CAAA;IACH,CAAC;IAEO,QAAQ;QACd,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAC7B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACjB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,CAAA;QACtB,IAAI,CAAC,KAAK,EAAE,CAAA;IACd,CAAC;IAEO,OAAO;QACb,0CAA0C;QAC1C,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;YAChC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;YAClB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,CAAA;QACxB,CAAC,EAAE,GAAG,CAAC,CAAA;IACT,CAAC;IAEO,QAAQ,CAAC,CAAQ;QACvB,IAAI,CAAC,KAAK,GAAI,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAA;QACjD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACjB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,CAAA;IACxB,CAAC;IAED,sEAAsE;IAC9D,cAAc,CAAC,CAAQ;QAC7B,IAAI,CAAC,KAAK,GAAI,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAA;QACjD,IAAI,CAAC,WAAW,EAAE,CAAA;IACpB,CAAC;IAEO,UAAU,CAAC,CAAgB;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,CAAA;QAC/B,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;YAC/B,IAAI,CAAC,CAAC,GAAG,KAAK,QAAQ;gBAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;YAC1C,OAAM;QACR,CAAC;QACD,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW,EAAE,CAAC;YAC1B,CAAC,CAAC,cAAc,EAAE,CAAA;YAClB,IAAI,CAAC,YAAY,GAAG,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,CAAA;QAC1D,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC/B,CAAC,CAAC,cAAc,EAAE,CAAA;YAClB,IAAI,CAAC,YAAY,GAAG,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAA;QACvE,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;YAC7B,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,EAAE,CAAC;gBAC3B,CAAC,CAAC,cAAc,EAAE,CAAA;gBAClB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAA;YAC3C,CAAC;QACH,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC9B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;YAClB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,CAAA;QACxB,CAAC;IACH,CAAC;IAEO,OAAO,CAAC,EAAU,EAAE,CAAa;QACvC,gCAAgC;QAChC,CAAC,CAAC,cAAc,EAAE,CAAA;QAClB,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;IACvB,CAAC;IAEO,YAAY,CAAC,EAAU;;QAC7B,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;QACf,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,CAAA;QACtB,IAAI,IAAI,CAAC,MAAM;YAAE,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAA;QACvC,IAAI,CAAC,WAAW,EAAE,CAAA;QAClB,MAAA,IAAI,CAAC,MAAM,0CAAE,IAAI,EAAE,CAAA;IACrB,CAAC;IAEO,WAAW;QACjB,wDAAwD;QACxD,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,YAAY;YAAE,OAAM;QAC5C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,CAAA;QAC9B,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IAClF,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,KAAK;QACjB,qDAAqD;QACrD,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;YAC5B,IAAI,CAAC,YAAY,GAAG,KAAK,CAAA;YACzB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAA;YACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,CAAA;YAC3B,OAAM;QACR,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,IAAI,CAAC,IAAI,GAAG,EAAE,CAAA;YACd,OAAM;QACR,CAAC;QACD,wDAAwD;QACxD,IAAI,IAAI,CAAC,cAAc,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YACzC,IAAI,CAAC,IAAI,GAAG,EAAE,CAAA;YACd,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;YACxB,OAAM;QACR,CAAC;QACD,IAAI,CAAC,YAAY,GAAG,KAAK,CAAA;QACzB,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAA;QAC3B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAA;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;YACrG,IAAI,GAAG,KAAK,IAAI,CAAC,QAAQ;gBAAE,OAAM,CAAC,kBAAkB;YACpD,IAAI,CAAC,IAAI,GAAG,GAAG,CAAA;QACjB,CAAC;QAAC,WAAM,CAAC;YACP,IAAI,GAAG,KAAK,IAAI,CAAC,QAAQ;gBAAE,IAAI,CAAC,IAAI,GAAG,EAAE,CAAA;QAC3C,CAAC;gBAAS,CAAC;YACT,IAAI,GAAG,KAAK,IAAI,CAAC,QAAQ;gBAAE,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAA;QAClD,CAAC;IACH,CAAC;;AAlSM,sBAAM,GAAG;IACd,eAAe;IACf,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyEJ;CACA,AA5EY,CA4EZ;AAG2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;8CAAW;AAEV;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;mDAAgB;AAEf;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;gDAAa;AAEX;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;uDAAuB;AAExB;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;mDAAmC;AACjC;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;oDAAiB;AAE3B;IAAhB,KAAK,EAAE;8CAAsB;AAEb;IAAhB,KAAK,EAAE;6CAA4B;AACnB;IAAhB,KAAK,EAAE;iDAAyB;AAEhB;IAAhB,KAAK,EAAE;qDAA6B;AAEpB;IAAhB,KAAK,EAAE;qDAA0B;AAEV;IAAvB,KAAK,CAAC,OAAO,CAAC;+CAAkC","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, LitElement, PropertyValues } from 'lit'\nimport { property, query, state } from 'lit/decorators.js'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { searchFigureIds, filterFigureIds } from '@fmsim/api'\n\n/**\n * 컴포넌트 ID 입력기 — 자유 입력 + 타입별 백엔드(MCS) 검색 선택을 함께 제공.\n *\n * - 기존처럼 자유롭게 텍스트로 ID 를 입력할 수 있다 (commit = blur/Enter).\n * - input 에 *포커스가 가면* 아래로 검색 드롭다운이 펼쳐진다. `entityType`\n * (컴포넌트 타입) 으로 MCS 에 정의된 ID 후보를 조회한다.\n * - machine 타입 → 머신 id 가 1레벨 평면으로 (선택 가능)\n * - unit 타입 → machine 이 그룹 헤더, 그 아래 unit id 가 자식 (선택 가능).\n * 컴포넌트가 부모 machine 을 알면(`machine` prop) 그 머신으로 좁혀 조회.\n *\n * value-key=\"id\" 와 함께 property-grid 안에 두면, 이 host 가 `change`(bubbles+\n * composed) 를 발행하므로 AbstractProperty 제너릭 핸들러가 `element.value` 를\n * 읽어 property-change 로 전파한다.\n *\n * 등록: 패널 자식 커스텀 엘리먼트 컨벤션에 따라 전역 `@customElement` 가 아니라\n * 부모 패널(PropertyShapes)의 `scopedElements` 로 등록한다 (@open-wc/scoped-elements).\n */\nexport class OxInputEntityId extends LitElement {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n position: relative;\n display: flex;\n align-items: stretch;\n width: 100%;\n height: 100%;\n }\n\n input {\n flex: 1 1 auto;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n margin: 0;\n padding: var(--input-padding, 2px 4px);\n font: inherit;\n border: var(--property-sidebar-fieldset-border, 1px solid #ccc);\n }\n\n .dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 100;\n\n max-height: 220px;\n overflow-y: auto;\n\n margin-top: 2px;\n background: var(--md-sys-color-surface, #fff);\n border: 1px solid var(--md-sys-color-outline-variant, #c4c7c5);\n border-radius: 4px;\n box-shadow: 0 4px 10px rgba(0, 0, 0, 0.18);\n }\n\n .group-header {\n padding: 3px 8px;\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--md-sys-color-on-surface-variant, #777);\n background: var(--md-sys-color-surface-variant, #eef0f3);\n position: sticky;\n top: 0;\n }\n\n .item {\n padding: 4px 8px;\n cursor: pointer;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font: var(--property-sidebar-fieldset-label, 12px sans-serif);\n }\n\n /* unit 항목 (그룹 자식) 은 살짝 들여쓰기 */\n .item.child {\n padding-left: 18px;\n }\n\n .item:hover,\n .item.active {\n background: var(--md-sys-color-secondary-container, #d7e3ff);\n }\n\n .hint {\n padding: 6px 8px;\n color: var(--md-sys-color-on-surface-variant, #888);\n font-size: 11px;\n }\n `\n ]\n\n /** 현재 ID 값. */\n @property({ type: String }) value = ''\n /** 검색 조건 — 선택된 컴포넌트 타입 (예: 'STOCKER', 'AGV', 'Port'). */\n @property({ type: String }) entityType = ''\n /** 검색 조건 — 부모 machine id (unit 컴포넌트가 소속 machine 을 알 때). */\n @property({ type: String }) machine = ''\n /** machine 필수 타입(예: Port) — machine 없으면 조회하지 않는다 (후보 과다 방지). */\n @property({ type: Boolean }) requireMachine = false\n /** 백엔드 조회 대신 사용할 로컬 후보 리스트 (예: parentObject — 씬의 머신들). null 이면 백엔드 조회. */\n @property({ type: Array }) localItems: string[] | null = null\n @property({ type: String }) placeholder = ''\n\n @state() private _open = false\n /** 서버에서 받은 전체 후보 리스트 (포커스 시 1회 조회, 키워드 미적용). */\n @state() private _all: string[] = []\n @state() private _loading = false\n /** machine 필수인데 machine 미지정이라 조회를 보류한 상태. */\n @state() private _needMachine = false\n /** 선택 가능 항목 기준 active index. */\n @state() private _activeIndex = -1\n\n @query('input') private _input!: HTMLInputElement\n\n private _blurTimer?: ReturnType<typeof setTimeout>\n private _loadSeq = 0\n /** 마지막으로 발행한 값 — 같은 값 재발행(undo 중복) 방지. */\n private _lastEmitted?: string\n\n disconnectedCallback() {\n super.disconnectedCallback()\n clearTimeout(this._blurTimer)\n }\n\n updated(changed: PropertyValues) {\n // 열린 상태에서 조회 조건(타입/부모 머신)이 바뀌면 재조회 (예: 부모 머신을 방금 지정).\n if (this._open && (changed.has('entityType') || changed.has('machine') || changed.has('localItems'))) {\n this._load()\n }\n // 화살표 키로 active 항목 이동 시 항상 보이도록 드롭다운을 스크롤.\n if (changed.has('_activeIndex') && this._open && this._activeIndex >= 0) {\n const el = this.renderRoot.querySelector('.item.active') as HTMLElement | null\n el?.scrollIntoView({ block: 'nearest' })\n }\n }\n\n /**\n * 드롭다운 표시 항목 — 입력값(초기값 포함)으로 풀텍스트 로컬 필터.\n * 단, 매치가 0이면 전체 후보로 fallback (막다른 \"결과 없음\" 방지 — 예: committed\n * id 가 후보 형식과 달라 안 맞는 경우). 서버 재조회 없음.\n */\n private get _items(): string[] {\n if (!this._all.length) return []\n const filtered = filterFigureIds(this._all, this.value || '')\n return filtered.length ? filtered : this._all\n }\n\n /** 드롭다운에서 선택 가능한 id 들 (표시 순서). */\n private get _selectableIds(): string[] {\n return this._items\n }\n\n render() {\n return html`\n <input\n .value=${this.value || ''}\n placeholder=${this.placeholder}\n autocomplete=\"off\"\n @focus=${this._onFocus}\n @blur=${this._onBlur}\n @input=${this._onInput}\n @change=${this._onInputCommit}\n @keydown=${this._onKeydown}\n />\n ${this._open ? this._renderDropdown() : ''}\n `\n }\n\n private _renderDropdown() {\n if (this._needMachine) {\n return html`<div class=\"dropdown\">\n <div class=\"hint\"><ox-i18n msgid=\"text.need-parent-machine\">상위 머신을 먼저 지정하세요</ox-i18n></div>\n </div>`\n }\n if (this._loading) {\n return html`<div class=\"dropdown\"><div class=\"hint\"><ox-i18n msgid=\"text.searching\">searching…</ox-i18n></div></div>`\n }\n if (!this._items.length) {\n return html`<div class=\"dropdown\"><div class=\"hint\"><ox-i18n msgid=\"text.no-results\">no results</ox-i18n></div></div>`\n }\n\n return html`\n <div class=\"dropdown\">\n ${this._items.map(\n (id, idx) => html`\n <div\n class=\"item ${idx === this._activeIndex ? 'active' : ''}\"\n @mousedown=${(e: MouseEvent) => this._select(id, e)}\n >\n ${id}\n </div>\n `\n )}\n </div>\n `\n }\n\n private _onFocus() {\n clearTimeout(this._blurTimer)\n this._open = true\n this._activeIndex = -1\n this._load()\n }\n\n private _onBlur() {\n // 드롭다운 항목 mousedown 이 먼저 처리되도록 약간 지연 후 닫기\n this._blurTimer = setTimeout(() => {\n this._open = false\n this._activeIndex = -1\n }, 150)\n }\n\n private _onInput(e: Event) {\n this.value = (e.target as HTMLInputElement).value\n this._open = true\n this._activeIndex = -1\n }\n\n /** 자유 입력 commit (blur/Enter 의 native change) — 기존 free-text 동작 보존. */\n private _onInputCommit(e: Event) {\n this.value = (e.target as HTMLInputElement).value\n this._emitChange()\n }\n\n private _onKeydown(e: KeyboardEvent) {\n const ids = this._selectableIds\n if (!this._open || !ids.length) {\n if (e.key === 'Escape') this._open = false\n return\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n this._activeIndex = (this._activeIndex + 1) % ids.length\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n this._activeIndex = (this._activeIndex - 1 + ids.length) % ids.length\n } else if (e.key === 'Enter') {\n if (this._activeIndex >= 0) {\n e.preventDefault()\n this._commitValue(ids[this._activeIndex])\n }\n } else if (e.key === 'Escape') {\n this._open = false\n this._activeIndex = -1\n }\n }\n\n private _select(id: string, e: MouseEvent) {\n // mousedown 기본동작(blur) 막아 흐름 제어\n e.preventDefault()\n this._commitValue(id)\n }\n\n private _commitValue(id: string) {\n this.value = id\n this._open = false\n this._activeIndex = -1\n if (this._input) this._input.value = id\n this._emitChange()\n this._input?.blur()\n }\n\n private _emitChange() {\n // 같은 값 재발행 skip (예: 드롭다운 선택 직후 blur 의 native change 중복)\n if (this.value === this._lastEmitted) return\n this._lastEmitted = this.value\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n }\n\n /**\n * 후보 전체를 1회 조회 (포커스 시). keyword 는 보내지 않으며, 표시 필터는 _items\n * 게터가 로컬에서 처리한다. entityType(+unit 이면 machine) 만 서버로 보낸다.\n */\n private async _load() {\n // 로컬 리스트 모드(예: parentObject) — 백엔드 조회 없이 주어진 리스트 사용.\n if (this.localItems != null) {\n this._needMachine = false\n this._loading = false\n this._all = this.localItems\n return\n }\n if (!this.entityType) {\n this._all = []\n return\n }\n // machine 필수 타입(Port 등)인데 부모 머신 미지정 → 조회 보류 (후보 과다 방지).\n if (this.requireMachine && !this.machine) {\n this._all = []\n this._needMachine = true\n return\n }\n this._needMachine = false\n const seq = ++this._loadSeq\n this._loading = true\n try {\n const ids = await searchFigureIds(this.entityType, '', this.machine ? { machine: this.machine } : {})\n if (seq !== this._loadSeq) return // 더 최신 조회가 있으면 폐기\n this._all = ids\n } catch {\n if (seq === this._loadSeq) this._all = []\n } finally {\n if (seq === this._loadSeq) this._loading = false\n }\n }\n}\n"]}