@operato/property-panel 10.0.0-beta.57 → 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.
- package/CHANGELOG.md +29 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +6 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/property-panel/data-binding/data-binding.js +4 -3
- package/dist/src/property-panel/data-binding/data-binding.js.map +1 -1
- package/dist/src/property-panel/effects/effects.js +1 -1
- package/dist/src/property-panel/effects/effects.js.map +1 -1
- package/dist/src/property-panel/effects/property-event-hover.d.ts +1 -1
- package/dist/src/property-panel/effects/property-event.d.ts +15 -7
- package/dist/src/property-panel/effects/property-event.js +17 -38
- package/dist/src/property-panel/effects/property-event.js.map +1 -1
- package/dist/src/property-panel/event-handlers/event-handlers-help.d.ts +23 -0
- package/dist/src/property-panel/event-handlers/event-handlers-help.js +356 -0
- package/dist/src/property-panel/event-handlers/event-handlers-help.js.map +1 -0
- package/dist/src/property-panel/event-handlers/event-handlers-mapper.d.ts +31 -0
- package/dist/src/property-panel/event-handlers/event-handlers-mapper.js +238 -0
- package/dist/src/property-panel/event-handlers/event-handlers-mapper.js.map +1 -0
- package/dist/src/property-panel/event-handlers/event-handlers-popup.d.ts +42 -0
- package/dist/src/property-panel/event-handlers/event-handlers-popup.js +375 -0
- package/dist/src/property-panel/event-handlers/event-handlers-popup.js.map +1 -0
- package/dist/src/property-panel/event-handlers/event-handlers.d.ts +54 -0
- package/dist/src/property-panel/event-handlers/event-handlers.js +410 -0
- package/dist/src/property-panel/event-handlers/event-handlers.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/translations/en.json +7 -0
- package/translations/ja.json +7 -0
- package/translations/ko.json +7 -0
- package/translations/ms.json +7 -0
- package/translations/zh.json +7 -0
|
@@ -0,0 +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 border: 1px solid rgba(0, 0, 0, 0.15);\n background: #fff;\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"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright © HatioLab Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* EventHandlers popup — N 핸들러 large editor. data-binding-popup 의 형제.
|
|
5
|
+
*
|
|
6
|
+
* 2-column layout:
|
|
7
|
+
* Left: handler list — add / duplicate / disable / reorder / delete
|
|
8
|
+
* Right: 선택된 handler 의 mapper (큰 code editor 영역)
|
|
9
|
+
*/
|
|
10
|
+
import '@material/web/icon/icon.js';
|
|
11
|
+
import '@operato/i18n/ox-i18n.js';
|
|
12
|
+
import { LitElement } from 'lit';
|
|
13
|
+
import type { Scene } from '@hatiolab/things-scene';
|
|
14
|
+
import { EventHandlersMapper, type EventHandlerSpec } from './event-handlers-mapper.js';
|
|
15
|
+
import { EventHandlersHelp } from './event-handlers-help.js';
|
|
16
|
+
declare const EventHandlersPopup_base: typeof LitElement & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/types.js").ScopedElementsHost>;
|
|
17
|
+
export declare class EventHandlersPopup extends EventHandlersPopup_base {
|
|
18
|
+
static get scopedElements(): {
|
|
19
|
+
'event-handlers-mapper': typeof EventHandlersMapper;
|
|
20
|
+
'event-handlers-help': typeof EventHandlersHelp;
|
|
21
|
+
};
|
|
22
|
+
static styles: import("lit").CSSResult[];
|
|
23
|
+
handlers: EventHandlerSpec[];
|
|
24
|
+
scene?: Scene;
|
|
25
|
+
selectedIndex: number;
|
|
26
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
27
|
+
private _renderListItem;
|
|
28
|
+
private _summarize;
|
|
29
|
+
private _addHandler;
|
|
30
|
+
private _duplicateHandler;
|
|
31
|
+
private _toggleDisabled;
|
|
32
|
+
private _moveHandler;
|
|
33
|
+
private _removeHandler;
|
|
34
|
+
private _onHandlerChanged;
|
|
35
|
+
/**
|
|
36
|
+
* help pane 의 snippet 버튼 → 현재 선택 handler 의 code 에 *append* (덮어쓰지
|
|
37
|
+
* 않음). action 이 script 가 아니면 자동으로 script 로 전환 + code 초기화.
|
|
38
|
+
*/
|
|
39
|
+
private _onInsertSnippet;
|
|
40
|
+
private _dispatch;
|
|
41
|
+
}
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright © HatioLab Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* EventHandlers popup — N 핸들러 large editor. data-binding-popup 의 형제.
|
|
5
|
+
*
|
|
6
|
+
* 2-column layout:
|
|
7
|
+
* Left: handler list — add / duplicate / disable / reorder / delete
|
|
8
|
+
* Right: 선택된 handler 의 mapper (큰 code editor 영역)
|
|
9
|
+
*/
|
|
10
|
+
import { __decorate } from "tslib";
|
|
11
|
+
import '@material/web/icon/icon.js';
|
|
12
|
+
import '@operato/i18n/ox-i18n.js';
|
|
13
|
+
import { css, html, LitElement } from 'lit';
|
|
14
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
|
15
|
+
import { ScopedElementsMixin } from '@open-wc/scoped-elements';
|
|
16
|
+
import { ScrollbarStyles } from '@operato/styles';
|
|
17
|
+
import { EventHandlersMapper } from './event-handlers-mapper.js';
|
|
18
|
+
import { EventHandlersHelp } from './event-handlers-help.js';
|
|
19
|
+
let EventHandlersPopup = class EventHandlersPopup extends ScopedElementsMixin(LitElement) {
|
|
20
|
+
constructor() {
|
|
21
|
+
super(...arguments);
|
|
22
|
+
this.handlers = [];
|
|
23
|
+
this.selectedIndex = 0;
|
|
24
|
+
}
|
|
25
|
+
static get scopedElements() {
|
|
26
|
+
return {
|
|
27
|
+
'event-handlers-mapper': EventHandlersMapper,
|
|
28
|
+
'event-handlers-help': EventHandlersHelp
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
render() {
|
|
32
|
+
var _a;
|
|
33
|
+
const handlers = (_a = this.handlers) !== null && _a !== void 0 ? _a : [];
|
|
34
|
+
const current = handlers[this.selectedIndex];
|
|
35
|
+
return html `
|
|
36
|
+
<div handler-list>
|
|
37
|
+
<header>
|
|
38
|
+
<span><ox-i18n msgid="label.handlers">Handlers</ox-i18n> (${handlers.length})</span>
|
|
39
|
+
<md-icon @click=${() => this._addHandler()} title="add">add</md-icon>
|
|
40
|
+
</header>
|
|
41
|
+
${handlers.length === 0
|
|
42
|
+
? html `<div empty><ox-i18n msgid="label.no-handlers">No handlers — add one</ox-i18n></div>`
|
|
43
|
+
: html `<ul>${handlers.map((h, i) => this._renderListItem(h, i, handlers.length))}</ul>`}
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div editor>
|
|
47
|
+
${current
|
|
48
|
+
? html `
|
|
49
|
+
<event-handlers-mapper
|
|
50
|
+
.handler=${current}
|
|
51
|
+
.scene=${this.scene}
|
|
52
|
+
@value-change=${(e) => this._onHandlerChanged(e)}
|
|
53
|
+
></event-handlers-mapper>
|
|
54
|
+
`
|
|
55
|
+
: html `<div empty><ox-i18n msgid="label.select-or-add-handler">Select or add a handler</ox-i18n></div>`}
|
|
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>
|
|
64
|
+
`;
|
|
65
|
+
}
|
|
66
|
+
_renderListItem(h, i, total) {
|
|
67
|
+
return html `
|
|
68
|
+
<li
|
|
69
|
+
?active=${i === this.selectedIndex}
|
|
70
|
+
?disabled=${h.disabled}
|
|
71
|
+
@click=${() => (this.selectedIndex = i)}
|
|
72
|
+
>
|
|
73
|
+
<div class="meta">
|
|
74
|
+
<span class="trigger">${h.trigger}${h.disabled ? ' (off)' : ''}</span>
|
|
75
|
+
<span class="summary">${this._summarize(h)}</span>
|
|
76
|
+
</div>
|
|
77
|
+
<md-icon
|
|
78
|
+
@click=${(e) => this._toggleDisabled(i, e)}
|
|
79
|
+
title=${h.disabled ? 'enable' : 'disable'}
|
|
80
|
+
>${h.disabled ? 'toggle_off' : 'toggle_on'}</md-icon>
|
|
81
|
+
<md-icon
|
|
82
|
+
@click=${(e) => this._duplicateHandler(i, e)}
|
|
83
|
+
title="duplicate"
|
|
84
|
+
>content_copy</md-icon>
|
|
85
|
+
<md-icon
|
|
86
|
+
@click=${(e) => this._moveHandler(i, -1, e)}
|
|
87
|
+
title="move up"
|
|
88
|
+
?style=${i === 0 ? 'opacity:0.2;pointer-events:none' : ''}
|
|
89
|
+
>arrow_upward</md-icon>
|
|
90
|
+
<md-icon
|
|
91
|
+
@click=${(e) => this._moveHandler(i, +1, e)}
|
|
92
|
+
title="move down"
|
|
93
|
+
?style=${i === total - 1 ? 'opacity:0.2;pointer-events:none' : ''}
|
|
94
|
+
>arrow_downward</md-icon>
|
|
95
|
+
<md-icon
|
|
96
|
+
class="danger"
|
|
97
|
+
@click=${(e) => this._removeHandler(i, e)}
|
|
98
|
+
title="delete"
|
|
99
|
+
>delete</md-icon>
|
|
100
|
+
</li>
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
_summarize(h) {
|
|
104
|
+
if (h.action === 'script') {
|
|
105
|
+
const c = (h.code || '').replace(/\s+/g, ' ').trim();
|
|
106
|
+
return 'script · ' + (c.length > 60 ? c.slice(0, 60) + '…' : (c || '(empty)'));
|
|
107
|
+
}
|
|
108
|
+
return [h.action || '(none)', h.target ? `→${h.target}` : ''].filter(Boolean).join(' ');
|
|
109
|
+
}
|
|
110
|
+
_addHandler() {
|
|
111
|
+
const next = { trigger: 'click', action: '' };
|
|
112
|
+
this.handlers = [...(this.handlers || []), next];
|
|
113
|
+
this.selectedIndex = this.handlers.length - 1;
|
|
114
|
+
this._dispatch();
|
|
115
|
+
}
|
|
116
|
+
_duplicateHandler(idx, e) {
|
|
117
|
+
e.stopPropagation();
|
|
118
|
+
const original = this.handlers[idx];
|
|
119
|
+
if (!original)
|
|
120
|
+
return;
|
|
121
|
+
const copy = JSON.parse(JSON.stringify(original));
|
|
122
|
+
const next = [...(this.handlers || [])];
|
|
123
|
+
next.splice(idx + 1, 0, copy);
|
|
124
|
+
this.handlers = next;
|
|
125
|
+
this.selectedIndex = idx + 1;
|
|
126
|
+
this._dispatch();
|
|
127
|
+
}
|
|
128
|
+
_toggleDisabled(idx, e) {
|
|
129
|
+
e.stopPropagation();
|
|
130
|
+
const next = [...(this.handlers || [])];
|
|
131
|
+
next[idx] = { ...next[idx], disabled: !next[idx].disabled };
|
|
132
|
+
this.handlers = next;
|
|
133
|
+
this._dispatch();
|
|
134
|
+
}
|
|
135
|
+
_moveHandler(idx, delta, e) {
|
|
136
|
+
e.stopPropagation();
|
|
137
|
+
const next = [...(this.handlers || [])];
|
|
138
|
+
const newIdx = idx + delta;
|
|
139
|
+
if (newIdx < 0 || newIdx >= next.length)
|
|
140
|
+
return;
|
|
141
|
+
const [m] = next.splice(idx, 1);
|
|
142
|
+
next.splice(newIdx, 0, m);
|
|
143
|
+
this.handlers = next;
|
|
144
|
+
if (this.selectedIndex === idx)
|
|
145
|
+
this.selectedIndex = newIdx;
|
|
146
|
+
this._dispatch();
|
|
147
|
+
}
|
|
148
|
+
_removeHandler(idx, e) {
|
|
149
|
+
e.stopPropagation();
|
|
150
|
+
const next = [...(this.handlers || [])];
|
|
151
|
+
next.splice(idx, 1);
|
|
152
|
+
this.handlers = next;
|
|
153
|
+
this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, next.length - 1));
|
|
154
|
+
this._dispatch();
|
|
155
|
+
}
|
|
156
|
+
_onHandlerChanged(e) {
|
|
157
|
+
var _a;
|
|
158
|
+
const handler = (_a = e.detail) === null || _a === void 0 ? void 0 : _a.handler;
|
|
159
|
+
if (!handler)
|
|
160
|
+
return;
|
|
161
|
+
const next = [...(this.handlers || [])];
|
|
162
|
+
next[this.selectedIndex] = handler;
|
|
163
|
+
this.handlers = next;
|
|
164
|
+
this._dispatch();
|
|
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
|
+
}
|
|
191
|
+
_dispatch() {
|
|
192
|
+
// action 이 (none) — 즉 빈 문자열 — 인 핸들러는 외부 모델에 포함하지 않는다.
|
|
193
|
+
// UI 안에서는 placeholder 로 남겨두고, 사용자가 action 을 선택하면 그제서야
|
|
194
|
+
// 저장 모델에 포함된다.
|
|
195
|
+
const valid = (this.handlers || []).filter(h => !!h && !!h.action);
|
|
196
|
+
this.dispatchEvent(new CustomEvent('handlers-change', {
|
|
197
|
+
bubbles: true,
|
|
198
|
+
composed: true,
|
|
199
|
+
detail: { handlers: valid }
|
|
200
|
+
}));
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
EventHandlersPopup.styles = [
|
|
204
|
+
ScrollbarStyles,
|
|
205
|
+
css `
|
|
206
|
+
:host {
|
|
207
|
+
display: flex;
|
|
208
|
+
width: 1400px;
|
|
209
|
+
height: 680px;
|
|
210
|
+
overflow: hidden;
|
|
211
|
+
font-family: 'Roboto', Arial, sans-serif;
|
|
212
|
+
font-size: 13px;
|
|
213
|
+
color: var(--md-sys-color-on-surface, #1c1b1f);
|
|
214
|
+
background: var(--md-sys-color-surface, #fff);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
[handler-list] {
|
|
218
|
+
width: 240px;
|
|
219
|
+
min-width: 240px;
|
|
220
|
+
border-right: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.12));
|
|
221
|
+
display: flex;
|
|
222
|
+
flex-direction: column;
|
|
223
|
+
background: var(--md-sys-color-surface-container-low, #f7f2fa);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
[handler-list] header {
|
|
227
|
+
display: flex;
|
|
228
|
+
align-items: center;
|
|
229
|
+
justify-content: space-between;
|
|
230
|
+
padding: 10px 12px;
|
|
231
|
+
font-weight: 600;
|
|
232
|
+
font-size: 12px;
|
|
233
|
+
text-transform: uppercase;
|
|
234
|
+
letter-spacing: 0.5px;
|
|
235
|
+
color: var(--md-sys-color-on-surface-variant, #49454f);
|
|
236
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.12));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
[handler-list] header md-icon {
|
|
240
|
+
cursor: pointer;
|
|
241
|
+
--md-icon-size: 18px;
|
|
242
|
+
border-radius: 4px;
|
|
243
|
+
padding: 2px;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
[handler-list] header md-icon:hover {
|
|
247
|
+
background: rgba(0, 0, 0, 0.05);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
[handler-list] ul {
|
|
251
|
+
list-style: none;
|
|
252
|
+
margin: 0;
|
|
253
|
+
padding: 0;
|
|
254
|
+
overflow-y: auto;
|
|
255
|
+
flex: 1;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
[handler-list] li {
|
|
259
|
+
padding: 8px 10px;
|
|
260
|
+
cursor: pointer;
|
|
261
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.08));
|
|
262
|
+
display: flex;
|
|
263
|
+
align-items: center;
|
|
264
|
+
gap: 6px;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
[handler-list] li[active] {
|
|
268
|
+
background: var(--md-sys-color-secondary-container, #e8def8);
|
|
269
|
+
color: var(--md-sys-color-on-secondary-container, #1d192b);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
[handler-list] li[disabled] {
|
|
273
|
+
opacity: 0.5;
|
|
274
|
+
font-style: italic;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
[handler-list] li:hover {
|
|
278
|
+
background: rgba(0, 0, 0, 0.04);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
[handler-list] li .meta {
|
|
282
|
+
display: flex;
|
|
283
|
+
flex-direction: column;
|
|
284
|
+
flex: 1;
|
|
285
|
+
min-width: 0;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
[handler-list] li .trigger {
|
|
289
|
+
font-weight: 600;
|
|
290
|
+
font-size: 12px;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
[handler-list] li .summary {
|
|
294
|
+
font-size: 11px;
|
|
295
|
+
opacity: 0.7;
|
|
296
|
+
white-space: nowrap;
|
|
297
|
+
overflow: hidden;
|
|
298
|
+
text-overflow: ellipsis;
|
|
299
|
+
margin-top: 2px;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
[handler-list] li md-icon {
|
|
303
|
+
--md-icon-size: 16px;
|
|
304
|
+
opacity: 0.5;
|
|
305
|
+
cursor: pointer;
|
|
306
|
+
padding: 3px;
|
|
307
|
+
border-radius: 4px;
|
|
308
|
+
flex-shrink: 0;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
[handler-list] li md-icon:hover {
|
|
312
|
+
opacity: 1;
|
|
313
|
+
background: rgba(0, 0, 0, 0.05);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
[handler-list] li md-icon.danger:hover {
|
|
317
|
+
color: var(--md-sys-color-error, #b3261e);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
[handler-list] [empty] {
|
|
321
|
+
padding: 24px 12px;
|
|
322
|
+
text-align: center;
|
|
323
|
+
color: var(--md-sys-color-on-surface-variant, #49454f);
|
|
324
|
+
opacity: 0.6;
|
|
325
|
+
font-size: 12px;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
[editor] {
|
|
329
|
+
flex: 1;
|
|
330
|
+
display: flex;
|
|
331
|
+
flex-direction: column;
|
|
332
|
+
overflow-y: auto;
|
|
333
|
+
padding: 16px 20px;
|
|
334
|
+
background: var(--md-sys-color-surface, #fff);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
[editor] [empty] {
|
|
338
|
+
display: flex;
|
|
339
|
+
align-items: center;
|
|
340
|
+
justify-content: center;
|
|
341
|
+
flex: 1;
|
|
342
|
+
color: var(--md-sys-color-on-surface-variant, #49454f);
|
|
343
|
+
font-size: 13px;
|
|
344
|
+
opacity: 0.6;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
[editor] event-handlers-mapper {
|
|
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;
|
|
359
|
+
}
|
|
360
|
+
`
|
|
361
|
+
];
|
|
362
|
+
__decorate([
|
|
363
|
+
property({ type: Array })
|
|
364
|
+
], EventHandlersPopup.prototype, "handlers", void 0);
|
|
365
|
+
__decorate([
|
|
366
|
+
property({ type: Object })
|
|
367
|
+
], EventHandlersPopup.prototype, "scene", void 0);
|
|
368
|
+
__decorate([
|
|
369
|
+
state()
|
|
370
|
+
], EventHandlersPopup.prototype, "selectedIndex", void 0);
|
|
371
|
+
EventHandlersPopup = __decorate([
|
|
372
|
+
customElement('event-handlers-popup')
|
|
373
|
+
], EventHandlersPopup);
|
|
374
|
+
export { EventHandlersPopup };
|
|
375
|
+
//# sourceMappingURL=event-handlers-popup.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright © HatioLab Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* EventHandlers panel — 컴포넌트의 `state.eventHandlers[]` 편집기.
|
|
5
|
+
*
|
|
6
|
+
* 데이터바인딩 panel 의 형제로 *완전 분리*. 데이터바인딩은 데이터 흐름 전용,
|
|
7
|
+
* eventHandlers 는 사용자 액션 (tap / hover / 등) 의 first-class 핸들러 등록.
|
|
8
|
+
*
|
|
9
|
+
* data-binding 패턴 그대로 — tabs (N handlers) + handler mapper + add/delete/copy/paste.
|
|
10
|
+
*/
|
|
11
|
+
import '@material/web/icon/icon.js';
|
|
12
|
+
import '@operato/help/ox-title-with-help.js';
|
|
13
|
+
import '@operato/input/ox-buttons-radio.js';
|
|
14
|
+
import '@operato/i18n/ox-i18n.js';
|
|
15
|
+
import { PropertyValues } from 'lit';
|
|
16
|
+
import type { Properties, Scene } from '@hatiolab/things-scene';
|
|
17
|
+
import { AbstractProperty } from '../abstract-property.js';
|
|
18
|
+
import { EventHandlersMapper, type EventHandlerSpec } from './event-handlers-mapper.js';
|
|
19
|
+
declare const PropertyEventHandlers_base: typeof AbstractProperty & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/types.js").ScopedElementsHost>;
|
|
20
|
+
export declare class PropertyEventHandlers extends PropertyEventHandlers_base {
|
|
21
|
+
static styles: import("lit").CSSResult[];
|
|
22
|
+
value?: Properties;
|
|
23
|
+
scene?: Scene;
|
|
24
|
+
handlerIndex: number;
|
|
25
|
+
_afterRender?: Function | null;
|
|
26
|
+
tabs: HTMLElement;
|
|
27
|
+
tabNavLeftButton: HTMLElement;
|
|
28
|
+
tabNavRightButton: HTMLElement;
|
|
29
|
+
/**
|
|
30
|
+
* 두 속성 통합 — 신규 `eventHandlers` 우선, 없으면 legacy `event` 객체를
|
|
31
|
+
* *read-only* 변환해 표시. 사용자가 수정 시 `eventHandlers` 에만 저장 — legacy
|
|
32
|
+
* 는 손대지 않음 (옛 코드 호환 / rollback 안전망).
|
|
33
|
+
*/
|
|
34
|
+
get handlers(): EventHandlerSpec[];
|
|
35
|
+
firstUpdated(): void;
|
|
36
|
+
updated(changes: PropertyValues<this>): void;
|
|
37
|
+
static get scopedElements(): {
|
|
38
|
+
'event-handlers-mapper': typeof EventHandlersMapper;
|
|
39
|
+
};
|
|
40
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
41
|
+
_setHandlerIndex(idx: number): void;
|
|
42
|
+
_clearHandler(): void;
|
|
43
|
+
_copyHandler(): void;
|
|
44
|
+
_pasteHandler(): void;
|
|
45
|
+
_onHandlerChanged(e: CustomEvent): void;
|
|
46
|
+
private _dispatchHandlersChange;
|
|
47
|
+
onValueChanged(): Promise<void>;
|
|
48
|
+
_openHandlersPopup(): Promise<void>;
|
|
49
|
+
get tabContainer(): HTMLElement;
|
|
50
|
+
_onTabScroll(): void;
|
|
51
|
+
_onTabScrollNavLeft(): void;
|
|
52
|
+
_onTabScrollNavRight(): void;
|
|
53
|
+
}
|
|
54
|
+
export {};
|