@operato/scene-visualizer 10.0.0-beta.7 → 10.0.0-beta.9

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.
Files changed (48) hide show
  1. package/dist/editors/index.d.ts +2 -0
  2. package/dist/editors/index.js +10 -0
  3. package/dist/editors/index.js.map +1 -1
  4. package/dist/editors/property-editor-gltf-fill-targets.d.ts +4 -1
  5. package/dist/editors/property-editor-gltf-fill-targets.js +178 -74
  6. package/dist/editors/property-editor-gltf-fill-targets.js.map +1 -1
  7. package/dist/editors/property-editor-gltf-info.d.ts +19 -4
  8. package/dist/editors/property-editor-gltf-info.js +279 -84
  9. package/dist/editors/property-editor-gltf-info.js.map +1 -1
  10. package/dist/editors/property-editor-gltf-play-targets.d.ts +25 -0
  11. package/dist/editors/property-editor-gltf-play-targets.js +388 -0
  12. package/dist/editors/property-editor-gltf-play-targets.js.map +1 -0
  13. package/dist/editors/property-editor-stocker-location.d.ts +13 -0
  14. package/dist/editors/property-editor-stocker-location.js +151 -0
  15. package/dist/editors/property-editor-stocker-location.js.map +1 -0
  16. package/dist/editors/property-editor-stocker-ports.d.ts +8 -0
  17. package/dist/editors/property-editor-stocker-ports.js +112 -0
  18. package/dist/editors/property-editor-stocker-ports.js.map +1 -0
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/stocker-3d.d.ts +23 -0
  23. package/dist/stocker-3d.js +352 -0
  24. package/dist/stocker-3d.js.map +1 -0
  25. package/dist/stocker-port-3d.d.ts +14 -0
  26. package/dist/stocker-port-3d.js +80 -0
  27. package/dist/stocker-port-3d.js.map +1 -0
  28. package/dist/stocker-port.d.ts +254 -0
  29. package/dist/stocker-port.js +123 -0
  30. package/dist/stocker-port.js.map +1 -0
  31. package/dist/stocker.d.ts +340 -0
  32. package/dist/stocker.js +370 -0
  33. package/dist/stocker.js.map +1 -0
  34. package/dist/templates/index.d.ts +40 -0
  35. package/dist/templates/index.js +5 -1
  36. package/dist/templates/index.js.map +1 -1
  37. package/dist/templates/stocker-port.d.ts +17 -0
  38. package/dist/templates/stocker-port.js +17 -0
  39. package/dist/templates/stocker-port.js.map +1 -0
  40. package/dist/templates/stocker.d.ts +27 -0
  41. package/dist/templates/stocker.js +38 -0
  42. package/dist/templates/stocker.js.map +1 -0
  43. package/package.json +2 -2
  44. package/translations/en.json +6 -0
  45. package/translations/ja.json +5 -0
  46. package/translations/ko.json +6 -1
  47. package/translations/ms.json +5 -0
  48. package/translations/zh.json +5 -0
