@fmsim/layout-view 3.0.0-beta.1 → 3.0.0-beta.11

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.
@@ -1,4 +1,6 @@
1
1
  import './ox-input-animation-map.js';
2
+ import '@things-factory/figure-library/dist-client/legend-looks/ox-legend-looks.js';
3
+ import { LitElement } from 'lit';
2
4
  import { OxGristEditor } from '@operato/data-grist';
3
5
  export declare class AnimationMapEditor extends OxGristEditor {
4
6
  private popup?;
@@ -8,3 +10,16 @@ export declare class AnimationMapEditor extends OxGristEditor {
8
10
  _onkeydown(e: KeyboardEvent): void;
9
11
  openSelector(): Promise<void>;
10
12
  }
13
+ /**
14
+ * An animation map edited whole: each code's 2D motion (ox-input-animation-map) and its 3D colour and blink, which a
15
+ * carrier figure's body parts show (ADR-0167 decision 12), with the one legend editor (ox-legend-looks) that board legend
16
+ * themes use. Both edit one value, entry by entry, each keeping what the other wrote.
17
+ */
18
+ export declare class AnimationMapPopup extends LitElement {
19
+ static styles: import("lit").CSSResult;
20
+ /** code → entry: the 2D motion under `animation`, colour and effect beside it (animationEntriesOf) */
21
+ value: Record<string, Record<string, unknown>>;
22
+ render(): import("lit-html").TemplateResult<1>;
23
+ /** Whether a 2D motion row has no code: the map cannot be kept so (each code is the map's key) */
24
+ hasEmptyKey(): boolean;
25
+ }
@@ -1,13 +1,15 @@
1
- import { __decorate } from "tslib";
1
+ import { __decorate, __metadata } from "tslib";
2
2
  import './ox-input-animation-map.js';
3
+ import '@things-factory/figure-library/dist-client/legend-looks/ox-legend-looks.js';
3
4
  import JSON5 from 'json5';
4
- import { html } from 'lit';
5
- import { customElement } from 'lit/decorators.js';
5
+ import { css, html, LitElement } from 'lit';
6
+ import { customElement, property } from 'lit/decorators.js';
6
7
  import { createRef, ref } from 'lit/directives/ref.js';
7
8
  import { cloneDeep } from 'lodash-es';
8
9
  import { OxGristEditor } from '@operato/data-grist';
9
10
  import { i18next } from '@operato/i18n';
10
11
  import { openPopup } from '@operato/popup';
12
+ import { animationEntriesOf } from '@fmsim/machine';
11
13
  let AnimationMapEditor = class AnimationMapEditor extends OxGristEditor {
12
14
  get options() {
13
15
  var _a;
@@ -48,17 +50,16 @@ let AnimationMapEditor = class AnimationMapEditor extends OxGristEditor {
48
50
  };
49
51
  let value;
50
52
  try {
51
- value = !objectified && typeof this.value === 'string' ? JSON5.parse(this.value) : cloneDeep(this.value || {});
53
+ value = animationEntriesOf(!objectified && typeof this.value === 'string' ? JSON5.parse(this.value) : cloneDeep(this.value || {}));
52
54
  }
53
55
  catch (e) {
54
56
  value = {};
55
57
  }
56
- let latestValue = value;
57
- const editorRef = createRef();
58
+ const popupRef = createRef();
58
59
  const onConfirm = () => {
59
- var _a, _b, _c;
60
+ var _a, _b, _c, _d;
60
61
  // key 없이 정의된 애니메이션이 있으면 확인(저장) 불가 — key 는 맵의 식별자.
61
- if ((_b = (_a = editorRef.value) === null || _a === void 0 ? void 0 : _a.hasEmptyKey) === null || _b === void 0 ? void 0 : _b.call(_a)) {
62
+ if ((_a = popupRef.value) === null || _a === void 0 ? void 0 : _a.hasEmptyKey()) {
62
63
  document.dispatchEvent(new CustomEvent('notify', {
63
64
  detail: {
64
65
  level: 'warn',
@@ -69,8 +70,8 @@ let AnimationMapEditor = class AnimationMapEditor extends OxGristEditor {
69
70
  }));
70
71
  return;
71
72
  }
72
- confirmCallback(latestValue);
73
- (_c = this.popup) === null || _c === void 0 ? void 0 : _c.close();
73
+ confirmCallback((_c = (_b = popupRef.value) === null || _b === void 0 ? void 0 : _b.value) !== null && _c !== void 0 ? _c : value);
74
+ (_d = this.popup) === null || _d === void 0 ? void 0 : _d.close();
74
75
  };
75
76
  const template = html `
76
77
  <style>
@@ -81,12 +82,7 @@ let AnimationMapEditor = class AnimationMapEditor extends OxGristEditor {
81
82
  </style>
82
83
  <div class="animation-map-popup" style="display:flex;flex-direction:row;height:100%;box-sizing:border-box;gap:0;">
83
84
  <div style="display:flex;flex-direction:column;flex:2;overflow:hidden;">
84
- <ox-input-animation-map
85
- ${ref(editorRef)}
86
- .value=${value}
87
- @change=${(e) => { latestValue = e.detail; }}
88
- style="flex:1;overflow:auto;padding:12px 12px 0;"
89
- ></ox-input-animation-map>
85
+ <animation-map-popup ${ref(popupRef)} .value=${value} style="flex:1;overflow:auto;"></animation-map-popup>
90
86
  <div style="display:flex;justify-content:flex-end;gap:8px;padding:12px;">
91
87
  <button @click=${onConfirm}
92
88
  style="padding:6px 16px;background:var(--md-sys-color-primary,#1976d2);color:#fff;border:none;border-radius:4px;cursor:pointer;">
@@ -98,7 +94,6 @@ let AnimationMapEditor = class AnimationMapEditor extends OxGristEditor {
98
94
  </button>
99
95
  </div>
100
96
  </div>
101
-
102
97
  <div style="flex:1;padding:12px 16px;overflow:auto;border-left:1px solid var(--md-sys-color-outline-variant,#ddd);
103
98
  font-size:12px;line-height:1.6;color:var(--md-sys-color-on-surface-variant,#555);background:var(--md-sys-color-surface-container-low,#f4f4f4);">
104
99
  <h3 style="margin:0 0 10px;font-size:15px;font-weight:700;color:var(--md-sys-color-primary,#1976d2);">Animation Types</h3>
@@ -147,4 +142,73 @@ AnimationMapEditor = __decorate([
147
142
  customElement('animation-map-editor')
148
143
  ], AnimationMapEditor);
149
144
  export { AnimationMapEditor };
145
+ /**
146
+ * An animation map edited whole: each code's 2D motion (ox-input-animation-map) and its 3D colour and blink, which a
147
+ * carrier figure's body parts show (ADR-0167 decision 12), with the one legend editor (ox-legend-looks) that board legend
148
+ * themes use. Both edit one value, entry by entry, each keeping what the other wrote.
149
+ */
150
+ let AnimationMapPopup = class AnimationMapPopup extends LitElement {
151
+ constructor() {
152
+ super(...arguments);
153
+ /** code → entry: the 2D motion under `animation`, colour and effect beside it (animationEntriesOf) */
154
+ this.value = {};
155
+ }
156
+ render() {
157
+ return html `
158
+ <h3>${i18next.t('label.animation-2d', { defaultValue: '2D animation' })}</h3>
159
+ <ox-input-animation-map
160
+ .value=${this.value}
161
+ @change=${(e) => {
162
+ e.stopPropagation();
163
+ this.value = animationEntriesOf(e.detail);
164
+ }}
165
+ ></ox-input-animation-map>
166
+ <h3>${i18next.t('label.animation-3d-looks', { defaultValue: '3D colour and blink' })}</h3>
167
+ <p>${i18next.t('text.animation-3d-looks', { defaultValue: "Shown on a carrier figure's body parts." })}</p>
168
+ <ox-legend-looks
169
+ .looks=${this.value}
170
+ effects
171
+ color-optional
172
+ no-values
173
+ @looks-change=${(e) => {
174
+ e.stopPropagation();
175
+ this.value = animationEntriesOf(e.detail.looks);
176
+ }}
177
+ ></ox-legend-looks>
178
+ `;
179
+ }
180
+ /** Whether a 2D motion row has no code: the map cannot be kept so (each code is the map's key) */
181
+ hasEmptyKey() {
182
+ var _a, _b;
183
+ return !!((_b = (_a = this.renderRoot.querySelector('ox-input-animation-map')) === null || _a === void 0 ? void 0 : _a.hasEmptyKey) === null || _b === void 0 ? void 0 : _b.call(_a));
184
+ }
185
+ };
186
+ AnimationMapPopup.styles = css `
187
+ :host {
188
+ display: block;
189
+ padding: 12px 12px 0;
190
+ }
191
+ h3 {
192
+ margin: 12px 0 6px;
193
+ font-size: 13px;
194
+ font-weight: 700;
195
+ color: var(--md-sys-color-primary, #1976d2);
196
+ }
197
+ h3:first-child {
198
+ margin-top: 0;
199
+ }
200
+ p {
201
+ margin: 0 0 6px;
202
+ font-size: 12px;
203
+ color: var(--md-sys-color-on-surface-variant, #555);
204
+ }
205
+ `;
206
+ __decorate([
207
+ property({ type: Object }),
208
+ __metadata("design:type", Object)
209
+ ], AnimationMapPopup.prototype, "value", void 0);
210
+ AnimationMapPopup = __decorate([
211
+ customElement('animation-map-popup')
212
+ ], AnimationMapPopup);
213
+ export { AnimationMapPopup };
150
214
  //# sourceMappingURL=animation-map-editor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"animation-map-editor.js","sourceRoot":"","sources":["../../client/data-grist/animation-map-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import './ox-input-animation-map.js'\n\nimport JSON5 from 'json5'\nimport { html } from 'lit'\nimport { customElement } from 'lit/decorators.js'\nimport { createRef, ref } from 'lit/directives/ref.js'\n\nimport { cloneDeep } from 'lodash-es'\n\nimport { OxGristEditor } from '@operato/data-grist'\nimport { i18next } from '@operato/i18n'\nimport { openPopup, PopupHandle } from '@operato/popup'\n\n@customElement('animation-map-editor')\nexport class AnimationMapEditor extends OxGristEditor {\n private popup?: PopupHandle\n\n get options() {\n const overidable = this.column.record?.options || {}\n\n return {\n name: `Animation : ${this.record.name}`,\n objectified: false,\n ...overidable\n }\n }\n\n get editorTemplate() {\n const value = typeof this.value === 'object' ? JSON5.stringify(this.value) : this.value\n return html`<div tabindex=\"0\">${value || ''}</div>`\n }\n\n _onclick(e: Event): void {\n e.stopPropagation()\n this.openSelector()\n }\n\n _onkeydown(e: KeyboardEvent): void {\n if (e.key === 'Enter') {\n e.stopPropagation()\n this.openSelector()\n }\n }\n\n async openSelector() {\n if (this.popup) {\n delete this.popup\n }\n\n const { name, objectified = false } = this.options || {}\n\n const confirmCallback = (newval: any) => {\n this.dispatchEvent(\n new CustomEvent('field-change', {\n bubbles: true,\n composed: true,\n detail: {\n before: this.value,\n after: !objectified ? JSON5.stringify(newval, null, 2) : newval,\n record: this.record,\n column: this.column,\n row: this.row\n }\n })\n )\n }\n\n let value: any\n try {\n value = !objectified && typeof this.value === 'string' ? JSON5.parse(this.value) : cloneDeep(this.value || {})\n } catch (e) {\n value = {}\n }\n\n let latestValue = value\n\n const editorRef = createRef<any>()\n\n const onConfirm = () => {\n // key 없이 정의된 애니메이션이 있으면 확인(저장) 불가 — key 는 맵의 식별자.\n if (editorRef.value?.hasEmptyKey?.()) {\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: {\n level: 'warn',\n message: i18next.t('text.animation_key_required', {\n defaultValue: 'Enter a key for each animation definition.'\n })\n }\n })\n )\n return\n }\n confirmCallback(latestValue)\n this.popup?.close()\n }\n\n const template = html`\n <style>\n .animation-map-popup ::-webkit-scrollbar { width: 5px; height: 5px; }\n .animation-map-popup ::-webkit-scrollbar-track { background-color: transparent; }\n .animation-map-popup ::-webkit-scrollbar-thumb { background-color: var(--md-sys-color-outline, #888); border-radius: 0; }\n .animation-map-popup ::-webkit-scrollbar-thumb:hover { background-color: var(--md-sys-color-outline-variant, #aaa); }\n </style>\n <div class=\"animation-map-popup\" style=\"display:flex;flex-direction:row;height:100%;box-sizing:border-box;gap:0;\">\n <div style=\"display:flex;flex-direction:column;flex:2;overflow:hidden;\">\n <ox-input-animation-map\n ${ref(editorRef)}\n .value=${value}\n @change=${(e: CustomEvent) => { latestValue = e.detail }}\n style=\"flex:1;overflow:auto;padding:12px 12px 0;\"\n ></ox-input-animation-map>\n <div style=\"display:flex;justify-content:flex-end;gap:8px;padding:12px;\">\n <button @click=${onConfirm}\n style=\"padding:6px 16px;background:var(--md-sys-color-primary,#1976d2);color:#fff;border:none;border-radius:4px;cursor:pointer;\">\n ${i18next.t('button.confirm') || 'Confirm'}\n </button>\n <button @click=${() => this.popup?.close()}\n style=\"padding:6px 16px;background:var(--md-sys-color-surface-variant,#e0e0e0);color:#333;border:none;border-radius:4px;cursor:pointer;\">\n ${i18next.t('button.cancel') || 'Cancel'}\n </button>\n </div>\n </div>\n\n <div style=\"flex:1;padding:12px 16px;overflow:auto;border-left:1px solid var(--md-sys-color-outline-variant,#ddd);\n font-size:12px;line-height:1.6;color:var(--md-sys-color-on-surface-variant,#555);background:var(--md-sys-color-surface-container-low,#f4f4f4);\">\n <h3 style=\"margin:0 0 10px;font-size:15px;font-weight:700;color:var(--md-sys-color-primary,#1976d2);\">Animation Types</h3>\n <dl style=\"margin:0;padding:0;\">\n <dt style=\"font-weight:600;margin-top:6px;\">Rotation</dt>\n <dd style=\"margin:0 0 4px 8px;\">Theta(°) 각도만큼 회전. 360°이면 1회전.</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Vibration</dt>\n <dd style=\"margin:0 0 4px 8px;\">Theta(°) 범위 내에서 좌우 진동.</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Heartbeat</dt>\n <dd style=\"margin:0 0 4px 8px;\">Scale 비율로 확대/축소 반복. 1.2 = 20% 확대.</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Moving</dt>\n <dd style=\"margin:0 0 4px 8px;\">X, Y 픽셀만큼 이동 후 복귀.</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Fade</dt>\n <dd style=\"margin:0 0 4px 8px;\">Start/End Alpha(0~1) 사이에서 투명도 변화.</dd>\n </dl>\n\n <h3 style=\"margin:18px 0 10px;font-size:15px;font-weight:700;color:var(--md-sys-color-primary,#1976d2);\">Common Properties</h3>\n <dl style=\"margin:0;padding:0;\">\n <dt style=\"font-weight:600;margin-top:6px;\">Delay</dt>\n <dd style=\"margin:0 0 4px 8px;\">애니메이션 시작 전 대기 시간 (ms).</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Duration</dt>\n <dd style=\"margin:0 0 4px 8px;\">한 사이클 소요 시간 (ms). 1000 = 1초.</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Interval</dt>\n <dd style=\"margin:0 0 4px 8px;\">반복 사이 대기 시간 (ms).</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Delta</dt>\n <dd style=\"margin:0 0 4px 8px;\">보간 함수. linear(균일), quad(가속), circ(원형), back(오버슈트), bounce(바운스), elastic(탄성).</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Ease</dt>\n <dd style=\"margin:0 0 4px 8px;\">이징 방향. in(시작 느림), out(끝 느림), inout(양쪽 느림).</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Repeat</dt>\n <dd style=\"margin:0 0 4px 8px;\">체크 시 무한 반복.</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Autoplay</dt>\n <dd style=\"margin:0 0 4px 8px;\">체크 시 로드 즉시 자동 재생.</dd>\n </dl>\n\n </div>\n </div>\n `\n\n this.popup = openPopup(template, {\n backdrop: true,\n size: 'large',\n title: `${name?.toUpperCase() || 'ANIMATION MAP'}`\n })\n }\n}\n"]}
1
+ {"version":3,"file":"animation-map-editor.js","sourceRoot":"","sources":["../../client/data-grist/animation-map-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import './ox-input-animation-map.js'\nimport '@things-factory/figure-library/dist-client/legend-looks/ox-legend-looks.js'\n\nimport JSON5 from 'json5'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { createRef, ref } from 'lit/directives/ref.js'\n\nimport { cloneDeep } from 'lodash-es'\n\nimport { OxGristEditor } from '@operato/data-grist'\nimport { i18next } from '@operato/i18n'\nimport { openPopup, PopupHandle } from '@operato/popup'\nimport { animationEntriesOf } from '@fmsim/machine'\n\n@customElement('animation-map-editor')\nexport class AnimationMapEditor extends OxGristEditor {\n private popup?: PopupHandle\n\n get options() {\n const overidable = this.column.record?.options || {}\n\n return {\n name: `Animation : ${this.record.name}`,\n objectified: false,\n ...overidable\n }\n }\n\n get editorTemplate() {\n const value = typeof this.value === 'object' ? JSON5.stringify(this.value) : this.value\n return html`<div tabindex=\"0\">${value || ''}</div>`\n }\n\n _onclick(e: Event): void {\n e.stopPropagation()\n this.openSelector()\n }\n\n _onkeydown(e: KeyboardEvent): void {\n if (e.key === 'Enter') {\n e.stopPropagation()\n this.openSelector()\n }\n }\n\n async openSelector() {\n if (this.popup) {\n delete this.popup\n }\n\n const { name, objectified = false } = this.options || {}\n\n const confirmCallback = (newval: any) => {\n this.dispatchEvent(\n new CustomEvent('field-change', {\n bubbles: true,\n composed: true,\n detail: {\n before: this.value,\n after: !objectified ? JSON5.stringify(newval, null, 2) : newval,\n record: this.record,\n column: this.column,\n row: this.row\n }\n })\n )\n }\n\n let value: any\n try {\n value = animationEntriesOf(!objectified && typeof this.value === 'string' ? JSON5.parse(this.value) : cloneDeep(this.value || {}))\n } catch (e) {\n value = {}\n }\n\n const popupRef = createRef<AnimationMapPopup>()\n\n const onConfirm = () => {\n // key 없이 정의된 애니메이션이 있으면 확인(저장) 불가 — key 는 맵의 식별자.\n if (popupRef.value?.hasEmptyKey()) {\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: {\n level: 'warn',\n message: i18next.t('text.animation_key_required', {\n defaultValue: 'Enter a key for each animation definition.'\n })\n }\n })\n )\n return\n }\n confirmCallback(popupRef.value?.value ?? value)\n this.popup?.close()\n }\n\n const template = html`\n <style>\n .animation-map-popup ::-webkit-scrollbar { width: 5px; height: 5px; }\n .animation-map-popup ::-webkit-scrollbar-track { background-color: transparent; }\n .animation-map-popup ::-webkit-scrollbar-thumb { background-color: var(--md-sys-color-outline, #888); border-radius: 0; }\n .animation-map-popup ::-webkit-scrollbar-thumb:hover { background-color: var(--md-sys-color-outline-variant, #aaa); }\n </style>\n <div class=\"animation-map-popup\" style=\"display:flex;flex-direction:row;height:100%;box-sizing:border-box;gap:0;\">\n <div style=\"display:flex;flex-direction:column;flex:2;overflow:hidden;\">\n <animation-map-popup ${ref(popupRef)} .value=${value} style=\"flex:1;overflow:auto;\"></animation-map-popup>\n <div style=\"display:flex;justify-content:flex-end;gap:8px;padding:12px;\">\n <button @click=${onConfirm}\n style=\"padding:6px 16px;background:var(--md-sys-color-primary,#1976d2);color:#fff;border:none;border-radius:4px;cursor:pointer;\">\n ${i18next.t('button.confirm') || 'Confirm'}\n </button>\n <button @click=${() => this.popup?.close()}\n style=\"padding:6px 16px;background:var(--md-sys-color-surface-variant,#e0e0e0);color:#333;border:none;border-radius:4px;cursor:pointer;\">\n ${i18next.t('button.cancel') || 'Cancel'}\n </button>\n </div>\n </div>\n <div style=\"flex:1;padding:12px 16px;overflow:auto;border-left:1px solid var(--md-sys-color-outline-variant,#ddd);\n font-size:12px;line-height:1.6;color:var(--md-sys-color-on-surface-variant,#555);background:var(--md-sys-color-surface-container-low,#f4f4f4);\">\n <h3 style=\"margin:0 0 10px;font-size:15px;font-weight:700;color:var(--md-sys-color-primary,#1976d2);\">Animation Types</h3>\n <dl style=\"margin:0;padding:0;\">\n <dt style=\"font-weight:600;margin-top:6px;\">Rotation</dt>\n <dd style=\"margin:0 0 4px 8px;\">Theta(°) 각도만큼 회전. 360°이면 1회전.</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Vibration</dt>\n <dd style=\"margin:0 0 4px 8px;\">Theta(°) 범위 내에서 좌우 진동.</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Heartbeat</dt>\n <dd style=\"margin:0 0 4px 8px;\">Scale 비율로 확대/축소 반복. 1.2 = 20% 확대.</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Moving</dt>\n <dd style=\"margin:0 0 4px 8px;\">X, Y 픽셀만큼 이동 후 복귀.</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Fade</dt>\n <dd style=\"margin:0 0 4px 8px;\">Start/End Alpha(0~1) 사이에서 투명도 변화.</dd>\n </dl>\n\n <h3 style=\"margin:18px 0 10px;font-size:15px;font-weight:700;color:var(--md-sys-color-primary,#1976d2);\">Common Properties</h3>\n <dl style=\"margin:0;padding:0;\">\n <dt style=\"font-weight:600;margin-top:6px;\">Delay</dt>\n <dd style=\"margin:0 0 4px 8px;\">애니메이션 시작 전 대기 시간 (ms).</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Duration</dt>\n <dd style=\"margin:0 0 4px 8px;\">한 사이클 소요 시간 (ms). 1000 = 1초.</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Interval</dt>\n <dd style=\"margin:0 0 4px 8px;\">반복 사이 대기 시간 (ms).</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Delta</dt>\n <dd style=\"margin:0 0 4px 8px;\">보간 함수. linear(균일), quad(가속), circ(원형), back(오버슈트), bounce(바운스), elastic(탄성).</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Ease</dt>\n <dd style=\"margin:0 0 4px 8px;\">이징 방향. in(시작 느림), out(끝 느림), inout(양쪽 느림).</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Repeat</dt>\n <dd style=\"margin:0 0 4px 8px;\">체크 시 무한 반복.</dd>\n <dt style=\"font-weight:600;margin-top:6px;\">Autoplay</dt>\n <dd style=\"margin:0 0 4px 8px;\">체크 시 로드 즉시 자동 재생.</dd>\n </dl>\n\n </div>\n </div>\n `\n\n this.popup = openPopup(template, {\n backdrop: true,\n size: 'large',\n title: `${name?.toUpperCase() || 'ANIMATION MAP'}`\n })\n }\n}\n\n/**\n * An animation map edited whole: each code's 2D motion (ox-input-animation-map) and its 3D colour and blink, which a\n * carrier figure's body parts show (ADR-0167 decision 12), with the one legend editor (ox-legend-looks) that board legend\n * themes use. Both edit one value, entry by entry, each keeping what the other wrote.\n */\n@customElement('animation-map-popup')\nexport class AnimationMapPopup extends LitElement {\n static styles = css`\n :host {\n display: block;\n padding: 12px 12px 0;\n }\n h3 {\n margin: 12px 0 6px;\n font-size: 13px;\n font-weight: 700;\n color: var(--md-sys-color-primary, #1976d2);\n }\n h3:first-child {\n margin-top: 0;\n }\n p {\n margin: 0 0 6px;\n font-size: 12px;\n color: var(--md-sys-color-on-surface-variant, #555);\n }\n `\n\n /** code → entry: the 2D motion under `animation`, colour and effect beside it (animationEntriesOf) */\n @property({ type: Object }) value: Record<string, Record<string, unknown>> = {}\n\n render() {\n return html`\n <h3>${i18next.t('label.animation-2d', { defaultValue: '2D animation' })}</h3>\n <ox-input-animation-map\n .value=${this.value}\n @change=${(e: CustomEvent) => {\n e.stopPropagation()\n this.value = animationEntriesOf(e.detail)\n }}\n ></ox-input-animation-map>\n <h3>${i18next.t('label.animation-3d-looks', { defaultValue: '3D colour and blink' })}</h3>\n <p>${i18next.t('text.animation-3d-looks', { defaultValue: \"Shown on a carrier figure's body parts.\" })}</p>\n <ox-legend-looks\n .looks=${this.value}\n effects\n color-optional\n no-values\n @looks-change=${(e: CustomEvent) => {\n e.stopPropagation()\n this.value = animationEntriesOf(e.detail.looks)\n }}\n ></ox-legend-looks>\n `\n }\n\n /** Whether a 2D motion row has no code: the map cannot be kept so (each code is the map's key) */\n hasEmptyKey(): boolean {\n return !!(this.renderRoot.querySelector('ox-input-animation-map') as any)?.hasEmptyKey?.()\n }\n}\n"]}
@@ -1,18 +1,8 @@
1
1
  /**
2
- * Animation Map 에디터
2
+ * An animation map's 2D motions, one row per code: the code and its motion (ox-input-animation-config).
3
3
  *
4
- * key-value 맵에서 value가 애니메이션 설정 객체인 경우를 위한 편집기.
5
- * 각 행은 key 입력 + ox-input-animation-config 폼으로 구성된다.
6
- *
7
- * value 구조 (실제 데이터):
8
- * {
9
- * "CRITICAL": "{ animation: { type: 'vibration', duration: 1000, ... }, icon: { ... } }",
10
- * "WARN": "{ animation: { type: 'vibration', ... } }",
11
- * "default": ""
12
- * }
13
- *
14
- * 각 entry value는 JSON5 string이며, 내부에 animation 키가 있으면 그 속성만 폼에 표시.
15
- * 저장 시 animation 외 다른 키(icon 등)는 보존됨.
4
+ * The value is read by animationEntriesOf: each entry a record, its 2D motion under `animation` and what else it holds
5
+ * beside it -- the 3D colour and effect (ADR-0167 decision 12), an icon -- kept as it is when the motion changes.
16
6
  */
17
7
  import '@material/web/icon/icon.js';
18
8
  import './ox-input-animation-config.js';
@@ -1,25 +1,15 @@
1
1
  /**
2
- * Animation Map 에디터
2
+ * An animation map's 2D motions, one row per code: the code and its motion (ox-input-animation-config).
3
3
  *
4
- * key-value 맵에서 value가 애니메이션 설정 객체인 경우를 위한 편집기.
5
- * 각 행은 key 입력 + ox-input-animation-config 폼으로 구성된다.
6
- *
7
- * value 구조 (실제 데이터):
8
- * {
9
- * "CRITICAL": "{ animation: { type: 'vibration', duration: 1000, ... }, icon: { ... } }",
10
- * "WARN": "{ animation: { type: 'vibration', ... } }",
11
- * "default": ""
12
- * }
13
- *
14
- * 각 entry value는 JSON5 string이며, 내부에 animation 키가 있으면 그 속성만 폼에 표시.
15
- * 저장 시 animation 외 다른 키(icon 등)는 보존됨.
4
+ * The value is read by animationEntriesOf: each entry a record, its 2D motion under `animation` and what else it holds
5
+ * beside it -- the 3D colour and effect (ADR-0167 decision 12), an icon -- kept as it is when the motion changes.
16
6
  */
17
7
  import { __decorate, __metadata } from "tslib";
18
8
  import '@material/web/icon/icon.js';
19
9
  import './ox-input-animation-config.js';
20
- import JSON5 from 'json5';
21
10
  import { css, html, LitElement } from 'lit';
22
11
  import { customElement, property } from 'lit/decorators.js';
12
+ import { animationEntriesOf } from '@fmsim/machine';
23
13
  import { keyed } from 'lit/directives/keyed.js';
24
14
  let OxInputAnimationMap = class OxInputAnimationMap extends LitElement {
25
15
  constructor() {
@@ -109,8 +99,7 @@ let OxInputAnimationMap = class OxInputAnimationMap extends LitElement {
109
99
  if (!entry)
110
100
  return;
111
101
  // 항상 { animation: config } 구조로 저장 (원본에 다른 키가 있으면 보존)
112
- const newEntryValue = entry.isNested
113
- ? Object.assign(Object.assign({}, entry.full), { animation: e.detail }) : { animation: e.detail };
102
+ const newEntryValue = Object.assign(Object.assign({}, entry.full), { animation: e.detail });
114
103
  this.value = Object.assign(Object.assign({}, this.value), { [entry.key]: newEntryValue });
115
104
  this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }));
116
105
  }
@@ -132,15 +121,14 @@ let OxInputAnimationMap = class OxInputAnimationMap extends LitElement {
132
121
  const records = this.renderRoot.querySelectorAll('[data-record]');
133
122
  const newMap = {};
134
123
  records.forEach((record, idx) => {
135
- var _a;
124
+ var _a, _b;
136
125
  const key = (_a = record.querySelector('[data-key]')) === null || _a === void 0 ? void 0 : _a.value;
137
126
  if (!key)
138
127
  return;
139
128
  const configEl = record.querySelector('ox-input-animation-config');
140
129
  const animConfig = (configEl === null || configEl === void 0 ? void 0 : configEl.value) || {};
141
130
  const entry = this._parsed[idx];
142
- newMap[key] = (entry === null || entry === void 0 ? void 0 : entry.isNested)
143
- ? Object.assign(Object.assign({}, entry.full), { animation: animConfig }) : { animation: animConfig };
131
+ newMap[key] = Object.assign(Object.assign({}, ((_b = entry === null || entry === void 0 ? void 0 : entry.full) !== null && _b !== void 0 ? _b : {})), { animation: animConfig });
144
132
  });
145
133
  if (((_a = this.value) === null || _a === void 0 ? void 0 : _a.default) !== undefined) {
146
134
  newMap.default = this.value.default;
@@ -170,51 +158,14 @@ let OxInputAnimationMap = class OxInputAnimationMap extends LitElement {
170
158
  this.value = newMap;
171
159
  this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }));
172
160
  }
161
+ /* Entries are records, read by the one rule (animationEntriesOf): the 2D motion under `animation`, the rest kept */
173
162
  _parseEntries(map) {
174
- const arr = [];
175
- for (const key in map) {
176
- if (key === 'default')
177
- continue;
178
- let val = map[key];
179
- // JSON5 string → object 파싱
180
- if (typeof val === 'string') {
181
- try {
182
- val = JSON5.parse(val);
183
- }
184
- catch (e) {
185
- val = {};
186
- }
187
- }
188
- if (typeof val !== 'object' || val === null) {
189
- val = {};
190
- }
191
- // 중첩 구조 { animation: {...}, icon: {...} } 처리
192
- const isNested = 'animation' in val;
193
- const animation = isNested ? (val.animation || {}) : val;
194
- arr.push({ key, animation, full: val, isNested });
195
- }
196
- return arr;
163
+ return Object.entries(map)
164
+ .filter(([key]) => key !== 'default')
165
+ .map(([key, full]) => ({ key, animation: (full === null || full === void 0 ? void 0 : full.animation) || {}, full }));
197
166
  }
198
- // 입력 value의 모든 string entry를 object로 정규화 (JSON/JSON5 호환)
199
167
  _normalize(map) {
200
- if (!map || typeof map !== 'object')
201
- return {};
202
- const result = {};
203
- for (const key in map) {
204
- const val = map[key];
205
- if (typeof val === 'string' && val.trim()) {
206
- try {
207
- result[key] = JSON5.parse(val);
208
- }
209
- catch (_a) {
210
- result[key] = val;
211
- }
212
- }
213
- else {
214
- result[key] = val;
215
- }
216
- }
217
- return result;
168
+ return animationEntriesOf(map);
218
169
  }
219
170
  };
220
171
  OxInputAnimationMap.styles = css `
@@ -1 +1 @@
1
- {"version":3,"file":"ox-input-animation-map.js","sourceRoot":"","sources":["../../client/data-grist/ox-input-animation-map.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Animation Map 에디터\n *\n * key-value 맵에서 value가 애니메이션 설정 객체인 경우를 위한 편집기.\n * 각 행은 key 입력 + ox-input-animation-config 폼으로 구성된다.\n *\n * value 구조 (실제 데이터):\n * {\n * \"CRITICAL\": \"{ animation: { type: 'vibration', duration: 1000, ... }, icon: { ... } }\",\n * \"WARN\": \"{ animation: { type: 'vibration', ... } }\",\n * \"default\": \"\"\n * }\n *\n * 각 entry value는 JSON5 string이며, 내부에 animation 키가 있으면 그 속성만 폼에 표시.\n * 저장 시 animation 외 다른 키(icon 등)는 보존됨.\n */\n\nimport '@material/web/icon/icon.js'\nimport './ox-input-animation-config.js'\n\nimport JSON5 from 'json5'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { keyed } from 'lit/directives/keyed.js'\n\ntype AnimationConfig = {\n type?: string\n delay?: number\n duration?: number\n interval?: number\n theta?: number\n scale?: number\n x?: number\n y?: number\n startAlpha?: number\n endAlpha?: number\n repeat?: boolean\n autoplay?: boolean\n delta?: string\n ease?: string\n}\n\ntype AnimationMap = { default?: any; [key: string]: AnimationConfig | any }\n\n// 파싱된 entry: 원본 전체 객체와 animation config를 분리 보관\ntype ParsedEntry = {\n key: string\n animation: AnimationConfig\n full: any // 원본 파싱 결과 전체 (icon 등 보존용)\n isNested: boolean // animation 키가 있는 중첩 구조 여부\n}\n\n@customElement('ox-input-animation-map')\nexport class OxInputAnimationMap extends LitElement {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: var(--fontsize-default, 12px);\n\n --md-icon-size: 16px;\n }\n\n .entry {\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n gap: 6px;\n padding: 6px 8px;\n border: 1px solid var(--md-sys-color-outline-variant, #ddd);\n border-radius: 4px;\n background: var(--md-sys-color-surface-container, #f8f8f8);\n }\n\n .entry-key {\n flex: 0 0 80px;\n padding-top: 3px;\n }\n\n .entry-key input {\n width: 100%;\n box-sizing: border-box;\n border: 0;\n border-bottom: 1px solid transparent;\n padding: 3px 4px;\n font: var(--input-font, 12px sans-serif);\n font-weight: 600;\n color: var(--md-sys-color-on-primary-container, #333);\n background: transparent;\n }\n\n .entry-key input:focus {\n outline: none;\n border-bottom-color: var(--md-sys-color-primary, #1976d2);\n }\n\n .entry-value {\n flex: 1;\n min-width: 0;\n }\n\n .entry-action {\n flex: 0 0 auto;\n padding-top: 2px;\n }\n\n button {\n border: none;\n background: transparent;\n cursor: pointer;\n padding: 2px;\n color: var(--md-sys-color-on-surface-variant, #666);\n line-height: 0;\n }\n\n button:hover {\n color: var(--md-sys-color-primary, #1976d2);\n }\n\n .add-entry {\n opacity: 0.6;\n }\n\n .add-entry:focus-within {\n opacity: 1;\n }\n `\n\n @property({ type: Object })\n set value(v: AnimationMap) {\n const old = this._value\n this._value = this._normalize(v)\n this.requestUpdate('value', old)\n }\n get value(): AnimationMap { return this._value }\n private _value: AnimationMap = {}\n\n // 파싱된 entry 캐시 (원본 구조 보존용)\n private _parsed: ParsedEntry[] = []\n\n /**\n * 확인(저장) 가능 여부 검증 — key 없이 정의된 애니메이션이 하나라도 있으면 true.\n * 1) 기존 entry 행에 key(공백 포함)가 비어 있는 경우\n * 2) 새 항목 행에서 애니메이션 설정은 채웠는데 key 를 입력하지 않은 경우\n * key 는 애니메이션 맵의 식별자라, 비면 저장 시 항목이 유실/중복되므로 확인을 막는다.\n */\n hasEmptyKey(): boolean {\n const rows = this.renderRoot.querySelectorAll('[data-record]')\n for (const row of rows) {\n const key = (row.querySelector('[data-key]') as HTMLInputElement)?.value?.trim()\n if (!key) return true\n }\n\n const newRow = this.renderRoot.querySelector('[data-record-new]')\n if (newRow) {\n const key = (newRow.querySelector('[data-key]') as HTMLInputElement)?.value?.trim()\n const cfg = (newRow.querySelector('ox-input-animation-config') as any)?.value\n const hasCfg = cfg && typeof cfg === 'object' && Object.keys(cfg).length > 0\n if (!key && hasCfg) return true\n }\n return false\n }\n\n render() {\n this._parsed = this._parseEntries(this.value)\n\n return html`\n ${this._parsed.map(\n (item, idx) => html`\n <div class=\"entry\" data-record data-index=${idx}>\n <div class=\"entry-key\">\n <input type=\"text\" data-key .value=${item.key} placeholder=\"key\" />\n </div>\n <div class=\"entry-value\">\n <ox-input-animation-config\n .value=${item.animation}\n @change=${(e: CustomEvent) => this._onConfigChange(e, idx)}\n ></ox-input-animation-config>\n </div>\n <div class=\"entry-action\">\n <button @click=${() => this._delete(idx)} tabindex=\"-1\">\n <md-icon>remove</md-icon>\n </button>\n </div>\n </div>\n `\n )}\n ${keyed(\n Date.now(),\n html`\n <div class=\"entry add-entry\" data-record-new>\n <div class=\"entry-key\">\n <input type=\"text\" data-key value=\"\" placeholder=\"new key\" @change=${this._onNewKeyChange} />\n </div>\n <div class=\"entry-value\">\n <ox-input-animation-config .value=${{}}></ox-input-animation-config>\n </div>\n <div class=\"entry-action\">\n <button @click=${() => this._add()} tabindex=\"-1\">\n <md-icon>add</md-icon>\n </button>\n </div>\n </div>\n `\n )}\n `\n }\n\n private _onConfigChange(e: CustomEvent, idx: number) {\n e.stopPropagation()\n\n const entry = this._parsed[idx]\n if (!entry) return\n\n // 항상 { animation: config } 구조로 저장 (원본에 다른 키가 있으면 보존)\n const newEntryValue = entry.isNested\n ? { ...entry.full, animation: e.detail }\n : { animation: e.detail }\n\n this.value = { ...this.value, [entry.key]: newEntryValue }\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))\n }\n\n private _onNewKeyChange = (e: Event) => {\n e.stopPropagation()\n const input = e.target as HTMLInputElement\n if (input.value) {\n this._add()\n }\n }\n\n firstUpdated() {\n this.renderRoot.addEventListener('change', (e: Event) => {\n const target = e.target as HTMLElement\n const entry = target.closest('[data-record]')\n if (!entry) return\n\n // key 변경 시 rebuild\n if (target.hasAttribute('data-key')) {\n e.stopPropagation()\n this._build()\n }\n })\n }\n\n private _build() {\n const records = this.renderRoot.querySelectorAll('[data-record]')\n const newMap: AnimationMap = {}\n\n records.forEach((record, idx) => {\n const key = (record.querySelector('[data-key]') as HTMLInputElement)?.value\n if (!key) return\n\n const configEl = record.querySelector('ox-input-animation-config') as any\n const animConfig = configEl?.value || {}\n const entry = this._parsed[idx]\n\n newMap[key] = entry?.isNested\n ? { ...entry.full, animation: animConfig }\n : { animation: animConfig }\n })\n\n if (this.value?.default !== undefined) {\n newMap.default = this.value.default\n }\n\n this.value = newMap\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))\n }\n\n private _add() {\n const newEntry = this.renderRoot.querySelector('[data-record-new]')\n if (!newEntry) return\n\n const key = (newEntry.querySelector('[data-key]') as HTMLInputElement)?.value\n if (!key) return\n\n const configEl = newEntry.querySelector('ox-input-animation-config') as any\n const config = configEl?.value || {}\n\n this.value = { ...this.value, [key]: { animation: config } }\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))\n }\n\n private _delete(idx: number) {\n const entry = this._parsed[idx]\n if (!entry) return\n\n const newMap = { ...this.value }\n delete newMap[entry.key]\n this.value = newMap\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))\n }\n\n private _parseEntries(map: AnimationMap): ParsedEntry[] {\n const arr: ParsedEntry[] = []\n for (const key in map) {\n if (key === 'default') continue\n let val = map[key]\n\n // JSON5 string → object 파싱\n if (typeof val === 'string') {\n try { val = JSON5.parse(val) } catch (e) { val = {} }\n }\n\n if (typeof val !== 'object' || val === null) {\n val = {}\n }\n\n // 중첩 구조 { animation: {...}, icon: {...} } 처리\n const isNested = 'animation' in val\n const animation = isNested ? (val.animation || {}) : val\n\n arr.push({ key, animation, full: val, isNested })\n }\n return arr\n }\n\n // 입력 value의 모든 string entry를 object로 정규화 (JSON/JSON5 호환)\n private _normalize(map: AnimationMap): AnimationMap {\n if (!map || typeof map !== 'object') return {}\n const result: AnimationMap = {}\n for (const key in map) {\n const val = map[key]\n if (typeof val === 'string' && val.trim()) {\n try { result[key] = JSON5.parse(val) } catch { result[key] = val }\n } else {\n result[key] = val\n }\n }\n return result\n }\n}\n"]}
1
+ {"version":3,"file":"ox-input-animation-map.js","sourceRoot":"","sources":["../../client/data-grist/ox-input-animation-map.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;;AAEH,OAAO,4BAA4B,CAAA;AACnC,OAAO,gCAAgC,CAAA;AAEvC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AA6BxC,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,UAAU;IAA5C;;QAmFG,WAAM,GAAiB,EAAE,CAAA;QAEjC,2BAA2B;QACnB,YAAO,GAAkB,EAAE,CAAA;QAmF3B,oBAAe,GAAG,CAAC,CAAQ,EAAE,EAAE;YACrC,CAAC,CAAC,eAAe,EAAE,CAAA;YACnB,MAAM,KAAK,GAAG,CAAC,CAAC,MAA0B,CAAA;YAC1C,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBAChB,IAAI,CAAC,IAAI,EAAE,CAAA;YACb,CAAC;QACH,CAAC,CAAA;IAyEH,CAAC;IA5KC,IACI,KAAK,CAAC,CAAe;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAA;QACvB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;QAChC,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;IAClC,CAAC;IACD,IAAI,KAAK,KAAmB,OAAO,IAAI,CAAC,MAAM,CAAA,CAAC,CAAC;IAMhD;;;;;OAKG;IACH,WAAW;;QACT,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAA;QAC9D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,GAAG,GAAG,MAAA,MAAC,GAAG,CAAC,aAAa,CAAC,YAAY,CAAsB,0CAAE,KAAK,0CAAE,IAAI,EAAE,CAAA;YAChF,IAAI,CAAC,GAAG;gBAAE,OAAO,IAAI,CAAA;QACvB,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,mBAAmB,CAAC,CAAA;QACjE,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,GAAG,GAAG,MAAA,MAAC,MAAM,CAAC,aAAa,CAAC,YAAY,CAAsB,0CAAE,KAAK,0CAAE,IAAI,EAAE,CAAA;YACnF,MAAM,GAAG,GAAG,MAAC,MAAM,CAAC,aAAa,CAAC,2BAA2B,CAAS,0CAAE,KAAK,CAAA;YAC7E,MAAM,MAAM,GAAG,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAA;YAC5E,IAAI,CAAC,GAAG,IAAI,MAAM;gBAAE,OAAO,IAAI,CAAA;QACjC,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM;QACJ,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAE7C,OAAO,IAAI,CAAA;QACP,IAAI,CAAC,OAAO,CAAC,GAAG,CAChB,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAA;sDAC2B,GAAG;;mDAEN,IAAI,CAAC,GAAG;;;;yBAIlC,IAAI,CAAC,SAAS;0BACb,CAAC,CAAc,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,EAAE,GAAG,CAAC;;;;+BAI3C,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;;;;;SAK7C,CACF;QACC,KAAK,CACL,IAAI,CAAC,GAAG,EAAE,EACV,IAAI,CAAA;;;mFAGuE,IAAI,CAAC,eAAe;;;kDAGrD,EAAE;;;+BAGrB,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE;;;;;SAKvC,CACF;KACF,CAAA;IACH,CAAC;IAEO,eAAe,CAAC,CAAc,EAAE,GAAW;QACjD,CAAC,CAAC,eAAe,EAAE,CAAA;QAEnB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAC/B,IAAI,CAAC,KAAK;YAAE,OAAM;QAElB,qDAAqD;QACrD,MAAM,aAAa,mCAAQ,KAAK,CAAC,IAAI,KAAE,SAAS,EAAE,CAAC,CAAC,MAAM,GAAE,CAAA;QAE5D,IAAI,CAAC,KAAK,mCAAQ,IAAI,CAAC,KAAK,KAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,aAAa,GAAE,CAAA;QAC1D,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IACtG,CAAC;IAUD,YAAY;QACV,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,CAAQ,EAAE,EAAE;YACtD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAqB,CAAA;YACtC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA;YAC7C,IAAI,CAAC,KAAK;gBAAE,OAAM;YAElB,mBAAmB;YACnB,IAAI,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;gBACpC,CAAC,CAAC,eAAe,EAAE,CAAA;gBACnB,IAAI,CAAC,MAAM,EAAE,CAAA;YACf,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;IAEO,MAAM;;QACZ,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAA;QACjE,MAAM,MAAM,GAAiB,EAAE,CAAA;QAE/B,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;;YAC9B,MAAM,GAAG,GAAG,MAAC,MAAM,CAAC,aAAa,CAAC,YAAY,CAAsB,0CAAE,KAAK,CAAA;YAC3E,IAAI,CAAC,GAAG;gBAAE,OAAM;YAEhB,MAAM,QAAQ,GAAG,MAAM,CAAC,aAAa,CAAC,2BAA2B,CAAQ,CAAA;YACzE,MAAM,UAAU,GAAG,CAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,KAAK,KAAI,EAAE,CAAA;YACxC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAE/B,MAAM,CAAC,GAAG,CAAC,mCAAQ,CAAC,MAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,mCAAI,EAAE,CAAC,KAAE,SAAS,EAAE,UAAU,GAAE,CAAA;QACjE,CAAC,CAAC,CAAA;QAEF,IAAI,CAAA,MAAA,IAAI,CAAC,KAAK,0CAAE,OAAO,MAAK,SAAS,EAAE,CAAC;YACtC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAA;QACrC,CAAC;QAED,IAAI,CAAC,KAAK,GAAG,MAAM,CAAA;QACnB,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IACtG,CAAC;IAEO,IAAI;;QACV,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,mBAAmB,CAAC,CAAA;QACnE,IAAI,CAAC,QAAQ;YAAE,OAAM;QAErB,MAAM,GAAG,GAAG,MAAC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAsB,0CAAE,KAAK,CAAA;QAC7E,IAAI,CAAC,GAAG;YAAE,OAAM;QAEhB,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,2BAA2B,CAAQ,CAAA;QAC3E,MAAM,MAAM,GAAG,CAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,KAAK,KAAI,EAAE,CAAA;QAEpC,IAAI,CAAC,KAAK,mCAAQ,IAAI,CAAC,KAAK,KAAE,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,GAAE,CAAA;QAC5D,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IACtG,CAAC;IAEO,OAAO,CAAC,GAAW;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAC/B,IAAI,CAAC,KAAK;YAAE,OAAM;QAElB,MAAM,MAAM,qBAAQ,IAAI,CAAC,KAAK,CAAE,CAAA;QAChC,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACxB,IAAI,CAAC,KAAK,GAAG,MAAM,CAAA;QACnB,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IACtG,CAAC;IAED,oHAAoH;IAC5G,aAAa,CAAC,GAAiB;QACrC,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC;aACvB,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,SAAS,CAAC;aACpC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,SAA6B,KAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACjG,CAAC;IAEO,UAAU,CAAC,GAAiB;QAClC,OAAO,kBAAkB,CAAC,GAAG,CAAC,CAAA;IAChC,CAAC;;AAtPM,0BAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyElB,AAzEY,CAyEZ;AAED;IAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;;gDAK1B;AAjFU,mBAAmB;IAD/B,aAAa,CAAC,wBAAwB,CAAC;GAC3B,mBAAmB,CAwP/B","sourcesContent":["/**\n * An animation map's 2D motions, one row per code: the code and its motion (ox-input-animation-config).\n *\n * The value is read by animationEntriesOf: each entry a record, its 2D motion under `animation` and what else it holds\n * beside it -- the 3D colour and effect (ADR-0167 decision 12), an icon -- kept as it is when the motion changes.\n */\n\nimport '@material/web/icon/icon.js'\nimport './ox-input-animation-config.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { animationEntriesOf } from '@fmsim/machine'\nimport { keyed } from 'lit/directives/keyed.js'\n\ntype AnimationConfig = {\n type?: string\n delay?: number\n duration?: number\n interval?: number\n theta?: number\n scale?: number\n x?: number\n y?: number\n startAlpha?: number\n endAlpha?: number\n repeat?: boolean\n autoplay?: boolean\n delta?: string\n ease?: string\n}\n\ntype AnimationMap = { default?: any; [key: string]: AnimationConfig | any }\n\n// 파싱된 entry: 원본 전체 객체와 animation config를 분리 보관\ntype ParsedEntry = {\n key: string\n animation: AnimationConfig\n full: any // 원본 파싱 결과 전체 (icon 등 보존용)\n}\n\n@customElement('ox-input-animation-map')\nexport class OxInputAnimationMap extends LitElement {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: var(--fontsize-default, 12px);\n\n --md-icon-size: 16px;\n }\n\n .entry {\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n gap: 6px;\n padding: 6px 8px;\n border: 1px solid var(--md-sys-color-outline-variant, #ddd);\n border-radius: 4px;\n background: var(--md-sys-color-surface-container, #f8f8f8);\n }\n\n .entry-key {\n flex: 0 0 80px;\n padding-top: 3px;\n }\n\n .entry-key input {\n width: 100%;\n box-sizing: border-box;\n border: 0;\n border-bottom: 1px solid transparent;\n padding: 3px 4px;\n font: var(--input-font, 12px sans-serif);\n font-weight: 600;\n color: var(--md-sys-color-on-primary-container, #333);\n background: transparent;\n }\n\n .entry-key input:focus {\n outline: none;\n border-bottom-color: var(--md-sys-color-primary, #1976d2);\n }\n\n .entry-value {\n flex: 1;\n min-width: 0;\n }\n\n .entry-action {\n flex: 0 0 auto;\n padding-top: 2px;\n }\n\n button {\n border: none;\n background: transparent;\n cursor: pointer;\n padding: 2px;\n color: var(--md-sys-color-on-surface-variant, #666);\n line-height: 0;\n }\n\n button:hover {\n color: var(--md-sys-color-primary, #1976d2);\n }\n\n .add-entry {\n opacity: 0.6;\n }\n\n .add-entry:focus-within {\n opacity: 1;\n }\n `\n\n @property({ type: Object })\n set value(v: AnimationMap) {\n const old = this._value\n this._value = this._normalize(v)\n this.requestUpdate('value', old)\n }\n get value(): AnimationMap { return this._value }\n private _value: AnimationMap = {}\n\n // 파싱된 entry 캐시 (원본 구조 보존용)\n private _parsed: ParsedEntry[] = []\n\n /**\n * 확인(저장) 가능 여부 검증 — key 없이 정의된 애니메이션이 하나라도 있으면 true.\n * 1) 기존 entry 행에 key(공백 포함)가 비어 있는 경우\n * 2) 새 항목 행에서 애니메이션 설정은 채웠는데 key 를 입력하지 않은 경우\n * key 는 애니메이션 맵의 식별자라, 비면 저장 시 항목이 유실/중복되므로 확인을 막는다.\n */\n hasEmptyKey(): boolean {\n const rows = this.renderRoot.querySelectorAll('[data-record]')\n for (const row of rows) {\n const key = (row.querySelector('[data-key]') as HTMLInputElement)?.value?.trim()\n if (!key) return true\n }\n\n const newRow = this.renderRoot.querySelector('[data-record-new]')\n if (newRow) {\n const key = (newRow.querySelector('[data-key]') as HTMLInputElement)?.value?.trim()\n const cfg = (newRow.querySelector('ox-input-animation-config') as any)?.value\n const hasCfg = cfg && typeof cfg === 'object' && Object.keys(cfg).length > 0\n if (!key && hasCfg) return true\n }\n return false\n }\n\n render() {\n this._parsed = this._parseEntries(this.value)\n\n return html`\n ${this._parsed.map(\n (item, idx) => html`\n <div class=\"entry\" data-record data-index=${idx}>\n <div class=\"entry-key\">\n <input type=\"text\" data-key .value=${item.key} placeholder=\"key\" />\n </div>\n <div class=\"entry-value\">\n <ox-input-animation-config\n .value=${item.animation}\n @change=${(e: CustomEvent) => this._onConfigChange(e, idx)}\n ></ox-input-animation-config>\n </div>\n <div class=\"entry-action\">\n <button @click=${() => this._delete(idx)} tabindex=\"-1\">\n <md-icon>remove</md-icon>\n </button>\n </div>\n </div>\n `\n )}\n ${keyed(\n Date.now(),\n html`\n <div class=\"entry add-entry\" data-record-new>\n <div class=\"entry-key\">\n <input type=\"text\" data-key value=\"\" placeholder=\"new key\" @change=${this._onNewKeyChange} />\n </div>\n <div class=\"entry-value\">\n <ox-input-animation-config .value=${{}}></ox-input-animation-config>\n </div>\n <div class=\"entry-action\">\n <button @click=${() => this._add()} tabindex=\"-1\">\n <md-icon>add</md-icon>\n </button>\n </div>\n </div>\n `\n )}\n `\n }\n\n private _onConfigChange(e: CustomEvent, idx: number) {\n e.stopPropagation()\n\n const entry = this._parsed[idx]\n if (!entry) return\n\n // 항상 { animation: config } 구조로 저장 (원본에 다른 키가 있으면 보존)\n const newEntryValue = { ...entry.full, animation: e.detail }\n\n this.value = { ...this.value, [entry.key]: newEntryValue }\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))\n }\n\n private _onNewKeyChange = (e: Event) => {\n e.stopPropagation()\n const input = e.target as HTMLInputElement\n if (input.value) {\n this._add()\n }\n }\n\n firstUpdated() {\n this.renderRoot.addEventListener('change', (e: Event) => {\n const target = e.target as HTMLElement\n const entry = target.closest('[data-record]')\n if (!entry) return\n\n // key 변경 시 rebuild\n if (target.hasAttribute('data-key')) {\n e.stopPropagation()\n this._build()\n }\n })\n }\n\n private _build() {\n const records = this.renderRoot.querySelectorAll('[data-record]')\n const newMap: AnimationMap = {}\n\n records.forEach((record, idx) => {\n const key = (record.querySelector('[data-key]') as HTMLInputElement)?.value\n if (!key) return\n\n const configEl = record.querySelector('ox-input-animation-config') as any\n const animConfig = configEl?.value || {}\n const entry = this._parsed[idx]\n\n newMap[key] = { ...(entry?.full ?? {}), animation: animConfig }\n })\n\n if (this.value?.default !== undefined) {\n newMap.default = this.value.default\n }\n\n this.value = newMap\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))\n }\n\n private _add() {\n const newEntry = this.renderRoot.querySelector('[data-record-new]')\n if (!newEntry) return\n\n const key = (newEntry.querySelector('[data-key]') as HTMLInputElement)?.value\n if (!key) return\n\n const configEl = newEntry.querySelector('ox-input-animation-config') as any\n const config = configEl?.value || {}\n\n this.value = { ...this.value, [key]: { animation: config } }\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))\n }\n\n private _delete(idx: number) {\n const entry = this._parsed[idx]\n if (!entry) return\n\n const newMap = { ...this.value }\n delete newMap[entry.key]\n this.value = newMap\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))\n }\n\n /* Entries are records, read by the one rule (animationEntriesOf): the 2D motion under `animation`, the rest kept */\n private _parseEntries(map: AnimationMap): ParsedEntry[] {\n return Object.entries(map)\n .filter(([key]) => key !== 'default')\n .map(([key, full]) => ({ key, animation: (full?.animation as AnimationConfig) || {}, full }))\n }\n\n private _normalize(map: AnimationMap): AnimationMap {\n return animationEntriesOf(map)\n }\n}\n"]}
@@ -157,7 +157,7 @@ let BoardModellerPage = class BoardModellerPage extends connect(store)(PageView)
157
157
  this.preparing = true;
158
158
  var { id, name, description, groupId } = this.board;
159
159
  var model = this.scene.model;
160
- var thumbnail = captureBoardThumbnail(this.scene);
160
+ var thumbnail = await captureBoardThumbnail(this.scene);
161
161
  await updateBoard({ id, name, description, model, groupId, thumbnail });
162
162
  document.dispatchEvent(new CustomEvent('notify', {
163
163
  detail: {
@@ -1 +1 @@
1
- {"version":3,"file":"board-modeller-page.js","sourceRoot":"","sources":["../../client/pages/board-modeller-page.ts"],"names":[],"mappings":"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","sourcesContent":["import './things-scene-components.import'\nimport '@fmsim/board/ox-board-modeller.js'\n\nimport { css, html } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\nimport { BoardModelCache, captureBoardThumbnail } from '@fmsim/board'\nimport { BoardModeller } from '@fmsim/board/ox-board-modeller.js'\nimport { itemTypesFromThemes, LoadTracker } from '@hatiolab/things-scene'\nimport { OxPropertyEditor } from '@operato/property-editor'\nimport { connect, PageView, store } from '@operato/shell'\nimport { fetchBoardById, updateBoard, CARRIER_TYPES_THEME } from '@fmsim/api'\n\nimport { provider } from '../board-provider'\nimport components from './things-scene-components-with-tools.import'\n\nconst NOOP = () => {}\n\n@customElement('board-modeller-page')\nexport class BoardModellerPage extends connect(store)(PageView) {\n constructor() {\n super()\n\n components.forEach(({ templates = [], groups = [] }) => {\n groups.forEach(group => BoardModeller.registerGroup(group))\n BoardModeller.registerTemplate(templates)\n })\n\n /* 컴포넌트에서 정의된 에디터들을 MODELLER_EDITORS에 등록 */\n var addedEditors = {}\n for (let component in components) {\n let { editors } = components[component]\n\n editors &&\n editors.forEach(editor => {\n let { type, element } = editor\n\n addedEditors[type] = element\n })\n }\n\n OxPropertyEditor.register(addedEditors)\n\n this.boardName = ''\n this.model = null\n this.themes = null\n this.baseUrl = ''\n this.selected = []\n this.mode = 1\n this.hideProperty = false\n this.overlay = null\n this.scene = null\n this.fonts = []\n this.board = null\n this.componentGroupList = BoardModeller.groups\n }\n\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n\n overflow: hidden;\n position: relative;\n }\n\n board-modeller {\n flex: 1;\n }\n\n oops-note {\n display: block;\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n }\n `\n ]\n\n @property({ type: String }) boardId?: string | null\n @property({ type: String }) boardName?: string\n @property({ type: Object }) model: any\n @property({ type: Object }) themes: any\n /** The domain's carrier type table, its active carrier-types theme (ADR-0155): the scene's item types */\n @property({ type: Object }) itemTypes?: unknown\n @property({ type: String }) baseUrl?: string\n @property({ type: Array }) selected: any\n @property({ type: Number }) mode?: number\n @property({ type: Boolean }) hideProperty?: boolean\n @property({ type: String }) overlay?: string | null\n @property({ type: Object }) scene: any\n @property({ type: Array }) componentGroupList?: any\n @property({ type: Array }) fonts: any\n @property({ type: Array }) propertyEditor: any\n @property({ type: Boolean }) preparing?: boolean\n\n board: any\n private _loadTracker?: LoadTracker\n @query('ox-board-modeller') modeller?: BoardModeller\n\n get context() {\n return {\n title: this.board ? this.boardName : this.preparing ? 'Fetching board...' : 'Board Not Found',\n widebleed: true\n }\n }\n\n get oopsNote() {\n return {\n icon: 'color_lens',\n title: 'EMPTY BOARD',\n description: 'There are no board to be designed'\n }\n }\n\n async refresh() {\n if (!this.boardId) {\n this.board = null\n this.model = null\n\n return\n }\n try {\n this.board = null\n this.model = null\n this._loadTracker = new LoadTracker()\n this._loadTracker.setPhase('fetch')\n this.preparing = true\n this.updateContext()\n\n var { board, themes } = (await fetchBoardById(this.boardId)) || {}\n\n if (!board) {\n this.board = null\n throw 'board not found'\n }\n\n this.board = {\n ...board,\n model: {\n ...JSON.parse(board.model)\n }\n }\n\n this.boardName = this.board.name\n this.model = {\n ...this.board.model\n }\n this.itemTypes = itemTypesFromThemes(themes?.items, CARRIER_TYPES_THEME)\n this.themes = themes?.items.reduce((style, theme) => {\n style[theme.name] = theme.value\n return style\n }, {})\n } catch (ex) {\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: {\n level: 'error',\n message: ex,\n ex\n }\n })\n )\n } finally {\n this.preparing = false\n this.updateContext()\n }\n }\n\n updated(changes) {\n if (changes.has('boardId')) {\n this.refresh()\n }\n }\n\n pageUpdated(changes, lifecycle) {\n if (this.active) {\n this.boardId = lifecycle.resourceId\n } else {\n this.boardId = null\n this.model = null\n this.modeller?.close()\n }\n }\n\n stateChanged(state) {\n this.baseUrl = state.route.rootPath\n this.fonts = state.font\n }\n\n render() {\n var oops = !this.preparing && !this.model && this.oopsNote\n\n return oops\n ? html` <oops-note icon=${oops.icon} title=${oops.title} description=${oops.description}></oops-note> `\n : html`\n <ox-board-modeller\n .mode=${this.mode}\n .loadTracker=${this._loadTracker}\n @mode-changed=${e => {\n this.mode = e.detail.value\n }}\n .model=${this.model}\n @model-changed=${e => {\n this.model = e.detail.value\n }}\n .scene=${this.scene}\n @scene-changed=${e => {\n this.scene = e.detail.value\n }}\n .selected=${this.selected}\n @selected-changed=${e => {\n this.selected = e.detail.value\n }}\n .baseUrl=${this.baseUrl}\n .provider=${provider}\n @save-model=${e => this.saveBoard()}\n .componentGroupList=${this.componentGroupList}\n .fonts=${this.fonts}\n .themes=${this.themes}\n .itemTypes=${this.itemTypes}\n .hideProperty=${this.hideProperty}\n >\n </ox-board-modeller>\n `\n }\n\n async updateBoard() {\n try {\n this.preparing = true\n\n var { id, name, description, groupId } = this.board\n var model = this.scene.model\n var thumbnail = captureBoardThumbnail(this.scene)\n\n await updateBoard({ id, name, description, model, groupId, thumbnail })\n\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: {\n level: 'info',\n message: 'saved'\n }\n })\n )\n } catch (ex) {\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: {\n level: 'error',\n message: ex,\n ex: ex\n }\n })\n )\n } finally {\n this.preparing = false\n }\n\n this.updateContext()\n }\n\n async saveBoard() {\n await this.updateBoard()\n // 저장 후 캐시 무효화 — viewer에서 다음 로드 시 최신 모델 fetch\n await BoardModelCache.remove(this.board.id)\n }\n}\n"]}
1
+ {"version":3,"file":"board-modeller-page.js","sourceRoot":"","sources":["../../client/pages/board-modeller-page.ts"],"names":[],"mappings":";AAAA,OAAO,kCAAkC,CAAA;AACzC,OAAO,mCAAmC,CAAA;AAE1C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAA;AACjE,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAE7E,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,UAAU,MAAM,6CAA6C,CAAA;AAEpE,MAAM,IAAI,GAAG,GAAG,EAAE,GAAE,CAAC,CAAA;AAGd,IAAM,iBAAiB,GAAvB,MAAM,iBAAkB,SAAQ,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC;IAC7D;QACE,KAAK,EAAE,CAAA;QAEP,UAAU,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,GAAG,EAAE,EAAE,MAAM,GAAG,EAAE,EAAE,EAAE,EAAE;YACrD,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAA;YAC3D,aAAa,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAA;QAC3C,CAAC,CAAC,CAAA;QAEF,2CAA2C;QAC3C,IAAI,YAAY,GAAG,EAAE,CAAA;QACrB,KAAK,IAAI,SAAS,IAAI,UAAU,EAAE,CAAC;YACjC,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC,SAAS,CAAC,CAAA;YAEvC,OAAO;gBACL,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;oBACvB,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,CAAA;oBAE9B,YAAY,CAAC,IAAI,CAAC,GAAG,OAAO,CAAA;gBAC9B,CAAC,CAAC,CAAA;QACN,CAAC;QAED,gBAAgB,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAA;QAEvC,IAAI,CAAC,SAAS,GAAG,EAAE,CAAA;QACnB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACjB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;QAClB,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;QACjB,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAA;QAClB,IAAI,CAAC,IAAI,GAAG,CAAC,CAAA;QACb,IAAI,CAAC,YAAY,GAAG,KAAK,CAAA;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACnB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACjB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;QACf,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACjB,IAAI,CAAC,kBAAkB,GAAG,aAAa,CAAC,MAAM,CAAA;IAChD,CAAC;IA+CD,IAAI,OAAO;QACT,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,iBAAiB;YAC7F,SAAS,EAAE,IAAI;SAChB,CAAA;IACH,CAAC;IAED,IAAI,QAAQ;QACV,OAAO;YACL,IAAI,EAAE,YAAY;YAClB,KAAK,EAAE,aAAa;YACpB,WAAW,EAAE,mCAAmC;SACjD,CAAA;IACH,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;YACjB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;YAEjB,OAAM;QACR,CAAC;QACD,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;YACjB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;YACjB,IAAI,CAAC,YAAY,GAAG,IAAI,WAAW,EAAE,CAAA;YACrC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;YACnC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;YACrB,IAAI,CAAC,aAAa,EAAE,CAAA;YAEpB,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAA;YAElE,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;gBACjB,MAAM,iBAAiB,CAAA;YACzB,CAAC;YAED,IAAI,CAAC,KAAK,mCACL,KAAK,KACR,KAAK,oBACA,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAE7B,CAAA;YAED,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAA;YAChC,IAAI,CAAC,KAAK,qBACL,IAAI,CAAC,KAAK,CAAC,KAAK,CACpB,CAAA;YACD,IAAI,CAAC,SAAS,GAAG,mBAAmB,CAAC,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,KAAK,EAAE,mBAAmB,CAAC,CAAA;YACxE,IAAI,CAAC,MAAM,GAAG,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;gBAClD,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAA;gBAC/B,OAAO,KAAK,CAAA;YACd,CAAC,EAAE,EAAE,CAAC,CAAA;QACR,CAAC;QAAC,OAAO,EAAE,EAAE,CAAC;YACZ,QAAQ,CAAC,aAAa,CACpB,IAAI,WAAW,CAAC,QAAQ,EAAE;gBACxB,MAAM,EAAE;oBACN,KAAK,EAAE,OAAO;oBACd,OAAO,EAAE,EAAE;oBACX,EAAE;iBACH;aACF,CAAC,CACH,CAAA;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,SAAS,GAAG,KAAK,CAAA;YACtB,IAAI,CAAC,aAAa,EAAE,CAAA;QACtB,CAAC;IACH,CAAC;IAED,OAAO,CAAC,OAAO;QACb,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;IACH,CAAC;IAED,WAAW,CAAC,OAAO,EAAE,SAAS;;QAC5B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC,UAAU,CAAA;QACrC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;YACnB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;YACjB,MAAA,IAAI,CAAC,QAAQ,0CAAE,KAAK,EAAE,CAAA;QACxB,CAAC;IACH,CAAC;IAED,YAAY,CAAC,KAAK;QAChB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAA;QACnC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAA;IACzB,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAA;QAE1D,OAAO,IAAI;YACT,CAAC,CAAC,IAAI,CAAA,oBAAoB,IAAI,CAAC,IAAI,UAAU,IAAI,CAAC,KAAK,gBAAgB,IAAI,CAAC,WAAW,gBAAgB;YACvG,CAAC,CAAC,IAAI,CAAA;;oBAEQ,IAAI,CAAC,IAAI;2BACF,IAAI,CAAC,YAAY;4BAChB,CAAC,CAAC,EAAE;gBAClB,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;YAC5B,CAAC;qBACQ,IAAI,CAAC,KAAK;6BACF,CAAC,CAAC,EAAE;gBACnB,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;YAC7B,CAAC;qBACQ,IAAI,CAAC,KAAK;6BACF,CAAC,CAAC,EAAE;gBACnB,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;YAC7B,CAAC;wBACW,IAAI,CAAC,QAAQ;gCACL,CAAC,CAAC,EAAE;gBACtB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;YAChC,CAAC;uBACU,IAAI,CAAC,OAAO;wBACX,QAAQ;0BACN,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE;kCACb,IAAI,CAAC,kBAAkB;qBACpC,IAAI,CAAC,KAAK;sBACT,IAAI,CAAC,MAAM;yBACR,IAAI,CAAC,SAAS;4BACX,IAAI,CAAC,YAAY;;;SAGpC,CAAA;IACP,CAAC;IAED,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;YAErB,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,CAAA;YACnD,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAA;YAC5B,IAAI,SAAS,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAEvD,MAAM,WAAW,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAA;YAEvE,QAAQ,CAAC,aAAa,CACpB,IAAI,WAAW,CAAC,QAAQ,EAAE;gBACxB,MAAM,EAAE;oBACN,KAAK,EAAE,MAAM;oBACb,OAAO,EAAE,OAAO;iBACjB;aACF,CAAC,CACH,CAAA;QACH,CAAC;QAAC,OAAO,EAAE,EAAE,CAAC;YACZ,QAAQ,CAAC,aAAa,CACpB,IAAI,WAAW,CAAC,QAAQ,EAAE;gBACxB,MAAM,EAAE;oBACN,KAAK,EAAE,OAAO;oBACd,OAAO,EAAE,EAAE;oBACX,EAAE,EAAE,EAAE;iBACP;aACF,CAAC,CACH,CAAA;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,SAAS,GAAG,KAAK,CAAA;QACxB,CAAC;QAED,IAAI,CAAC,aAAa,EAAE,CAAA;IACtB,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,IAAI,CAAC,WAAW,EAAE,CAAA;QACxB,6CAA6C;QAC7C,MAAM,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC7C,CAAC;;AAnNM,wBAAM,GAAG;IACd,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;KAoBF;CACF,AAtBY,CAsBZ;AAE2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;kDAAwB;AACvB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;oDAAmB;AAClB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;gDAAW;AACV;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;iDAAY;AAEX;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;oDAAoB;AACnB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;kDAAiB;AACjB;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;;mDAAc;AACZ;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;+CAAc;AACZ;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;;uDAAuB;AACvB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;kDAAwB;AACvB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;gDAAW;AACX;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;;6DAAyB;AACxB;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;;gDAAW;AACV;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;;yDAAoB;AACjB;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;;oDAAoB;AAIpB;IAA3B,KAAK,CAAC,mBAAmB,CAAC;8BAAY,aAAa;mDAAA;AAjFzC,iBAAiB;IAD7B,aAAa,CAAC,qBAAqB,CAAC;;GACxB,iBAAiB,CA0P7B","sourcesContent":["import './things-scene-components.import'\nimport '@fmsim/board/ox-board-modeller.js'\n\nimport { css, html } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\nimport { BoardModelCache, captureBoardThumbnail } from '@fmsim/board'\nimport { BoardModeller } from '@fmsim/board/ox-board-modeller.js'\nimport { itemTypesFromThemes, LoadTracker } from '@hatiolab/things-scene'\nimport { OxPropertyEditor } from '@operato/property-editor'\nimport { connect, PageView, store } from '@operato/shell'\nimport { fetchBoardById, updateBoard, CARRIER_TYPES_THEME } from '@fmsim/api'\n\nimport { provider } from '../board-provider'\nimport components from './things-scene-components-with-tools.import'\n\nconst NOOP = () => {}\n\n@customElement('board-modeller-page')\nexport class BoardModellerPage extends connect(store)(PageView) {\n constructor() {\n super()\n\n components.forEach(({ templates = [], groups = [] }) => {\n groups.forEach(group => BoardModeller.registerGroup(group))\n BoardModeller.registerTemplate(templates)\n })\n\n /* 컴포넌트에서 정의된 에디터들을 MODELLER_EDITORS에 등록 */\n var addedEditors = {}\n for (let component in components) {\n let { editors } = components[component]\n\n editors &&\n editors.forEach(editor => {\n let { type, element } = editor\n\n addedEditors[type] = element\n })\n }\n\n OxPropertyEditor.register(addedEditors)\n\n this.boardName = ''\n this.model = null\n this.themes = null\n this.baseUrl = ''\n this.selected = []\n this.mode = 1\n this.hideProperty = false\n this.overlay = null\n this.scene = null\n this.fonts = []\n this.board = null\n this.componentGroupList = BoardModeller.groups\n }\n\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n\n overflow: hidden;\n position: relative;\n }\n\n board-modeller {\n flex: 1;\n }\n\n oops-note {\n display: block;\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n }\n `\n ]\n\n @property({ type: String }) boardId?: string | null\n @property({ type: String }) boardName?: string\n @property({ type: Object }) model: any\n @property({ type: Object }) themes: any\n /** The domain's carrier type table, its active carrier-types theme (ADR-0155): the scene's item types */\n @property({ type: Object }) itemTypes?: unknown\n @property({ type: String }) baseUrl?: string\n @property({ type: Array }) selected: any\n @property({ type: Number }) mode?: number\n @property({ type: Boolean }) hideProperty?: boolean\n @property({ type: String }) overlay?: string | null\n @property({ type: Object }) scene: any\n @property({ type: Array }) componentGroupList?: any\n @property({ type: Array }) fonts: any\n @property({ type: Array }) propertyEditor: any\n @property({ type: Boolean }) preparing?: boolean\n\n board: any\n private _loadTracker?: LoadTracker\n @query('ox-board-modeller') modeller?: BoardModeller\n\n get context() {\n return {\n title: this.board ? this.boardName : this.preparing ? 'Fetching board...' : 'Board Not Found',\n widebleed: true\n }\n }\n\n get oopsNote() {\n return {\n icon: 'color_lens',\n title: 'EMPTY BOARD',\n description: 'There are no board to be designed'\n }\n }\n\n async refresh() {\n if (!this.boardId) {\n this.board = null\n this.model = null\n\n return\n }\n try {\n this.board = null\n this.model = null\n this._loadTracker = new LoadTracker()\n this._loadTracker.setPhase('fetch')\n this.preparing = true\n this.updateContext()\n\n var { board, themes } = (await fetchBoardById(this.boardId)) || {}\n\n if (!board) {\n this.board = null\n throw 'board not found'\n }\n\n this.board = {\n ...board,\n model: {\n ...JSON.parse(board.model)\n }\n }\n\n this.boardName = this.board.name\n this.model = {\n ...this.board.model\n }\n this.itemTypes = itemTypesFromThemes(themes?.items, CARRIER_TYPES_THEME)\n this.themes = themes?.items.reduce((style, theme) => {\n style[theme.name] = theme.value\n return style\n }, {})\n } catch (ex) {\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: {\n level: 'error',\n message: ex,\n ex\n }\n })\n )\n } finally {\n this.preparing = false\n this.updateContext()\n }\n }\n\n updated(changes) {\n if (changes.has('boardId')) {\n this.refresh()\n }\n }\n\n pageUpdated(changes, lifecycle) {\n if (this.active) {\n this.boardId = lifecycle.resourceId\n } else {\n this.boardId = null\n this.model = null\n this.modeller?.close()\n }\n }\n\n stateChanged(state) {\n this.baseUrl = state.route.rootPath\n this.fonts = state.font\n }\n\n render() {\n var oops = !this.preparing && !this.model && this.oopsNote\n\n return oops\n ? html` <oops-note icon=${oops.icon} title=${oops.title} description=${oops.description}></oops-note> `\n : html`\n <ox-board-modeller\n .mode=${this.mode}\n .loadTracker=${this._loadTracker}\n @mode-changed=${e => {\n this.mode = e.detail.value\n }}\n .model=${this.model}\n @model-changed=${e => {\n this.model = e.detail.value\n }}\n .scene=${this.scene}\n @scene-changed=${e => {\n this.scene = e.detail.value\n }}\n .selected=${this.selected}\n @selected-changed=${e => {\n this.selected = e.detail.value\n }}\n .baseUrl=${this.baseUrl}\n .provider=${provider}\n @save-model=${e => this.saveBoard()}\n .componentGroupList=${this.componentGroupList}\n .fonts=${this.fonts}\n .themes=${this.themes}\n .itemTypes=${this.itemTypes}\n .hideProperty=${this.hideProperty}\n >\n </ox-board-modeller>\n `\n }\n\n async updateBoard() {\n try {\n this.preparing = true\n\n var { id, name, description, groupId } = this.board\n var model = this.scene.model\n var thumbnail = await captureBoardThumbnail(this.scene)\n\n await updateBoard({ id, name, description, model, groupId, thumbnail })\n\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: {\n level: 'info',\n message: 'saved'\n }\n })\n )\n } catch (ex) {\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: {\n level: 'error',\n message: ex,\n ex: ex\n }\n })\n )\n } finally {\n this.preparing = false\n }\n\n this.updateContext()\n }\n\n async saveBoard() {\n await this.updateBoard()\n // 저장 후 캐시 무효화 — viewer에서 다음 로드 시 최신 모델 fetch\n await BoardModelCache.remove(this.board.id)\n }\n}\n"]}