@operato/property-panel 10.0.0-beta.58 → 10.0.0-beta.59

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.
@@ -12,10 +12,12 @@ import '@operato/i18n/ox-i18n.js';
12
12
  import { LitElement } from 'lit';
13
13
  import type { Scene } from '@hatiolab/things-scene';
14
14
  import { EventHandlersMapper, type EventHandlerSpec } from './event-handlers-mapper.js';
15
+ import { EventHandlersHelp } from './event-handlers-help.js';
15
16
  declare const EventHandlersPopup_base: typeof LitElement & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/types.js").ScopedElementsHost>;
16
17
  export declare class EventHandlersPopup extends EventHandlersPopup_base {
17
18
  static get scopedElements(): {
18
19
  'event-handlers-mapper': typeof EventHandlersMapper;
20
+ 'event-handlers-help': typeof EventHandlersHelp;
19
21
  };
20
22
  static styles: import("lit").CSSResult[];
21
23
  handlers: EventHandlerSpec[];
@@ -30,6 +32,11 @@ export declare class EventHandlersPopup extends EventHandlersPopup_base {
30
32
  private _moveHandler;
31
33
  private _removeHandler;
32
34
  private _onHandlerChanged;
35
+ /**
36
+ * help pane 의 snippet 버튼 → 현재 선택 handler 의 code 에 *append* (덮어쓰지
37
+ * 않음). action 이 script 가 아니면 자동으로 script 로 전환 + code 초기화.
38
+ */
39
+ private _onInsertSnippet;
33
40
  private _dispatch;
34
41
  }
35
42
  export {};
@@ -13,7 +13,9 @@ import '@operato/i18n/ox-i18n.js';
13
13
  import { css, html, LitElement } from 'lit';
14
14
  import { customElement, property, state } from 'lit/decorators.js';
15
15
  import { ScopedElementsMixin } from '@open-wc/scoped-elements';
16
+ import { ScrollbarStyles } from '@operato/styles';
16
17
  import { EventHandlersMapper } from './event-handlers-mapper.js';
18
+ import { EventHandlersHelp } from './event-handlers-help.js';
17
19
  let EventHandlersPopup = class EventHandlersPopup extends ScopedElementsMixin(LitElement) {
18
20
  constructor() {
19
21
  super(...arguments);
@@ -22,7 +24,8 @@ let EventHandlersPopup = class EventHandlersPopup extends ScopedElementsMixin(Li
22
24
  }
23
25
  static get scopedElements() {
24
26
  return {
25
- 'event-handlers-mapper': EventHandlersMapper
27
+ 'event-handlers-mapper': EventHandlersMapper,
28
+ 'event-handlers-help': EventHandlersHelp
26
29
  };
27
30
  }
28
31
  render() {
@@ -51,6 +54,13 @@ let EventHandlersPopup = class EventHandlersPopup extends ScopedElementsMixin(Li
51
54
  `
52
55
  : html `<div empty><ox-i18n msgid="label.select-or-add-handler">Select or add a handler</ox-i18n></div>`}
53
56
  </div>
57
+
58
+ <div help>
59
+ <event-handlers-help
60
+ .handler=${current}
61
+ @insert-snippet=${(e) => this._onInsertSnippet(e)}
62
+ ></event-handlers-help>
63
+ </div>
54
64
  `;
55
65
  }
56
66
  _renderListItem(h, i, total) {
@@ -153,19 +163,49 @@ let EventHandlersPopup = class EventHandlersPopup extends ScopedElementsMixin(Li
153
163
  this.handlers = next;
154
164
  this._dispatch();
155
165
  }
166
+ /**
167
+ * help pane 의 snippet 버튼 → 현재 선택 handler 의 code 에 *append* (덮어쓰지
168
+ * 않음). action 이 script 가 아니면 자동으로 script 로 전환 + code 초기화.
169
+ */
170
+ _onInsertSnippet(e) {
171
+ var _a;
172
+ const snippet = (_a = e.detail) === null || _a === void 0 ? void 0 : _a.code;
173
+ if (typeof snippet !== 'string' || !snippet)
174
+ return;
175
+ const cur = this.handlers[this.selectedIndex];
176
+ if (!cur)
177
+ return;
178
+ const isScript = cur.action === 'script';
179
+ const baseCode = isScript ? (cur.code || '') : '';
180
+ const nextCode = baseCode ? baseCode.replace(/\s*$/, '') + '\n\n' + snippet : snippet;
181
+ const nextHandler = {
182
+ ...cur,
183
+ action: 'script',
184
+ code: nextCode
185
+ };
186
+ const next = [...this.handlers];
187
+ next[this.selectedIndex] = nextHandler;
188
+ this.handlers = next;
189
+ this._dispatch();
190
+ }
156
191
  _dispatch() {
192
+ // action 이 (none) — 즉 빈 문자열 — 인 핸들러는 외부 모델에 포함하지 않는다.
193
+ // UI 안에서는 placeholder 로 남겨두고, 사용자가 action 을 선택하면 그제서야
194
+ // 저장 모델에 포함된다.
195
+ const valid = (this.handlers || []).filter(h => !!h && !!h.action);
157
196
  this.dispatchEvent(new CustomEvent('handlers-change', {
158
197
  bubbles: true,
159
198
  composed: true,
160
- detail: { handlers: this.handlers }
199
+ detail: { handlers: valid }
161
200
  }));
162
201
  }
163
202
  };
164
203
  EventHandlersPopup.styles = [
204
+ ScrollbarStyles,
165
205
  css `
166
206
  :host {
167
207
  display: flex;
168
- width: 1100px;
208
+ width: 1400px;
169
209
  height: 680px;
170
210
  overflow: hidden;
171
211
  font-family: 'Roboto', Arial, sans-serif;
@@ -175,8 +215,8 @@ EventHandlersPopup.styles = [
175
215
  }
176
216
 
177
217
  [handler-list] {
178
- width: 280px;
179
- min-width: 280px;
218
+ width: 240px;
219
+ min-width: 240px;
180
220
  border-right: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.12));
181
221
  display: flex;
182
222
  flex-direction: column;
@@ -306,6 +346,16 @@ EventHandlersPopup.styles = [
306
346
 
307
347
  [editor] event-handlers-mapper {
308
348
  min-height: 100%;
349
+ /* popup 에서는 위에 toolbar 가 없다 — 상하 모두 둥근 모서리로 균형. */
350
+ border-radius: 6px;
351
+ }
352
+
353
+ [help] {
354
+ width: 320px;
355
+ min-width: 320px;
356
+ max-width: 320px;
357
+ display: flex;
358
+ flex-direction: column;
309
359
  }
310
360
  `
311
361
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"event-handlers-popup.js","sourceRoot":"","sources":["../../../../src/property-panel/event-handlers/event-handlers-popup.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * EventHandlers popup — N 핸들러 large editor. data-binding-popup 의 형제.\n *\n * 2-column layout:\n * Left: handler list — add / duplicate / disable / reorder / delete\n * Right: 선택된 handler 의 mapper (큰 code editor 영역)\n */\n\nimport '@material/web/icon/icon.js'\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport type { Scene } from '@hatiolab/things-scene'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\n\nimport { EventHandlersMapper, type EventHandlerSpec } from './event-handlers-mapper.js'\n\n@customElement('event-handlers-popup')\nexport class EventHandlersPopup extends ScopedElementsMixin(LitElement) {\n static get scopedElements() {\n return {\n 'event-handlers-mapper': EventHandlersMapper\n }\n }\n\n static styles = [\n css`\n :host {\n display: flex;\n width: 1100px;\n height: 680px;\n overflow: hidden;\n font-family: 'Roboto', Arial, sans-serif;\n font-size: 13px;\n color: var(--md-sys-color-on-surface, #1c1b1f);\n background: var(--md-sys-color-surface, #fff);\n }\n\n [handler-list] {\n width: 280px;\n min-width: 280px;\n border-right: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.12));\n display: flex;\n flex-direction: column;\n background: var(--md-sys-color-surface-container-low, #f7f2fa);\n }\n\n [handler-list] header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n border-bottom: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.12));\n }\n\n [handler-list] header md-icon {\n cursor: pointer;\n --md-icon-size: 18px;\n border-radius: 4px;\n padding: 2px;\n }\n\n [handler-list] header md-icon:hover {\n background: rgba(0, 0, 0, 0.05);\n }\n\n [handler-list] ul {\n list-style: none;\n margin: 0;\n padding: 0;\n overflow-y: auto;\n flex: 1;\n }\n\n [handler-list] li {\n padding: 8px 10px;\n cursor: pointer;\n border-bottom: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.08));\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n [handler-list] li[active] {\n background: var(--md-sys-color-secondary-container, #e8def8);\n color: var(--md-sys-color-on-secondary-container, #1d192b);\n }\n\n [handler-list] li[disabled] {\n opacity: 0.5;\n font-style: italic;\n }\n\n [handler-list] li:hover {\n background: rgba(0, 0, 0, 0.04);\n }\n\n [handler-list] li .meta {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n }\n\n [handler-list] li .trigger {\n font-weight: 600;\n font-size: 12px;\n }\n\n [handler-list] li .summary {\n font-size: 11px;\n opacity: 0.7;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n margin-top: 2px;\n }\n\n [handler-list] li md-icon {\n --md-icon-size: 16px;\n opacity: 0.5;\n cursor: pointer;\n padding: 3px;\n border-radius: 4px;\n flex-shrink: 0;\n }\n\n [handler-list] li md-icon:hover {\n opacity: 1;\n background: rgba(0, 0, 0, 0.05);\n }\n\n [handler-list] li md-icon.danger:hover {\n color: var(--md-sys-color-error, #b3261e);\n }\n\n [handler-list] [empty] {\n padding: 24px 12px;\n text-align: center;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n opacity: 0.6;\n font-size: 12px;\n }\n\n [editor] {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n padding: 16px 20px;\n background: var(--md-sys-color-surface, #fff);\n }\n\n [editor] [empty] {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 1;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n font-size: 13px;\n opacity: 0.6;\n }\n\n [editor] event-handlers-mapper {\n min-height: 100%;\n }\n `\n ]\n\n @property({ type: Array }) handlers: EventHandlerSpec[] = []\n @property({ type: Object }) scene?: Scene\n @state() selectedIndex: number = 0\n\n render() {\n const handlers = this.handlers ?? []\n const current = handlers[this.selectedIndex]\n\n return html`\n <div handler-list>\n <header>\n <span><ox-i18n msgid=\"label.handlers\">Handlers</ox-i18n> (${handlers.length})</span>\n <md-icon @click=${() => this._addHandler()} title=\"add\">add</md-icon>\n </header>\n ${handlers.length === 0\n ? html`<div empty><ox-i18n msgid=\"label.no-handlers\">No handlers — add one</ox-i18n></div>`\n : html`<ul>${handlers.map((h, i) => this._renderListItem(h, i, handlers.length))}</ul>`}\n </div>\n\n <div editor>\n ${current\n ? html`\n <event-handlers-mapper\n .handler=${current}\n .scene=${this.scene}\n @value-change=${(e: CustomEvent) => this._onHandlerChanged(e)}\n ></event-handlers-mapper>\n `\n : html`<div empty><ox-i18n msgid=\"label.select-or-add-handler\">Select or add a handler</ox-i18n></div>`}\n </div>\n `\n }\n\n private _renderListItem(h: EventHandlerSpec, i: number, total: number) {\n return html`\n <li\n ?active=${i === this.selectedIndex}\n ?disabled=${h.disabled}\n @click=${() => (this.selectedIndex = i)}\n >\n <div class=\"meta\">\n <span class=\"trigger\">${h.trigger}${h.disabled ? ' (off)' : ''}</span>\n <span class=\"summary\">${this._summarize(h)}</span>\n </div>\n <md-icon\n @click=${(e: Event) => this._toggleDisabled(i, e)}\n title=${h.disabled ? 'enable' : 'disable'}\n >${h.disabled ? 'toggle_off' : 'toggle_on'}</md-icon>\n <md-icon\n @click=${(e: Event) => this._duplicateHandler(i, e)}\n title=\"duplicate\"\n >content_copy</md-icon>\n <md-icon\n @click=${(e: Event) => this._moveHandler(i, -1, e)}\n title=\"move up\"\n ?style=${i === 0 ? 'opacity:0.2;pointer-events:none' : ''}\n >arrow_upward</md-icon>\n <md-icon\n @click=${(e: Event) => this._moveHandler(i, +1, e)}\n title=\"move down\"\n ?style=${i === total - 1 ? 'opacity:0.2;pointer-events:none' : ''}\n >arrow_downward</md-icon>\n <md-icon\n class=\"danger\"\n @click=${(e: Event) => this._removeHandler(i, e)}\n title=\"delete\"\n >delete</md-icon>\n </li>\n `\n }\n\n private _summarize(h: EventHandlerSpec): string {\n if (h.action === 'script') {\n const c = (h.code || '').replace(/\\s+/g, ' ').trim()\n return 'script · ' + (c.length > 60 ? c.slice(0, 60) + '…' : (c || '(empty)'))\n }\n return [h.action || '(none)', h.target ? `→${h.target}` : ''].filter(Boolean).join(' ')\n }\n\n private _addHandler() {\n const next: EventHandlerSpec = { trigger: 'click', action: '' }\n this.handlers = [...(this.handlers || []), next]\n this.selectedIndex = this.handlers.length - 1\n this._dispatch()\n }\n\n private _duplicateHandler(idx: number, e: Event) {\n e.stopPropagation()\n const original = this.handlers[idx]\n if (!original) return\n const copy: EventHandlerSpec = JSON.parse(JSON.stringify(original))\n const next = [...(this.handlers || [])]\n next.splice(idx + 1, 0, copy)\n this.handlers = next\n this.selectedIndex = idx + 1\n this._dispatch()\n }\n\n private _toggleDisabled(idx: number, e: Event) {\n e.stopPropagation()\n const next = [...(this.handlers || [])]\n next[idx] = { ...next[idx], disabled: !next[idx].disabled }\n this.handlers = next\n this._dispatch()\n }\n\n private _moveHandler(idx: number, delta: -1 | 1, e: Event) {\n e.stopPropagation()\n const next = [...(this.handlers || [])]\n const newIdx = idx + delta\n if (newIdx < 0 || newIdx >= next.length) return\n const [m] = next.splice(idx, 1)\n next.splice(newIdx, 0, m)\n this.handlers = next\n if (this.selectedIndex === idx) this.selectedIndex = newIdx\n this._dispatch()\n }\n\n private _removeHandler(idx: number, e: Event) {\n e.stopPropagation()\n const next = [...(this.handlers || [])]\n next.splice(idx, 1)\n this.handlers = next\n this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, next.length - 1))\n this._dispatch()\n }\n\n private _onHandlerChanged(e: CustomEvent) {\n const handler = e.detail?.handler as EventHandlerSpec\n if (!handler) return\n const next = [...(this.handlers || [])]\n next[this.selectedIndex] = handler\n this.handlers = next\n this._dispatch()\n }\n\n private _dispatch() {\n this.dispatchEvent(\n new CustomEvent('handlers-change', {\n bubbles: true,\n composed: true,\n detail: { handlers: this.handlers }\n })\n )\n }\n}\n"]}
1
+ {"version":3,"file":"event-handlers-popup.js","sourceRoot":"","sources":["../../../../src/property-panel/event-handlers/event-handlers-popup.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * EventHandlers popup — N 핸들러 large editor. data-binding-popup 의 형제.\n *\n * 2-column layout:\n * Left: handler list — add / duplicate / disable / reorder / delete\n * Right: 선택된 handler 의 mapper (큰 code editor 영역)\n */\n\nimport '@material/web/icon/icon.js'\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport type { Scene } from '@hatiolab/things-scene'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { EventHandlersMapper, type EventHandlerSpec } from './event-handlers-mapper.js'\nimport { EventHandlersHelp } from './event-handlers-help.js'\n\n@customElement('event-handlers-popup')\nexport class EventHandlersPopup extends ScopedElementsMixin(LitElement) {\n static get scopedElements() {\n return {\n 'event-handlers-mapper': EventHandlersMapper,\n 'event-handlers-help': EventHandlersHelp\n }\n }\n\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n width: 1400px;\n height: 680px;\n overflow: hidden;\n font-family: 'Roboto', Arial, sans-serif;\n font-size: 13px;\n color: var(--md-sys-color-on-surface, #1c1b1f);\n background: var(--md-sys-color-surface, #fff);\n }\n\n [handler-list] {\n width: 240px;\n min-width: 240px;\n border-right: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.12));\n display: flex;\n flex-direction: column;\n background: var(--md-sys-color-surface-container-low, #f7f2fa);\n }\n\n [handler-list] header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n border-bottom: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.12));\n }\n\n [handler-list] header md-icon {\n cursor: pointer;\n --md-icon-size: 18px;\n border-radius: 4px;\n padding: 2px;\n }\n\n [handler-list] header md-icon:hover {\n background: rgba(0, 0, 0, 0.05);\n }\n\n [handler-list] ul {\n list-style: none;\n margin: 0;\n padding: 0;\n overflow-y: auto;\n flex: 1;\n }\n\n [handler-list] li {\n padding: 8px 10px;\n cursor: pointer;\n border-bottom: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.08));\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n [handler-list] li[active] {\n background: var(--md-sys-color-secondary-container, #e8def8);\n color: var(--md-sys-color-on-secondary-container, #1d192b);\n }\n\n [handler-list] li[disabled] {\n opacity: 0.5;\n font-style: italic;\n }\n\n [handler-list] li:hover {\n background: rgba(0, 0, 0, 0.04);\n }\n\n [handler-list] li .meta {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n }\n\n [handler-list] li .trigger {\n font-weight: 600;\n font-size: 12px;\n }\n\n [handler-list] li .summary {\n font-size: 11px;\n opacity: 0.7;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n margin-top: 2px;\n }\n\n [handler-list] li md-icon {\n --md-icon-size: 16px;\n opacity: 0.5;\n cursor: pointer;\n padding: 3px;\n border-radius: 4px;\n flex-shrink: 0;\n }\n\n [handler-list] li md-icon:hover {\n opacity: 1;\n background: rgba(0, 0, 0, 0.05);\n }\n\n [handler-list] li md-icon.danger:hover {\n color: var(--md-sys-color-error, #b3261e);\n }\n\n [handler-list] [empty] {\n padding: 24px 12px;\n text-align: center;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n opacity: 0.6;\n font-size: 12px;\n }\n\n [editor] {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n padding: 16px 20px;\n background: var(--md-sys-color-surface, #fff);\n }\n\n [editor] [empty] {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 1;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n font-size: 13px;\n opacity: 0.6;\n }\n\n [editor] event-handlers-mapper {\n min-height: 100%;\n /* popup 에서는 위에 toolbar 가 없다 — 상하 모두 둥근 모서리로 균형. */\n border-radius: 6px;\n }\n\n [help] {\n width: 320px;\n min-width: 320px;\n max-width: 320px;\n display: flex;\n flex-direction: column;\n }\n `\n ]\n\n @property({ type: Array }) handlers: EventHandlerSpec[] = []\n @property({ type: Object }) scene?: Scene\n @state() selectedIndex: number = 0\n\n render() {\n const handlers = this.handlers ?? []\n const current = handlers[this.selectedIndex]\n\n return html`\n <div handler-list>\n <header>\n <span><ox-i18n msgid=\"label.handlers\">Handlers</ox-i18n> (${handlers.length})</span>\n <md-icon @click=${() => this._addHandler()} title=\"add\">add</md-icon>\n </header>\n ${handlers.length === 0\n ? html`<div empty><ox-i18n msgid=\"label.no-handlers\">No handlers — add one</ox-i18n></div>`\n : html`<ul>${handlers.map((h, i) => this._renderListItem(h, i, handlers.length))}</ul>`}\n </div>\n\n <div editor>\n ${current\n ? html`\n <event-handlers-mapper\n .handler=${current}\n .scene=${this.scene}\n @value-change=${(e: CustomEvent) => this._onHandlerChanged(e)}\n ></event-handlers-mapper>\n `\n : html`<div empty><ox-i18n msgid=\"label.select-or-add-handler\">Select or add a handler</ox-i18n></div>`}\n </div>\n\n <div help>\n <event-handlers-help\n .handler=${current}\n @insert-snippet=${(e: CustomEvent) => this._onInsertSnippet(e)}\n ></event-handlers-help>\n </div>\n `\n }\n\n private _renderListItem(h: EventHandlerSpec, i: number, total: number) {\n return html`\n <li\n ?active=${i === this.selectedIndex}\n ?disabled=${h.disabled}\n @click=${() => (this.selectedIndex = i)}\n >\n <div class=\"meta\">\n <span class=\"trigger\">${h.trigger}${h.disabled ? ' (off)' : ''}</span>\n <span class=\"summary\">${this._summarize(h)}</span>\n </div>\n <md-icon\n @click=${(e: Event) => this._toggleDisabled(i, e)}\n title=${h.disabled ? 'enable' : 'disable'}\n >${h.disabled ? 'toggle_off' : 'toggle_on'}</md-icon>\n <md-icon\n @click=${(e: Event) => this._duplicateHandler(i, e)}\n title=\"duplicate\"\n >content_copy</md-icon>\n <md-icon\n @click=${(e: Event) => this._moveHandler(i, -1, e)}\n title=\"move up\"\n ?style=${i === 0 ? 'opacity:0.2;pointer-events:none' : ''}\n >arrow_upward</md-icon>\n <md-icon\n @click=${(e: Event) => this._moveHandler(i, +1, e)}\n title=\"move down\"\n ?style=${i === total - 1 ? 'opacity:0.2;pointer-events:none' : ''}\n >arrow_downward</md-icon>\n <md-icon\n class=\"danger\"\n @click=${(e: Event) => this._removeHandler(i, e)}\n title=\"delete\"\n >delete</md-icon>\n </li>\n `\n }\n\n private _summarize(h: EventHandlerSpec): string {\n if (h.action === 'script') {\n const c = (h.code || '').replace(/\\s+/g, ' ').trim()\n return 'script · ' + (c.length > 60 ? c.slice(0, 60) + '…' : (c || '(empty)'))\n }\n return [h.action || '(none)', h.target ? `→${h.target}` : ''].filter(Boolean).join(' ')\n }\n\n private _addHandler() {\n const next: EventHandlerSpec = { trigger: 'click', action: '' }\n this.handlers = [...(this.handlers || []), next]\n this.selectedIndex = this.handlers.length - 1\n this._dispatch()\n }\n\n private _duplicateHandler(idx: number, e: Event) {\n e.stopPropagation()\n const original = this.handlers[idx]\n if (!original) return\n const copy: EventHandlerSpec = JSON.parse(JSON.stringify(original))\n const next = [...(this.handlers || [])]\n next.splice(idx + 1, 0, copy)\n this.handlers = next\n this.selectedIndex = idx + 1\n this._dispatch()\n }\n\n private _toggleDisabled(idx: number, e: Event) {\n e.stopPropagation()\n const next = [...(this.handlers || [])]\n next[idx] = { ...next[idx], disabled: !next[idx].disabled }\n this.handlers = next\n this._dispatch()\n }\n\n private _moveHandler(idx: number, delta: -1 | 1, e: Event) {\n e.stopPropagation()\n const next = [...(this.handlers || [])]\n const newIdx = idx + delta\n if (newIdx < 0 || newIdx >= next.length) return\n const [m] = next.splice(idx, 1)\n next.splice(newIdx, 0, m)\n this.handlers = next\n if (this.selectedIndex === idx) this.selectedIndex = newIdx\n this._dispatch()\n }\n\n private _removeHandler(idx: number, e: Event) {\n e.stopPropagation()\n const next = [...(this.handlers || [])]\n next.splice(idx, 1)\n this.handlers = next\n this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, next.length - 1))\n this._dispatch()\n }\n\n private _onHandlerChanged(e: CustomEvent) {\n const handler = e.detail?.handler as EventHandlerSpec\n if (!handler) return\n const next = [...(this.handlers || [])]\n next[this.selectedIndex] = handler\n this.handlers = next\n this._dispatch()\n }\n\n /**\n * help pane 의 snippet 버튼 → 현재 선택 handler 의 code 에 *append* (덮어쓰지\n * 않음). action 이 script 가 아니면 자동으로 script 로 전환 + code 초기화.\n */\n private _onInsertSnippet(e: CustomEvent) {\n const snippet = e.detail?.code\n if (typeof snippet !== 'string' || !snippet) return\n const cur = this.handlers[this.selectedIndex]\n if (!cur) return\n const isScript = cur.action === 'script'\n const baseCode = isScript ? (cur.code || '') : ''\n const nextCode = baseCode ? baseCode.replace(/\\s*$/, '') + '\\n\\n' + snippet : snippet\n const nextHandler: EventHandlerSpec = {\n ...cur,\n action: 'script',\n code: nextCode\n }\n const next = [...this.handlers]\n next[this.selectedIndex] = nextHandler\n this.handlers = next\n this._dispatch()\n }\n\n private _dispatch() {\n // action 이 (none) — 즉 빈 문자열 — 인 핸들러는 외부 모델에 포함하지 않는다.\n // UI 안에서는 placeholder 로 남겨두고, 사용자가 action 을 선택하면 그제서야\n // 저장 모델에 포함된다.\n const valid = (this.handlers || []).filter(h => !!h && !!h.action)\n this.dispatchEvent(\n new CustomEvent('handlers-change', {\n bubbles: true,\n composed: true,\n detail: { handlers: valid }\n })\n )\n }\n}\n"]}
@@ -91,7 +91,7 @@ export class PropertyEventHandlers extends ScopedElementsMixin(AbstractProperty)
91
91
  return html `
92
92
  <fieldset>
93
93
  <legend style="box-sizing:border-box;width:100%">
94
- <ox-title-with-help topic="board-modeller/event-handlers" msgid="label.event-handlers"
94
+ <ox-title-with-help topic="board-modeller/effects/event-handlers" msgid="label.event-handlers"
95
95
  >Event Handlers</ox-title-with-help
96
96
  >
97
97
  <span style="font-size:11px;opacity:0.65;margin-left:6px">(${handlers.length})</span>
@@ -154,7 +154,7 @@ export class PropertyEventHandlers extends ScopedElementsMixin(AbstractProperty)
154
154
  _clearHandler() {
155
155
  const next = [...this.handlers];
156
156
  next.splice(this.handlerIndex, 1);
157
- this._dispatchHandlersChange(next.filter(Boolean));
157
+ this._dispatchHandlersChange(next);
158
158
  }
159
159
  _copyHandler() {
160
160
  var _a;
@@ -189,13 +189,16 @@ export class PropertyEventHandlers extends ScopedElementsMixin(AbstractProperty)
189
189
  this._setHandlerIndex(currentIdx);
190
190
  this.tabContainer.scrollLeft = this.tabContainer.scrollWidth;
191
191
  };
192
- this._dispatchHandlersChange(next.filter(h => !!h && !!h.action));
192
+ this._dispatchHandlersChange(next);
193
193
  }
194
194
  _dispatchHandlersChange(handlers) {
195
+ // action 이 비어있는 handler 는 외부 모델에서 제외 — UI placeholder 는 유지하되
196
+ // 저장은 action 이 선택된 핸들러만.
197
+ const valid = (handlers || []).filter(h => !!h && !!h.action);
195
198
  this.dispatchEvent(new CustomEvent('property-change', {
196
199
  bubbles: true,
197
200
  composed: true,
198
- detail: { eventHandlers: handlers }
201
+ detail: { eventHandlers: valid }
199
202
  }));
200
203
  }
201
204
  async onValueChanged() {
@@ -265,10 +268,40 @@ export class PropertyEventHandlers extends ScopedElementsMixin(AbstractProperty)
265
268
  PropertyEventHandlers.styles = [
266
269
  PropertyGridStyles,
267
270
  css `
271
+ /* panel 자체가 부모 (사이드바) 너비를 넘기지 않도록. ox-input-code 같은
272
+ * 자유 너비 자식이 panel 폭을 키워서 우측 open_in_new 버튼이 잘리는
273
+ * 현상 방지. */
274
+ :host {
275
+ display: block;
276
+ min-width: 0;
277
+ max-width: 100%;
278
+ box-sizing: border-box;
279
+ overflow: hidden;
280
+ }
281
+
282
+ fieldset {
283
+ min-width: 0;
284
+ max-width: 100%;
285
+ box-sizing: border-box;
286
+ overflow: hidden;
287
+ }
288
+
289
+ event-handlers-mapper {
290
+ display: block;
291
+ min-width: 0;
292
+ max-width: 100%;
293
+ box-sizing: border-box;
294
+ }
295
+
296
+ /* 카드 상단 — chevron + tabs 를 하나의 둥근 모서리 박스로 통합. */
268
297
  #tab-header {
269
298
  display: flex;
270
- align-items: center;
299
+ align-items: stretch;
271
300
  justify-content: space-between;
301
+ border: 1px solid rgba(0, 0, 0, 0.18);
302
+ border-radius: 6px 6px 0 0;
303
+ overflow: hidden;
304
+ background: var(--md-sys-color-surface, #fff);
272
305
  }
273
306
 
274
307
  #tab-header > md-icon {
@@ -277,14 +310,15 @@ PropertyEventHandlers.styles = [
277
310
  width: 25px;
278
311
  height: 25px;
279
312
  font-size: x-large;
280
- border-bottom: 1px solid rgba(0, 0, 0, 0.2);
313
+ align-self: center;
281
314
  }
282
315
 
283
316
  ox-buttons-radio {
284
317
  flex: 1;
285
318
  height: 25px;
286
- border: 1px solid rgba(0, 0, 0, 0.2);
287
- border-width: 1px 1px 0 1px;
319
+ /* 외곽 border #tab-header 들고 있다 — 여기는 0. */
320
+ border: 0;
321
+ border-radius: 0;
288
322
  text-align: center;
289
323
  display: flex;
290
324
  padding: 0;
@@ -328,11 +362,9 @@ PropertyEventHandlers.styles = [
328
362
  background-color: var(--md-sys-color-surface);
329
363
  color: var(--md-sys-color-on-surface);
330
364
  overflow: hidden;
331
- border-style: solid;
332
- border-color: rgba(0, 0, 0, 0.2);
333
- border-image: initial;
334
- border-width: 0px 1px;
335
- padding: 7px 5px 2px 5px;
365
+ border: 1px solid rgba(0, 0, 0, 0.18);
366
+ border-width: 0 1px;
367
+ padding: 6px 6px 4px 6px;
336
368
  }
337
369
 
338
370
  md-icon {
@@ -1 +1 @@
1
- {"version":3,"file":"event-handlers.js","sourceRoot":"","sources":["../../../../src/property-panel/event-handlers/event-handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;;AAEH,OAAO,4BAA4B,CAAA;AACnC,OAAO,qCAAqC,CAAA;AAC5C,OAAO,oCAAoC,CAAA;AAC3C,OAAO,0BAA0B,CAAA;AAEjC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAkB,MAAM,KAAK,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAG1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AAC5E,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,EAAE,mBAAmB,EAAyB,MAAM,4BAA4B,CAAA;AAEvF,IAAI,SAAS,GAAG,iCAAiC,CAAA;AAEjD,MAAM,OAAO,qBAAsB,SAAQ,mBAAmB,CAAC,gBAAgB,CAAC;IAAhF;;QAgGW,iBAAY,GAAW,CAAC,CAAA;IAwPnC,CAAC;IAjPC;;;;OAIG;IACH,IAAI,QAAQ;;QACV,MAAM,CAAC,GAAG,IAAI,CAAC,KAAY,CAAA;QAC3B,IAAI,CAAC,CAAC;YAAE,OAAO,EAAE,CAAA;QACjB,QAAQ;QACR,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC;YAAE,OAAO,CAAC,CAAC,aAAmC,CAAA;QAChF,qCAAqC;QACrC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAA;QAClB,IAAI,CAAC,EAAE,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YAAE,OAAO,EAAE,CAAA;QACjE,MAAM,OAAO,GAA2B;YACtC,GAAG,EAAE,OAAO;YACZ,KAAK,EAAE,YAAY;YACnB,QAAQ,EAAE,UAAU;YACpB,SAAS,EAAE,WAAW;SACvB,CAAA;QACD,MAAM,GAAG,GAAuB,EAAE,CAAA;QAClC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;YACzB,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;YACjB,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,SAAQ;YAClD,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAA,MAAA,CAAC,CAAC,KAAK,0CAAE,MAAM,mCAAI,EAAE,CAAC,CAAA;gBAC5E,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAA;YACvE,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,IAAI,CAAC;oBACP,OAAO,EAAE,IAAI;oBACb,MAAM,EAAE,CAAC,CAAC,MAAM;oBAChB,MAAM,EAAE,CAAC,CAAC,MAAM;oBAChB,KAAK,EAAE,CAAC,CAAC,KAAK;oBACd,OAAO,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,MAAA,CAAC,CAAC,OAAO,mCAAI,EAAE,CAAC,EAAE;iBAClG,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,YAAY;QACV,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAA;IACzE,CAAC;IAED,OAAO,CAAC,OAA6B;QACnC,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,cAAc,EAAE,CAAA;QACvB,CAAC;IACH,CAAC;IAED,MAAM,KAAK,cAAc;QACvB,OAAO;YACL,uBAAuB,EAAE,mBAAmB;SAC7C,CAAA;IACH,CAAC;IAED,MAAM;QACJ,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAA;QAC9B,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;QAE/E,OAAO,IAAI,CAAA;;;;;;uEAMwD,QAAQ,CAAC,MAAM;;;qBAGjE,GAAG,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE;;;;;;;qDAOC,GAAG,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE;;;;;;qBAMhE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC;sBACxB,CAAC,CAAQ,EAAE,EAAE;YACrB,CAAC,CAAC,eAAe,EAAE,CAAA;YACnB,IAAI,CAAC,gBAAgB,CAAE,CAAC,CAAC,MAAc,CAAC,KAAK,CAAC,CAAA;QAChD,CAAC;;cAEC,QAAQ,CAAC,GAAG,CACZ,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAA;kCACM,CAAC,WAAW,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,IAAI,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE;oBAC/F,CAAC,CAAC,OAAO;;eAEd,CACF;8BACiB,QAAQ,CAAC,MAAM;;;sDAGS,GAAG,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE;;;;;;mDAMpC,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE;;;4BAGjD,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE;mDACH,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE;;;;;;0BAMlD,CAAC,CAAc,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC;mBACpD,IAAI,CAAC,KAAK;qBACR,OAAO;;;KAGvB,CAAA;IACH,CAAC;IAED,gBAAgB,CAAC,GAAW;QAC1B,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACxD,IAAI,CAAC,YAAY,EAAE,CAAA;IACrB,CAAC;IAED,aAAa;QACX,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC/B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAA;QACjC,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;IACpD,CAAC;IAED,YAAY;;QACV,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,MAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,mCAAI,EAAE,CAAC,CAAA;IACpE,CAAC;IAED,aAAa;QACX,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;YACpC,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAA;YAC/B,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,MAAM,CAAA;YAChC,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAA;QACpC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,uBAAuB;QACzB,CAAC;IACH,CAAC;IAED,iBAAiB,CAAC,CAAc;;QAC9B,MAAM,OAAO,GAAG,MAAA,CAAC,CAAC,MAAM,0CAAE,OAA2B,CAAA;QACrD,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC/B,oBAAoB;QACpB,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACpB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,OAAO,CAAA;QACnC,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAA;QACpC,IAAI,CAAC,YAAY,GAAG,GAAG,EAAE;YACvB,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAA;YACjC,IAAI,CAAC,YAAY,CAAC,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAA;QAC9D,CAAC,CAAA;QACD,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;IACnE,CAAC;IAEO,uBAAuB,CAAC,QAA4B;QAC1D,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,iBAAiB,EAAE;YACjC,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,EAAE,aAAa,EAAE,QAAQ,EAAE;SACpC,CAAC,CACH,CAAA;IACH,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,IAAI,CAAC,cAAc,CAAA;QACzB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,IAAI,CAAC,YAAY,EAAE,CAAA;QACrB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAA;QAC1B,CAAC;QACD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;IAC1B,CAAC;IAED,KAAK,CAAC,kBAAkB;QACtB,2DAA2D;QAC3D,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAA;QAEzC,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAA;QAEnC,MAAM,QAAQ,GAAG,IAAI,CAAA;;oBAEL,QAAQ;iBACX,IAAI,CAAC,KAAK;2BACA,CAAC,CAAc,EAAE,EAAE;;YACpC,IAAI,CAAC,uBAAuB,CAAC,CAAA,MAAA,CAAC,CAAC,MAAM,0CAAE,QAAQ,KAAI,EAAE,CAAC,CAAA;QACxD,CAAC;;KAEJ,CAAA;QAED,SAAS,CAAC,QAAQ,EAAE;YAClB,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC;SACzC,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,IAAI,CAAA;IAClB,CAAC;IAED,YAAY;QACV,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,gBAAgB;YAAE,OAAM;QACxD,IAAI,IAAI,CAAC,YAAY,CAAC,WAAW,IAAI,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;YACnE,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;YAClD,IAAI,CAAC,iBAAiB,CAAC,YAAY,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;QACrD,CAAC;aAAM,IAAI,IAAI,CAAC,YAAY,CAAC,UAAU,IAAI,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;YAClD,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC,UAAU,CAAC,CAAA;QACpD,CAAC;aAAM,IAAI,IAAI,CAAC,YAAY,CAAC,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,IAAI,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;YACzG,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,UAAU,CAAC,CAAA;YACjD,IAAI,CAAC,iBAAiB,CAAC,YAAY,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;QACrD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,UAAU,CAAC,CAAA;YACjD,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC,UAAU,CAAC,CAAA;QACpD,CAAC;IACH,CAAC;IAED,mBAAmB;QACjB,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,cAAc,GAAG,QAAQ,CAAA;QACjD,IAAI,CAAC,YAAY,CAAC,UAAU,IAAI,IAAI,CAAC,YAAY,CAAC,WAAW,CAAA;QAC7D,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,cAAc,GAAG,MAAM,CAAA;IACjD,CAAC;IAED,oBAAoB;QAClB,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,cAAc,GAAG,QAAQ,CAAA;QACjD,IAAI,CAAC,YAAY,CAAC,UAAU,IAAI,IAAI,CAAC,YAAY,CAAC,WAAW,CAAA;QAC7D,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,cAAc,GAAG,MAAM,CAAA;IACjD,CAAC;;AAtVM,4BAAM,GAAG;IACd,kBAAkB;IAClB,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAuFF;CACF,AA1FY,CA0FZ;AAE2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;oDAAmB;AAClB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;oDAAc;AAEhC;IAAR,KAAK,EAAE;2DAAyB;AACxB;IAAR,KAAK,EAAE;2DAA+B;AAEvB;IAAf,KAAK,CAAC,OAAO,CAAC;mDAAmB;AACH;IAA9B,KAAK,CAAC,sBAAsB,CAAC;+DAA+B;AAC7B;IAA/B,KAAK,CAAC,uBAAuB,CAAC;gEAAgC;AAqPjE,cAAc,CAAC,MAAM,CAAC,yBAAyB,EAAE,qBAAqB,CAAC,CAAA","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * EventHandlers panel — 컴포넌트의 `state.eventHandlers[]` 편집기.\n *\n * 데이터바인딩 panel 의 형제로 *완전 분리*. 데이터바인딩은 데이터 흐름 전용,\n * eventHandlers 는 사용자 액션 (tap / hover / 등) 의 first-class 핸들러 등록.\n *\n * data-binding 패턴 그대로 — tabs (N handlers) + handler mapper + add/delete/copy/paste.\n */\n\nimport '@material/web/icon/icon.js'\nimport '@operato/help/ox-title-with-help.js'\nimport '@operato/input/ox-buttons-radio.js'\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, PropertyValues } from 'lit'\nimport { property, query, state } from 'lit/decorators.js'\n\nimport type { Properties, Scene } from '@hatiolab/things-scene'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\nimport { PropertyGridStyles } from '@operato/styles/property-grid-styles.js'\nimport { openPopup } from '@operato/layout'\nimport { i18next } from '@operato/i18n'\n\nimport { AbstractProperty } from '../abstract-property.js'\nimport { EventHandlersMapper, type EventHandlerSpec } from './event-handlers-mapper.js'\n\nvar clipboard = '{\"trigger\":\"click\",\"action\":\"\"}'\n\nexport class PropertyEventHandlers extends ScopedElementsMixin(AbstractProperty) {\n static styles = [\n PropertyGridStyles,\n css`\n #tab-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n #tab-header > md-icon {\n padding: 0;\n margin: 0;\n width: 25px;\n height: 25px;\n font-size: x-large;\n border-bottom: 1px solid rgba(0, 0, 0, 0.2);\n }\n\n ox-buttons-radio {\n flex: 1;\n height: 25px;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-width: 1px 1px 0 1px;\n text-align: center;\n display: flex;\n padding: 0;\n box-sizing: border-box;\n width: 0;\n overflow-x: hidden;\n }\n\n ox-buttons-radio > div {\n background-color: rgba(0, 0, 0, 0.2);\n border: 1px solid rgba(0, 0, 0, 0.07);\n border-width: 0 0 2px 0;\n padding: 0 8px;\n margin: 0;\n color: #fff;\n font-size: 12px;\n max-width: 110px;\n min-width: 40px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n line-height: 23px;\n }\n\n ox-buttons-radio > div[disabled] {\n background-color: rgba(0, 0, 0, 0.1);\n }\n\n ox-buttons-radio > div[active] {\n border-color: rgb(242, 71, 28);\n }\n\n ox-buttons-radio > div.iron-selected {\n background-color: var(--md-sys-color-surface);\n color: var(--md-sys-color-on-surface);\n }\n\n div[handler-toolbar] {\n display: flex;\n flex-direction: row-reverse;\n background-color: var(--md-sys-color-surface);\n color: var(--md-sys-color-on-surface);\n overflow: hidden;\n border-style: solid;\n border-color: rgba(0, 0, 0, 0.2);\n border-image: initial;\n border-width: 0px 1px;\n padding: 7px 5px 2px 5px;\n }\n\n md-icon {\n margin-left: 5px;\n color: var(--md-sys-color-on-secondary-container);\n opacity: 0.8;\n cursor: pointer;\n --md-icon-size: 18px;\n }\n\n md-icon:hover {\n color: var(--md-sys-color-on-primary-container);\n opacity: 1;\n }\n\n md-icon[disabled] {\n color: rgba(0, 0, 0, 0.1);\n }\n `\n ]\n\n @property({ type: Object }) value?: Properties\n @property({ type: Object }) scene?: Scene\n\n @state() handlerIndex: number = 0\n @state() _afterRender?: Function | null\n\n @query('#tabs') tabs!: HTMLElement\n @query('#tab-nav-left-button') tabNavLeftButton!: HTMLElement\n @query('#tab-nav-right-button') tabNavRightButton!: HTMLElement\n\n /**\n * 두 속성 통합 — 신규 `eventHandlers` 우선, 없으면 legacy `event` 객체를\n * *read-only* 변환해 표시. 사용자가 수정 시 `eventHandlers` 에만 저장 — legacy\n * 는 손대지 않음 (옛 코드 호환 / rollback 안전망).\n */\n get handlers(): EventHandlerSpec[] {\n const v = this.value as any\n if (!v) return []\n // 1. 신규\n if (Array.isArray(v.eventHandlers)) return v.eventHandlers as EventHandlerSpec[]\n // 2. legacy 객체 → 변환 (표시만, 저장 시점이 아님)\n const ev = v.event\n if (!ev || typeof ev !== 'object' || Array.isArray(ev)) return []\n const trigMap: Record<string, string> = {\n tap: 'click',\n hover: 'mouseenter',\n dblclick: 'dblclick',\n longpress: 'longpress'\n }\n const out: EventHandlerSpec[] = []\n for (const key of Object.keys(ev)) {\n const trig = trigMap[key]\n const e = ev[key]\n if (!trig || !e || typeof e !== 'object') continue\n if (e.action === 'script') {\n const code = typeof e.value === 'string' ? e.value : (e.value?.script ?? '')\n out.push({ trigger: trig, action: 'script', code, target: e.target })\n } else {\n out.push({\n trigger: trig,\n action: e.action,\n target: e.target,\n value: e.value,\n options: { emphasize: e.emphasize, restore: e.restore, pressed: e.pressed, ...(e.options ?? {}) }\n })\n }\n }\n return out\n }\n\n firstUpdated() {\n this.tabContainer.addEventListener('scroll', () => this._onTabScroll())\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('value')) {\n this.onValueChanged()\n }\n }\n\n static get scopedElements() {\n return {\n 'event-handlers-mapper': EventHandlersMapper\n }\n }\n\n render() {\n const handlers = this.handlers\n const current = handlers[this.handlerIndex] || { trigger: 'click', action: '' }\n\n return html`\n <fieldset>\n <legend style=\"box-sizing:border-box;width:100%\">\n <ox-title-with-help topic=\"board-modeller/event-handlers\" msgid=\"label.event-handlers\"\n >Event Handlers</ox-title-with-help\n >\n <span style=\"font-size:11px;opacity:0.65;margin-left:6px\">(${handlers.length})</span>\n <md-icon\n style=\"float:right;font-size:medium;margin:0;cursor:pointer\"\n @click=${() => this._openHandlersPopup()}\n title=\"open in popup\"\n >open_in_new</md-icon\n >\n </legend>\n\n <div id=\"tab-header\">\n <md-icon id=\"tab-nav-left-button\" @click=${() => this._onTabScrollNavLeft()} disabled\n >chevron_left</md-icon\n >\n\n <ox-buttons-radio\n id=\"tabs\"\n .value=${String(this.handlerIndex)}\n @change=${(e: Event) => {\n e.stopPropagation()\n this._setHandlerIndex((e.target as any).value)\n }}\n >\n ${handlers.map(\n (h, i) => html`\n <div data-value=${i} title=\"${h.trigger} · ${h.action || '(none)'}${h.disabled ? ' (disabled)' : ''}\">\n ${h.trigger}\n </div>\n `\n )}\n <div data-value=${handlers.length} disabled title=\"add new\">+</div>\n </ox-buttons-radio>\n\n <md-icon id=\"tab-nav-right-button\" @click=${() => this._onTabScrollNavRight()} disabled\n >chevron_right</md-icon\n >\n </div>\n\n <div handler-toolbar>\n <md-icon style=\"font-size:19px\" @click=${() => this._clearHandler()} title=\"delete current\"\n >delete_forever</md-icon\n >\n <md-icon @click=${() => this._pasteHandler()} title=\"paste\">content_paste</md-icon>\n <md-icon style=\"font-size:17px\" @click=${() => this._copyHandler()} title=\"copy\"\n >content_copy</md-icon\n >\n </div>\n\n <event-handlers-mapper\n @value-change=${(e: CustomEvent) => this._onHandlerChanged(e)}\n .scene=${this.scene}\n .handler=${current}\n ></event-handlers-mapper>\n </fieldset>\n `\n }\n\n _setHandlerIndex(idx: number) {\n this.handlerIndex = isNaN(Number(idx)) ? 0 : Number(idx)\n this._onTabScroll()\n }\n\n _clearHandler() {\n const next = [...this.handlers]\n next.splice(this.handlerIndex, 1)\n this._dispatchHandlersChange(next.filter(Boolean))\n }\n\n _copyHandler() {\n clipboard = JSON.stringify(this.handlers[this.handlerIndex] ?? {})\n }\n\n _pasteHandler() {\n try {\n const parsed = JSON.parse(clipboard)\n const next = [...this.handlers]\n next[this.handlerIndex] = parsed\n this._dispatchHandlersChange(next)\n } catch (_) {\n /* invalid clipboard */\n }\n }\n\n _onHandlerChanged(e: CustomEvent) {\n const handler = e.detail?.handler as EventHandlerSpec\n if (!handler) return\n const next = [...this.handlers]\n // 신규 — last+1 위치 추가\n if (this.handlerIndex >= next.length) {\n next.push(handler)\n } else {\n next[this.handlerIndex] = handler\n }\n const currentIdx = this.handlerIndex\n this._afterRender = () => {\n this._setHandlerIndex(currentIdx)\n this.tabContainer.scrollLeft = this.tabContainer.scrollWidth\n }\n this._dispatchHandlersChange(next.filter(h => !!h && !!h.action))\n }\n\n private _dispatchHandlersChange(handlers: EventHandlerSpec[]) {\n this.dispatchEvent(\n new CustomEvent('property-change', {\n bubbles: true,\n composed: true,\n detail: { eventHandlers: handlers }\n })\n )\n }\n\n async onValueChanged() {\n await this.updateComplete\n if (this._afterRender) {\n this._afterRender()\n } else {\n this._setHandlerIndex(0)\n }\n this._afterRender = null\n }\n\n async _openHandlersPopup() {\n // 큰 popup 으로 펼치기 — data-binding 의 _openBindingPopup 패턴 동일.\n await import('./event-handlers-popup.js')\n\n const handlers = [...this.handlers]\n\n const template = html`\n <event-handlers-popup\n .handlers=${handlers}\n .scene=${this.scene}\n @handlers-change=${(e: CustomEvent) => {\n this._dispatchHandlersChange(e.detail?.handlers || [])\n }}\n ></event-handlers-popup>\n `\n\n openPopup(template, {\n backdrop: true,\n size: 'large',\n title: i18next.t('label.event-handlers')\n })\n }\n\n get tabContainer() {\n return this.tabs\n }\n\n _onTabScroll() {\n if (!this.tabContainer || !this.tabNavLeftButton) return\n if (this.tabContainer.clientWidth == this.tabContainer.scrollWidth) {\n this.tabNavLeftButton.setAttribute('disabled', '')\n this.tabNavRightButton.setAttribute('disabled', '')\n } else if (this.tabContainer.scrollLeft == 0) {\n this.tabNavLeftButton.setAttribute('disabled', '')\n this.tabNavRightButton.removeAttribute('disabled')\n } else if (this.tabContainer.scrollLeft + this.tabContainer.clientWidth >= this.tabContainer.scrollWidth) {\n this.tabNavLeftButton.removeAttribute('disabled')\n this.tabNavRightButton.setAttribute('disabled', '')\n } else {\n this.tabNavLeftButton.removeAttribute('disabled')\n this.tabNavRightButton.removeAttribute('disabled')\n }\n }\n\n _onTabScrollNavLeft() {\n this.tabContainer.style.scrollBehavior = 'smooth'\n this.tabContainer.scrollLeft -= this.tabContainer.clientWidth\n this.tabContainer.style.scrollBehavior = 'auto'\n }\n\n _onTabScrollNavRight() {\n this.tabContainer.style.scrollBehavior = 'smooth'\n this.tabContainer.scrollLeft += this.tabContainer.clientWidth\n this.tabContainer.style.scrollBehavior = 'auto'\n }\n}\n\ncustomElements.define('property-event-handlers', PropertyEventHandlers)\n"]}
1
+ {"version":3,"file":"event-handlers.js","sourceRoot":"","sources":["../../../../src/property-panel/event-handlers/event-handlers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * EventHandlers panel — 컴포넌트의 `state.eventHandlers[]` 편집기.\n *\n * 데이터바인딩 panel 의 형제로 *완전 분리*. 데이터바인딩은 데이터 흐름 전용,\n * eventHandlers 는 사용자 액션 (tap / hover / 등) 의 first-class 핸들러 등록.\n *\n * data-binding 패턴 그대로 — tabs (N handlers) + handler mapper + add/delete/copy/paste.\n */\n\nimport '@material/web/icon/icon.js'\nimport '@operato/help/ox-title-with-help.js'\nimport '@operato/input/ox-buttons-radio.js'\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, PropertyValues } from 'lit'\nimport { property, query, state } from 'lit/decorators.js'\n\nimport type { Properties, Scene } from '@hatiolab/things-scene'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\nimport { PropertyGridStyles } from '@operato/styles/property-grid-styles.js'\nimport { openPopup } from '@operato/layout'\nimport { i18next } from '@operato/i18n'\n\nimport { AbstractProperty } from '../abstract-property.js'\nimport { EventHandlersMapper, type EventHandlerSpec } from './event-handlers-mapper.js'\n\nvar clipboard = '{\"trigger\":\"click\",\"action\":\"\"}'\n\nexport class PropertyEventHandlers extends ScopedElementsMixin(AbstractProperty) {\n static styles = [\n PropertyGridStyles,\n css`\n /* panel 자체가 부모 (사이드바) 너비를 넘기지 않도록. ox-input-code 같은\n * 자유 너비 자식이 panel 폭을 키워서 우측 open_in_new 버튼이 잘리는\n * 현상 방지. */\n :host {\n display: block;\n min-width: 0;\n max-width: 100%;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n fieldset {\n min-width: 0;\n max-width: 100%;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n event-handlers-mapper {\n display: block;\n min-width: 0;\n max-width: 100%;\n box-sizing: border-box;\n }\n\n /* 카드 상단 — chevron + tabs 를 하나의 둥근 모서리 박스로 통합. */\n #tab-header {\n display: flex;\n align-items: stretch;\n justify-content: space-between;\n border: 1px solid rgba(0, 0, 0, 0.18);\n border-radius: 6px 6px 0 0;\n overflow: hidden;\n background: var(--md-sys-color-surface, #fff);\n }\n\n #tab-header > md-icon {\n padding: 0;\n margin: 0;\n width: 25px;\n height: 25px;\n font-size: x-large;\n align-self: center;\n }\n\n ox-buttons-radio {\n flex: 1;\n height: 25px;\n /* 외곽 border 는 #tab-header 가 들고 있다 — 여기는 0. */\n border: 0;\n border-radius: 0;\n text-align: center;\n display: flex;\n padding: 0;\n box-sizing: border-box;\n width: 0;\n overflow-x: hidden;\n }\n\n ox-buttons-radio > div {\n background-color: rgba(0, 0, 0, 0.2);\n border: 1px solid rgba(0, 0, 0, 0.07);\n border-width: 0 0 2px 0;\n padding: 0 8px;\n margin: 0;\n color: #fff;\n font-size: 12px;\n max-width: 110px;\n min-width: 40px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n line-height: 23px;\n }\n\n ox-buttons-radio > div[disabled] {\n background-color: rgba(0, 0, 0, 0.1);\n }\n\n ox-buttons-radio > div[active] {\n border-color: rgb(242, 71, 28);\n }\n\n ox-buttons-radio > div.iron-selected {\n background-color: var(--md-sys-color-surface);\n color: var(--md-sys-color-on-surface);\n }\n\n div[handler-toolbar] {\n display: flex;\n flex-direction: row-reverse;\n background-color: var(--md-sys-color-surface);\n color: var(--md-sys-color-on-surface);\n overflow: hidden;\n border: 1px solid rgba(0, 0, 0, 0.18);\n border-width: 0 1px;\n padding: 6px 6px 4px 6px;\n }\n\n md-icon {\n margin-left: 5px;\n color: var(--md-sys-color-on-secondary-container);\n opacity: 0.8;\n cursor: pointer;\n --md-icon-size: 18px;\n }\n\n md-icon:hover {\n color: var(--md-sys-color-on-primary-container);\n opacity: 1;\n }\n\n md-icon[disabled] {\n color: rgba(0, 0, 0, 0.1);\n }\n `\n ]\n\n @property({ type: Object }) value?: Properties\n @property({ type: Object }) scene?: Scene\n\n @state() handlerIndex: number = 0\n @state() _afterRender?: Function | null\n\n @query('#tabs') tabs!: HTMLElement\n @query('#tab-nav-left-button') tabNavLeftButton!: HTMLElement\n @query('#tab-nav-right-button') tabNavRightButton!: HTMLElement\n\n /**\n * 두 속성 통합 — 신규 `eventHandlers` 우선, 없으면 legacy `event` 객체를\n * *read-only* 변환해 표시. 사용자가 수정 시 `eventHandlers` 에만 저장 — legacy\n * 는 손대지 않음 (옛 코드 호환 / rollback 안전망).\n */\n get handlers(): EventHandlerSpec[] {\n const v = this.value as any\n if (!v) return []\n // 1. 신규\n if (Array.isArray(v.eventHandlers)) return v.eventHandlers as EventHandlerSpec[]\n // 2. legacy 객체 → 변환 (표시만, 저장 시점이 아님)\n const ev = v.event\n if (!ev || typeof ev !== 'object' || Array.isArray(ev)) return []\n const trigMap: Record<string, string> = {\n tap: 'click',\n hover: 'mouseenter',\n dblclick: 'dblclick',\n longpress: 'longpress'\n }\n const out: EventHandlerSpec[] = []\n for (const key of Object.keys(ev)) {\n const trig = trigMap[key]\n const e = ev[key]\n if (!trig || !e || typeof e !== 'object') continue\n if (e.action === 'script') {\n const code = typeof e.value === 'string' ? e.value : (e.value?.script ?? '')\n out.push({ trigger: trig, action: 'script', code, target: e.target })\n } else {\n out.push({\n trigger: trig,\n action: e.action,\n target: e.target,\n value: e.value,\n options: { emphasize: e.emphasize, restore: e.restore, pressed: e.pressed, ...(e.options ?? {}) }\n })\n }\n }\n return out\n }\n\n firstUpdated() {\n this.tabContainer.addEventListener('scroll', () => this._onTabScroll())\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('value')) {\n this.onValueChanged()\n }\n }\n\n static get scopedElements() {\n return {\n 'event-handlers-mapper': EventHandlersMapper\n }\n }\n\n render() {\n const handlers = this.handlers\n const current = handlers[this.handlerIndex] || { trigger: 'click', action: '' }\n\n return html`\n <fieldset>\n <legend style=\"box-sizing:border-box;width:100%\">\n <ox-title-with-help topic=\"board-modeller/effects/event-handlers\" msgid=\"label.event-handlers\"\n >Event Handlers</ox-title-with-help\n >\n <span style=\"font-size:11px;opacity:0.65;margin-left:6px\">(${handlers.length})</span>\n <md-icon\n style=\"float:right;font-size:medium;margin:0;cursor:pointer\"\n @click=${() => this._openHandlersPopup()}\n title=\"open in popup\"\n >open_in_new</md-icon\n >\n </legend>\n\n <div id=\"tab-header\">\n <md-icon id=\"tab-nav-left-button\" @click=${() => this._onTabScrollNavLeft()} disabled\n >chevron_left</md-icon\n >\n\n <ox-buttons-radio\n id=\"tabs\"\n .value=${String(this.handlerIndex)}\n @change=${(e: Event) => {\n e.stopPropagation()\n this._setHandlerIndex((e.target as any).value)\n }}\n >\n ${handlers.map(\n (h, i) => html`\n <div data-value=${i} title=\"${h.trigger} · ${h.action || '(none)'}${h.disabled ? ' (disabled)' : ''}\">\n ${h.trigger}\n </div>\n `\n )}\n <div data-value=${handlers.length} disabled title=\"add new\">+</div>\n </ox-buttons-radio>\n\n <md-icon id=\"tab-nav-right-button\" @click=${() => this._onTabScrollNavRight()} disabled\n >chevron_right</md-icon\n >\n </div>\n\n <div handler-toolbar>\n <md-icon style=\"font-size:19px\" @click=${() => this._clearHandler()} title=\"delete current\"\n >delete_forever</md-icon\n >\n <md-icon @click=${() => this._pasteHandler()} title=\"paste\">content_paste</md-icon>\n <md-icon style=\"font-size:17px\" @click=${() => this._copyHandler()} title=\"copy\"\n >content_copy</md-icon\n >\n </div>\n\n <event-handlers-mapper\n @value-change=${(e: CustomEvent) => this._onHandlerChanged(e)}\n .scene=${this.scene}\n .handler=${current}\n ></event-handlers-mapper>\n </fieldset>\n `\n }\n\n _setHandlerIndex(idx: number) {\n this.handlerIndex = isNaN(Number(idx)) ? 0 : Number(idx)\n this._onTabScroll()\n }\n\n _clearHandler() {\n const next = [...this.handlers]\n next.splice(this.handlerIndex, 1)\n this._dispatchHandlersChange(next)\n }\n\n _copyHandler() {\n clipboard = JSON.stringify(this.handlers[this.handlerIndex] ?? {})\n }\n\n _pasteHandler() {\n try {\n const parsed = JSON.parse(clipboard)\n const next = [...this.handlers]\n next[this.handlerIndex] = parsed\n this._dispatchHandlersChange(next)\n } catch (_) {\n /* invalid clipboard */\n }\n }\n\n _onHandlerChanged(e: CustomEvent) {\n const handler = e.detail?.handler as EventHandlerSpec\n if (!handler) return\n const next = [...this.handlers]\n // 신규 — last+1 위치 추가\n if (this.handlerIndex >= next.length) {\n next.push(handler)\n } else {\n next[this.handlerIndex] = handler\n }\n const currentIdx = this.handlerIndex\n this._afterRender = () => {\n this._setHandlerIndex(currentIdx)\n this.tabContainer.scrollLeft = this.tabContainer.scrollWidth\n }\n this._dispatchHandlersChange(next)\n }\n\n private _dispatchHandlersChange(handlers: EventHandlerSpec[]) {\n // action 이 비어있는 handler 는 외부 모델에서 제외 — UI placeholder 는 유지하되\n // 저장은 action 이 선택된 핸들러만.\n const valid = (handlers || []).filter(h => !!h && !!h.action)\n this.dispatchEvent(\n new CustomEvent('property-change', {\n bubbles: true,\n composed: true,\n detail: { eventHandlers: valid }\n })\n )\n }\n\n async onValueChanged() {\n await this.updateComplete\n if (this._afterRender) {\n this._afterRender()\n } else {\n this._setHandlerIndex(0)\n }\n this._afterRender = null\n }\n\n async _openHandlersPopup() {\n // 큰 popup 으로 펼치기 — data-binding 의 _openBindingPopup 패턴 동일.\n await import('./event-handlers-popup.js')\n\n const handlers = [...this.handlers]\n\n const template = html`\n <event-handlers-popup\n .handlers=${handlers}\n .scene=${this.scene}\n @handlers-change=${(e: CustomEvent) => {\n this._dispatchHandlersChange(e.detail?.handlers || [])\n }}\n ></event-handlers-popup>\n `\n\n openPopup(template, {\n backdrop: true,\n size: 'large',\n title: i18next.t('label.event-handlers')\n })\n }\n\n get tabContainer() {\n return this.tabs\n }\n\n _onTabScroll() {\n if (!this.tabContainer || !this.tabNavLeftButton) return\n if (this.tabContainer.clientWidth == this.tabContainer.scrollWidth) {\n this.tabNavLeftButton.setAttribute('disabled', '')\n this.tabNavRightButton.setAttribute('disabled', '')\n } else if (this.tabContainer.scrollLeft == 0) {\n this.tabNavLeftButton.setAttribute('disabled', '')\n this.tabNavRightButton.removeAttribute('disabled')\n } else if (this.tabContainer.scrollLeft + this.tabContainer.clientWidth >= this.tabContainer.scrollWidth) {\n this.tabNavLeftButton.removeAttribute('disabled')\n this.tabNavRightButton.setAttribute('disabled', '')\n } else {\n this.tabNavLeftButton.removeAttribute('disabled')\n this.tabNavRightButton.removeAttribute('disabled')\n }\n }\n\n _onTabScrollNavLeft() {\n this.tabContainer.style.scrollBehavior = 'smooth'\n this.tabContainer.scrollLeft -= this.tabContainer.clientWidth\n this.tabContainer.style.scrollBehavior = 'auto'\n }\n\n _onTabScrollNavRight() {\n this.tabContainer.style.scrollBehavior = 'smooth'\n this.tabContainer.scrollLeft += this.tabContainer.clientWidth\n this.tabContainer.style.scrollBehavior = 'auto'\n }\n}\n\ncustomElements.define('property-event-handlers', PropertyEventHandlers)\n"]}