@@ -0,0 +1,388 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ *
4
+ * GLTF 애니메이션 타겟 선택 에디터.
5
+ * All / None / Custom 모드로 started에 바인딩할 애니메이션 선택.
6
+ * 각 애니메이션을 개별 미리보기(play/stop) 가능.
7
+ */
8
+ import { __decorate } from "tslib";
9
+ import '@material/web/icon/icon.js';
10
+ import '@operato/i18n/ox-i18n.js';
11
+ import { css, html, nothing } from 'lit';
12
+ import { customElement, property, state } from 'lit/decorators.js';
13
+ import { OxPropertyEditor } from '@operato/property-editor';
14
+ import { ScrollbarStyles } from '@operato/styles/scrollbar-styles.js';
15
+ function getMode(value) {
16
+ if (value === '*')
17
+ return 'all';
18
+ if (value === 'none')
19
+ return 'none';
20
+ if (Array.isArray(value) && value.length > 0)
21
+ return 'custom';
22
+ if (Array.isArray(value))
23
+ return 'custom'; // empty array = custom with nothing
24
+ if (!value)
25
+ return 'all'; // undefined = default = all
26
+ return 'all';
27
+ }
28
+ let GLTFPlayTargetsEditor = class GLTFPlayTargetsEditor extends OxPropertyEditor {
29
+ static styles = [
30
+ ...OxPropertyEditor.styles,
31
+ ScrollbarStyles,
32
+ css `
33
+ .mode-selector {
34
+ display: flex;
35
+ gap: 0;
36
+ margin-bottom: 6px;
37
+ border: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.15));
38
+ border-radius: 6px;
39
+ overflow: hidden;
40
+ }
41
+
42
+ .mode-btn {
43
+ flex: 1;
44
+ padding: 5px 0;
45
+ font-size: 11px;
46
+ font-weight: 500;
47
+ text-align: center;
48
+ cursor: pointer;
49
+ background: var(--md-sys-color-surface-container, #f3edf7);
50
+ color: var(--md-sys-color-on-surface, #1c1b1f);
51
+ border: none;
52
+ border-right: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.1));
53
+ transition: all 0.15s;
54
+ }
55
+
56
+ .mode-btn:last-child {
57
+ border-right: none;
58
+ }
59
+
60
+ .mode-btn:hover {
61
+ background: var(--md-sys-color-surface-container-highest, #e6e0e9);
62
+ }
63
+
64
+ .mode-btn[active] {
65
+ background: var(--md-sys-color-primary, #6750a4);
66
+ color: var(--md-sys-color-on-primary, #fff);
67
+ }
68
+
69
+ .anim-list {
70
+ display: flex;
71
+ flex-direction: column;
72
+ gap: 1px;
73
+ max-height: 200px;
74
+ overflow-y: auto;
75
+ font-size: 12px;
76
+ }
77
+
78
+ .anim-item {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 6px;
82
+ padding: 4px 8px;
83
+ border-radius: 4px;
84
+ transition: background 0.1s;
85
+ }
86
+
87
+ .anim-item:hover {
88
+ background: var(--md-sys-color-primary-container, rgba(103, 80, 164, 0.12));
89
+ }
90
+
91
+ .anim-item input[type='checkbox'] {
92
+ margin: 0;
93
+ accent-color: var(--md-sys-color-primary, #6750a4);
94
+ }
95
+
96
+ .anim-item span {
97
+ flex: 1;
98
+ font-size: 12px;
99
+ color: var(--md-sys-color-on-surface, #1c1b1f);
100
+ }
101
+
102
+ .anim-item md-icon {
103
+ --md-icon-size: 18px;
104
+ cursor: pointer;
105
+ color: var(--md-sys-color-on-surface-variant, #666);
106
+ transition: color 0.15s;
107
+ }
108
+
109
+ .anim-item md-icon:hover {
110
+ color: var(--md-sys-color-primary, #6750a4);
111
+ }
112
+
113
+ .anim-item md-icon[playing] {
114
+ color: var(--md-sys-color-primary, #6750a4);
115
+ }
116
+
117
+ .mode-desc {
118
+ display: flex;
119
+ align-items: center;
120
+ gap: 6px;
121
+ font-size: 11px;
122
+ color: var(--md-sys-color-on-surface-variant, #666);
123
+ padding: 4px 0;
124
+ }
125
+
126
+ .mini-btn {
127
+ font-size: 10px;
128
+ padding: 2px 8px;
129
+ border: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.15));
130
+ border-radius: 4px;
131
+ background: var(--md-sys-color-surface-container, #f3edf7);
132
+ color: var(--md-sys-color-on-surface, #1c1b1f);
133
+ cursor: pointer;
134
+ }
135
+
136
+ .mini-btn:hover {
137
+ background: var(--md-sys-color-surface-container-highest, #e6e0e9);
138
+ }
139
+
140
+ .play-toggle {
141
+ display: flex;
142
+ align-items: center;
143
+ gap: 6px;
144
+ padding: 4px 0;
145
+ margin-bottom: 4px;
146
+ cursor: pointer;
147
+ font-size: 12px;
148
+ font-weight: 500;
149
+ color: var(--md-sys-color-on-surface, #1c1b1f);
150
+ }
151
+
152
+ .play-toggle input[type='checkbox'] {
153
+ accent-color: var(--md-sys-color-primary, #6750a4);
154
+ }
155
+
156
+ .empty {
157
+ color: var(--md-sys-color-on-surface-variant, #999);
158
+ font-size: 11px;
159
+ padding: 8px;
160
+ text-align: center;
161
+ }
162
+ `
163
+ ];
164
+ _component = null;
165
+ _lastExternalValue = undefined;
166
+ updated(changes) {
167
+ if (changes.has('src')) {
168
+ this.dispatchEvent(new CustomEvent('i-need-selected', {
169
+ bubbles: true,
170
+ composed: true,
171
+ detail: {
172
+ callback: (selected) => {
173
+ this._component = selected?.[0];
174
+ this._refreshAnimNames();
175
+ }
176
+ }
177
+ }));
178
+ }
179
+ }
180
+ _refreshAnimNames() {
181
+ if (!this._component) {
182
+ this._animNames = [];
183
+ return;
184
+ }
185
+ this._animNames = this._component.animationNames ?? [];
186
+ }
187
+ _pollTimer;
188
+ connectedCallback() {
189
+ super.connectedCallback();
190
+ // GLTF 비동기 로드 후 애니메이션 목록 갱신을 위한 폴링 (최대 20회 = 20초)
191
+ let retries = 0;
192
+ this._pollTimer = window.setInterval(() => {
193
+ if (!this._animNames?.length) {
194
+ this._refreshAnimNames();
195
+ }
196
+ if (this._animNames?.length || ++retries >= 20) {
197
+ clearInterval(this._pollTimer);
198
+ this._pollTimer = undefined;
199
+ }
200
+ }, 1000);
201
+ }
202
+ disconnectedCallback() {
203
+ super.disconnectedCallback();
204
+ if (this._pollTimer) {
205
+ clearInterval(this._pollTimer);
206
+ }
207
+ }
208
+ editorTemplate(value, _spec) {
209
+ if (!this._animNames?.length) {
210
+ this._refreshAnimNames();
211
+ }
212
+ if (value !== this._lastExternalValue) {
213
+ this._lastExternalValue = value;
214
+ this._mode = getMode(value);
215
+ }
216
+ const mode = this._mode;
217
+ const animCount = this._animNames?.length ?? 0;
218
+ const targets = Array.isArray(this.value) ? this.value : (Array.isArray(value) ? value : []);
219
+ const isPlaying = this._getPlayState();
220
+ return html `
221
+ <fieldset fullwidth>
222
+ ${animCount > 0
223
+ ? html `
224
+ <label class="play-toggle">
225
+ <input
226
+ type="checkbox"
227
+ .checked=${isPlaying}
228
+ @change=${(e) => {
229
+ e.stopPropagation();
230
+ this._setPlay(e.target.checked);
231
+ }}
232
+ />
233
+ <span>Play</span>
234
+ </label>
235
+
236
+ <div class="mode-selector">
237
+ <div class="mode-btn" ?active=${mode === 'all'} @click=${() => this._setMode('all')}>All</div>
238
+ <div class="mode-btn" ?active=${mode === 'none'} @click=${() => this._setMode('none')}>None</div>
239
+ <div class="mode-btn" ?active=${mode === 'custom'} @click=${() => this._setMode('custom')}>Custom</div>
240
+ </div>
241
+
242
+ ${mode === 'all'
243
+ ? html `<div class="mode-desc">${animCount} animations — play all</div>`
244
+ : nothing}
245
+ ${mode === 'none'
246
+ ? html `<div class="mode-desc">No animations auto-play</div>`
247
+ : nothing}
248
+ ${mode === 'custom'
249
+ ? html `
250
+ <div class="mode-desc">
251
+ <span style="flex:1">${targets.length} / ${animCount} selected</span>
252
+ <button class="mini-btn" @click=${() => this._applyValue([...(this._animNames ?? [])])}>All</button>
253
+ <button class="mini-btn" @click=${() => this._applyValue([])}>Clear</button>
254
+ </div>
255
+ `
256
+ : nothing}
257
+
258
+ <div class="anim-list">
259
+ ${(this._animNames ?? []).map(name => html `
260
+ <div class="anim-item">
261
+ ${mode === 'custom'
262
+ ? html `
263
+ <input
264
+ type="checkbox"
265
+ .checked=${targets.includes(name)}
266
+ @change=${(e) => {
267
+ e.stopPropagation();
268
+ this._onToggle(name, e.target.checked, targets);
269
+ }}
270
+ />
271
+ `
272
+ : nothing}
273
+ <span>${name}</span>
274
+ <md-icon
275
+ ?playing=${this._playingAnim === name}
276
+ @click=${() => this._togglePreview(name)}
277
+ title="preview"
278
+ >${this._playingAnim === name ? 'stop' : 'play_arrow'}</md-icon>
279
+ </div>
280
+ `)}
281
+ </div>
282
+ `
283
+ : html `<div class="empty">No animations available</div>`}
284
+ </fieldset>
285
+ `;
286
+ }
287
+ _setMode(mode) {
288
+ const prevMode = this._mode;
289
+ this._mode = mode;
290
+ let newValue;
291
+ switch (mode) {
292
+ case 'all':
293
+ newValue = '*';
294
+ break;
295
+ case 'none':
296
+ newValue = 'none';
297
+ break;
298
+ case 'custom':
299
+ if (prevMode === 'all') {
300
+ newValue = [...(this._animNames ?? [])];
301
+ }
302
+ else if (prevMode === 'none') {
303
+ newValue = [];
304
+ }
305
+ else {
306
+ newValue = Array.isArray(this.value) ? this.value : [];
307
+ }
308
+ break;
309
+ }
310
+ this._applyValue(newValue);
311
+ }
312
+ _onToggle(name, checked, current) {
313
+ const list = [...current];
314
+ if (checked) {
315
+ if (!list.includes(name))
316
+ list.push(name);
317
+ }
318
+ else {
319
+ const idx = list.indexOf(name);
320
+ if (idx >= 0)
321
+ list.splice(idx, 1);
322
+ }
323
+ this._applyValue(list);
324
+ }
325
+ _applyValue(newValue) {
326
+ this.value = newValue;
327
+ this.requestUpdate();
328
+ this.dispatchEvent(new CustomEvent('i-need-selected', {
329
+ bubbles: true,
330
+ composed: true,
331
+ detail: {
332
+ callback: (selected) => {
333
+ selected[0].set('playTargets', newValue);
334
+ }
335
+ }
336
+ }));
337
+ }
338
+ _getPlayState() {
339
+ return this._component?.state?.play !== false;
340
+ }
341
+ _setPlay(value) {
342
+ if (!this._component)
343
+ return;
344
+ this._component.set('play', value);
345
+ this.requestUpdate();
346
+ }
347
+ _togglePreview(name) {
348
+ if (!this._component)
349
+ return;
350
+ const ro = this._component.realObject;
351
+ if (!ro?._animationActions)
352
+ return;
353
+ const action = ro._animationActions.get(name);
354
+ if (!action)
355
+ return;
356
+ if (this._playingAnim === name) {
357
+ action.stop();
358
+ this._playingAnim = null;
359
+ }
360
+ else {
361
+ // 이전 프리뷰 중지
362
+ if (this._playingAnim) {
363
+ ro._animationActions.get(this._playingAnim)?.stop();
364
+ }
365
+ action.play();
366
+ this._playingAnim = name;
367
+ }
368
+ this._component.invalidate();
369
+ this.requestUpdate();
370
+ }
371
+ };
372
+ __decorate([
373
+ property({ type: String })
374
+ ], GLTFPlayTargetsEditor.prototype, "src", void 0);
375
+ __decorate([
376
+ state()
377
+ ], GLTFPlayTargetsEditor.prototype, "_animNames", void 0);
378
+ __decorate([
379
+ state()
380
+ ], GLTFPlayTargetsEditor.prototype, "_mode", void 0);
381
+ __decorate([
382
+ state()
383
+ ], GLTFPlayTargetsEditor.prototype, "_playingAnim", void 0);
384
+ GLTFPlayTargetsEditor = __decorate([
385
+ customElement('property-editor-gltf-play-targets')
386
+ ], GLTFPlayTargetsEditor);
387
+ export default GLTFPlayTargetsEditor;
388
+ //# sourceMappingURL=property-editor-gltf-play-targets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"property-editor-gltf-play-targets.js","sourceRoot":"","sources":["../../src/editors/property-editor-gltf-play-targets.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;;AAEH,OAAO,4BAA4B,CAAA;AACnC,OAAO,0BAA0B,CAAA;AAEjC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAkC,OAAO,EAAE,MAAM,KAAK,CAAA;AACxE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAAE,gBAAgB,EAAgB,MAAM,0BAA0B,CAAA;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAA;AAIrE,SAAS,OAAO,CAAC,KAAU;IACzB,IAAI,KAAK,KAAK,GAAG;QAAE,OAAO,KAAK,CAAA;IAC/B,IAAI,KAAK,KAAK,MAAM;QAAE,OAAO,MAAM,CAAA;IACnC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAA;IAC7D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAA,CAAC,oCAAoC;IAC9E,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA,CAAC,4BAA4B;IACrD,OAAO,KAAK,CAAA;AACd,CAAC;AAGc,IAAM,qBAAqB,GAA3B,MAAM,qBAAsB,SAAQ,gBAAgB;IACjE,MAAM,CAAC,MAAM,GAAG;QACd,GAAG,gBAAgB,CAAC,MAAM;QAC1B,eAAe;QACf,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAkIF;KACF,CAAA;IAQO,UAAU,GAAQ,IAAI,CAAA;IACtB,kBAAkB,GAAQ,SAAS,CAAA;IAE3C,OAAO,CAAC,OAA6B;QACnC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,iBAAiB,EAAE;gBACjC,OAAO,EAAE,IAAI;gBACb,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE;oBACN,QAAQ,EAAE,CAAC,QAAe,EAAE,EAAE;wBAC5B,IAAI,CAAC,UAAU,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAA;wBAC/B,IAAI,CAAC,iBAAiB,EAAE,CAAA;oBAC1B,CAAC;iBACF;aACF,CAAC,CACH,CAAA;QACH,CAAC;IACH,CAAC;IAEO,iBAAiB;QACvB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,IAAI,CAAC,UAAU,GAAG,EAAE,CAAA;YACpB,OAAM;QACR,CAAC;QAED,IAAI,CAAC,UAAU,GAAI,IAAI,CAAC,UAAU,CAAC,cAA2B,IAAI,EAAE,CAAA;IACtE,CAAC;IAEO,UAAU,CAAS;IAE3B,iBAAiB;QACf,KAAK,CAAC,iBAAiB,EAAE,CAAA;QACzB,kDAAkD;QAClD,IAAI,OAAO,GAAG,CAAC,CAAA;QACf,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE;YACxC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;gBAC7B,IAAI,CAAC,iBAAiB,EAAE,CAAA;YAC1B,CAAC;YACD,IAAI,IAAI,CAAC,UAAU,EAAE,MAAM,IAAI,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;gBAC/C,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;gBAC9B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAA;YAC7B,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,CAAA;IACV,CAAC;IAED,oBAAoB;QAClB,KAAK,CAAC,oBAAoB,EAAE,CAAA;QAC5B,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAChC,CAAC;IACH,CAAC;IAED,cAAc,CAAC,KAAU,EAAE,KAAmB;QAC5C,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;YAC7B,IAAI,CAAC,iBAAiB,EAAE,CAAA;QAC1B,CAAC;QAED,IAAI,KAAK,KAAK,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACtC,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAA;YAC/B,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;QAC7B,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAA;QACvB,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,IAAI,CAAC,CAAA;QAC9C,MAAM,OAAO,GAAa,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QAEtG,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,EAAE,CAAA;QAEtC,OAAO,IAAI,CAAA;;UAEL,SAAS,GAAG,CAAC;YACb,CAAC,CAAC,IAAI,CAAA;;;;6BAIa,SAAS;4BACV,CAAC,CAAQ,EAAE,EAAE;gBACrB,CAAC,CAAC,eAAe,EAAE,CAAA;gBACnB,IAAI,CAAC,QAAQ,CAAE,CAAC,CAAC,MAA2B,CAAC,OAAO,CAAC,CAAA;YACvD,CAAC;;;;;;gDAM6B,IAAI,KAAK,KAAK,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gDACnD,IAAI,KAAK,MAAM,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;gDACrD,IAAI,KAAK,QAAQ,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;;;gBAGzF,IAAI,KAAK,KAAK;gBACd,CAAC,CAAC,IAAI,CAAA,0BAA0B,SAAS,8BAA8B;gBACvE,CAAC,CAAC,OAAO;gBACT,IAAI,KAAK,MAAM;gBACf,CAAC,CAAC,IAAI,CAAA,sDAAsD;gBAC5D,CAAC,CAAC,OAAO;gBACT,IAAI,KAAK,QAAQ;gBACjB,CAAC,CAAC,IAAI,CAAA;;6CAEuB,OAAO,CAAC,MAAM,MAAM,SAAS;wDAClB,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC;wDACpD,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;;mBAE/D;gBACH,CAAC,CAAC,OAAO;;;kBAGP,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAC3B,IAAI,CAAC,EAAE,CAAC,IAAI,CAAA;;wBAEN,IAAI,KAAK,QAAQ;gBACjB,CAAC,CAAC,IAAI,CAAA;;;yCAGW,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;wCACvB,CAAC,CAAQ,EAAE,EAAE;oBACrB,CAAC,CAAC,eAAe,EAAE,CAAA;oBACnB,IAAI,CAAC,SAAS,CAAC,IAAI,EAAG,CAAC,CAAC,MAA2B,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;gBACvE,CAAC;;2BAEJ;gBACH,CAAC,CAAC,OAAO;8BACH,IAAI;;mCAEC,IAAI,CAAC,YAAY,KAAK,IAAI;iCAC5B,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC;;yBAEvC,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY;;mBAExD,CACF;;aAEJ;YACH,CAAC,CAAC,IAAI,CAAA,kDAAkD;;KAE7D,CAAA;IACH,CAAC;IAEO,QAAQ,CAAC,IAAc;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAA;QAC3B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QAEjB,IAAI,QAAa,CAAA;QACjB,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,KAAK;gBACR,QAAQ,GAAG,GAAG,CAAA;gBACd,MAAK;YACP,KAAK,MAAM;gBACT,QAAQ,GAAG,MAAM,CAAA;gBACjB,MAAK;YACP,KAAK,QAAQ;gBACX,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;oBACvB,QAAQ,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,CAAA;gBACzC,CAAC;qBAAM,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;oBAC/B,QAAQ,GAAG,EAAE,CAAA;gBACf,CAAC;qBAAM,CAAC;oBACN,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;gBACxD,CAAC;gBACD,MAAK;QACT,CAAC;QAED,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAA;IAC5B,CAAC;IAEO,SAAS,CAAC,IAAY,EAAE,OAAgB,EAAE,OAAiB;QACjE,MAAM,IAAI,GAAG,CAAC,GAAG,OAAO,CAAC,CAAA;QACzB,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC3C,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;YAC9B,IAAI,GAAG,IAAI,CAAC;gBAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;QACnC,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;IACxB,CAAC;IAEO,WAAW,CAAC,QAAa;QAC/B,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAA;QACrB,IAAI,CAAC,aAAa,EAAE,CAAA;QAEpB,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,iBAAiB,EAAE;YACjC,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE;gBACN,QAAQ,EAAE,CAAC,QAAe,EAAE,EAAE;oBAC5B,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAA;gBAC1C,CAAC;aACF;SACF,CAAC,CACH,CAAA;IACH,CAAC;IAEO,aAAa;QACnB,OAAO,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,KAAK,KAAK,CAAA;IAC/C,CAAC;IAEO,QAAQ,CAAC,KAAc;QAC7B,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAM;QAC5B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;QAClC,IAAI,CAAC,aAAa,EAAE,CAAA;IACtB,CAAC;IAEO,cAAc,CAAC,IAAY;QACjC,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAM;QAE5B,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,UAAiB,CAAA;QAC5C,IAAI,CAAC,EAAE,EAAE,iBAAiB;YAAE,OAAM;QAElC,MAAM,MAAM,GAAG,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAC7C,IAAI,CAAC,MAAM;YAAE,OAAM;QAEnB,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,EAAE,CAAA;YACb,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;QAC1B,CAAC;aAAM,CAAC;YACN,YAAY;YACZ,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtB,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,CAAA;YACrD,CAAC;YACD,MAAM,CAAC,IAAI,EAAE,CAAA;YACb,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;QAC1B,CAAC;QAED,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAA;QAC5B,IAAI,CAAC,aAAa,EAAE,CAAA;IACtB,CAAC;;AAxOmC;IAAnC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;kDAAgC;AAElC;IAAxB,KAAK,EAAE;yDAAqC;AACpB;IAAxB,KAAK,EAAE;oDAAgC;AACf;IAAxB,KAAK,EAAE;2DAA4C;AA7IjC,qBAAqB;IADzC,aAAa,CAAC,mCAAmC,CAAC;GAC9B,qBAAqB,CAkXzC;eAlXoB,qBAAqB","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * GLTF 애니메이션 타겟 선택 에디터.\n * All / None / Custom 모드로 started에 바인딩할 애니메이션 선택.\n * 각 애니메이션을 개별 미리보기(play/stop) 가능.\n */\n\nimport '@material/web/icon/icon.js'\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, PropertyValues, TemplateResult, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { OxPropertyEditor, PropertySpec } from '@operato/property-editor'\nimport { ScrollbarStyles } from '@operato/styles/scrollbar-styles.js'\n\ntype AnimMode = 'all' | 'none' | 'custom'\n\nfunction getMode(value: any): AnimMode {\n if (value === '*') return 'all'\n if (value === 'none') return 'none'\n if (Array.isArray(value) && value.length > 0) return 'custom'\n if (Array.isArray(value)) return 'custom' // empty array = custom with nothing\n if (!value) return 'all' // undefined = default = all\n return 'all'\n}\n\n@customElement('property-editor-gltf-play-targets')\nexport default class GLTFPlayTargetsEditor extends OxPropertyEditor {\n static styles = [\n ...OxPropertyEditor.styles,\n ScrollbarStyles,\n css`\n .mode-selector {\n display: flex;\n gap: 0;\n margin-bottom: 6px;\n border: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.15));\n border-radius: 6px;\n overflow: hidden;\n }\n\n .mode-btn {\n flex: 1;\n padding: 5px 0;\n font-size: 11px;\n font-weight: 500;\n text-align: center;\n cursor: pointer;\n background: var(--md-sys-color-surface-container, #f3edf7);\n color: var(--md-sys-color-on-surface, #1c1b1f);\n border: none;\n border-right: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.1));\n transition: all 0.15s;\n }\n\n .mode-btn:last-child {\n border-right: none;\n }\n\n .mode-btn:hover {\n background: var(--md-sys-color-surface-container-highest, #e6e0e9);\n }\n\n .mode-btn[active] {\n background: var(--md-sys-color-primary, #6750a4);\n color: var(--md-sys-color-on-primary, #fff);\n }\n\n .anim-list {\n display: flex;\n flex-direction: column;\n gap: 1px;\n max-height: 200px;\n overflow-y: auto;\n font-size: 12px;\n }\n\n .anim-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px;\n border-radius: 4px;\n transition: background 0.1s;\n }\n\n .anim-item:hover {\n background: var(--md-sys-color-primary-container, rgba(103, 80, 164, 0.12));\n }\n\n .anim-item input[type='checkbox'] {\n margin: 0;\n accent-color: var(--md-sys-color-primary, #6750a4);\n }\n\n .anim-item span {\n flex: 1;\n font-size: 12px;\n color: var(--md-sys-color-on-surface, #1c1b1f);\n }\n\n .anim-item md-icon {\n --md-icon-size: 18px;\n cursor: pointer;\n color: var(--md-sys-color-on-surface-variant, #666);\n transition: color 0.15s;\n }\n\n .anim-item md-icon:hover {\n color: var(--md-sys-color-primary, #6750a4);\n }\n\n .anim-item md-icon[playing] {\n color: var(--md-sys-color-primary, #6750a4);\n }\n\n .mode-desc {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n color: var(--md-sys-color-on-surface-variant, #666);\n padding: 4px 0;\n }\n\n .mini-btn {\n font-size: 10px;\n padding: 2px 8px;\n border: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.15));\n border-radius: 4px;\n background: var(--md-sys-color-surface-container, #f3edf7);\n color: var(--md-sys-color-on-surface, #1c1b1f);\n cursor: pointer;\n }\n\n .mini-btn:hover {\n background: var(--md-sys-color-surface-container-highest, #e6e0e9);\n }\n\n .play-toggle {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 0;\n margin-bottom: 4px;\n cursor: pointer;\n font-size: 12px;\n font-weight: 500;\n color: var(--md-sys-color-on-surface, #1c1b1f);\n }\n\n .play-toggle input[type='checkbox'] {\n accent-color: var(--md-sys-color-primary, #6750a4);\n }\n\n .empty {\n color: var(--md-sys-color-on-surface-variant, #999);\n font-size: 11px;\n padding: 8px;\n text-align: center;\n }\n `\n ]\n\n @property({ type: String }) declare src: string | undefined\n\n @state() declare private _animNames: string[]\n @state() declare private _mode: AnimMode\n @state() declare private _playingAnim: string | null\n\n private _component: any = null\n private _lastExternalValue: any = undefined\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('src')) {\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: (selected: any[]) => {\n this._component = selected?.[0]\n this._refreshAnimNames()\n }\n }\n })\n )\n }\n }\n\n private _refreshAnimNames() {\n if (!this._component) {\n this._animNames = []\n return\n }\n\n this._animNames = (this._component.animationNames as string[]) ?? []\n }\n\n private _pollTimer?: number\n\n connectedCallback() {\n super.connectedCallback()\n // GLTF 비동기 로드 후 애니메이션 목록 갱신을 위한 폴링 (최대 20회 = 20초)\n let retries = 0\n this._pollTimer = window.setInterval(() => {\n if (!this._animNames?.length) {\n this._refreshAnimNames()\n }\n if (this._animNames?.length || ++retries >= 20) {\n clearInterval(this._pollTimer)\n this._pollTimer = undefined\n }\n }, 1000)\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n if (this._pollTimer) {\n clearInterval(this._pollTimer)\n }\n }\n\n editorTemplate(value: any, _spec: PropertySpec): TemplateResult {\n if (!this._animNames?.length) {\n this._refreshAnimNames()\n }\n\n if (value !== this._lastExternalValue) {\n this._lastExternalValue = value\n this._mode = getMode(value)\n }\n\n const mode = this._mode\n const animCount = this._animNames?.length ?? 0\n const targets: string[] = Array.isArray(this.value) ? this.value : (Array.isArray(value) ? value : [])\n\n const isPlaying = this._getPlayState()\n\n return html`\n <fieldset fullwidth>\n ${animCount > 0\n ? html`\n <label class=\"play-toggle\">\n <input\n type=\"checkbox\"\n .checked=${isPlaying}\n @change=${(e: Event) => {\n e.stopPropagation()\n this._setPlay((e.target as HTMLInputElement).checked)\n }}\n />\n <span>Play</span>\n </label>\n\n <div class=\"mode-selector\">\n <div class=\"mode-btn\" ?active=${mode === 'all'} @click=${() => this._setMode('all')}>All</div>\n <div class=\"mode-btn\" ?active=${mode === 'none'} @click=${() => this._setMode('none')}>None</div>\n <div class=\"mode-btn\" ?active=${mode === 'custom'} @click=${() => this._setMode('custom')}>Custom</div>\n </div>\n\n ${mode === 'all'\n ? html`<div class=\"mode-desc\">${animCount} animations — play all</div>`\n : nothing}\n ${mode === 'none'\n ? html`<div class=\"mode-desc\">No animations auto-play</div>`\n : nothing}\n ${mode === 'custom'\n ? html`\n <div class=\"mode-desc\">\n <span style=\"flex:1\">${targets.length} / ${animCount} selected</span>\n <button class=\"mini-btn\" @click=${() => this._applyValue([...(this._animNames ?? [])])}>All</button>\n <button class=\"mini-btn\" @click=${() => this._applyValue([])}>Clear</button>\n </div>\n `\n : nothing}\n\n <div class=\"anim-list\">\n ${(this._animNames ?? []).map(\n name => html`\n <div class=\"anim-item\">\n ${mode === 'custom'\n ? html`\n <input\n type=\"checkbox\"\n .checked=${targets.includes(name)}\n @change=${(e: Event) => {\n e.stopPropagation()\n this._onToggle(name, (e.target as HTMLInputElement).checked, targets)\n }}\n />\n `\n : nothing}\n <span>${name}</span>\n <md-icon\n ?playing=${this._playingAnim === name}\n @click=${() => this._togglePreview(name)}\n title=\"preview\"\n >${this._playingAnim === name ? 'stop' : 'play_arrow'}</md-icon>\n </div>\n `\n )}\n </div>\n `\n : html`<div class=\"empty\">No animations available</div>`}\n </fieldset>\n `\n }\n\n private _setMode(mode: AnimMode) {\n const prevMode = this._mode\n this._mode = mode\n\n let newValue: any\n switch (mode) {\n case 'all':\n newValue = '*'\n break\n case 'none':\n newValue = 'none'\n break\n case 'custom':\n if (prevMode === 'all') {\n newValue = [...(this._animNames ?? [])]\n } else if (prevMode === 'none') {\n newValue = []\n } else {\n newValue = Array.isArray(this.value) ? this.value : []\n }\n break\n }\n\n this._applyValue(newValue)\n }\n\n private _onToggle(name: string, checked: boolean, current: string[]) {\n const list = [...current]\n if (checked) {\n if (!list.includes(name)) list.push(name)\n } else {\n const idx = list.indexOf(name)\n if (idx >= 0) list.splice(idx, 1)\n }\n this._applyValue(list)\n }\n\n private _applyValue(newValue: any) {\n this.value = newValue\n this.requestUpdate()\n\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: (selected: any[]) => {\n selected[0].set('playTargets', newValue)\n }\n }\n })\n )\n }\n\n private _getPlayState(): boolean {\n return this._component?.state?.play !== false\n }\n\n private _setPlay(value: boolean) {\n if (!this._component) return\n this._component.set('play', value)\n this.requestUpdate()\n }\n\n private _togglePreview(name: string) {\n if (!this._component) return\n\n const ro = this._component.realObject as any\n if (!ro?._animationActions) return\n\n const action = ro._animationActions.get(name)\n if (!action) return\n\n if (this._playingAnim === name) {\n action.stop()\n this._playingAnim = null\n } else {\n // 이전 프리뷰 중지\n if (this._playingAnim) {\n ro._animationActions.get(this._playingAnim)?.stop()\n }\n action.play()\n this._playingAnim = name\n }\n\n this._component.invalidate()\n this.requestUpdate()\n }\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import { TemplateResult } from 'lit';
2
+ import { OxPropertyEditor, PropertySpec } from '@operato/property-editor';
3
+ export default class StockerLocationEditor extends OxPropertyEditor {
4
+ static styles: import("lit").CSSResult[];
5
+ private _rule;
6
+ private _lastExternalValue;
7
+ constructor();
8
+ private _parseValue;
9
+ editorTemplate(value: any, _spec: PropertySpec): TemplateResult;
10
+ private _set;
11
+ private _setSideLabel;
12
+ private _apply;
13
+ }
@@ -0,0 +1,151 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ *
4
+ * Stocker Location Rule Editor
5
+ */
6
+ import { __decorate } from "tslib";
7
+ import { css, html } from 'lit';
8
+ import { customElement, state } from 'lit/decorators.js';
9
+ import { OxPropertyEditor } from '@operato/property-editor';
10
+ import { DEFAULT_LOCATION_RULE, buildLocationId } from '../stocker.js';
11
+ let StockerLocationEditor = class StockerLocationEditor extends OxPropertyEditor {
12
+ static styles = [
13
+ ...OxPropertyEditor.styles,
14
+ css `
15
+ .loc-grid { display: grid; grid-template-columns: 80px 1fr; gap: 3px 6px; font-size: 11px; align-items: center; }
16
+ .loc-grid label { color: #555; font-weight: 500; font-size: 10px; }
17
+ .loc-grid input, .loc-grid select {
18
+ width: 100%; box-sizing: border-box; font-size: 11px; padding: 3px 4px;
19
+ border: 1px solid rgba(0,0,0,0.15); border-radius: 4px; background: #fff; color: #1c1b1f;
20
+ }
21
+ .loc-grid input[type="number"] { width: 50px; }
22
+ .side-labels { display: flex; gap: 4px; align-items: center; font-size: 10px; }
23
+ .side-labels input { width: 30px; text-align: center; }
24
+ .section-title { grid-column: 1 / -1; font-weight: 600; font-size: 10px; color: #6750a4; margin-top: 6px; padding-bottom: 2px; border-bottom: 1px solid rgba(0,0,0,0.08); }
25
+ .preview { grid-column: 1 / -1; margin-top: 6px; padding: 6px 8px; background: #f3edf7; border-radius: 6px; font-family: monospace; font-size: 11px; }
26
+ .preview-title { font-size: 9px; font-weight: 600; color: #555; margin-bottom: 3px; }
27
+ .preview-samples { display: flex; flex-wrap: wrap; gap: 4px; }
28
+ .preview-sample { padding: 1px 5px; background: #fff; border-radius: 3px; font-size: 10px; }
29
+ `
30
+ ];
31
+ _lastExternalValue = undefined;
32
+ constructor() {
33
+ super();
34
+ this._rule = { ...DEFAULT_LOCATION_RULE };
35
+ }
36
+ _parseValue(value) {
37
+ if (!value)
38
+ return { ...DEFAULT_LOCATION_RULE };
39
+ if (typeof value === 'string') {
40
+ try {
41
+ return { ...DEFAULT_LOCATION_RULE, ...JSON.parse(value) };
42
+ }
43
+ catch {
44
+ return { ...DEFAULT_LOCATION_RULE };
45
+ }
46
+ }
47
+ return { ...DEFAULT_LOCATION_RULE, ...value };
48
+ }
49
+ editorTemplate(value, _spec) {
50
+ const serialized = typeof value === 'string' ? value : JSON.stringify(value);
51
+ if (serialized !== this._lastExternalValue) {
52
+ this._lastExternalValue = serialized;
53
+ this._rule = this._parseValue(value);
54
+ }
55
+ const r = this._rule;
56
+ const rackConfig = { bays: 10, levels: 5, depthCount: 1 };
57
+ const samples = [
58
+ buildLocationId(r, 'L', 1, 1, 1, rackConfig),
59
+ buildLocationId(r, 'L', 5, 3, 1, rackConfig),
60
+ buildLocationId(r, 'R', 10, 5, 1, rackConfig)
61
+ ];
62
+ return html `
63
+ <fieldset fullwidth>
64
+ <div class="loc-grid">
65
+ <div class="section-title">Pattern</div>
66
+ <label>Format</label>
67
+ <input type="text" .value=${r.pattern} placeholder="{side}{bay}-{level}-{depth}"
68
+ @change=${(e) => { e.stopPropagation(); this._set('pattern', e.target.value); }} />
69
+
70
+ <label>Side labels</label>
71
+ <div class="side-labels">
72
+ L=<input type="text" .value=${r.sideLabels.L}
73
+ @change=${(e) => { e.stopPropagation(); this._setSideLabel('L', e.target.value); }} />
74
+ R=<input type="text" .value=${r.sideLabels.R}
75
+ @change=${(e) => { e.stopPropagation(); this._setSideLabel('R', e.target.value); }} />
76
+ </div>
77
+
78
+ <div class="section-title">Direction</div>
79
+ <label>Bay</label>
80
+ <select .value=${r.bayDir}
81
+ @change=${(e) => { e.stopPropagation(); this._set('bayDir', e.target.value); }}>
82
+ <option value="ltr">Left → Right</option>
83
+ <option value="rtl">Right → Left</option>
84
+ </select>
85
+
86
+ <label>Level</label>
87
+ <select .value=${r.levelDir}
88
+ @change=${(e) => { e.stopPropagation(); this._set('levelDir', e.target.value); }}>
89
+ <option value="btt">Bottom → Top</option>
90
+ <option value="ttb">Top → Bottom</option>
91
+ </select>
92
+
93
+ <div class="section-title">Start number</div>
94
+ <label>Bay start</label>
95
+ <input type="number" .value=${String(r.bayStart)} min="0" step="1"
96
+ @change=${(e) => { e.stopPropagation(); this._set('bayStart', Number(e.target.value)); }} />
97
+ <label>Level start</label>
98
+ <input type="number" .value=${String(r.levelStart)} min="0" step="1"
99
+ @change=${(e) => { e.stopPropagation(); this._set('levelStart', Number(e.target.value)); }} />
100
+ <label>Depth start</label>
101
+ <input type="number" .value=${String(r.depthStart)} min="0" step="1"
102
+ @change=${(e) => { e.stopPropagation(); this._set('depthStart', Number(e.target.value)); }} />
103
+
104
+ <div class="section-title">Digit padding</div>
105
+ <label>Bay digits</label>
106
+ <input type="number" .value=${String(r.bayDigits)} min="0" max="4" step="1"
107
+ @change=${(e) => { e.stopPropagation(); this._set('bayDigits', Number(e.target.value)); }} />
108
+ <label>Level digits</label>
109
+ <input type="number" .value=${String(r.levelDigits)} min="0" max="4" step="1"
110
+ @change=${(e) => { e.stopPropagation(); this._set('levelDigits', Number(e.target.value)); }} />
111
+ <label>Depth digits</label>
112
+ <input type="number" .value=${String(r.depthDigits)} min="0" max="4" step="1"
113
+ @change=${(e) => { e.stopPropagation(); this._set('depthDigits', Number(e.target.value)); }} />
114
+
115
+ <div class="preview">
116
+ <div class="preview-title">Preview (10 bays, 5 levels)</div>
117
+ <div class="preview-samples">
118
+ ${samples.map(s => html `<span class="preview-sample">${s}</span>`)}
119
+ </div>
120
+ </div>
121
+ </div>
122
+ </fieldset>
123
+ `;
124
+ }
125
+ _set(field, value) {
126
+ this._rule = { ...this._rule, [field]: value };
127
+ this._apply();
128
+ }
129
+ _setSideLabel(side, value) {
130
+ this._rule = { ...this._rule, sideLabels: { ...this._rule.sideLabels, [side]: value || side } };
131
+ this._apply();
132
+ }
133
+ _apply() {
134
+ const jsonValue = JSON.stringify(this._rule);
135
+ this.value = jsonValue;
136
+ this._lastExternalValue = jsonValue;
137
+ this.requestUpdate();
138
+ this.dispatchEvent(new CustomEvent('i-need-selected', {
139
+ bubbles: true, composed: true,
140
+ detail: { callback: (selected) => { selected[0]?.set('locationRule', jsonValue); } }
141
+ }));
142
+ }
143
+ };
144
+ __decorate([
145
+ state()
146
+ ], StockerLocationEditor.prototype, "_rule", void 0);
147
+ StockerLocationEditor = __decorate([
148
+ customElement('property-editor-stocker-location')
149
+ ], StockerLocationEditor);
150
+ export default StockerLocationEditor;
151
+ //# sourceMappingURL=property-editor-stocker-location.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"property-editor-stocker-location.js","sourceRoot":"","sources":["../../src/editors/property-editor-stocker-location.ts"],"names":[],"mappings":"AAAA;;;;GAIG;;AAEH,OAAO,EAAE,GAAG,EAAE,IAAI,EAAkB,MAAM,KAAK,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAgB,MAAM,0BAA0B,CAAA;AAEzE,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAGvD,IAAM,qBAAqB,GAA3B,MAAM,qBAAsB,SAAQ,gBAAgB;IACjE,MAAM,CAAC,MAAM,GAAG;QACd,GAAG,gBAAgB,CAAC,MAAM;QAC1B,GAAG,CAAA;;;;;;;;;;;;;;;KAeF;KACF,CAAA;IAGO,kBAAkB,GAAQ,SAAS,CAAA;IAE3C;QACE,KAAK,EAAE,CAAA;QACP,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,qBAAqB,EAAE,CAAA;IAC3C,CAAC;IAEO,WAAW,CAAC,KAAU;QAC5B,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,GAAG,qBAAqB,EAAE,CAAA;QAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,IAAI,CAAC;gBAAC,OAAO,EAAE,GAAG,qBAAqB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAA;YAAC,CAAC;YAAC,MAAM,CAAC;gBAAC,OAAO,EAAE,GAAG,qBAAqB,EAAE,CAAA;YAAC,CAAC;QACjH,CAAC;QACD,OAAO,EAAE,GAAG,qBAAqB,EAAE,GAAG,KAAK,EAAE,CAAA;IAC/C,CAAC;IAED,cAAc,CAAC,KAAU,EAAE,KAAmB;QAC5C,MAAM,UAAU,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QAC5E,IAAI,UAAU,KAAK,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC3C,IAAI,CAAC,kBAAkB,GAAG,UAAU,CAAA;YACpC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;QACtC,CAAC;QAED,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAA;QACpB,MAAM,UAAU,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAA;QACzD,MAAM,OAAO,GAAG;YACd,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,UAAU,CAAC;YAC5C,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,UAAU,CAAC;YAC5C,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,UAAU,CAAC;SAC9C,CAAA;QAED,OAAO,IAAI,CAAA;;;;;sCAKuB,CAAC,CAAC,OAAO;sBACzB,CAAC,CAAQ,EAAE,EAAE,GAAG,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAG,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC;;;;0CAI7E,CAAC,CAAC,UAAU,CAAC,CAAC;wBAChC,CAAC,CAAQ,EAAE,EAAE,GAAG,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,EAAG,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC;0CAClF,CAAC,CAAC,UAAU,CAAC,CAAC;wBAChC,CAAC,CAAQ,EAAE,EAAE,GAAG,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,EAAG,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC;;;;;2BAKjG,CAAC,CAAC,MAAM;sBACb,CAAC,CAAQ,EAAE,EAAE,GAAG,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAG,CAAC,CAAC,MAA4B,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC;;;;;;2BAM5F,CAAC,CAAC,QAAQ;sBACf,CAAC,CAAQ,EAAE,EAAE,GAAG,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAG,CAAC,CAAC,MAA4B,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC;;;;;;;wCAOjF,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;sBACpC,CAAC,CAAQ,EAAE,EAAE,GAAG,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAE,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAC,CAAC,CAAA,CAAC,CAAC;;wCAExF,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC;sBACtC,CAAC,CAAQ,EAAE,EAAE,GAAG,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAE,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAC,CAAC,CAAA,CAAC,CAAC;;wCAE1F,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC;sBACtC,CAAC,CAAQ,EAAE,EAAE,GAAG,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAE,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAC,CAAC,CAAA,CAAC,CAAC;;;;wCAI1F,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;sBACrC,CAAC,CAAQ,EAAE,EAAE,GAAG,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAE,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAC,CAAC,CAAA,CAAC,CAAC;;wCAEzF,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC;sBACvC,CAAC,CAAQ,EAAE,EAAE,GAAG,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAE,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAC,CAAC,CAAA,CAAC,CAAC;;wCAE3F,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC;sBACvC,CAAC,CAAQ,EAAE,EAAE,GAAG,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAE,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAC,CAAC,CAAA,CAAC,CAAC;;;;;gBAKnH,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAA,gCAAgC,CAAC,SAAS,CAAC;;;;;KAK3E,CAAA;IACH,CAAC;IAEO,IAAI,CAAC,KAAa,EAAE,KAAU;QACpC,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAA;QAC9C,IAAI,CAAC,MAAM,EAAE,CAAA;IACf,CAAC;IAEO,aAAa,CAAC,IAAe,EAAE,KAAa;QAClD,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,IAAI,EAAE,EAAE,CAAA;QAC/F,IAAI,CAAC,MAAM,EAAE,CAAA;IACf,CAAC;IAEO,MAAM;QACZ,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAC5C,IAAI,CAAC,KAAK,GAAG,SAAS,CAAA;QACtB,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAA;QACnC,IAAI,CAAC,aAAa,EAAE,CAAA;QAEpB,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,iBAAiB,EAAE;YACpD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI;YAC7B,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,QAAe,EAAE,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,cAAc,EAAE,SAAS,CAAC,CAAA,CAAC,CAAC,EAAE;SAC3F,CAAC,CAAC,CAAA;IACL,CAAC;;AAnHwB;IAAxB,KAAK,EAAE;oDAAoC;AArBzB,qBAAqB;IADzC,aAAa,CAAC,kCAAkC,CAAC;GAC7B,qBAAqB,CAyIzC;eAzIoB,qBAAqB","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * Stocker Location Rule Editor\n */\n\nimport { css, html, TemplateResult } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { OxPropertyEditor, PropertySpec } from '@operato/property-editor'\nimport type { LocationRule } from '../stocker.js'\nimport { DEFAULT_LOCATION_RULE, buildLocationId } from '../stocker.js'\n\n@customElement('property-editor-stocker-location')\nexport default class StockerLocationEditor extends OxPropertyEditor {\n static styles = [\n ...OxPropertyEditor.styles,\n css`\n .loc-grid { display: grid; grid-template-columns: 80px 1fr; gap: 3px 6px; font-size: 11px; align-items: center; }\n .loc-grid label { color: #555; font-weight: 500; font-size: 10px; }\n .loc-grid input, .loc-grid select {\n width: 100%; box-sizing: border-box; font-size: 11px; padding: 3px 4px;\n border: 1px solid rgba(0,0,0,0.15); border-radius: 4px; background: #fff; color: #1c1b1f;\n }\n .loc-grid input[type=\"number\"] { width: 50px; }\n .side-labels { display: flex; gap: 4px; align-items: center; font-size: 10px; }\n .side-labels input { width: 30px; text-align: center; }\n .section-title { grid-column: 1 / -1; font-weight: 600; font-size: 10px; color: #6750a4; margin-top: 6px; padding-bottom: 2px; border-bottom: 1px solid rgba(0,0,0,0.08); }\n .preview { grid-column: 1 / -1; margin-top: 6px; padding: 6px 8px; background: #f3edf7; border-radius: 6px; font-family: monospace; font-size: 11px; }\n .preview-title { font-size: 9px; font-weight: 600; color: #555; margin-bottom: 3px; }\n .preview-samples { display: flex; flex-wrap: wrap; gap: 4px; }\n .preview-sample { padding: 1px 5px; background: #fff; border-radius: 3px; font-size: 10px; }\n `\n ]\n\n @state() private declare _rule: LocationRule\n private _lastExternalValue: any = undefined\n\n constructor() {\n super()\n this._rule = { ...DEFAULT_LOCATION_RULE }\n }\n\n private _parseValue(value: any): LocationRule {\n if (!value) return { ...DEFAULT_LOCATION_RULE }\n if (typeof value === 'string') {\n try { return { ...DEFAULT_LOCATION_RULE, ...JSON.parse(value) } } catch { return { ...DEFAULT_LOCATION_RULE } }\n }\n return { ...DEFAULT_LOCATION_RULE, ...value }\n }\n\n editorTemplate(value: any, _spec: PropertySpec): TemplateResult {\n const serialized = typeof value === 'string' ? value : JSON.stringify(value)\n if (serialized !== this._lastExternalValue) {\n this._lastExternalValue = serialized\n this._rule = this._parseValue(value)\n }\n\n const r = this._rule\n const rackConfig = { bays: 10, levels: 5, depthCount: 1 }\n const samples = [\n buildLocationId(r, 'L', 1, 1, 1, rackConfig),\n buildLocationId(r, 'L', 5, 3, 1, rackConfig),\n buildLocationId(r, 'R', 10, 5, 1, rackConfig)\n ]\n\n return html`\n <fieldset fullwidth>\n <div class=\"loc-grid\">\n <div class=\"section-title\">Pattern</div>\n <label>Format</label>\n <input type=\"text\" .value=${r.pattern} placeholder=\"{side}{bay}-{level}-{depth}\"\n @change=${(e: Event) => { e.stopPropagation(); this._set('pattern', (e.target as HTMLInputElement).value) }} />\n\n <label>Side labels</label>\n <div class=\"side-labels\">\n L=<input type=\"text\" .value=${r.sideLabels.L}\n @change=${(e: Event) => { e.stopPropagation(); this._setSideLabel('L', (e.target as HTMLInputElement).value) }} />\n R=<input type=\"text\" .value=${r.sideLabels.R}\n @change=${(e: Event) => { e.stopPropagation(); this._setSideLabel('R', (e.target as HTMLInputElement).value) }} />\n </div>\n\n <div class=\"section-title\">Direction</div>\n <label>Bay</label>\n <select .value=${r.bayDir}\n @change=${(e: Event) => { e.stopPropagation(); this._set('bayDir', (e.target as HTMLSelectElement).value) }}>\n <option value=\"ltr\">Left → Right</option>\n <option value=\"rtl\">Right → Left</option>\n </select>\n\n <label>Level</label>\n <select .value=${r.levelDir}\n @change=${(e: Event) => { e.stopPropagation(); this._set('levelDir', (e.target as HTMLSelectElement).value) }}>\n <option value=\"btt\">Bottom → Top</option>\n <option value=\"ttb\">Top → Bottom</option>\n </select>\n\n <div class=\"section-title\">Start number</div>\n <label>Bay start</label>\n <input type=\"number\" .value=${String(r.bayStart)} min=\"0\" step=\"1\"\n @change=${(e: Event) => { e.stopPropagation(); this._set('bayStart', Number((e.target as HTMLInputElement).value)) }} />\n <label>Level start</label>\n <input type=\"number\" .value=${String(r.levelStart)} min=\"0\" step=\"1\"\n @change=${(e: Event) => { e.stopPropagation(); this._set('levelStart', Number((e.target as HTMLInputElement).value)) }} />\n <label>Depth start</label>\n <input type=\"number\" .value=${String(r.depthStart)} min=\"0\" step=\"1\"\n @change=${(e: Event) => { e.stopPropagation(); this._set('depthStart', Number((e.target as HTMLInputElement).value)) }} />\n\n <div class=\"section-title\">Digit padding</div>\n <label>Bay digits</label>\n <input type=\"number\" .value=${String(r.bayDigits)} min=\"0\" max=\"4\" step=\"1\"\n @change=${(e: Event) => { e.stopPropagation(); this._set('bayDigits', Number((e.target as HTMLInputElement).value)) }} />\n <label>Level digits</label>\n <input type=\"number\" .value=${String(r.levelDigits)} min=\"0\" max=\"4\" step=\"1\"\n @change=${(e: Event) => { e.stopPropagation(); this._set('levelDigits', Number((e.target as HTMLInputElement).value)) }} />\n <label>Depth digits</label>\n <input type=\"number\" .value=${String(r.depthDigits)} min=\"0\" max=\"4\" step=\"1\"\n @change=${(e: Event) => { e.stopPropagation(); this._set('depthDigits', Number((e.target as HTMLInputElement).value)) }} />\n\n <div class=\"preview\">\n <div class=\"preview-title\">Preview (10 bays, 5 levels)</div>\n <div class=\"preview-samples\">\n ${samples.map(s => html`<span class=\"preview-sample\">${s}</span>`)}\n </div>\n </div>\n </div>\n </fieldset>\n `\n }\n\n private _set(field: string, value: any) {\n this._rule = { ...this._rule, [field]: value }\n this._apply()\n }\n\n private _setSideLabel(side: 'L' | 'R', value: string) {\n this._rule = { ...this._rule, sideLabels: { ...this._rule.sideLabels, [side]: value || side } }\n this._apply()\n }\n\n private _apply() {\n const jsonValue = JSON.stringify(this._rule)\n this.value = jsonValue\n this._lastExternalValue = jsonValue\n this.requestUpdate()\n\n this.dispatchEvent(new CustomEvent('i-need-selected', {\n bubbles: true, composed: true,\n detail: { callback: (selected: any[]) => { selected[0]?.set('locationRule', jsonValue) } }\n }))\n }\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import { TemplateResult } from 'lit';
2
+ import { OxPropertyEditor, PropertySpec } from '@operato/property-editor';
3
+ export default class StockerPortsEditor extends OxPropertyEditor {
4
+ static styles: import("lit").CSSResult[];
5
+ editorTemplate(value: any, _spec: PropertySpec): TemplateResult;
6
+ private _set;
7
+ private _applyPorts;
8
+ }