@operato/property-panel 10.20.0 → 10.23.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.
- package/CHANGELOG.md +32 -0
- package/dist/src/ox-property-panel.d.ts +1 -0
- package/dist/src/ox-property-panel.js +15 -0
- package/dist/src/ox-property-panel.js.map +1 -1
- package/dist/src/property-panel/data-binding/data-binding-mapper.js +20 -7
- package/dist/src/property-panel/data-binding/data-binding-mapper.js.map +1 -1
- package/dist/src/property-panel/data-binding/data-binding.d.ts +4 -0
- package/dist/src/property-panel/data-binding/data-binding.js +167 -56
- package/dist/src/property-panel/data-binding/data-binding.js.map +1 -1
- package/dist/src/property-panel/event-handlers/event-handlers-mapper.js +37 -16
- package/dist/src/property-panel/event-handlers/event-handlers-mapper.js.map +1 -1
- package/dist/src/property-panel/event-handlers/event-handlers-popup.d.ts +1 -0
- package/dist/src/property-panel/event-handlers/event-handlers-popup.js +48 -0
- package/dist/src/property-panel/event-handlers/event-handlers-popup.js.map +1 -1
- package/dist/src/property-panel/event-handlers/event-handlers.d.ts +3 -0
- package/dist/src/property-panel/event-handlers/event-handlers.js +173 -86
- package/dist/src/property-panel/event-handlers/event-handlers.js.map +1 -1
- package/dist/src/property-panel/shapes/shapes.d.ts +2 -0
- package/dist/src/property-panel/shapes/shapes.js +78 -29
- package/dist/src/property-panel/shapes/shapes.js.map +1 -1
- package/dist/src/property-panel/specifics/specifics.js +62 -6
- package/dist/src/property-panel/specifics/specifics.js.map +1 -1
- package/dist/src/property-panel/styles/styles.js +61 -31
- package/dist/src/property-panel/styles/styles.js.map +1 -1
- package/dist/src/property-panel/threed/property-material3d.js +10 -9
- package/dist/src/property-panel/threed/property-material3d.js.map +1 -1
- package/dist/src/property-panel/threed/property-scene3d.js +11 -9
- package/dist/src/property-panel/threed/property-scene3d.js.map +1 -1
- package/dist/src/property-panel/threed/threed.js +41 -18
- package/dist/src/property-panel/threed/threed.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-handlers-mapper.js","sourceRoot":"","sources":["../../../../src/property-panel/event-handlers/event-handlers-mapper.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * Single EventHandler editor — trigger / action / (code 또는 target+value).\n *\n * 'script' 도 action 의 한 값. action='script' 시 code 필드를, 다른 declarative\n * action 시 target+value 필드를 노출. action 미선택 (= '') 시 no-op.\n */\n\nimport '@operato/input/ox-input-code.js'\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { property } from 'lit/decorators.js'\n\nimport type { Scene } from '@hatiolab/things-scene'\n\n/** EventHandler model — things-scene 의 EventHandler interface 와 호환. */\nexport type EventHandlerSpec = {\n trigger: string // click | dblclick | mouseenter | mouseleave | longpress | mousedown | mouseup\n action?: string // '' | 'script' | 'data-toggle' | 'data-set' | 'emphasize' | ...\n code?: string // action='script' 시 JS 코드\n target?: string // selector\n value?: any\n options?: Record<string, any>\n disabled?: boolean\n}\n\nconst TRIGGERS = [\n 'click', 'dblclick',\n 'mouseenter', 'mouseleave',\n 'longpress',\n 'mousedown', 'mouseup'\n]\n\n/** 'script' 는 action 의 한 값. */\nconst ACTIONS = [\n '',\n 'script',\n 'data-toggle',\n 'data-tristate',\n 'data-spreading',\n 'data-set',\n 'partial-data-set',\n 'value-set',\n 'partial-value-set',\n 'info-window',\n 'emphasize'\n]\n\nexport class EventHandlersMapper extends LitElement {\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 10px 10px 10px;\n /* 카드 외곽 — tabs (위) + toolbar (중간) + mapper (아래) 가 하나의 카드.\n * 위쪽 1px 는 toolbar 와의 internal divider. radius 6px 으로 통일. */\n border: 1px solid rgba(0, 0, 0, 0.18);\n border-radius: 0 0 6px 6px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n line-height: 2;\n background: var(--md-sys-color-surface, #fff);\n min-width: 0;\n width: 100%;\n box-sizing: border-box;\n overflow: hidden;\n flex: 1;\n min-height: 0;\n }\n\n [script-field] {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex: 1;\n margin-top: 6px;\n padding-top: 10px;\n /* trigger/action/target/value 입력 묶음과 code editor 사이의 구분선.\n * dashed 대신 solid + 옅은 opacity 로 modern. */\n border-top: 1px solid rgba(0, 0, 0, 0.08);\n }\n\n [script-field] > .script-header {\n display: flex;\n align-items: baseline;\n gap: 8px;\n margin-bottom: 6px;\n font-size: 10.5px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.6px;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n }\n\n [script-field] > .script-header small {\n font-weight: 400;\n text-transform: none;\n letter-spacing: 0;\n opacity: 0.65;\n font-size: 10.5px;\n }\n\n .row {\n display: grid;\n grid-template-columns: 80px 1fr;\n gap: 6px;\n align-items: center;\n min-width: 0;\n }\n\n .row > select,\n .row > input[type='text'] {\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n padding: 3px 6px;\n font-size: 13px;\n border-radius: 4px;\n /* #fff 와 rgba(0,0,0,.15) 는 다크에서 흰 칸 + 안 보이는 테두리가 된다.\n 프레임워크의 --input-field-* 가족을 쓰고, 없을 때도 자립하도록 md 토큰을 기본값으로 둔다. */\n border: var(--input-field-border, 1px solid var(--md-sys-color-outline));\n background: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\n }\n\n ox-input-code {\n /* display 는 *지정하지 않는다* — outside selector 가 ox-input-code 의\n * :host { display: flex; flex-direction: column } 를 덮어쓰면, 그 안의\n * .cm-editor { flex: 1 } 가 작동을 멈춰 텍스트 라인 1개 크기로 축소된다. */\n width: 100%;\n max-width: 100%;\n min-width: 0;\n box-sizing: border-box;\n overflow: hidden;\n min-height: 180px;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 4px;\n /* popup 모드에서 script-field 의 남은 세로 공간을 모두 차지 — 아래 빈공간 X.\n * inline 모드에서는 script-field 가 grow 하지 않으므로 min-height 180px 만 사용. */\n flex: 1;\n }\n\n label {\n font-size: 12px;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n }\n `\n ]\n\n @property({ type: Object }) handler: EventHandlerSpec = { trigger: 'click', action: '' }\n @property({ type: Object }) scene?: Scene\n\n render() {\n const h = this.handler || { trigger: 'click', action: '' }\n return html`\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.trigger\">Trigger</ox-i18n></label>\n <select\n .value=${h.trigger || 'click'}\n @change=${(e: Event) => this._update('trigger', (e.target as HTMLSelectElement).value)}\n >\n ${TRIGGERS.map(t => html`<option value=${t} ?selected=${h.trigger === t}>${t}</option>`)}\n </select>\n </div>\n\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.action\">Action</ox-i18n></label>\n <select\n .value=${h.action ?? ''}\n @change=${(e: Event) => this._update('action', (e.target as HTMLSelectElement).value)}\n >\n ${ACTIONS.map(a => html`<option value=${a} ?selected=${(h.action ?? '') === a}>${a || '(none)'}</option>`)}\n </select>\n </div>\n\n ${h.action === 'script'\n ? this._renderScriptFields(h)\n : (h.action ? this._renderActionFields(h) : null)}\n `\n }\n\n private _renderScriptFields(h: EventHandlerSpec) {\n return html`\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.target\">Target</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"optional selector (#id, .class, ...)\"\n .value=${h.target || ''}\n @change=${(e: Event) => this._update('target', (e.target as HTMLInputElement).value)}\n />\n </div>\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.value\">Value</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"optional · script 안 value 변수로 전달\"\n .value=${h.value !== undefined ? String(h.value) : ''}\n @change=${(e: Event) => this._update('value', (e.target as HTMLInputElement).value)}\n />\n </div>\n <div script-field>\n <div class=\"script-header\">\n <ox-i18n msgid=\"label.script\">Script</ox-i18n>\n <small>vars: component, scene, event, targets, value · await 지원</small>\n </div>\n <ox-input-code\n mode=\"javascript\"\n .value=${h.code || ''}\n @change=${(e: any) => this._update('code', e.detail?.value ?? e.target?.value ?? '')}\n ></ox-input-code>\n </div>\n `\n }\n\n private _renderActionFields(h: EventHandlerSpec) {\n return html`\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.target\">Target</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"selector (#id, .class, ...)\"\n .value=${h.target || ''}\n @change=${(e: Event) => this._update('target', (e.target as HTMLInputElement).value)}\n />\n </div>\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.value\">Value</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"value or accessor\"\n .value=${h.value !== undefined ? String(h.value) : ''}\n @change=${(e: Event) => this._update('value', (e.target as HTMLInputElement).value)}\n />\n </div>\n `\n }\n\n private _update(key: keyof EventHandlerSpec, value: any) {\n const next: EventHandlerSpec = { ...(this.handler || { trigger: 'click', action: '' }), [key]: value }\n // action 전환 시 비-script 진입은 code 정리. script 진입 시 target/value 는\n // 의미가 호환 (target=findAll selector, value=script 입력) 이라 유지.\n if (key === 'action' && value !== 'script') {\n delete next.code\n }\n this.handler = next\n this.dispatchEvent(\n new CustomEvent('value-change', {\n bubbles: true,\n composed: true,\n detail: { handler: next, changed: { [key]: value } }\n })\n )\n }\n}\n\ncustomElements.define('event-handlers-mapper', EventHandlersMapper)\n"]}
|
|
1
|
+
{"version":3,"file":"event-handlers-mapper.js","sourceRoot":"","sources":["../../../../src/property-panel/event-handlers/event-handlers-mapper.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * Single EventHandler editor — trigger / action / (code 또는 target+value).\n *\n * 'script' 도 action 의 한 값. action='script' 시 code 필드를, 다른 declarative\n * action 시 target+value 필드를 노출. action 미선택 (= '') 시 no-op.\n */\n\nimport '@operato/input/ox-input-code.js'\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { property } from 'lit/decorators.js'\n\nimport type { Scene } from '@hatiolab/things-scene'\n\n/** EventHandler model — things-scene 의 EventHandler interface 와 호환. */\nexport type EventHandlerSpec = {\n trigger: string // click | dblclick | mouseenter | mouseleave | longpress | mousedown | mouseup\n action?: string // '' | 'script' | 'data-toggle' | 'data-set' | 'emphasize' | ...\n code?: string // action='script' 시 JS 코드\n target?: string // selector\n value?: any\n options?: Record<string, any>\n disabled?: boolean\n}\n\nconst TRIGGERS = [\n 'click', 'dblclick',\n 'mouseenter', 'mouseleave',\n 'longpress',\n 'mousedown', 'mouseup'\n]\n\n/** 'script' 는 action 의 한 값. */\nconst ACTIONS = [\n '',\n 'script',\n 'data-toggle',\n 'data-tristate',\n 'data-spreading',\n 'data-set',\n 'partial-data-set',\n 'value-set',\n 'partial-value-set',\n 'info-window',\n 'emphasize'\n]\n\nexport class EventHandlersMapper extends LitElement {\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 0;\n /* 카드 외곽 — tabs (위) + toolbar (중간) + mapper (아래) 가 하나의 카드.\n * 위쪽 1px 는 toolbar 와의 internal divider. radius 6px 으로 통일. */\n border: 0;\n border-radius: 0;\n box-shadow: none;\n line-height: normal;\n background: transparent;\n min-width: 0;\n width: 100%;\n box-sizing: border-box;\n overflow: hidden;\n flex: 1;\n min-height: 0;\n }\n\n [script-field] {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex: 1;\n margin-top: 6px;\n padding-top: 10px;\n /* trigger/action/target/value 입력 묶음과 code editor 사이의 구분선.\n * dashed 대신 solid + 옅은 opacity 로 modern. */\n border-top: 1px solid var(--md-sys-color-outline-variant);\n }\n\n [script-field] > .script-header {\n display: flex;\n align-items: baseline;\n gap: 8px;\n margin-bottom: 6px;\n font-size: 10.5px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.6px;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n }\n\n [script-field] > .script-header small {\n font-weight: 400;\n text-transform: none;\n letter-spacing: 0;\n opacity: 0.65;\n font-size: 10.5px;\n }\n\n /* The same label/editor split as the panel grid (9 of 24 columns for the label). */\n .row {\n display: grid;\n grid-template-columns: 9fr 15fr;\n gap: 6px 10px;\n align-items: center;\n min-width: 0;\n }\n\n .row > label {\n justify-self: end;\n text-align: right;\n font-size: 0.74rem;\n color: var(--md-sys-color-on-surface-variant);\n text-transform: capitalize;\n }\n\n .row > select,\n .row > input[type='text'] {\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n min-height: 24px;\n padding: 2px 8px;\n font-size: 0.76rem;\n border-radius: 6px;\n /* #fff 와 rgba(0,0,0,.15) 는 다크에서 흰 칸 + 안 보이는 테두리가 된다.\n 프레임워크의 --input-field-* 가족을 쓰고, 없을 때도 자립하도록 md 토큰을 기본값으로 둔다. */\n border: 1px solid transparent;\n background: var(--property-input-background, var(--md-sys-color-surface-container-high));\n color: var(--md-sys-color-on-surface);\n outline: none;\n }\n\n .row > select:hover,\n .row > input[type='text']:hover {\n border-color: var(--md-sys-color-outline-variant);\n }\n\n .row > select:focus,\n .row > input[type='text']:focus {\n border-color: var(--md-sys-color-primary);\n }\n\n ox-input-code {\n /* display 는 *지정하지 않는다* — outside selector 가 ox-input-code 의\n * :host { display: flex; flex-direction: column } 를 덮어쓰면, 그 안의\n * .cm-editor { flex: 1 } 가 작동을 멈춰 텍스트 라인 1개 크기로 축소된다. */\n width: 100%;\n max-width: 100%;\n min-width: 0;\n box-sizing: border-box;\n overflow: hidden;\n min-height: 180px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 4px;\n /* popup 모드에서 script-field 의 남은 세로 공간을 모두 차지 — 아래 빈공간 X.\n * inline 모드에서는 script-field 가 grow 하지 않으므로 min-height 180px 만 사용. */\n flex: 1;\n }\n\n label {\n font-size: 12px;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n }\n `\n ]\n\n @property({ type: Object }) handler: EventHandlerSpec = { trigger: 'click', action: '' }\n @property({ type: Object }) scene?: Scene\n\n render() {\n const h = this.handler || { trigger: 'click', action: '' }\n return html`\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.trigger\">Trigger</ox-i18n></label>\n <select\n .value=${h.trigger || 'click'}\n @change=${(e: Event) => this._update('trigger', (e.target as HTMLSelectElement).value)}\n >\n ${TRIGGERS.map(t => html`<option value=${t} ?selected=${h.trigger === t}>${t}</option>`)}\n </select>\n </div>\n\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.action\">Action</ox-i18n></label>\n <select\n .value=${h.action ?? ''}\n @change=${(e: Event) => this._update('action', (e.target as HTMLSelectElement).value)}\n >\n ${ACTIONS.map(a => html`<option value=${a} ?selected=${(h.action ?? '') === a}>${a || '(none)'}</option>`)}\n </select>\n </div>\n\n ${h.action === 'script'\n ? this._renderScriptFields(h)\n : (h.action ? this._renderActionFields(h) : null)}\n `\n }\n\n private _renderScriptFields(h: EventHandlerSpec) {\n return html`\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.target\">Target</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"optional selector (#id, .class, ...)\"\n .value=${h.target || ''}\n @change=${(e: Event) => this._update('target', (e.target as HTMLInputElement).value)}\n />\n </div>\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.value\">Value</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"optional · script 안 value 변수로 전달\"\n .value=${h.value !== undefined ? String(h.value) : ''}\n @change=${(e: Event) => this._update('value', (e.target as HTMLInputElement).value)}\n />\n </div>\n <div script-field>\n <div class=\"script-header\">\n <ox-i18n msgid=\"label.script\">Script</ox-i18n>\n <small>vars: component, scene, event, targets, value · await 지원</small>\n </div>\n <ox-input-code\n mode=\"javascript\"\n .value=${h.code || ''}\n @change=${(e: any) => this._update('code', e.detail?.value ?? e.target?.value ?? '')}\n ></ox-input-code>\n </div>\n `\n }\n\n private _renderActionFields(h: EventHandlerSpec) {\n return html`\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.target\">Target</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"selector (#id, .class, ...)\"\n .value=${h.target || ''}\n @change=${(e: Event) => this._update('target', (e.target as HTMLInputElement).value)}\n />\n </div>\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.value\">Value</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"value or accessor\"\n .value=${h.value !== undefined ? String(h.value) : ''}\n @change=${(e: Event) => this._update('value', (e.target as HTMLInputElement).value)}\n />\n </div>\n `\n }\n\n private _update(key: keyof EventHandlerSpec, value: any) {\n const next: EventHandlerSpec = { ...(this.handler || { trigger: 'click', action: '' }), [key]: value }\n // action 전환 시 비-script 진입은 code 정리. script 진입 시 target/value 는\n // 의미가 호환 (target=findAll selector, value=script 입력) 이라 유지.\n if (key === 'action' && value !== 'script') {\n delete next.code\n }\n this.handler = next\n this.dispatchEvent(\n new CustomEvent('value-change', {\n bubbles: true,\n composed: true,\n detail: { handler: next, changed: { [key]: value } }\n })\n )\n }\n}\n\ncustomElements.define('event-handlers-mapper', EventHandlersMapper)\n"]}
|
|
@@ -14,6 +14,7 @@ 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
16
|
import { ScrollbarStyles } from '@operato/styles';
|
|
17
|
+
import { i18next } from '@operato/i18n';
|
|
17
18
|
import { EventHandlersMapper } from './event-handlers-mapper.js';
|
|
18
19
|
import { EventHandlersHelp } from './event-handlers-help.js';
|
|
19
20
|
let EventHandlersPopup = class EventHandlersPopup extends ScopedElementsMixin(LitElement) {
|
|
@@ -41,6 +42,14 @@ let EventHandlersPopup = class EventHandlersPopup extends ScopedElementsMixin(Li
|
|
|
41
42
|
${handlers.length === 0
|
|
42
43
|
? html `<div empty><ox-i18n msgid="label.no-handlers">No handlers — add one</ox-i18n></div>`
|
|
43
44
|
: html `<ul>${handlers.map((h, i) => this._renderListItem(h, i, handlers.length))}</ul>`}
|
|
45
|
+
<footer>
|
|
46
|
+
<small>
|
|
47
|
+
${i18next.t('text.handlers-apply-live', {
|
|
48
|
+
defaultValue: 'Changes apply to the shape as you edit. Save the board to keep them.'
|
|
49
|
+
})}
|
|
50
|
+
</small>
|
|
51
|
+
<button @click=${() => this._done()}>${i18next.t('button.done', { defaultValue: 'Done' })}</button>
|
|
52
|
+
</footer>
|
|
44
53
|
</div>
|
|
45
54
|
|
|
46
55
|
<div editor>
|
|
@@ -188,6 +197,10 @@ let EventHandlersPopup = class EventHandlersPopup extends ScopedElementsMixin(Li
|
|
|
188
197
|
this.handlers = next;
|
|
189
198
|
this._dispatch();
|
|
190
199
|
}
|
|
200
|
+
/* Nothing to commit - the last edit is already on the shape - so this only dismisses the popup. */
|
|
201
|
+
_done() {
|
|
202
|
+
this.dispatchEvent(new CustomEvent('close-overlay', { bubbles: true, composed: true }));
|
|
203
|
+
}
|
|
191
204
|
_dispatch() {
|
|
192
205
|
// action 이 (none) — 즉 빈 문자열 — 인 핸들러는 외부 모델에 포함하지 않는다.
|
|
193
206
|
// UI 안에서는 placeholder 로 남겨두고, 사용자가 action 을 선택하면 그제서야
|
|
@@ -317,6 +330,41 @@ EventHandlersPopup.styles = [
|
|
|
317
330
|
color: var(--md-sys-color-error, #b3261e);
|
|
318
331
|
}
|
|
319
332
|
|
|
333
|
+
/* There is no save step: every edit already reached the shape. The footer says so, and
|
|
334
|
+
* offers the one thing left to do. */
|
|
335
|
+
[handler-list] footer {
|
|
336
|
+
margin-top: auto;
|
|
337
|
+
padding: 10px 12px 12px;
|
|
338
|
+
border-top: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.12));
|
|
339
|
+
display: flex;
|
|
340
|
+
flex-direction: column;
|
|
341
|
+
gap: 8px;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
[handler-list] footer small {
|
|
345
|
+
font-size: 11px;
|
|
346
|
+
line-height: 1.35;
|
|
347
|
+
color: var(--md-sys-color-on-surface-variant, #49454f);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
[handler-list] footer button {
|
|
351
|
+
align-self: flex-end;
|
|
352
|
+
height: 28px;
|
|
353
|
+
padding: 0 16px;
|
|
354
|
+
border: 0;
|
|
355
|
+
border-radius: 999px;
|
|
356
|
+
font: inherit;
|
|
357
|
+
font-size: 12px;
|
|
358
|
+
font-weight: 500;
|
|
359
|
+
color: var(--md-sys-color-on-primary, #fff);
|
|
360
|
+
background: var(--md-sys-color-primary, #6750a4);
|
|
361
|
+
cursor: pointer;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
[handler-list] footer button:hover {
|
|
365
|
+
filter: brightness(1.08);
|
|
366
|
+
}
|
|
367
|
+
|
|
320
368
|
[handler-list] [empty] {
|
|
321
369
|
padding: 24px 12px;
|
|
322
370
|
text-align: center;
|
|
@@ -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'\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"]}
|
|
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 { i18next } from '@operato/i18n'\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 /* There is no save step: every edit already reached the shape. The footer says so, and\n * offers the one thing left to do. */\n [handler-list] footer {\n margin-top: auto;\n padding: 10px 12px 12px;\n border-top: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.12));\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n [handler-list] footer small {\n font-size: 11px;\n line-height: 1.35;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n }\n\n [handler-list] footer button {\n align-self: flex-end;\n height: 28px;\n padding: 0 16px;\n border: 0;\n border-radius: 999px;\n font: inherit;\n font-size: 12px;\n font-weight: 500;\n color: var(--md-sys-color-on-primary, #fff);\n background: var(--md-sys-color-primary, #6750a4);\n cursor: pointer;\n }\n\n [handler-list] footer button:hover {\n filter: brightness(1.08);\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 <footer>\n <small>\n ${i18next.t('text.handlers-apply-live', {\n defaultValue: 'Changes apply to the shape as you edit. Save the board to keep them.'\n })}\n </small>\n <button @click=${() => this._done()}>${i18next.t('button.done', { defaultValue: 'Done' })}</button>\n </footer>\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 /* Nothing to commit - the last edit is already on the shape - so this only dismisses the popup. */\n private _done() {\n this.dispatchEvent(new CustomEvent('close-overlay', { bubbles: true, composed: true }))\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"]}
|
|
@@ -22,6 +22,8 @@ export declare class PropertyEventHandlers extends PropertyEventHandlers_base {
|
|
|
22
22
|
value?: Properties;
|
|
23
23
|
scene?: Scene;
|
|
24
24
|
handlerIndex: number;
|
|
25
|
+
private _composing;
|
|
26
|
+
private _composingFor?;
|
|
25
27
|
_afterRender?: Function | null;
|
|
26
28
|
tabs: HTMLElement;
|
|
27
29
|
tabNavLeftButton: HTMLElement;
|
|
@@ -38,6 +40,7 @@ export declare class PropertyEventHandlers extends PropertyEventHandlers_base {
|
|
|
38
40
|
'event-handlers-mapper': typeof EventHandlersMapper;
|
|
39
41
|
};
|
|
40
42
|
render(): import("lit-html").TemplateResult<1>;
|
|
43
|
+
private _startHandler;
|
|
41
44
|
_setHandlerIndex(idx: number): void;
|
|
42
45
|
_clearHandler(): void;
|
|
43
46
|
_copyHandler(): void;
|
|
@@ -13,7 +13,7 @@ import '@material/web/icon/icon.js';
|
|
|
13
13
|
import '@operato/help/ox-title-with-help.js';
|
|
14
14
|
import '@operato/input/ox-buttons-radio.js';
|
|
15
15
|
import '@operato/i18n/ox-i18n.js';
|
|
16
|
-
import { css, html } from 'lit';
|
|
16
|
+
import { css, html, nothing } from 'lit';
|
|
17
17
|
import { property, query, state } from 'lit/decorators.js';
|
|
18
18
|
import { ScopedElementsMixin } from '@open-wc/scoped-elements';
|
|
19
19
|
import { PropertyGridStyles } from '@operato/styles/property-grid-styles.js';
|
|
@@ -26,6 +26,8 @@ export class PropertyEventHandlers extends ScopedElementsMixin(AbstractProperty)
|
|
|
26
26
|
constructor() {
|
|
27
27
|
super(...arguments);
|
|
28
28
|
this.handlerIndex = 0;
|
|
29
|
+
/* True while a new handler is being composed and is not yet in the model (no action chosen). */
|
|
30
|
+
this._composing = false;
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
31
33
|
* 두 속성 통합 — 신규 `eventHandlers` 우선, 없으면 legacy `event` 객체를
|
|
@@ -73,9 +75,18 @@ export class PropertyEventHandlers extends ScopedElementsMixin(AbstractProperty)
|
|
|
73
75
|
return out;
|
|
74
76
|
}
|
|
75
77
|
firstUpdated() {
|
|
76
|
-
|
|
78
|
+
/* The tab strip only exists once there is a handler; scroll does not bubble, so capture it at the root. */
|
|
79
|
+
this.renderRoot.addEventListener('scroll', e => e.target.id === 'tabs' && this._onTabScroll(), true);
|
|
77
80
|
}
|
|
78
81
|
updated(changes) {
|
|
82
|
+
var _a, _b;
|
|
83
|
+
if (changes.has('value')) {
|
|
84
|
+
const selected = (_b = (_a = this.scene) === null || _a === void 0 ? void 0 : _a.selected) === null || _b === void 0 ? void 0 : _b[0];
|
|
85
|
+
if (selected !== this._composingFor) {
|
|
86
|
+
this._composingFor = selected;
|
|
87
|
+
this._composing = false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
79
90
|
if (changes.has('value')) {
|
|
80
91
|
this.onValueChanged();
|
|
81
92
|
}
|
|
@@ -94,59 +105,73 @@ export class PropertyEventHandlers extends ScopedElementsMixin(AbstractProperty)
|
|
|
94
105
|
<ox-title-with-help topic="board-modeller/effects/event-handlers" msgid="label.event-handlers"
|
|
95
106
|
>Event Handlers</ox-title-with-help
|
|
96
107
|
>
|
|
97
|
-
|
|
108
|
+
${handlers.length ? html `<span class="count">${handlers.length}</span>` : nothing}
|
|
98
109
|
<md-icon
|
|
99
|
-
|
|
110
|
+
class="open-popup"
|
|
100
111
|
@click=${() => this._openHandlersPopup()}
|
|
101
112
|
title="open in popup"
|
|
102
113
|
>open_in_new</md-icon
|
|
103
114
|
>
|
|
104
115
|
</legend>
|
|
105
116
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
117
|
+
${handlers.length || this._composing
|
|
118
|
+
? html `
|
|
119
|
+
<div id="tab-header">
|
|
120
|
+
<md-icon id="tab-nav-left-button" @click=${() => this._onTabScrollNavLeft()} disabled
|
|
121
|
+
>chevron_left</md-icon
|
|
122
|
+
>
|
|
110
123
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
<ox-buttons-radio
|
|
125
|
+
id="tabs"
|
|
126
|
+
.value=${String(this.handlerIndex)}
|
|
127
|
+
@change=${(e) => {
|
|
128
|
+
e.stopPropagation();
|
|
129
|
+
this._setHandlerIndex(e.target.value);
|
|
130
|
+
}}
|
|
131
|
+
>
|
|
132
|
+
${handlers.map((h, i) => html `
|
|
133
|
+
<div data-value=${i} title="${h.trigger} · ${h.action || '(none)'}${h.disabled ? ' (disabled)' : ''}">
|
|
134
|
+
${i + 1}
|
|
135
|
+
</div>
|
|
136
|
+
`)}
|
|
137
|
+
<div data-value=${handlers.length} class="add-tab" title="add" @click=${() => (this._composing = true)}>
|
|
138
|
+
+
|
|
139
|
+
</div>
|
|
140
|
+
</ox-buttons-radio>
|
|
126
141
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
</div>
|
|
142
|
+
<md-icon id="tab-nav-right-button" @click=${() => this._onTabScrollNavRight()} disabled
|
|
143
|
+
>chevron_right</md-icon
|
|
144
|
+
>
|
|
131
145
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
146
|
+
<div handler-toolbar>
|
|
147
|
+
<md-icon style="font-size:19px" @click=${() => this._clearHandler()} title="delete current"
|
|
148
|
+
>delete_forever</md-icon
|
|
149
|
+
>
|
|
150
|
+
<md-icon @click=${() => this._pasteHandler()} title="paste">content_paste</md-icon>
|
|
151
|
+
<md-icon style="font-size:17px" @click=${() => this._copyHandler()} title="copy"
|
|
152
|
+
>content_copy</md-icon
|
|
153
|
+
>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
141
156
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
157
|
+
<event-handlers-mapper
|
|
158
|
+
@value-change=${(e) => this._onHandlerChanged(e)}
|
|
159
|
+
.scene=${this.scene}
|
|
160
|
+
.handler=${current}
|
|
161
|
+
></event-handlers-mapper>
|
|
162
|
+
`
|
|
163
|
+
: html `
|
|
164
|
+
<button class="add" @click=${() => this._startHandler()}>
|
|
165
|
+
<md-icon>add</md-icon>${i18next.t('button.add-handler', { defaultValue: 'Add a handler' })}
|
|
166
|
+
</button>
|
|
167
|
+
`}
|
|
147
168
|
</fieldset>
|
|
148
169
|
`;
|
|
149
170
|
}
|
|
171
|
+
_startHandler() {
|
|
172
|
+
this._composing = true;
|
|
173
|
+
this.handlerIndex = this.handlers.length;
|
|
174
|
+
}
|
|
150
175
|
_setHandlerIndex(idx) {
|
|
151
176
|
this.handlerIndex = isNaN(Number(idx)) ? 0 : Number(idx);
|
|
152
177
|
this._onTabScroll();
|
|
@@ -286,102 +311,161 @@ PropertyEventHandlers.styles = [
|
|
|
286
311
|
overflow: hidden;
|
|
287
312
|
}
|
|
288
313
|
|
|
314
|
+
/* The mapper lays its rows out itself; this outer rule must not flatten it to a block,
|
|
315
|
+
* or the row gap it declares on its own host is lost. */
|
|
289
316
|
event-handlers-mapper {
|
|
290
|
-
display:
|
|
317
|
+
display: flex;
|
|
318
|
+
flex-direction: column;
|
|
319
|
+
gap: 6px;
|
|
291
320
|
min-width: 0;
|
|
292
321
|
max-width: 100%;
|
|
293
322
|
box-sizing: border-box;
|
|
294
323
|
}
|
|
295
324
|
|
|
296
325
|
/* 카드 상단 — chevron + tabs 를 하나의 둥근 모서리 박스로 통합. */
|
|
326
|
+
/* Not a card: a row of chips (one per handler, plus "+"), then the fields like any other section. */
|
|
327
|
+
/* How many there are, as a small muted count next to the title. */
|
|
328
|
+
legend > .count {
|
|
329
|
+
margin-left: 6px;
|
|
330
|
+
padding: 0 6px;
|
|
331
|
+
border-radius: 999px;
|
|
332
|
+
font-size: 0.66rem;
|
|
333
|
+
font-weight: 500;
|
|
334
|
+
line-height: 16px;
|
|
335
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
336
|
+
background: var(--md-sys-color-surface-container-high);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* Nothing yet: one quiet way in, instead of an empty form for a handler that does not exist. */
|
|
340
|
+
button.add {
|
|
341
|
+
display: inline-flex;
|
|
342
|
+
align-items: center;
|
|
343
|
+
gap: 4px;
|
|
344
|
+
height: 26px;
|
|
345
|
+
padding: 0 10px 0 6px;
|
|
346
|
+
border: 1px dashed var(--md-sys-color-outline-variant);
|
|
347
|
+
border-radius: 999px;
|
|
348
|
+
background: transparent;
|
|
349
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
350
|
+
font: inherit;
|
|
351
|
+
font-size: 0.74rem;
|
|
352
|
+
cursor: pointer;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
button.add md-icon {
|
|
356
|
+
--md-icon-size: 16px;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
button.add:hover {
|
|
360
|
+
color: var(--md-sys-color-on-surface);
|
|
361
|
+
border-color: var(--md-sys-color-outline);
|
|
362
|
+
background: var(--md-sys-color-surface-container-high);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/* The "+" chip is an offer until it is pressed; pressed, it is the same chip as the numbered ones. */
|
|
366
|
+
ox-buttons-radio > div.add-tab:not([active]) {
|
|
367
|
+
line-height: 20px;
|
|
368
|
+
border: 1px dashed var(--md-sys-color-outline-variant);
|
|
369
|
+
background: transparent;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/* The "open in popup" action is the last thing on the title line, at the far right. */
|
|
373
|
+
legend > md-icon.open-popup {
|
|
374
|
+
margin-left: auto;
|
|
375
|
+
--md-icon-size: 16px;
|
|
376
|
+
color: var(--md-sys-color-outline);
|
|
377
|
+
opacity: 0.6;
|
|
378
|
+
cursor: pointer;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
legend > md-icon.open-popup:hover {
|
|
382
|
+
color: var(--md-sys-color-on-surface);
|
|
383
|
+
}
|
|
384
|
+
|
|
297
385
|
#tab-header {
|
|
298
386
|
display: flex;
|
|
299
|
-
align-items:
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
border-radius: 6px 6px 0 0;
|
|
303
|
-
overflow: hidden;
|
|
304
|
-
background: var(--md-sys-color-surface, #fff);
|
|
387
|
+
align-items: center;
|
|
388
|
+
gap: 2px;
|
|
389
|
+
margin-bottom: 6px;
|
|
305
390
|
}
|
|
306
391
|
|
|
307
392
|
#tab-header > md-icon {
|
|
308
393
|
padding: 0;
|
|
309
394
|
margin: 0;
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
395
|
+
--md-icon-size: 18px;
|
|
396
|
+
color: var(--md-sys-color-outline);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
#tab-header > md-icon[disabled] {
|
|
400
|
+
opacity: 0.3;
|
|
314
401
|
}
|
|
315
402
|
|
|
316
403
|
ox-buttons-radio {
|
|
317
404
|
flex: 1;
|
|
318
|
-
height: 25px;
|
|
319
|
-
/* 외곽 border 는 #tab-header 가 들고 있다 — 여기는 0. */
|
|
320
|
-
border: 0;
|
|
321
|
-
border-radius: 0;
|
|
322
|
-
text-align: center;
|
|
323
405
|
display: flex;
|
|
406
|
+
gap: 4px;
|
|
324
407
|
padding: 0;
|
|
325
408
|
box-sizing: border-box;
|
|
326
409
|
width: 0;
|
|
327
410
|
overflow-x: hidden;
|
|
328
411
|
}
|
|
329
412
|
|
|
413
|
+
/* Tabs are quiet chips on the strip; the open one is lifted to the card's surface. */
|
|
330
414
|
ox-buttons-radio > div {
|
|
331
|
-
|
|
332
|
-
border:
|
|
333
|
-
|
|
334
|
-
padding: 0 8px;
|
|
415
|
+
border: 0;
|
|
416
|
+
border-radius: 999px;
|
|
417
|
+
padding: 0;
|
|
335
418
|
margin: 0;
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
419
|
+
width: 24px;
|
|
420
|
+
height: 22px;
|
|
421
|
+
line-height: 22px;
|
|
422
|
+
text-align: center;
|
|
423
|
+
font-size: 0.72rem;
|
|
424
|
+
box-sizing: border-box;
|
|
425
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
426
|
+
background-color: var(--md-sys-color-surface-container-high);
|
|
427
|
+
cursor: pointer;
|
|
344
428
|
}
|
|
345
429
|
|
|
346
430
|
ox-buttons-radio > div[disabled] {
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
ox-buttons-radio > div[active] {
|
|
351
|
-
border-color: rgb(242, 71, 28);
|
|
431
|
+
opacity: 0.4;
|
|
352
432
|
}
|
|
353
433
|
|
|
434
|
+
ox-buttons-radio > div[active],
|
|
354
435
|
ox-buttons-radio > div.iron-selected {
|
|
355
|
-
|
|
356
|
-
color: var(--md-sys-color-
|
|
436
|
+
color: var(--md-sys-color-on-secondary-container);
|
|
437
|
+
background-color: var(--md-sys-color-secondary-container);
|
|
357
438
|
}
|
|
358
439
|
|
|
359
440
|
div[handler-toolbar] {
|
|
360
441
|
display: flex;
|
|
361
442
|
flex-direction: row-reverse;
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
443
|
+
gap: 2px;
|
|
444
|
+
margin-left: auto;
|
|
445
|
+
padding-left: 6px;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
#tab-header > md-icon[disabled] {
|
|
449
|
+
display: none;
|
|
368
450
|
}
|
|
369
451
|
|
|
452
|
+
/* Section actions are offers, not content: outline ink at half strength until hovered. */
|
|
370
453
|
md-icon {
|
|
371
454
|
margin-left: 5px;
|
|
372
|
-
color: var(--md-sys-color-
|
|
373
|
-
opacity: 0.
|
|
455
|
+
color: var(--md-sys-color-outline);
|
|
456
|
+
opacity: 0.6;
|
|
374
457
|
cursor: pointer;
|
|
375
458
|
--md-icon-size: 18px;
|
|
376
459
|
}
|
|
377
460
|
|
|
378
461
|
md-icon:hover {
|
|
379
|
-
color: var(--md-sys-color-on-
|
|
462
|
+
color: var(--md-sys-color-on-surface);
|
|
380
463
|
opacity: 1;
|
|
381
464
|
}
|
|
382
465
|
|
|
383
466
|
md-icon[disabled] {
|
|
384
|
-
color:
|
|
467
|
+
color: var(--md-sys-color-outline-variant);
|
|
468
|
+
cursor: default;
|
|
385
469
|
}
|
|
386
470
|
`
|
|
387
471
|
];
|
|
@@ -394,6 +478,9 @@ __decorate([
|
|
|
394
478
|
__decorate([
|
|
395
479
|
state()
|
|
396
480
|
], PropertyEventHandlers.prototype, "handlerIndex", void 0);
|
|
481
|
+
__decorate([
|
|
482
|
+
state()
|
|
483
|
+
], PropertyEventHandlers.prototype, "_composing", void 0);
|
|
397
484
|
__decorate([
|
|
398
485
|
state()
|
|
399
486
|
], PropertyEventHandlers.prototype, "_afterRender", void 0);
|