@fmsim/layout-view 2.0.0-beta.2 → 2.0.0-beta.22

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,3 +1,4 @@
1
+ import JSON5 from 'json5';
1
2
  import { create, error, ReferenceMap } from '@hatiolab/things-scene';
2
3
  import { DataStorage } from '@fmsim/board';
3
4
  import { fetchBoardById } from '@fmsim/api';
@@ -8,7 +9,7 @@ export function createBoardProvider() {
8
9
  const { board, themes } = 'data' in fetched ? fetched.data : fetched || {};
9
10
  const style = themes === null || themes === void 0 ? void 0 : themes.items.reduce((style, theme) => {
10
11
  try {
11
- style[theme.name] = typeof theme.value == 'string' ? JSON.parse(theme.value) : theme.value;
12
+ style[theme.name] = typeof theme.value == 'string' ? JSON5.parse(theme.value) : theme.value;
12
13
  }
13
14
  catch (err) {
14
15
  console.error('themes parse error', err);
@@ -1 +1 @@
1
- {"version":3,"file":"board-provider.js","sourceRoot":"","sources":["../client/board-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAS,MAAM,wBAAwB,CAAA;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,MAAM,UAAU,mBAAmB;IACjC,IAAI,SAAS,GAAG,IAAI,YAAY,CAC9B,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAgB,EAAE;QAC/C,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAA;YAC7C,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAA;YAC1E,MAAM,KAAK,GAAG,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;gBAClD,IAAI,CAAC;oBACH,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,KAAK,CAAC,KAAK,IAAI,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAA;gBAC5F,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAA;gBAC1C,CAAC;gBACD,OAAO,KAAK,CAAA;YACd,CAAC,EAAE,EAAE,CAAC,CAAA;YAEN,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,CAAC,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC,CAAA;gBACvD,OAAM;YACR,CAAC;YAED,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YAEnC,IAAI,KAAY,CAAA;YAEhB,IAAI,CAAC;gBACH,KAAK,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;gBACnC,OAAO,CAAC,IAAI,CAAC,gCAAgC,EAAE,OAAO,CAAC,CAAA;YACzD,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,KAAK,GAAG,MAAM,CAAC;oBACb,KAAK;oBACL,KAAK;oBACL,IAAI,EAAE,CAAC;oBACP,WAAW,EAAE,QAAe;oBAC5B,WAAW,EAAE,IAAI,WAAW,CAAC,OAAO,CAAC;oBACrC,QAAQ,EAAE,CAAC,mBAAmB,EAAE,cAAc,EAAE,eAAe,CAAC;oBAChE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE,CAAC;iBACrF,CAAC,CAAA;gBAEF,6BAA6B;YAC/B,CAAC;YAED,OAAO,CAAC,KAAK,CAAC,CAAA;QAChB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,KAAK,CAAC,CAAC,CAAC,CAAA;YACR,MAAM,CAAC,CAAC,CAAC,CAAA;QACX,CAAC;IACH,CAAC,EACD,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE;QAChB,GAAG,CAAC,OAAO,EAAE,CAAA;IACf,CAAC,CACF,CAAA;IAED,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,mBAAmB,EAAE,CAAA","sourcesContent":["import { create, error, ReferenceMap, Scene } from '@hatiolab/things-scene'\nimport { DataStorage } from '@fmsim/board'\nimport { fetchBoardById } from '@fmsim/api'\n\nexport function createBoardProvider() {\n var _provider = new ReferenceMap(\n async (boardId, resolve, reject): Promise<any> => {\n try {\n const fetched = await fetchBoardById(boardId)\n const { board, themes } = 'data' in fetched ? fetched.data : fetched || {}\n const style = themes?.items.reduce((style, theme) => {\n try {\n style[theme.name] = typeof theme.value == 'string' ? JSON.parse(theme.value) : theme.value\n } catch (err) {\n console.error('themes parse error', err)\n }\n return style\n }, {})\n\n if (!board) {\n reject(new Error('Failed to get the requested board.'))\n return\n }\n\n var model = JSON.parse(board.model)\n\n var scene: Scene\n\n try {\n scene = await provider.get(boardId)\n console.warn('Board fetched more than twice.', boardId)\n } catch (e) {\n scene = create({\n model,\n style,\n mode: 0,\n refProvider: provider as any,\n dataStorage: new DataStorage(boardId),\n handlers: ['mcs-event-handler', 'zoom-handler', 'shift-handler'],\n layers: [{ type: 'scroll-layer' }, { type: 'movement-layer', id: 'movement-layer' }]\n })\n\n // s.app.baseUrl = undefined;\n }\n\n resolve(scene)\n } catch (e) {\n error(e)\n reject(e)\n }\n },\n async (id, ref) => {\n ref.dispose()\n }\n )\n\n return _provider\n}\n\nexport const provider = createBoardProvider()\n"]}
1
+ {"version":3,"file":"board-provider.js","sourceRoot":"","sources":["../client/board-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAS,MAAM,wBAAwB,CAAA;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,MAAM,UAAU,mBAAmB;IACjC,IAAI,SAAS,GAAG,IAAI,YAAY,CAC9B,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAgB,EAAE;QAC/C,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAA;YAC7C,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAA;YAC1E,MAAM,KAAK,GAAG,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;gBAClD,IAAI,CAAC;oBACH,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,KAAK,CAAC,KAAK,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAA;gBAC7F,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAA;gBAC1C,CAAC;gBACD,OAAO,KAAK,CAAA;YACd,CAAC,EAAE,EAAE,CAAC,CAAA;YAEN,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,CAAC,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC,CAAA;gBACvD,OAAM;YACR,CAAC;YAED,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YAEnC,IAAI,KAAY,CAAA;YAEhB,IAAI,CAAC;gBACH,KAAK,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;gBACnC,OAAO,CAAC,IAAI,CAAC,gCAAgC,EAAE,OAAO,CAAC,CAAA;YACzD,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,KAAK,GAAG,MAAM,CAAC;oBACb,KAAK;oBACL,KAAK;oBACL,IAAI,EAAE,CAAC;oBACP,WAAW,EAAE,QAAe;oBAC5B,WAAW,EAAE,IAAI,WAAW,CAAC,OAAO,CAAC;oBACrC,QAAQ,EAAE,CAAC,mBAAmB,EAAE,cAAc,EAAE,eAAe,CAAC;oBAChE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE,CAAC;iBACrF,CAAC,CAAA;gBAEF,6BAA6B;YAC/B,CAAC;YAED,OAAO,CAAC,KAAK,CAAC,CAAA;QAChB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,KAAK,CAAC,CAAC,CAAC,CAAA;YACR,MAAM,CAAC,CAAC,CAAC,CAAA;QACX,CAAC;IACH,CAAC,EACD,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE;QAChB,GAAG,CAAC,OAAO,EAAE,CAAA;IACf,CAAC,CACF,CAAA;IAED,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,mBAAmB,EAAE,CAAA","sourcesContent":["import JSON5 from 'json5'\nimport { create, error, ReferenceMap, Scene } from '@hatiolab/things-scene'\nimport { DataStorage } from '@fmsim/board'\nimport { fetchBoardById } from '@fmsim/api'\n\nexport function createBoardProvider() {\n var _provider = new ReferenceMap(\n async (boardId, resolve, reject): Promise<any> => {\n try {\n const fetched = await fetchBoardById(boardId)\n const { board, themes } = 'data' in fetched ? fetched.data : fetched || {}\n const style = themes?.items.reduce((style, theme) => {\n try {\n style[theme.name] = typeof theme.value == 'string' ? JSON5.parse(theme.value) : theme.value\n } catch (err) {\n console.error('themes parse error', err)\n }\n return style\n }, {})\n\n if (!board) {\n reject(new Error('Failed to get the requested board.'))\n return\n }\n\n var model = JSON.parse(board.model)\n\n var scene: Scene\n\n try {\n scene = await provider.get(boardId)\n console.warn('Board fetched more than twice.', boardId)\n } catch (e) {\n scene = create({\n model,\n style,\n mode: 0,\n refProvider: provider as any,\n dataStorage: new DataStorage(boardId),\n handlers: ['mcs-event-handler', 'zoom-handler', 'shift-handler'],\n layers: [{ type: 'scroll-layer' }, { type: 'movement-layer', id: 'movement-layer' }]\n })\n\n // s.app.baseUrl = undefined;\n }\n\n resolve(scene)\n } catch (e) {\n error(e)\n reject(e)\n }\n },\n async (id, ref) => {\n ref.dispose()\n }\n )\n\n return _provider\n}\n\nexport const provider = createBoardProvider()\n"]}
@@ -1,5 +1,6 @@
1
1
  import { __decorate } from "tslib";
2
2
  import './ox-input-animation-map.js';
3
+ import JSON5 from 'json5';
3
4
  import { html } from 'lit';
4
5
  import { customElement } from 'lit/decorators.js';
5
6
  import { cloneDeep } from 'lodash-es';
@@ -13,7 +14,7 @@ let AnimationMapEditor = class AnimationMapEditor extends OxGristEditor {
13
14
  return Object.assign({ name: `Animation : ${this.record.name}`, objectified: false }, overidable);
14
15
  }
15
16
  get editorTemplate() {
16
- const value = typeof this.value === 'object' ? JSON.stringify(this.value) : this.value;
17
+ const value = typeof this.value === 'object' ? JSON5.stringify(this.value) : this.value;
17
18
  return html `<div tabindex="0">${value || ''}</div>`;
18
19
  }
19
20
  _onclick(e) {
@@ -37,7 +38,7 @@ let AnimationMapEditor = class AnimationMapEditor extends OxGristEditor {
37
38
  composed: true,
38
39
  detail: {
39
40
  before: this.value,
40
- after: !objectified ? JSON.stringify(newval) : newval,
41
+ after: !objectified ? JSON5.stringify(newval, null, 2) : newval,
41
42
  record: this.record,
42
43
  column: this.column,
43
44
  row: this.row
@@ -46,7 +47,7 @@ let AnimationMapEditor = class AnimationMapEditor extends OxGristEditor {
46
47
  };
47
48
  let value;
48
49
  try {
49
- value = !objectified && typeof this.value === 'string' ? JSON.parse(this.value) : cloneDeep(this.value || {});
50
+ value = !objectified && typeof this.value === 'string' ? JSON5.parse(this.value) : cloneDeep(this.value || {});
50
51
  }
51
52
  catch (e) {
52
53
  value = {};
@@ -58,21 +59,65 @@ let AnimationMapEditor = class AnimationMapEditor extends OxGristEditor {
58
59
  (_a = this.popup) === null || _a === void 0 ? void 0 : _a.close();
59
60
  };
60
61
  const template = html `
61
- <div style="display:flex;flex-direction:column;height:100%;padding:12px;box-sizing:border-box;">
62
- <ox-input-animation-map
63
- .value=${value}
64
- @change=${(e) => { latestValue = e.detail; }}
65
- style="flex:1;overflow:auto;"
66
- ></ox-input-animation-map>
67
- <div style="display:flex;justify-content:flex-end;gap:8px;margin-top:12px;">
68
- <button @click=${onConfirm}
69
- style="padding:6px 16px;background:var(--md-sys-color-primary,#1976d2);color:#fff;border:none;border-radius:4px;cursor:pointer;">
70
- ${i18next.t('button.confirm') || 'Confirm'}
71
- </button>
72
- <button @click=${() => { var _a; return (_a = this.popup) === null || _a === void 0 ? void 0 : _a.close(); }}
73
- style="padding:6px 16px;background:var(--md-sys-color-surface-variant,#e0e0e0);color:#333;border:none;border-radius:4px;cursor:pointer;">
74
- ${i18next.t('button.cancel') || 'Cancel'}
75
- </button>
62
+ <style>
63
+ .animation-map-popup ::-webkit-scrollbar { width: 5px; height: 5px; }
64
+ .animation-map-popup ::-webkit-scrollbar-track { background-color: transparent; }
65
+ .animation-map-popup ::-webkit-scrollbar-thumb { background-color: var(--md-sys-color-outline, #888); border-radius: 0; }
66
+ .animation-map-popup ::-webkit-scrollbar-thumb:hover { background-color: var(--md-sys-color-outline-variant, #aaa); }
67
+ </style>
68
+ <div class="animation-map-popup" style="display:flex;flex-direction:row;height:100%;box-sizing:border-box;gap:0;">
69
+ <div style="display:flex;flex-direction:column;flex:2;overflow:hidden;">
70
+ <ox-input-animation-map
71
+ .value=${value}
72
+ @change=${(e) => { latestValue = e.detail; }}
73
+ style="flex:1;overflow:auto;padding:12px 12px 0;"
74
+ ></ox-input-animation-map>
75
+ <div style="display:flex;justify-content:flex-end;gap:8px;padding:12px;">
76
+ <button @click=${onConfirm}
77
+ style="padding:6px 16px;background:var(--md-sys-color-primary,#1976d2);color:#fff;border:none;border-radius:4px;cursor:pointer;">
78
+ ${i18next.t('button.confirm') || 'Confirm'}
79
+ </button>
80
+ <button @click=${() => { var _a; return (_a = this.popup) === null || _a === void 0 ? void 0 : _a.close(); }}
81
+ style="padding:6px 16px;background:var(--md-sys-color-surface-variant,#e0e0e0);color:#333;border:none;border-radius:4px;cursor:pointer;">
82
+ ${i18next.t('button.cancel') || 'Cancel'}
83
+ </button>
84
+ </div>
85
+ </div>
86
+
87
+ <div style="flex:1;padding:12px 16px;overflow:auto;border-left:1px solid var(--md-sys-color-outline-variant,#ddd);
88
+ 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);">
89
+ <h3 style="margin:0 0 10px;font-size:15px;font-weight:700;color:var(--md-sys-color-primary,#1976d2);">Animation Types</h3>
90
+ <dl style="margin:0;padding:0;">
91
+ <dt style="font-weight:600;margin-top:6px;">Rotation</dt>
92
+ <dd style="margin:0 0 4px 8px;">Theta(°) 각도만큼 회전. 360°이면 1회전.</dd>
93
+ <dt style="font-weight:600;margin-top:6px;">Vibration</dt>
94
+ <dd style="margin:0 0 4px 8px;">Theta(°) 범위 내에서 좌우 진동.</dd>
95
+ <dt style="font-weight:600;margin-top:6px;">Heartbeat</dt>
96
+ <dd style="margin:0 0 4px 8px;">Scale 비율로 확대/축소 반복. 1.2 = 20% 확대.</dd>
97
+ <dt style="font-weight:600;margin-top:6px;">Moving</dt>
98
+ <dd style="margin:0 0 4px 8px;">X, Y 픽셀만큼 이동 후 복귀.</dd>
99
+ <dt style="font-weight:600;margin-top:6px;">Fade</dt>
100
+ <dd style="margin:0 0 4px 8px;">Start/End Alpha(0~1) 사이에서 투명도 변화.</dd>
101
+ </dl>
102
+
103
+ <h3 style="margin:18px 0 10px;font-size:15px;font-weight:700;color:var(--md-sys-color-primary,#1976d2);">Common Properties</h3>
104
+ <dl style="margin:0;padding:0;">
105
+ <dt style="font-weight:600;margin-top:6px;">Delay</dt>
106
+ <dd style="margin:0 0 4px 8px;">애니메이션 시작 전 대기 시간 (ms).</dd>
107
+ <dt style="font-weight:600;margin-top:6px;">Duration</dt>
108
+ <dd style="margin:0 0 4px 8px;">한 사이클 소요 시간 (ms). 1000 = 1초.</dd>
109
+ <dt style="font-weight:600;margin-top:6px;">Interval</dt>
110
+ <dd style="margin:0 0 4px 8px;">반복 사이 대기 시간 (ms).</dd>
111
+ <dt style="font-weight:600;margin-top:6px;">Delta</dt>
112
+ <dd style="margin:0 0 4px 8px;">보간 함수. linear(균일), quad(가속), circ(원형), back(오버슈트), bounce(바운스), elastic(탄성).</dd>
113
+ <dt style="font-weight:600;margin-top:6px;">Ease</dt>
114
+ <dd style="margin:0 0 4px 8px;">이징 방향. in(시작 느림), out(끝 느림), inout(양쪽 느림).</dd>
115
+ <dt style="font-weight:600;margin-top:6px;">Repeat</dt>
116
+ <dd style="margin:0 0 4px 8px;">체크 시 무한 반복.</dd>
117
+ <dt style="font-weight:600;margin-top:6px;">Autoplay</dt>
118
+ <dd style="margin:0 0 4px 8px;">체크 시 로드 즉시 자동 재생.</dd>
119
+ </dl>
120
+
76
121
  </div>
77
122
  </div>
78
123
  `;
@@ -1 +1 @@
1
- {"version":3,"file":"animation-map-editor.js","sourceRoot":"","sources":["../../client/data-grist/animation-map-editor.ts"],"names":[],"mappings":";AAAA,OAAO,6BAA6B,CAAA;AAEpC,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEjD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,SAAS,EAAe,MAAM,gBAAgB,CAAA;AAGhD,IAAM,kBAAkB,GAAxB,MAAM,kBAAmB,SAAQ,aAAa;IAGnD,IAAI,OAAO;;QACT,MAAM,UAAU,GAAG,CAAA,MAAA,IAAI,CAAC,MAAM,CAAC,MAAM,0CAAE,OAAO,KAAI,EAAE,CAAA;QAEpD,uBACE,IAAI,EAAE,eAAe,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EACvC,WAAW,EAAE,KAAK,IACf,UAAU,EACd;IACH,CAAC;IAED,IAAI,cAAc;QAChB,MAAM,KAAK,GAAG,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAA;QACtF,OAAO,IAAI,CAAA,qBAAqB,KAAK,IAAI,EAAE,QAAQ,CAAA;IACrD,CAAC;IAED,QAAQ,CAAC,CAAQ;QACf,CAAC,CAAC,eAAe,EAAE,CAAA;QACnB,IAAI,CAAC,YAAY,EAAE,CAAA;IACrB,CAAC;IAED,UAAU,CAAC,CAAgB;QACzB,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;YACtB,CAAC,CAAC,eAAe,EAAE,CAAA;YACnB,IAAI,CAAC,YAAY,EAAE,CAAA;QACrB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,OAAO,IAAI,CAAC,KAAK,CAAA;QACnB,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,CAAA;QAExD,MAAM,eAAe,GAAG,CAAC,MAAW,EAAE,EAAE;YACtC,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,cAAc,EAAE;gBAC9B,OAAO,EAAE,IAAI;gBACb,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE;oBACN,MAAM,EAAE,IAAI,CAAC,KAAK;oBAClB,KAAK,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM;oBACrD,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,GAAG,EAAE,IAAI,CAAC,GAAG;iBACd;aACF,CAAC,CACH,CAAA;QACH,CAAC,CAAA;QAED,IAAI,KAAU,CAAA;QACd,IAAI,CAAC;YACH,KAAK,GAAG,CAAC,WAAW,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAA;QAC/G,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,KAAK,GAAG,EAAE,CAAA;QACZ,CAAC;QAED,IAAI,WAAW,GAAG,KAAK,CAAA;QAEvB,MAAM,SAAS,GAAG,GAAG,EAAE;;YACrB,eAAe,CAAC,WAAW,CAAC,CAAA;YAC5B,MAAA,IAAI,CAAC,KAAK,0CAAE,KAAK,EAAE,CAAA;QACrB,CAAC,CAAA;QAED,MAAM,QAAQ,GAAG,IAAI,CAAA;;;mBAGN,KAAK;oBACJ,CAAC,CAAc,EAAE,EAAE,GAAG,WAAW,GAAG,CAAC,CAAC,MAAM,CAAA,CAAC,CAAC;;;;2BAIvC,SAAS;;cAEtB,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,SAAS;;2BAE3B,GAAG,EAAE,WAAC,OAAA,MAAA,IAAI,CAAC,KAAK,0CAAE,KAAK,EAAE,CAAA,EAAA;;cAEtC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,QAAQ;;;;KAI/C,CAAA;QAED,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,QAAQ,EAAE;YAC/B,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,GAAG,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,WAAW,EAAE,KAAI,eAAe,EAAE;SACnD,CAAC,CAAA;IACJ,CAAC;CACF,CAAA;AA7FY,kBAAkB;IAD9B,aAAa,CAAC,sBAAsB,CAAC;GACzB,kBAAkB,CA6F9B","sourcesContent":["import './ox-input-animation-map.js'\n\nimport { html } from 'lit'\nimport { customElement } from 'lit/decorators.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' ? JSON.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 ? JSON.stringify(newval) : 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' ? JSON.parse(this.value) : cloneDeep(this.value || {})\n } catch (e) {\n value = {}\n }\n\n let latestValue = value\n\n const onConfirm = () => {\n confirmCallback(latestValue)\n this.popup?.close()\n }\n\n const template = html`\n <div style=\"display:flex;flex-direction:column;height:100%;padding:12px;box-sizing:border-box;\">\n <ox-input-animation-map\n .value=${value}\n @change=${(e: CustomEvent) => { latestValue = e.detail }}\n style=\"flex:1;overflow:auto;\"\n ></ox-input-animation-map>\n <div style=\"display:flex;justify-content:flex-end;gap:8px;margin-top: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\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'\n\nimport JSON5 from 'json5'\nimport { html } from 'lit'\nimport { customElement } from 'lit/decorators.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 onConfirm = () => {\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 .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"]}
@@ -4,6 +4,7 @@
4
4
  * AnimationConfig 객체 하나를 폼으로 편집한다.
5
5
  * type 선택에 따라 관련 필드만 표시된다.
6
6
  */
7
+ import '@operato/input/ox-input-angle.js';
7
8
  import { LitElement } from 'lit';
8
9
  type AnimationConfig = {
9
10
  type?: string;
@@ -5,6 +5,7 @@
5
5
  * type 선택에 따라 관련 필드만 표시된다.
6
6
  */
7
7
  import { __decorate, __metadata } from "tslib";
8
+ import '@operato/input/ox-input-angle.js';
8
9
  import { css, html, LitElement } from 'lit';
9
10
  import { customElement, property } from 'lit/decorators.js';
10
11
  let OxInputAnimationConfig = class OxInputAnimationConfig extends LitElement {
@@ -13,18 +14,18 @@ let OxInputAnimationConfig = class OxInputAnimationConfig extends LitElement {
13
14
  this.value = {};
14
15
  }
15
16
  render() {
16
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
17
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
17
18
  const v = this.value || {};
18
19
  return html `
19
20
  <div class="form">
20
21
  <label>Type</label>
21
- <select value-key="type" .value=${v.type || ''}>
22
- <option value="">None</option>
23
- <option value="rotation">Rotation</option>
24
- <option value="vibration">Vibration</option>
25
- <option value="heartbeat">Heartbeat</option>
26
- <option value="moving">Moving</option>
27
- <option value="fade">Fade</option>
22
+ <select value-key="type">
23
+ <option value="" ?selected=${!v.type}>None</option>
24
+ <option value="rotation" ?selected=${v.type === 'rotation'}>Rotation</option>
25
+ <option value="vibration" ?selected=${v.type === 'vibration'}>Vibration</option>
26
+ <option value="heartbeat" ?selected=${v.type === 'heartbeat'}>Heartbeat</option>
27
+ <option value="moving" ?selected=${v.type === 'moving'}>Moving</option>
28
+ <option value="fade" ?selected=${v.type === 'fade'}>Fade</option>
28
29
  </select>
29
30
 
30
31
  <label>Delay</label>
@@ -39,59 +40,59 @@ let OxInputAnimationConfig = class OxInputAnimationConfig extends LitElement {
39
40
  ${v.type === 'rotation' || v.type === 'vibration'
40
41
  ? html `
41
42
  <label>Theta</label>
42
- <input type="number" value-key="theta" .value=${String((_d = v.theta) !== null && _d !== void 0 ? _d : '')} />
43
+ <ox-input-angle value-key="theta" .value=${v.theta}></ox-input-angle>
43
44
  `
44
45
  : ''}
45
46
  ${v.type === 'heartbeat'
46
47
  ? html `
47
48
  <label>Scale</label>
48
- <input type="number" value-key="scale" .value=${String((_e = v.scale) !== null && _e !== void 0 ? _e : '')} />
49
+ <input type="number" value-key="scale" .value=${String((_d = v.scale) !== null && _d !== void 0 ? _d : '')} />
49
50
  `
50
51
  : ''}
51
52
  ${v.type === 'moving'
52
53
  ? html `
53
54
  <label>X</label>
54
- <input type="number" value-key="x" .value=${String((_f = v.x) !== null && _f !== void 0 ? _f : '')} />
55
+ <input type="number" value-key="x" .value=${String((_e = v.x) !== null && _e !== void 0 ? _e : '')} />
55
56
  <label>Y</label>
56
- <input type="number" value-key="y" .value=${String((_g = v.y) !== null && _g !== void 0 ? _g : '')} />
57
+ <input type="number" value-key="y" .value=${String((_f = v.y) !== null && _f !== void 0 ? _f : '')} />
57
58
  `
58
59
  : ''}
59
60
  ${v.type === 'fade'
60
61
  ? html `
61
62
  <label>Start α</label>
62
- <input type="number" value-key="startAlpha" .value=${String((_h = v.startAlpha) !== null && _h !== void 0 ? _h : '')} />
63
+ <input type="number" value-key="startAlpha" .value=${String((_g = v.startAlpha) !== null && _g !== void 0 ? _g : '')} />
63
64
  <label>End α</label>
64
- <input type="number" value-key="endAlpha" .value=${String((_j = v.endAlpha) !== null && _j !== void 0 ? _j : '')} />
65
+ <input type="number" value-key="endAlpha" .value=${String((_h = v.endAlpha) !== null && _h !== void 0 ? _h : '')} />
65
66
  `
66
67
  : ''}
67
68
 
68
69
  <label>Delta</label>
69
- <select value-key="delta" .value=${v.delta || 'linear'}>
70
- <option value="linear">linear</option>
71
- <option value="quad">quad</option>
72
- <option value="circ">circ</option>
73
- <option value="back">back</option>
74
- <option value="bounce">bounce</option>
75
- <option value="elastic">elastic</option>
70
+ <select value-key="delta">
71
+ <option value="linear" ?selected=${(v.delta || 'linear') === 'linear'}>linear</option>
72
+ <option value="quad" ?selected=${v.delta === 'quad'}>quad</option>
73
+ <option value="circ" ?selected=${v.delta === 'circ'}>circ</option>
74
+ <option value="back" ?selected=${v.delta === 'back'}>back</option>
75
+ <option value="bounce" ?selected=${v.delta === 'bounce'}>bounce</option>
76
+ <option value="elastic" ?selected=${v.delta === 'elastic'}>elastic</option>
76
77
  </select>
77
78
 
78
79
  <label>Ease</label>
79
- <select value-key="ease" .value=${v.ease || 'in'}>
80
- <option value="in">in</option>
81
- <option value="out">out</option>
82
- <option value="inout">inout</option>
80
+ <select value-key="ease">
81
+ <option value="in" ?selected=${(v.ease || 'in') === 'in'}>in</option>
82
+ <option value="out" ?selected=${v.ease === 'out'}>out</option>
83
+ <option value="inout" ?selected=${v.ease === 'inout'}>inout</option>
83
84
  </select>
84
- </div>
85
-
86
- <div class="checkbox-row">
87
- <label>
88
- <input type="checkbox" value-key="repeat" .checked=${!!v.repeat} />
89
- Repeat
90
- </label>
91
- <label>
92
- <input type="checkbox" value-key="autoplay" .checked=${(_k = v.autoplay) !== null && _k !== void 0 ? _k : true} />
93
- Autoplay
94
- </label>
85
+ <label></label>
86
+ <div class="checkbox-row">
87
+ <label>
88
+ <input type="checkbox" value-key="repeat" .checked=${!!v.repeat} />
89
+ Repeat
90
+ </label>
91
+ <label>
92
+ <input type="checkbox" value-key="autoplay" .checked=${(_j = v.autoplay) !== null && _j !== void 0 ? _j : true} />
93
+ Autoplay
94
+ </label>
95
+ </div>
95
96
  </div>
96
97
  `;
97
98
  }
@@ -113,7 +114,11 @@ let OxInputAnimationConfig = class OxInputAnimationConfig extends LitElement {
113
114
  else
114
115
  val = el.value;
115
116
  }
117
+ else if (el instanceof HTMLSelectElement) {
118
+ val = el.value;
119
+ }
116
120
  else {
121
+ // custom element (ox-input-angle 등): value 프로퍼티 사용
117
122
  val = el.value;
118
123
  }
119
124
  this.value = Object.assign(Object.assign({}, this.value), { [key]: val });
@@ -164,7 +169,7 @@ OxInputAnimationConfig.styles = css `
164
169
  .checkbox-row {
165
170
  display: flex;
166
171
  align-items: center;
167
- gap: 12px;
172
+ gap: 24px;
168
173
  }
169
174
 
170
175
  .checkbox-row label {
@@ -1 +1 @@
1
- {"version":3,"file":"ox-input-animation-config.js","sourceRoot":"","sources":["../../client/data-grist/ox-input-animation-config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 단일 애니메이션 설정 폼 컴포넌트\n *\n * AnimationConfig 객체 하나를 폼으로 편집한다.\n * type 선택에 따라 관련 필드만 표시된다.\n */\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.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\n@customElement('ox-input-animation-config')\nexport class OxInputAnimationConfig extends LitElement {\n static styles = css`\n :host {\n display: block;\n font-size: var(--fontsize-default, 12px);\n }\n\n .form {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 2px 6px;\n align-items: center;\n }\n\n label {\n font-size: 11px;\n color: var(--md-sys-color-on-surface-variant, #555);\n text-align: right;\n white-space: nowrap;\n }\n\n input,\n select {\n border: 0;\n border-bottom: 1px solid transparent;\n padding: 3px 4px;\n font: var(--input-font, 12px sans-serif);\n color: var(--md-sys-color-on-primary-container, #333);\n background: transparent;\n box-sizing: border-box;\n }\n\n input:focus,\n select:focus {\n outline: none;\n border-bottom-color: var(--md-sys-color-primary, #1976d2);\n }\n\n input[type='number'] {\n width: 80px;\n }\n\n .checkbox-row {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .checkbox-row label {\n display: flex;\n align-items: center;\n gap: 3px;\n cursor: pointer;\n }\n\n input[type='checkbox'] {\n width: auto;\n margin: 0;\n }\n `\n\n @property({ type: Object }) value: AnimationConfig = {}\n\n render() {\n const v = this.value || {}\n\n return html`\n <div class=\"form\">\n <label>Type</label>\n <select value-key=\"type\" .value=${v.type || ''}>\n <option value=\"\">None</option>\n <option value=\"rotation\">Rotation</option>\n <option value=\"vibration\">Vibration</option>\n <option value=\"heartbeat\">Heartbeat</option>\n <option value=\"moving\">Moving</option>\n <option value=\"fade\">Fade</option>\n </select>\n\n <label>Delay</label>\n <input type=\"number\" value-key=\"delay\" .value=${String(v.delay ?? '')} placeholder=\"ms\" />\n\n <label>Duration</label>\n <input type=\"number\" value-key=\"duration\" .value=${String(v.duration ?? '')} placeholder=\"ms\" />\n\n <label>Interval</label>\n <input type=\"number\" value-key=\"interval\" .value=${String(v.interval ?? '')} placeholder=\"ms\" />\n\n ${v.type === 'rotation' || v.type === 'vibration'\n ? html`\n <label>Theta</label>\n <input type=\"number\" value-key=\"theta\" .value=${String(v.theta ?? '')} />\n `\n : ''}\n ${v.type === 'heartbeat'\n ? html`\n <label>Scale</label>\n <input type=\"number\" value-key=\"scale\" .value=${String(v.scale ?? '')} />\n `\n : ''}\n ${v.type === 'moving'\n ? html`\n <label>X</label>\n <input type=\"number\" value-key=\"x\" .value=${String(v.x ?? '')} />\n <label>Y</label>\n <input type=\"number\" value-key=\"y\" .value=${String(v.y ?? '')} />\n `\n : ''}\n ${v.type === 'fade'\n ? html`\n <label>Start α</label>\n <input type=\"number\" value-key=\"startAlpha\" .value=${String(v.startAlpha ?? '')} />\n <label>End α</label>\n <input type=\"number\" value-key=\"endAlpha\" .value=${String(v.endAlpha ?? '')} />\n `\n : ''}\n\n <label>Delta</label>\n <select value-key=\"delta\" .value=${v.delta || 'linear'}>\n <option value=\"linear\">linear</option>\n <option value=\"quad\">quad</option>\n <option value=\"circ\">circ</option>\n <option value=\"back\">back</option>\n <option value=\"bounce\">bounce</option>\n <option value=\"elastic\">elastic</option>\n </select>\n\n <label>Ease</label>\n <select value-key=\"ease\" .value=${v.ease || 'in'}>\n <option value=\"in\">in</option>\n <option value=\"out\">out</option>\n <option value=\"inout\">inout</option>\n </select>\n </div>\n\n <div class=\"checkbox-row\">\n <label>\n <input type=\"checkbox\" value-key=\"repeat\" .checked=${!!v.repeat} />\n Repeat\n </label>\n <label>\n <input type=\"checkbox\" value-key=\"autoplay\" .checked=${v.autoplay ?? true} />\n Autoplay\n </label>\n </div>\n `\n }\n\n firstUpdated() {\n this.renderRoot.addEventListener('change', this._onChange.bind(this))\n }\n\n private _onChange(e: Event) {\n e.stopPropagation()\n\n const el = e.target as HTMLElement\n const key = el.getAttribute('value-key')\n if (!key) return\n\n let val: any\n if (el instanceof HTMLInputElement) {\n if (el.type === 'checkbox') val = el.checked\n else if (el.type === 'number') val = el.value ? Number(el.value) : undefined\n else val = el.value\n } else {\n val = (el as HTMLSelectElement).value\n }\n\n this.value = { ...this.value, [key]: val }\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))\n }\n}\n"]}
1
+ {"version":3,"file":"ox-input-animation-config.js","sourceRoot":"","sources":["../../client/data-grist/ox-input-animation-config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 단일 애니메이션 설정 폼 컴포넌트\n *\n * AnimationConfig 객체 하나를 폼으로 편집한다.\n * type 선택에 따라 관련 필드만 표시된다.\n */\n\nimport '@operato/input/ox-input-angle.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.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\n@customElement('ox-input-animation-config')\nexport class OxInputAnimationConfig extends LitElement {\n static styles = css`\n :host {\n display: block;\n font-size: var(--fontsize-default, 12px);\n }\n\n .form {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 2px 6px;\n align-items: center;\n }\n\n label {\n font-size: 11px;\n color: var(--md-sys-color-on-surface-variant, #555);\n text-align: right;\n white-space: nowrap;\n }\n\n input,\n select {\n border: 0;\n border-bottom: 1px solid transparent;\n padding: 3px 4px;\n font: var(--input-font, 12px sans-serif);\n color: var(--md-sys-color-on-primary-container, #333);\n background: transparent;\n box-sizing: border-box;\n }\n\n input:focus,\n select:focus {\n outline: none;\n border-bottom-color: var(--md-sys-color-primary, #1976d2);\n }\n\n input[type='number'] {\n width: 80px;\n }\n\n .checkbox-row {\n display: flex;\n align-items: center;\n gap: 24px;\n }\n\n .checkbox-row label {\n display: flex;\n align-items: center;\n gap: 3px;\n cursor: pointer;\n }\n\n input[type='checkbox'] {\n width: auto;\n margin: 0;\n }\n `\n\n @property({ type: Object }) value: AnimationConfig = {}\n\n render() {\n const v = this.value || {}\n\n return html`\n <div class=\"form\">\n <label>Type</label>\n <select value-key=\"type\">\n <option value=\"\" ?selected=${!v.type}>None</option>\n <option value=\"rotation\" ?selected=${v.type === 'rotation'}>Rotation</option>\n <option value=\"vibration\" ?selected=${v.type === 'vibration'}>Vibration</option>\n <option value=\"heartbeat\" ?selected=${v.type === 'heartbeat'}>Heartbeat</option>\n <option value=\"moving\" ?selected=${v.type === 'moving'}>Moving</option>\n <option value=\"fade\" ?selected=${v.type === 'fade'}>Fade</option>\n </select>\n\n <label>Delay</label>\n <input type=\"number\" value-key=\"delay\" .value=${String(v.delay ?? '')} placeholder=\"ms\" />\n\n <label>Duration</label>\n <input type=\"number\" value-key=\"duration\" .value=${String(v.duration ?? '')} placeholder=\"ms\" />\n\n <label>Interval</label>\n <input type=\"number\" value-key=\"interval\" .value=${String(v.interval ?? '')} placeholder=\"ms\" />\n\n ${v.type === 'rotation' || v.type === 'vibration'\n ? html`\n <label>Theta</label>\n <ox-input-angle value-key=\"theta\" .value=${v.theta}></ox-input-angle>\n `\n : ''}\n ${v.type === 'heartbeat'\n ? html`\n <label>Scale</label>\n <input type=\"number\" value-key=\"scale\" .value=${String(v.scale ?? '')} />\n `\n : ''}\n ${v.type === 'moving'\n ? html`\n <label>X</label>\n <input type=\"number\" value-key=\"x\" .value=${String(v.x ?? '')} />\n <label>Y</label>\n <input type=\"number\" value-key=\"y\" .value=${String(v.y ?? '')} />\n `\n : ''}\n ${v.type === 'fade'\n ? html`\n <label>Start α</label>\n <input type=\"number\" value-key=\"startAlpha\" .value=${String(v.startAlpha ?? '')} />\n <label>End α</label>\n <input type=\"number\" value-key=\"endAlpha\" .value=${String(v.endAlpha ?? '')} />\n `\n : ''}\n\n <label>Delta</label>\n <select value-key=\"delta\">\n <option value=\"linear\" ?selected=${(v.delta || 'linear') === 'linear'}>linear</option>\n <option value=\"quad\" ?selected=${v.delta === 'quad'}>quad</option>\n <option value=\"circ\" ?selected=${v.delta === 'circ'}>circ</option>\n <option value=\"back\" ?selected=${v.delta === 'back'}>back</option>\n <option value=\"bounce\" ?selected=${v.delta === 'bounce'}>bounce</option>\n <option value=\"elastic\" ?selected=${v.delta === 'elastic'}>elastic</option>\n </select>\n\n <label>Ease</label>\n <select value-key=\"ease\">\n <option value=\"in\" ?selected=${(v.ease || 'in') === 'in'}>in</option>\n <option value=\"out\" ?selected=${v.ease === 'out'}>out</option>\n <option value=\"inout\" ?selected=${v.ease === 'inout'}>inout</option>\n </select>\n <label></label>\n <div class=\"checkbox-row\">\n <label>\n <input type=\"checkbox\" value-key=\"repeat\" .checked=${!!v.repeat} />\n Repeat\n </label>\n <label>\n <input type=\"checkbox\" value-key=\"autoplay\" .checked=${v.autoplay ?? true} />\n Autoplay\n </label>\n </div>\n </div>\n `\n }\n\n firstUpdated() {\n this.renderRoot.addEventListener('change', this._onChange.bind(this))\n }\n\n private _onChange(e: Event) {\n e.stopPropagation()\n\n const el = e.target as HTMLElement\n const key = el.getAttribute('value-key')\n if (!key) return\n\n let val: any\n if (el instanceof HTMLInputElement) {\n if (el.type === 'checkbox') val = el.checked\n else if (el.type === 'number') val = el.value ? Number(el.value) : undefined\n else val = el.value\n } else if (el instanceof HTMLSelectElement) {\n val = el.value\n } else {\n // custom element (ox-input-angle 등): value 프로퍼티 사용\n val = (el as any).value\n }\n\n this.value = { ...this.value, [key]: val }\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))\n }\n}\n"]}
@@ -4,12 +4,15 @@
4
4
  * key-value 맵에서 value가 애니메이션 설정 객체인 경우를 위한 편집기.
5
5
  * 각 행은 key 입력 + ox-input-animation-config 폼으로 구성된다.
6
6
  *
7
- * value 구조:
7
+ * value 구조 (실제 데이터):
8
8
  * {
9
- * "1001": { type: "rotation", duration: 1000, delay: 0, theta: 360, repeat: true, delta: "linear", ease: "in" },
10
- * "1007": { type: "vibration", duration: 500, ... },
9
+ * "CRITICAL": "{ animation: { type: 'vibration', duration: 1000, ... }, icon: { ... } }",
10
+ * "WARN": "{ animation: { type: 'vibration', ... } }",
11
11
  * "default": ""
12
12
  * }
13
+ *
14
+ * 각 entry value는 JSON5 string이며, 내부에 animation 키가 있으면 그 속성만 폼에 표시.
15
+ * 저장 시 animation 외 다른 키(icon 등)는 보존됨.
13
16
  */
14
17
  import '@material/web/icon/icon.js';
15
18
  import './ox-input-animation-config.js';
@@ -36,7 +39,10 @@ type AnimationMap = {
36
39
  };
37
40
  export declare class OxInputAnimationMap extends LitElement {
38
41
  static styles: import("lit").CSSResult;
39
- value: AnimationMap;
42
+ set value(v: AnimationMap);
43
+ get value(): AnimationMap;
44
+ private _value;
45
+ private _parsed;
40
46
  render(): import("lit-html").TemplateResult<1>;
41
47
  private _onConfigChange;
42
48
  private _onNewKeyChange;
@@ -44,6 +50,7 @@ export declare class OxInputAnimationMap extends LitElement {
44
50
  private _build;
45
51
  private _add;
46
52
  private _delete;
47
- private _toArray;
53
+ private _parseEntries;
54
+ private _normalize;
48
55
  }
49
56
  export {};
@@ -4,23 +4,29 @@
4
4
  * key-value 맵에서 value가 애니메이션 설정 객체인 경우를 위한 편집기.
5
5
  * 각 행은 key 입력 + ox-input-animation-config 폼으로 구성된다.
6
6
  *
7
- * value 구조:
7
+ * value 구조 (실제 데이터):
8
8
  * {
9
- * "1001": { type: "rotation", duration: 1000, delay: 0, theta: 360, repeat: true, delta: "linear", ease: "in" },
10
- * "1007": { type: "vibration", duration: 500, ... },
9
+ * "CRITICAL": "{ animation: { type: 'vibration', duration: 1000, ... }, icon: { ... } }",
10
+ * "WARN": "{ animation: { type: 'vibration', ... } }",
11
11
  * "default": ""
12
12
  * }
13
+ *
14
+ * 각 entry value는 JSON5 string이며, 내부에 animation 키가 있으면 그 속성만 폼에 표시.
15
+ * 저장 시 animation 외 다른 키(icon 등)는 보존됨.
13
16
  */
14
17
  import { __decorate, __metadata } from "tslib";
15
18
  import '@material/web/icon/icon.js';
16
19
  import './ox-input-animation-config.js';
20
+ import JSON5 from 'json5';
17
21
  import { css, html, LitElement } from 'lit';
18
22
  import { customElement, property } from 'lit/decorators.js';
19
23
  import { keyed } from 'lit/directives/keyed.js';
20
24
  let OxInputAnimationMap = class OxInputAnimationMap extends LitElement {
21
25
  constructor() {
22
26
  super(...arguments);
23
- this.value = {};
27
+ this._value = {};
28
+ // 파싱된 entry 캐시 (원본 구조 보존용)
29
+ this._parsed = [];
24
30
  this._onNewKeyChange = (e) => {
25
31
  e.stopPropagation();
26
32
  const input = e.target;
@@ -29,17 +35,23 @@ let OxInputAnimationMap = class OxInputAnimationMap extends LitElement {
29
35
  }
30
36
  };
31
37
  }
38
+ set value(v) {
39
+ const old = this._value;
40
+ this._value = this._normalize(v);
41
+ this.requestUpdate('value', old);
42
+ }
43
+ get value() { return this._value; }
32
44
  render() {
33
- const entries = this._toArray(this.value);
45
+ this._parsed = this._parseEntries(this.value);
34
46
  return html `
35
- ${entries.map((item, idx) => html `
47
+ ${this._parsed.map((item, idx) => html `
36
48
  <div class="entry" data-record data-index=${idx}>
37
49
  <div class="entry-key">
38
50
  <input type="text" data-key .value=${item.key} placeholder="key" />
39
51
  </div>
40
52
  <div class="entry-value">
41
53
  <ox-input-animation-config
42
- .value=${item.value}
54
+ .value=${item.animation}
43
55
  @change=${(e) => this._onConfigChange(e, idx)}
44
56
  ></ox-input-animation-config>
45
57
  </div>
@@ -68,13 +80,14 @@ let OxInputAnimationMap = class OxInputAnimationMap extends LitElement {
68
80
  `;
69
81
  }
70
82
  _onConfigChange(e, idx) {
71
- var _a;
72
83
  e.stopPropagation();
73
- const entries = this._toArray(this.value);
74
- const key = (_a = entries[idx]) === null || _a === void 0 ? void 0 : _a.key;
75
- if (!key)
84
+ const entry = this._parsed[idx];
85
+ if (!entry)
76
86
  return;
77
- this.value = Object.assign(Object.assign({}, this.value), { [key]: e.detail });
87
+ // 항상 { animation: config } 구조로 저장 (원본에 다른 키가 있으면 보존)
88
+ const newEntryValue = entry.isNested
89
+ ? Object.assign(Object.assign({}, entry.full), { animation: e.detail }) : { animation: e.detail };
90
+ this.value = Object.assign(Object.assign({}, this.value), { [entry.key]: newEntryValue });
78
91
  this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }));
79
92
  }
80
93
  firstUpdated() {
@@ -94,13 +107,16 @@ let OxInputAnimationMap = class OxInputAnimationMap extends LitElement {
94
107
  var _a;
95
108
  const records = this.renderRoot.querySelectorAll('[data-record]');
96
109
  const newMap = {};
97
- records.forEach(record => {
110
+ records.forEach((record, idx) => {
98
111
  var _a;
99
112
  const key = (_a = record.querySelector('[data-key]')) === null || _a === void 0 ? void 0 : _a.value;
100
113
  if (!key)
101
114
  return;
102
115
  const configEl = record.querySelector('ox-input-animation-config');
103
- newMap[key] = (configEl === null || configEl === void 0 ? void 0 : configEl.value) || {};
116
+ const animConfig = (configEl === null || configEl === void 0 ? void 0 : configEl.value) || {};
117
+ const entry = this._parsed[idx];
118
+ newMap[key] = (entry === null || entry === void 0 ? void 0 : entry.isNested)
119
+ ? Object.assign(Object.assign({}, entry.full), { animation: animConfig }) : { animation: animConfig };
104
120
  });
105
121
  if (((_a = this.value) === null || _a === void 0 ? void 0 : _a.default) !== undefined) {
106
122
  newMap.default = this.value.default;
@@ -118,33 +134,64 @@ let OxInputAnimationMap = class OxInputAnimationMap extends LitElement {
118
134
  return;
119
135
  const configEl = newEntry.querySelector('ox-input-animation-config');
120
136
  const config = (configEl === null || configEl === void 0 ? void 0 : configEl.value) || {};
121
- this.value = Object.assign(Object.assign({}, this.value), { [key]: config });
137
+ this.value = Object.assign(Object.assign({}, this.value), { [key]: { animation: config } });
122
138
  this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }));
123
139
  }
124
140
  _delete(idx) {
125
- var _a;
126
- const entries = this._toArray(this.value);
127
- const key = (_a = entries[idx]) === null || _a === void 0 ? void 0 : _a.key;
128
- if (!key)
141
+ const entry = this._parsed[idx];
142
+ if (!entry)
129
143
  return;
130
144
  const newMap = Object.assign({}, this.value);
131
- delete newMap[key];
145
+ delete newMap[entry.key];
132
146
  this.value = newMap;
133
147
  this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }));
134
148
  }
135
- _toArray(map) {
149
+ _parseEntries(map) {
136
150
  const arr = [];
137
151
  for (const key in map) {
138
152
  if (key === 'default')
139
153
  continue;
140
- const val = map[key];
141
- arr.push({
142
- key,
143
- value: typeof val === 'object' ? val : {}
144
- });
154
+ let val = map[key];
155
+ // JSON5 string → object 파싱
156
+ if (typeof val === 'string') {
157
+ try {
158
+ val = JSON5.parse(val);
159
+ }
160
+ catch (e) {
161
+ val = {};
162
+ }
163
+ }
164
+ if (typeof val !== 'object' || val === null) {
165
+ val = {};
166
+ }
167
+ // 중첩 구조 { animation: {...}, icon: {...} } 처리
168
+ const isNested = 'animation' in val;
169
+ const animation = isNested ? (val.animation || {}) : val;
170
+ arr.push({ key, animation, full: val, isNested });
145
171
  }
146
172
  return arr;
147
173
  }
174
+ // 입력 value의 모든 string entry를 object로 정규화 (JSON/JSON5 호환)
175
+ _normalize(map) {
176
+ if (!map || typeof map !== 'object')
177
+ return {};
178
+ const result = {};
179
+ for (const key in map) {
180
+ const val = map[key];
181
+ if (typeof val === 'string' && val.trim()) {
182
+ try {
183
+ result[key] = JSON5.parse(val);
184
+ }
185
+ catch (_a) {
186
+ result[key] = val;
187
+ }
188
+ }
189
+ else {
190
+ result[key] = val;
191
+ }
192
+ }
193
+ return result;
194
+ }
148
195
  };
149
196
  OxInputAnimationMap.styles = css `
150
197
  :host {
@@ -222,8 +269,9 @@ OxInputAnimationMap.styles = css `
222
269
  `;
223
270
  __decorate([
224
271
  property({ type: Object }),
225
- __metadata("design:type", Object)
226
- ], OxInputAnimationMap.prototype, "value", void 0);
272
+ __metadata("design:type", Object),
273
+ __metadata("design:paramtypes", [Object])
274
+ ], OxInputAnimationMap.prototype, "value", null);
227
275
  OxInputAnimationMap = __decorate([
228
276
  customElement('ox-input-animation-map')
229
277
  ], OxInputAnimationMap);