@operato/property-panel 10.1.0 → 10.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/src/ox-property-panel.js +29 -10
- package/dist/src/ox-property-panel.js.map +1 -1
- package/dist/src/property-panel/event-handlers/event-handlers-mapper.js +5 -2
- package/dist/src/property-panel/event-handlers/event-handlers-mapper.js.map +1 -1
- package/dist/src/property-panel/inspector/inspector.js +3 -2
- package/dist/src/property-panel/inspector/inspector.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
### [10.2.1](https://github.com/hatiolab/operato/compare/v10.2.0...v10.2.1) (2026-07-31)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### :bug: Bug Fix
|
|
10
|
+
|
|
11
|
+
* **property-panel:** 속성 패널을 브랜드 컨테이너에서 작업 표면으로 ([3f6edfb](https://github.com/hatiolab/operato/commit/3f6edfb5903f84a05cf2ca1d420a1155280ab7aa))
|
|
12
|
+
* **ui:** 다크에서 밝게 뜨던 면들 정리 — 팝업·모델러 툴바·네이티브 인풋 ([a81265a](https://github.com/hatiolab/operato/commit/a81265a2a2a6a6bf2dc86240877cc32610ec1fc6))
|
|
13
|
+
* **ui:** 트리 핸들·트리라인·슬라이더 knob 를 테마 따라가게 + 툴바 스크롤바 회귀 수정 ([8948a19](https://github.com/hatiolab/operato/commit/8948a19c54464ec4049bf741a04bec9fcd62b07f))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## [10.2.0](https://github.com/hatiolab/operato/compare/v10.1.0...v10.2.0) (2026-07-30)
|
|
18
|
+
|
|
19
|
+
**Note:** Version bump only for package @operato/property-panel
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
6
25
|
## [10.1.0](https://github.com/hatiolab/operato/compare/v10.0.0...v10.1.0) (2026-07-30)
|
|
7
26
|
|
|
8
27
|
**Note:** Version bump only for package @operato/property-panel
|
|
@@ -352,23 +352,37 @@ let OxPropertyPanel = class OxPropertyPanel extends ScopedElementsMixin(LitEleme
|
|
|
352
352
|
OxPropertyPanel.styles = [
|
|
353
353
|
ScrollbarStyles,
|
|
354
354
|
css `
|
|
355
|
+
/*
|
|
356
|
+
* 속성 패널은 **작업 표면**이다. 브랜드 컨테이너(secondary-container)가 아니라 surface 계단을 쓴다.
|
|
357
|
+
*
|
|
358
|
+
* 이전 기본값이 secondary-container 였는데, 팔레트에 따라 중간 톤 브랜드색이 되어 패널 전체가
|
|
359
|
+
* 그 색으로 물들었다(operato-board 다크: rgb(98 88 83) 탁한 갈색). 도구 패널이 브랜드색을
|
|
360
|
+
* 크게 차지할 이유가 없고, 안에 놓이는 입력·트리·칩이 모두 그 위에서 대비를 잃는다.
|
|
361
|
+
*
|
|
362
|
+
* 경계선의 --md-sys-color-border 는 M3 에 없는 이름이라 항상 fallback #ccc 로 떨어졌다
|
|
363
|
+
* (다크에서 밝은 선). 경계선 역할 토큰으로 바꾼다.
|
|
364
|
+
*/
|
|
355
365
|
:host {
|
|
356
|
-
border-left: 1px solid var(--md-sys-color-
|
|
366
|
+
border-left: 1px solid var(--md-sys-color-outline-variant);
|
|
357
367
|
width: 270px;
|
|
358
368
|
display: flex;
|
|
359
369
|
flex-direction: column;
|
|
360
|
-
background-color: var(--property-sidebar-background-color, var(--md-sys-color-
|
|
361
|
-
color: var(--property-sidebar-color, var(--md-sys-color-on-
|
|
370
|
+
background-color: var(--property-sidebar-background-color, var(--md-sys-color-surface-container));
|
|
371
|
+
color: var(--property-sidebar-color, var(--md-sys-color-on-surface));
|
|
362
372
|
user-select: none;
|
|
363
373
|
|
|
364
374
|
--input-padding: var(--spacing-small);
|
|
365
375
|
--label-font: var(--property-sidebar-fieldset-label, roboto);
|
|
366
376
|
}
|
|
367
377
|
|
|
378
|
+
/*
|
|
379
|
+
* 탭 줄은 패널보다 한 단 위 표면에 둔다. rgba(0,0,0,.08) 은 어두운 배경에서 보이지 않았고,
|
|
380
|
+
* opacity: 0.85 는 아이콘까지 흐리게 만들었다.
|
|
381
|
+
*/
|
|
368
382
|
[tab] {
|
|
369
383
|
display: flex;
|
|
370
|
-
background-color:
|
|
371
|
-
|
|
384
|
+
background-color: var(--property-sidebar-tab-background, var(--md-sys-color-surface-container-high));
|
|
385
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
372
386
|
}
|
|
373
387
|
|
|
374
388
|
[tab] md-icon {
|
|
@@ -378,15 +392,20 @@ OxPropertyPanel.styles = [
|
|
|
378
392
|
align-items: center;
|
|
379
393
|
justify-content: center;
|
|
380
394
|
|
|
381
|
-
color: var(--property-sidebar-tab-icon-color);
|
|
395
|
+
color: var(--property-sidebar-tab-icon-color, var(--md-sys-color-on-surface-variant));
|
|
382
396
|
height: 40px;
|
|
397
|
+
border-bottom: 2px solid transparent;
|
|
398
|
+
cursor: pointer;
|
|
383
399
|
}
|
|
384
400
|
|
|
401
|
+
/*
|
|
402
|
+
* 선택 표시를 밑줄로 바꿨다. 이전에는 흰색/검정 1px 로 좌우를 둘러 입체감을 내려 했는데,
|
|
403
|
+
* 두 색 모두 특정 모드에서만 보여서 다크에서는 왼쪽 흰 선만 남았다.
|
|
404
|
+
*/
|
|
385
405
|
[tab] [selected] {
|
|
386
|
-
background-color: var(--property-sidebar-background-color, var(--md-sys-color-
|
|
387
|
-
|
|
388
|
-
border-
|
|
389
|
-
opacity: 1;
|
|
406
|
+
background-color: var(--property-sidebar-background-color, var(--md-sys-color-surface-container));
|
|
407
|
+
color: var(--md-sys-color-on-surface);
|
|
408
|
+
border-bottom-color: var(--md-sys-color-primary);
|
|
390
409
|
}
|
|
391
410
|
|
|
392
411
|
[content] {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ox-property-panel.js","sourceRoot":"","sources":["../../src/ox-property-panel.ts"],"names":[],"mappings":"AAAA;;GAEG;;AAEH,OAAO,4BAA4B,CAAA;AAEnC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAkB,MAAM,KAAK,CAAA;AAC3D,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAClE,OAAO,SAAS,MAAM,wBAAwB,CAAA;AAG9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAA;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAA;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAA;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAA;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAA;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAA;AAGtE,IAAM,eAAe,GAArB,MAAM,eAAgB,SAAQ,mBAAmB,CAAC,UAAU,CAAC;IAA7D;;QAwDuB,UAAK,GAAiB,IAAI,CAAA;QAC1B,WAAM,GAAQ,EAAE,CAAA;QAChB,UAAK,GAAiB,EAAE,CAAA;QACzB,aAAQ,GAAgB,EAAE,CAAA;QAEzB,YAAO,GAAkB,OAAO,CAAA;QAC/B,cAAS,GAAY,KAAK,CAAA;QAC5B,UAAK,GAAU,EAAE,CAAA;QACjB,mBAAc,GAAU,EAAE,CAAA;QAErD,mBAAc,GAAqB,IAAI,CAAA;QAEvC,gDAAgD;QAC/B,qBAAgB,GAAgB,EAAE,CAAA;IAkVrD,CAAC;IA/UC,YAAY;QACV,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAkB,CAAC,CAAA;QACvG,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,eAAe,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAkB,CAAC,CAAA;IACrG,CAAC;IAED,OAAO,CAAC,MAA4B;QAClC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAA;QAC5C,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,uBAAuB,EAAE,CAAA;QACxD,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IAC7D,CAAC;IAED;;;;OAIG;IACK,uBAAuB;QAC7B,IAAI,IAAI,CAAC,uBAAuB;YAAE,YAAY,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAA;QAC5E,IAAI,CAAC,uBAAuB,GAAG,UAAU,CAAC,GAAG,EAAE;YAC7C,IAAI,CAAC,uBAAuB,GAAG,SAAS,CAAA;YACxC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAA;YACzB,0DAA0D;YAC1D,kEAAkE;YAClE,kEAAkE;YAClE,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,CAAE,IAAI,CAAC,KAAa,CAAC,WAAW,EAAE,CAAC;gBACvE,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAA;gBAC3B,OAAM;YACR,CAAC;YACD,IAAI,CAAC,gBAAgB,GAAG,GAAG,CAAA;YAC3B,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAA;QAC7B,CAAC,EAAE,EAAE,CAAC,CAAA;IACR,CAAC;IAED,oBAAoB;QAClB,KAAK,CAAC,oBAAoB,EAAE,CAAA;QAC5B,IAAI,IAAI,CAAC,uBAAuB;YAAE,YAAY,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAA;IAC9E,CAAC;IAED,MAAM,KAAK,cAAc;QACvB,OAAO;YACL,gBAAgB,EAAE,cAAc;YAChC,gBAAgB,EAAE,cAAc;YAChC,iBAAiB,EAAE,eAAe;YAClC,mBAAmB,EAAE,gBAAgB;YACrC,aAAa,EAAE,UAAU;YACzB,kBAAkB,EAAE,eAAe;YACnC,uBAAuB,EAAE,mBAAmB;YAC5C,iBAAiB,EAAE,cAAc;SAClC,CAAA;IACH,CAAC;IAEO,QAAQ,CAAC,QAAqB;QACpC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,WAAC,OAAA,CAAA,MAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,KAAK,0CAAE,IAAI,MAAK,OAAO,CAAA,EAAA,CAAC,CAAA;IACvD,CAAC;IAED,MAAM;QACJ,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAA;QACnD,OAAO,IAAI,CAAA;;;iBAGE,CAAC,CAAa,EAAE,EAAE;YACzB,IAAI,CAAC,OAAO,GAAI,CAAC,CAAC,MAAsB,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QAC/D,CAAC;;0CAEiC,OAAO,IAAI,OAAO;0CAClB,OAAO,IAAI,OAAO;2CACjB,OAAO,IAAI,QAAQ;2CACnB,OAAO,IAAI,QAAQ;6CACjB,OAAO,IAAI,UAAU;iDACjB,OAAO,IAAI,cAAc;8CAC5B,OAAO,IAAI,WAAW;;;;UAI1D,IAAI,CAAA;YACF;YACA,KAAK,EAAE,IAAI,CAAA;;yBAEE,IAAI,CAAC,KAAK;0BACT,IAAI,CAAC,MAAM;4BACT,IAAI,CAAC,gBAAgB;0BACvB,OAAO,IAAI,OAAO;;;aAG/B;YACD,KAAK,EAAE,IAAI,CAAA;;yBAEE,IAAI,CAAC,KAAK;4BACP,IAAI,CAAC,gBAAgB;yBACxB,IAAI,CAAC,KAAK;0BACT,OAAO,IAAI,OAAO;2BACjB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;;;aAGxC;YACD,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAClC,CAAC,CAAC,IAAI,CAAA;;;;iBAIH;gBACH,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC;oBAC/B,CAAC,CAAC,IAAI,CAAA;;+BAES,IAAI,CAAC,KAAK;+BACV,IAAI,CAAC,KAAK;gCACT,OAAO,IAAI,QAAQ;;;mBAGhC;oBACH,CAAC,CAAC,IAAI,CAAA;;+BAES,IAAI,CAAC,KAAK;gCACT,IAAI,CAAC,MAAM;kCACT,IAAI,CAAC,gBAAgB;gCACvB,OAAO,IAAI,QAAQ;;;mBAGhC;YACP,MAAM,EAAE,IAAI,CAAA;wCACgB,IAAI,CAAC,KAAK,WAAW,IAAI,CAAC,KAAK,YAAY,OAAO,IAAI,QAAQ;;aAEzF;YACD,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;gBACpC,CAAC,CAAC,IAAI,CAAA,EAAE;gBACR,CAAC,CAAC,IAAI,CAAA;;6BAES,IAAI,CAAC,KAAK;6BACV,IAAI,CAAC,KAAK;gCACP,IAAI,CAAC,gBAAgB;6BACxB,IAAI,CAAC,aAAa;sCACT,IAAI,CAAC,cAAc;8BAC3B,OAAO,IAAI,UAAU;;;iBAGlC;YACL,cAAc,EAAE,IAAI,CAAA;8CACc,IAAI,CAAC,KAAK,WAAW,IAAI,CAAC,KAAK,YAAY,OAAO,IAAI,cAAc;;aAErG;YACD,SAAS,EAAE,IAAI,CAAA;wCACa,IAAI,CAAC,KAAK,YAAY,OAAO,IAAI,WAAW;aACvE;SACF,CAAC,IAAI,CAAC,OAAQ,CAAC;SACjB;;KAEJ,CAAA;IACH,CAAC;IAEO,iBAAiB,CAAC,CAAc;;QACtC,IAAI,MAAM,GAAG,CAAC,CAAC,MAAM,CAAA;QAErB,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,qBAAqB;YACrB,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;YAE/E,0DAA0D;YAC1D,2CAA2C;YAC3C,sCAAsC;YACtC,MAAM,SAAS,GAAG,MAAA,IAAI,CAAC,cAAc,CAAC,MAAM,0CAAE,UAAU,CAAA;YACxD,IAAI,SAAS,IAAI,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,EAAE,CAAC;gBACtD,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;YAC3C,CAAC;QACH,CAAC;aAAM,CAAC;YACN,qBAAqB;YACrB,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QAC1G,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,oBAAoB,CAAC,IAAW;;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAA;QAC9B,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAA;QACnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,IAAI,CAAA,MAAA,GAAG,CAAC,CAAC,CAAC,0CAAE,IAAI,OAAK,MAAA,IAAI,CAAC,CAAC,CAAC,0CAAE,IAAI,CAAA;gBAAE,OAAO,IAAI,CAAA;QACjD,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAEO,eAAe,CAAC,CAAc;QACpC,IAAI,MAAM,GAAG,CAAC,CAAC,MAAM,CAAA;QAErB,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,OAAM;QACR,CAAC;QAED,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,qBAAqB;YACrB,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,EAAE;gBAC7B,IAAI,CAAC,cAAe,CAAC,MAAM,GAAG;oBAC5B,GAAG,IAAI,CAAC,cAAe,CAAC,MAAM;oBAC9B,GAAG,MAAM;iBACV,CAAA;YACH,CAAC,CAAC,CAAA;QACJ,CAAC;aAAM,CAAC;YACN,qBAAqB;YACrB,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,EAAE;gBAC7B,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;oBAChC,SAAS,CAAC,MAAM,GAAG;wBACjB,GAAG,SAAS,CAAC,MAAM;wBACnB,GAAG,MAAM;qBACV,CAAA;gBACH,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAEO,gBAAgB;QACtB,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,IAAI,CAAC,KAAK,GAAG;gBACX,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK;aAC7B,CAAA;YACD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;QAC5C,CAAC;IACH,CAAC;IAEO,0BAA0B;;QAChC,4DAA4D;QAC5D,8DAA8D;QAC9D,wDAAwD;QACxD,IAAI,MAAC,IAAI,CAAC,KAAa,0CAAE,WAAW;YAAE,OAAM;QAC5C,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAA;YAC5B,IAAI,CAAC,aAAa,GAAG,EAAE,CAAA;YACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;YACjB,IAAI,CAAC,MAAM,GAAG,EAAE,CAAA;QAClB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;QAClC,CAAC;IACH,CAAC;IAEO,WAAW,CAAC,SAAkB;QACpC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;QAC5C,aAAa,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAA;IACpC,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,MAAM,IAAI,CAAC,cAAc,CAAA;QAEzB,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;IAClE,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAAC,KAAkB;QAChD,MAAM,IAAI,CAAC,cAAc,CAAA;QAGzB,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;YAChC,sDAAsD;YACtD,sDAAsD;YACtD,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC,IAAI,CAAC,cAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;YACtE,IAAI,CAAC,KAAK,GAAG;gBACX,GAAG,IAAI,CAAC,cAAe,CAAC,KAAK;aAC9B,CAAA;YACD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,cAAe,CAAC,MAAM,CAAC,CAAA;QAC7C,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAC7B,YAAY;YAEZ,IAAI,CAAC,0BAA0B,EAAE,CAAA;QACnC,CAAC;aAAM,CAAC;YACN,YAAY;YAEZ,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAA;YAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACtC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;oBAChC,IAAI,GAAG,SAAS,CAAA;oBAChB,MAAK;gBACP,CAAC;YACH,CAAC;YAED,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAA;YAE5B,IAAI,IAAI;gBAAE,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;;gBAC/D,IAAI,CAAC,aAAa,GAAG,IAAI,CAAA;YAE9B,IAAI,CAAC,KAAK,GAAG;gBACX,IAAI,EAAE,IAAI;gBACV,KAAK,EAAE,CAAC;aACT,CAAA;YACD,IAAI,CAAC,MAAM,GAAG,EAAE,CAAA;QAClB,CAAC;IACH,CAAC;IAEO,iBAAiB,CAAC,SAA2B;QACnD,IAAI,SAAS,GAAG,IAAI,CAAC,cAAc,CAAA;QAEnC,IAAI,SAAS,EAAE,CAAC;YACd,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAA;QACtD,CAAC;QACD,IAAI,SAAS,EAAE,CAAC;YACd,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAA;QACrD,CAAC;QAED,IAAI,CAAC,cAAc,GAAG,SAAS,CAAA;IACjC,CAAC;IAEO,SAAS,CAAC,MAAc;QAC9B,IAAI,CAAC,MAAM,GAAG;YACZ,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC;YAC/B,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;SAClC,CAAA;IACH,CAAC;IAEO,OAAO,CAAC,QAAqB;;QACnC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAA;QACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;QACzB,OAAO,CAAA,MAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,OAAO,qDAAI,MAAI,MAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,WAAW,qDAAI,CAAA,CAAA;IACrD,CAAC;IAEO,WAAW,CAAC,QAAqB;;QACvC,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,KAAI,MAAA,MAAA,QAAQ,CAAC,CAAC,CAAC,0CAAE,WAAW,kDAAI,CAAA,CAAA;IAC5D,CAAC;IAEO,MAAM,CAAC,QAAqB;QAClC,IAAI,MAAM,GAAG,KAAK,CAAA;QAElB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACzC,IAAI,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;YAEtB,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;gBACnC,MAAM,GAAG,KAAK,CAAA;gBACd,OAAO,MAAM,CAAA;YACf,CAAC;YAED,MAAM,GAAG,IAAI,CAAA;QACf,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;;AArZM,sBAAM,GAAG;IACd,eAAe;IACf,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAkDF;CACF,AArDY,CAqDZ;AAE2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;8CAA2B;AAC1B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;+CAAiB;AAChB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;8CAAyB;AACzB;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;iDAA2B;AAC1B;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;sDAAmB;AACjB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;gDAAiC;AAC/B;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;kDAA2B;AAC5B;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;8CAAkB;AACjB;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;uDAA2B;AAKpC;IAAhB,KAAK,EAAE;yDAA2C;AArExC,eAAe;IAD3B,aAAa,CAAC,mBAAmB,CAAC;GACtB,eAAe,CAuZ3B","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport '@material/web/icon/icon.js'\n\nimport { css, html, LitElement, PropertyValues } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport deepClone from 'lodash-es/cloneDeep.js'\n\nimport { BOUNDS, Component, Model, Scene } from '@hatiolab/things-scene'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { PropertyDataBinding } from './property-panel/data-binding/data-binding.js'\nimport { PropertyEffects } from './property-panel/effects/effects.js'\nimport { SceneInspector } from './property-panel/inspector/inspector.js'\nimport { PropertyShapes } from './property-panel/shapes/shapes.js'\nimport { PropertySpecific } from './property-panel/specifics/specifics.js'\nimport { PropertyStyles } from './property-panel/styles/styles.js'\nimport { Property3D } from './property-panel/threed/threed.js'\nimport { PropertyScene3D } from './property-panel/threed/property-scene3d.js'\n\n@customElement('ox-property-panel')\nexport class OxPropertyPanel extends ScopedElementsMixin(LitElement) {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n border-left: 1px solid var(--md-sys-color-border, #ccc);\n width: 270px;\n display: flex;\n flex-direction: column;\n background-color: var(--property-sidebar-background-color, var(--md-sys-color-secondary-container));\n color: var(--property-sidebar-color, var(--md-sys-color-on-secondary-container));\n user-select: none;\n\n --input-padding: var(--spacing-small);\n --label-font: var(--property-sidebar-fieldset-label, roboto);\n }\n\n [tab] {\n display: flex;\n background-color: rgba(0, 0, 0, 0.08);\n opacity: 0.85;\n }\n\n [tab] md-icon {\n flex: 1;\n\n display: flex;\n align-items: center;\n justify-content: center;\n\n color: var(--property-sidebar-tab-icon-color);\n height: 40px;\n }\n\n [tab] [selected] {\n background-color: var(--property-sidebar-background-color, var(--md-sys-color-secondary-container));\n border-left: 1px solid rgba(255, 255, 255, 0.5);\n border-right: 1px solid rgba(0, 0, 0, 0.15);\n opacity: 1;\n }\n\n [content] {\n flex: 1;\n\n overflow: hidden;\n overflow-y: auto;\n\n --md-icon-size: 22px;\n }\n\n [content] > :not([active]) {\n display: none;\n }\n `\n ]\n\n @property({ type: Object }) scene: Scene | null = null\n @property({ type: Object }) bounds: any = {}\n @property({ type: Object }) model: Model | null = {}\n @property({ type: Array }) selected: Component[] = []\n @property({ type: Array }) specificProps: any\n @property({ type: String }) tabName: string | null = 'shape'\n @property({ type: Boolean }) collapsed: boolean = false\n @property({ type: Array }) fonts: any[] = []\n @property({ type: Array }) propertyEditor: any[] = []\n\n propertyTarget: Component | null = null\n\n /** 렌더에 실제 반영되는(디바운스된) 선택 — 마퀴 드래그 중 번뜩임 방지용. */\n @state() private _settledSelected: Component[] = []\n private _selectionDebounceTimer?: ReturnType<typeof setTimeout>\n\n firstUpdated() {\n this.renderRoot.addEventListener('property-change', this.onPropertyChanged.bind(this) as EventListener)\n this.renderRoot.addEventListener('bounds-change', this.onBoundsChanged.bind(this) as EventListener)\n }\n\n updated(change: PropertyValues<this>) {\n change.has('scene') && this.onSceneChanged()\n change.has('selected') && this._scheduleSelectedUpdate()\n change.has('collapsed') && this.onCollapsed(this.collapsed)\n }\n\n /**\n * 선택 변경을 트레일링 디바운스해, 속성창(shape/3d 등)이 마퀴 드래그 중 매 mousemove 마다\n * 재빌드되어 번뜩이는 것을 막는다. 드래그가 멈춘 뒤 최종 선택으로 한 번만 갱신한다.\n * (드래그 중 render 는 재실행되나 _settledSelected 가 고정이라 동일 출력 → Lit no-op.)\n */\n private _scheduleSelectedUpdate() {\n if (this._selectionDebounceTimer) clearTimeout(this._selectionDebounceTimer)\n this._selectionDebounceTimer = setTimeout(() => {\n this._selectionDebounceTimer = undefined\n const sel = this.selected\n // 배경 클릭 등으로 선택이 빈([]) 상태이고, 마퀴가 아니어서 곧 root(model-layer)로\n // auto-select 될 상황이면 — 빈 상태를 렌더하지 않고(빈→root 깜박임 방지) auto-select 만\n // 유발한다. 결과로 생기는 [model-layer] 가 다음 사이클에서 _settledSelected 를 갱신한다.\n if (sel.length === 0 && this.scene && !(this.scene as any).isSelecting) {\n this.onSelectedChanged(sel)\n return\n }\n this._settledSelected = sel\n this.onSelectedChanged(sel)\n }, 80)\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n if (this._selectionDebounceTimer) clearTimeout(this._selectionDebounceTimer)\n }\n\n static get scopedElements() {\n return {\n 'property-shape': PropertyShapes,\n 'property-style': PropertyStyles,\n 'property-effect': PropertyEffects,\n 'property-specific': PropertySpecific,\n 'property-3d': Property3D,\n 'property-scene3d': PropertyScene3D,\n 'property-data-binding': PropertyDataBinding,\n 'scene-inspector': SceneInspector\n }\n }\n\n private hasGroup(selected: Component[]) {\n return selected.some(c => c?.model?.type === 'group')\n }\n\n render() {\n var tabName = this.tabName ? this.tabName : 'shape'\n return html`\n <div\n tab\n @click=${(e: MouseEvent) => {\n this.tabName = (e.target as HTMLElement).getAttribute('name')\n }}\n >\n <md-icon name=\"shape\" ?selected=${tabName == 'shape'}>format_shapes</md-icon>\n <md-icon name=\"style\" ?selected=${tabName == 'style'}>palette</md-icon>\n <md-icon name=\"threed\" ?selected=${tabName == 'threed'}>view_in_ar</md-icon>\n <md-icon name=\"effect\" ?selected=${tabName == 'effect'}>movie_filter</md-icon>\n <md-icon name=\"specific\" ?selected=${tabName == 'specific'}>tune</md-icon>\n <md-icon name=\"data-binding\" ?selected=${tabName == 'data-binding'}>share</md-icon>\n <md-icon name=\"inspector\" ?selected=${tabName == 'inspector'}>visibility</md-icon>\n </div>\n\n <div content>\n ${html`\n ${{\n shape: html`\n <property-shape\n .value=${this.model}\n .bounds=${this.bounds}\n .selected=${this._settledSelected}\n ?active=${tabName == 'shape'}\n >\n </property-shape>\n `,\n style: html`\n <property-style\n .value=${this.model}\n .selected=${this._settledSelected}\n .fonts=${this.fonts}\n ?active=${tabName == 'style'}\n ?is-line=${this.isLine(this.selected)}\n >\n </property-style>\n `,\n threed: !this.is3dish(this.selected)\n ? html`\n <div style=\"display:flex;align-items:center;justify-content:center;height:100%;color:var(--md-sys-color-on-surface-variant,#666);font-size:12px;opacity:0.7;padding:16px;text-align:center;\">\n Selected component(s) do not support 3D properties\n </div>\n `\n : this.isRootModel(this.selected)\n ? html`\n <property-scene3d\n .value=${this.model}\n .scene=${this.scene}\n ?active=${tabName == 'threed'}\n >\n </property-scene3d>\n `\n : html`\n <property-3d\n .value=${this.model}\n .bounds=${this.bounds}\n .selected=${this._settledSelected}\n ?active=${tabName == 'threed'}\n >\n </property-3d>\n `,\n effect: html`\n <property-effect .value=${this.model} .scene=${this.scene} ?active=${tabName == 'effect'}>\n </property-effect>\n `,\n specific: this.hasGroup(this.selected)\n ? html``\n : html`\n <property-specific\n .value=${this.model}\n .scene=${this.scene}\n .selected=${this._settledSelected}\n .props=${this.specificProps}\n .propertyEditor=${this.propertyEditor}\n ?active=${tabName == 'specific'}\n >\n </property-specific>\n `,\n 'data-binding': html`\n <property-data-binding .scene=${this.scene} .value=${this.model} ?active=${tabName == 'data-binding'}>\n </property-data-binding>\n `,\n inspector: html`\n <scene-inspector .scene=${this.scene} ?active=${tabName == 'inspector'}></scene-inspector>\n `\n }[this.tabName!]}\n `}\n </div>\n `\n }\n\n private onPropertyChanged(e: CustomEvent) {\n var detail = e.detail\n\n if (this.propertyTarget) {\n /* 단일 컴포넌트의 경우에 적용 */\n this.scene && this.scene.undoableChange(() => this.propertyTarget!.set(detail))\n\n // nature.properties 가 동적인 컴포넌트(예: robot-arm 의 chainPreset\n // 변경 시 joint 개수 변화)를 위해 set 직후 nature 재평가.\n // 정적 nature 컴포넌트는 동일한 배열이 반환되어 비용 미미.\n const nextProps = this.propertyTarget.nature?.properties\n if (nextProps && this.specificPropsChanged(nextProps)) {\n this.specificProps = deepClone(nextProps)\n }\n } else {\n /* 여러 컴포넌트의 경우에 적용 */\n this.scene && this.scene.undoableChange(() => this.selected.forEach(component => component.set(detail)))\n }\n }\n\n /**\n * 새 properties 배열이 현재 specificProps 와 *구조상* 다른지 빠르게 검사.\n * 단순히 length + 각 항목의 name 만 비교 — value 변경은 model 측에서 처리되므로\n * specificProps 의 reference 를 바꿀 필요가 없다.\n */\n private specificPropsChanged(next: any[]): boolean {\n const cur = this.specificProps\n if (!cur || cur.length !== next.length) return true\n for (let i = 0; i < next.length; i++) {\n if (cur[i]?.name !== next[i]?.name) return true\n }\n return false\n }\n\n private onBoundsChanged(e: CustomEvent) {\n var detail = e.detail\n\n if (!this.scene) {\n return\n }\n\n if (this.propertyTarget) {\n /* 단일 컴포넌트의 경우에 적용 */\n this.scene.undoableChange(() => {\n this.propertyTarget!.bounds = {\n ...this.propertyTarget!.bounds,\n ...detail\n }\n })\n } else {\n /* 여러 컴포넌트의 경우에 적용 */\n this.scene.undoableChange(() => {\n this.selected.forEach(component => {\n component.bounds = {\n ...component.bounds,\n ...detail\n }\n })\n })\n }\n }\n\n private onChangedByScene() {\n if (this.propertyTarget) {\n this.model = {\n ...this.propertyTarget.model\n }\n this.setBounds(this.propertyTarget.bounds)\n }\n }\n\n private setPropertyTargetAsDefault() {\n // 마퀴(영역) 선택 드래그 중에는 root(model-layer) auto-select 를 하지 않는다.\n // 마퀴가 만드는 transient 빈 선택([])과 auto-select([model-layer]) 가 싸워\n // 속성창(shape/3d)이 번뜩이는 것을 막는다. 드래그 종료 후에만 scene 기본속성 표시.\n if ((this.scene as any)?.isSelecting) return\n if (!this.scene) {\n this.setPropertyTarget(null)\n this.specificProps = []\n this.model = null\n this.bounds = {}\n } else {\n this.scene.select('model-layer')\n }\n }\n\n private onCollapsed(collapsed: boolean) {\n this.style.display = collapsed ? 'none' : ''\n dispatchEvent(new Event('resize'))\n }\n\n private async onSceneChanged() {\n await this.updateComplete\n\n if (this.scene) this.selected = this.scene.select('model-layer')\n }\n\n private async onSelectedChanged(after: Component[]) {\n await this.updateComplete\n\n\n if (after.length == 1) {\n this.setPropertyTarget(after[0])\n // 컴포넌트 특성 속성(specific properties)을 먼저 바꾸고, 모델을 바꾸어준다.\n // 컴포넌트 속성에 따라 UI 컴포넌트가 준비되고, 이후에 모델값을 보여주도록 하기 위해서이다.\n this.specificProps = deepClone(this.propertyTarget!.nature.properties)\n this.model = {\n ...this.propertyTarget!.model\n }\n this.setBounds(this.propertyTarget!.bounds)\n } else if (after.length == 0) {\n // 선택이 안된 경우\n\n this.setPropertyTargetAsDefault()\n } else {\n // 다중 선택된 경우\n\n var type = after[0].model.type\n for (let i = 1; i < after.length; i++) {\n if (after[i].model.type != type) {\n type = undefined\n break\n }\n }\n\n this.setPropertyTarget(null)\n\n if (type) this.specificProps = deepClone(after[0].nature.properties)\n else this.specificProps = null\n\n this.model = {\n type: type,\n alpha: 1\n }\n this.bounds = {}\n }\n }\n\n private setPropertyTarget(newTarget: Component | null) {\n var oldTarget = this.propertyTarget\n\n if (oldTarget) {\n oldTarget.off('change', this.onChangedByScene, this)\n }\n if (newTarget) {\n newTarget.on('change', this.onChangedByScene, this)\n }\n\n this.propertyTarget = newTarget\n }\n\n private setBounds(bounds: BOUNDS) {\n this.bounds = {\n left: bounds.left,\n top: bounds.top,\n width: Math.round(bounds.width),\n height: Math.round(bounds.height)\n }\n }\n\n private is3dish(selected: Component[]) {\n if (selected.length === 0) return false\n const first = selected[0]\n return first?.is3dish?.() || first?.isRootModel?.()\n }\n\n private isRootModel(selected: Component[]) {\n return selected.length > 0 && selected[0]?.isRootModel?.()\n }\n\n private isLine(selected: Component[]) {\n var isLine = false\n\n for (var i = 0; i < selected.length; i++) {\n var comp = selected[i]\n\n if (!comp.isLine || !comp.isLine()) {\n isLine = false\n return isLine\n }\n\n isLine = true\n }\n\n return isLine\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ox-property-panel.js","sourceRoot":"","sources":["../../src/ox-property-panel.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport '@material/web/icon/icon.js'\n\nimport { css, html, LitElement, PropertyValues } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport deepClone from 'lodash-es/cloneDeep.js'\n\nimport { BOUNDS, Component, Model, Scene } from '@hatiolab/things-scene'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { PropertyDataBinding } from './property-panel/data-binding/data-binding.js'\nimport { PropertyEffects } from './property-panel/effects/effects.js'\nimport { SceneInspector } from './property-panel/inspector/inspector.js'\nimport { PropertyShapes } from './property-panel/shapes/shapes.js'\nimport { PropertySpecific } from './property-panel/specifics/specifics.js'\nimport { PropertyStyles } from './property-panel/styles/styles.js'\nimport { Property3D } from './property-panel/threed/threed.js'\nimport { PropertyScene3D } from './property-panel/threed/property-scene3d.js'\n\n@customElement('ox-property-panel')\nexport class OxPropertyPanel extends ScopedElementsMixin(LitElement) {\n static styles = [\n ScrollbarStyles,\n css`\n /*\n * 속성 패널은 **작업 표면**이다. 브랜드 컨테이너(secondary-container)가 아니라 surface 계단을 쓴다.\n *\n * 이전 기본값이 secondary-container 였는데, 팔레트에 따라 중간 톤 브랜드색이 되어 패널 전체가\n * 그 색으로 물들었다(operato-board 다크: rgb(98 88 83) 탁한 갈색). 도구 패널이 브랜드색을\n * 크게 차지할 이유가 없고, 안에 놓이는 입력·트리·칩이 모두 그 위에서 대비를 잃는다.\n *\n * 경계선의 --md-sys-color-border 는 M3 에 없는 이름이라 항상 fallback #ccc 로 떨어졌다\n * (다크에서 밝은 선). 경계선 역할 토큰으로 바꾼다.\n */\n :host {\n border-left: 1px solid var(--md-sys-color-outline-variant);\n width: 270px;\n display: flex;\n flex-direction: column;\n background-color: var(--property-sidebar-background-color, var(--md-sys-color-surface-container));\n color: var(--property-sidebar-color, var(--md-sys-color-on-surface));\n user-select: none;\n\n --input-padding: var(--spacing-small);\n --label-font: var(--property-sidebar-fieldset-label, roboto);\n }\n\n /*\n * 탭 줄은 패널보다 한 단 위 표면에 둔다. rgba(0,0,0,.08) 은 어두운 배경에서 보이지 않았고,\n * opacity: 0.85 는 아이콘까지 흐리게 만들었다.\n */\n [tab] {\n display: flex;\n background-color: var(--property-sidebar-tab-background, var(--md-sys-color-surface-container-high));\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n\n [tab] md-icon {\n flex: 1;\n\n display: flex;\n align-items: center;\n justify-content: center;\n\n color: var(--property-sidebar-tab-icon-color, var(--md-sys-color-on-surface-variant));\n height: 40px;\n border-bottom: 2px solid transparent;\n cursor: pointer;\n }\n\n /*\n * 선택 표시를 밑줄로 바꿨다. 이전에는 흰색/검정 1px 로 좌우를 둘러 입체감을 내려 했는데,\n * 두 색 모두 특정 모드에서만 보여서 다크에서는 왼쪽 흰 선만 남았다.\n */\n [tab] [selected] {\n background-color: var(--property-sidebar-background-color, var(--md-sys-color-surface-container));\n color: var(--md-sys-color-on-surface);\n border-bottom-color: var(--md-sys-color-primary);\n }\n\n [content] {\n flex: 1;\n\n overflow: hidden;\n overflow-y: auto;\n\n --md-icon-size: 22px;\n }\n\n [content] > :not([active]) {\n display: none;\n }\n `\n ]\n\n @property({ type: Object }) scene: Scene | null = null\n @property({ type: Object }) bounds: any = {}\n @property({ type: Object }) model: Model | null = {}\n @property({ type: Array }) selected: Component[] = []\n @property({ type: Array }) specificProps: any\n @property({ type: String }) tabName: string | null = 'shape'\n @property({ type: Boolean }) collapsed: boolean = false\n @property({ type: Array }) fonts: any[] = []\n @property({ type: Array }) propertyEditor: any[] = []\n\n propertyTarget: Component | null = null\n\n /** 렌더에 실제 반영되는(디바운스된) 선택 — 마퀴 드래그 중 번뜩임 방지용. */\n @state() private _settledSelected: Component[] = []\n private _selectionDebounceTimer?: ReturnType<typeof setTimeout>\n\n firstUpdated() {\n this.renderRoot.addEventListener('property-change', this.onPropertyChanged.bind(this) as EventListener)\n this.renderRoot.addEventListener('bounds-change', this.onBoundsChanged.bind(this) as EventListener)\n }\n\n updated(change: PropertyValues<this>) {\n change.has('scene') && this.onSceneChanged()\n change.has('selected') && this._scheduleSelectedUpdate()\n change.has('collapsed') && this.onCollapsed(this.collapsed)\n }\n\n /**\n * 선택 변경을 트레일링 디바운스해, 속성창(shape/3d 등)이 마퀴 드래그 중 매 mousemove 마다\n * 재빌드되어 번뜩이는 것을 막는다. 드래그가 멈춘 뒤 최종 선택으로 한 번만 갱신한다.\n * (드래그 중 render 는 재실행되나 _settledSelected 가 고정이라 동일 출력 → Lit no-op.)\n */\n private _scheduleSelectedUpdate() {\n if (this._selectionDebounceTimer) clearTimeout(this._selectionDebounceTimer)\n this._selectionDebounceTimer = setTimeout(() => {\n this._selectionDebounceTimer = undefined\n const sel = this.selected\n // 배경 클릭 등으로 선택이 빈([]) 상태이고, 마퀴가 아니어서 곧 root(model-layer)로\n // auto-select 될 상황이면 — 빈 상태를 렌더하지 않고(빈→root 깜박임 방지) auto-select 만\n // 유발한다. 결과로 생기는 [model-layer] 가 다음 사이클에서 _settledSelected 를 갱신한다.\n if (sel.length === 0 && this.scene && !(this.scene as any).isSelecting) {\n this.onSelectedChanged(sel)\n return\n }\n this._settledSelected = sel\n this.onSelectedChanged(sel)\n }, 80)\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n if (this._selectionDebounceTimer) clearTimeout(this._selectionDebounceTimer)\n }\n\n static get scopedElements() {\n return {\n 'property-shape': PropertyShapes,\n 'property-style': PropertyStyles,\n 'property-effect': PropertyEffects,\n 'property-specific': PropertySpecific,\n 'property-3d': Property3D,\n 'property-scene3d': PropertyScene3D,\n 'property-data-binding': PropertyDataBinding,\n 'scene-inspector': SceneInspector\n }\n }\n\n private hasGroup(selected: Component[]) {\n return selected.some(c => c?.model?.type === 'group')\n }\n\n render() {\n var tabName = this.tabName ? this.tabName : 'shape'\n return html`\n <div\n tab\n @click=${(e: MouseEvent) => {\n this.tabName = (e.target as HTMLElement).getAttribute('name')\n }}\n >\n <md-icon name=\"shape\" ?selected=${tabName == 'shape'}>format_shapes</md-icon>\n <md-icon name=\"style\" ?selected=${tabName == 'style'}>palette</md-icon>\n <md-icon name=\"threed\" ?selected=${tabName == 'threed'}>view_in_ar</md-icon>\n <md-icon name=\"effect\" ?selected=${tabName == 'effect'}>movie_filter</md-icon>\n <md-icon name=\"specific\" ?selected=${tabName == 'specific'}>tune</md-icon>\n <md-icon name=\"data-binding\" ?selected=${tabName == 'data-binding'}>share</md-icon>\n <md-icon name=\"inspector\" ?selected=${tabName == 'inspector'}>visibility</md-icon>\n </div>\n\n <div content>\n ${html`\n ${{\n shape: html`\n <property-shape\n .value=${this.model}\n .bounds=${this.bounds}\n .selected=${this._settledSelected}\n ?active=${tabName == 'shape'}\n >\n </property-shape>\n `,\n style: html`\n <property-style\n .value=${this.model}\n .selected=${this._settledSelected}\n .fonts=${this.fonts}\n ?active=${tabName == 'style'}\n ?is-line=${this.isLine(this.selected)}\n >\n </property-style>\n `,\n threed: !this.is3dish(this.selected)\n ? html`\n <div style=\"display:flex;align-items:center;justify-content:center;height:100%;color:var(--md-sys-color-on-surface-variant,#666);font-size:12px;opacity:0.7;padding:16px;text-align:center;\">\n Selected component(s) do not support 3D properties\n </div>\n `\n : this.isRootModel(this.selected)\n ? html`\n <property-scene3d\n .value=${this.model}\n .scene=${this.scene}\n ?active=${tabName == 'threed'}\n >\n </property-scene3d>\n `\n : html`\n <property-3d\n .value=${this.model}\n .bounds=${this.bounds}\n .selected=${this._settledSelected}\n ?active=${tabName == 'threed'}\n >\n </property-3d>\n `,\n effect: html`\n <property-effect .value=${this.model} .scene=${this.scene} ?active=${tabName == 'effect'}>\n </property-effect>\n `,\n specific: this.hasGroup(this.selected)\n ? html``\n : html`\n <property-specific\n .value=${this.model}\n .scene=${this.scene}\n .selected=${this._settledSelected}\n .props=${this.specificProps}\n .propertyEditor=${this.propertyEditor}\n ?active=${tabName == 'specific'}\n >\n </property-specific>\n `,\n 'data-binding': html`\n <property-data-binding .scene=${this.scene} .value=${this.model} ?active=${tabName == 'data-binding'}>\n </property-data-binding>\n `,\n inspector: html`\n <scene-inspector .scene=${this.scene} ?active=${tabName == 'inspector'}></scene-inspector>\n `\n }[this.tabName!]}\n `}\n </div>\n `\n }\n\n private onPropertyChanged(e: CustomEvent) {\n var detail = e.detail\n\n if (this.propertyTarget) {\n /* 단일 컴포넌트의 경우에 적용 */\n this.scene && this.scene.undoableChange(() => this.propertyTarget!.set(detail))\n\n // nature.properties 가 동적인 컴포넌트(예: robot-arm 의 chainPreset\n // 변경 시 joint 개수 변화)를 위해 set 직후 nature 재평가.\n // 정적 nature 컴포넌트는 동일한 배열이 반환되어 비용 미미.\n const nextProps = this.propertyTarget.nature?.properties\n if (nextProps && this.specificPropsChanged(nextProps)) {\n this.specificProps = deepClone(nextProps)\n }\n } else {\n /* 여러 컴포넌트의 경우에 적용 */\n this.scene && this.scene.undoableChange(() => this.selected.forEach(component => component.set(detail)))\n }\n }\n\n /**\n * 새 properties 배열이 현재 specificProps 와 *구조상* 다른지 빠르게 검사.\n * 단순히 length + 각 항목의 name 만 비교 — value 변경은 model 측에서 처리되므로\n * specificProps 의 reference 를 바꿀 필요가 없다.\n */\n private specificPropsChanged(next: any[]): boolean {\n const cur = this.specificProps\n if (!cur || cur.length !== next.length) return true\n for (let i = 0; i < next.length; i++) {\n if (cur[i]?.name !== next[i]?.name) return true\n }\n return false\n }\n\n private onBoundsChanged(e: CustomEvent) {\n var detail = e.detail\n\n if (!this.scene) {\n return\n }\n\n if (this.propertyTarget) {\n /* 단일 컴포넌트의 경우에 적용 */\n this.scene.undoableChange(() => {\n this.propertyTarget!.bounds = {\n ...this.propertyTarget!.bounds,\n ...detail\n }\n })\n } else {\n /* 여러 컴포넌트의 경우에 적용 */\n this.scene.undoableChange(() => {\n this.selected.forEach(component => {\n component.bounds = {\n ...component.bounds,\n ...detail\n }\n })\n })\n }\n }\n\n private onChangedByScene() {\n if (this.propertyTarget) {\n this.model = {\n ...this.propertyTarget.model\n }\n this.setBounds(this.propertyTarget.bounds)\n }\n }\n\n private setPropertyTargetAsDefault() {\n // 마퀴(영역) 선택 드래그 중에는 root(model-layer) auto-select 를 하지 않는다.\n // 마퀴가 만드는 transient 빈 선택([])과 auto-select([model-layer]) 가 싸워\n // 속성창(shape/3d)이 번뜩이는 것을 막는다. 드래그 종료 후에만 scene 기본속성 표시.\n if ((this.scene as any)?.isSelecting) return\n if (!this.scene) {\n this.setPropertyTarget(null)\n this.specificProps = []\n this.model = null\n this.bounds = {}\n } else {\n this.scene.select('model-layer')\n }\n }\n\n private onCollapsed(collapsed: boolean) {\n this.style.display = collapsed ? 'none' : ''\n dispatchEvent(new Event('resize'))\n }\n\n private async onSceneChanged() {\n await this.updateComplete\n\n if (this.scene) this.selected = this.scene.select('model-layer')\n }\n\n private async onSelectedChanged(after: Component[]) {\n await this.updateComplete\n\n\n if (after.length == 1) {\n this.setPropertyTarget(after[0])\n // 컴포넌트 특성 속성(specific properties)을 먼저 바꾸고, 모델을 바꾸어준다.\n // 컴포넌트 속성에 따라 UI 컴포넌트가 준비되고, 이후에 모델값을 보여주도록 하기 위해서이다.\n this.specificProps = deepClone(this.propertyTarget!.nature.properties)\n this.model = {\n ...this.propertyTarget!.model\n }\n this.setBounds(this.propertyTarget!.bounds)\n } else if (after.length == 0) {\n // 선택이 안된 경우\n\n this.setPropertyTargetAsDefault()\n } else {\n // 다중 선택된 경우\n\n var type = after[0].model.type\n for (let i = 1; i < after.length; i++) {\n if (after[i].model.type != type) {\n type = undefined\n break\n }\n }\n\n this.setPropertyTarget(null)\n\n if (type) this.specificProps = deepClone(after[0].nature.properties)\n else this.specificProps = null\n\n this.model = {\n type: type,\n alpha: 1\n }\n this.bounds = {}\n }\n }\n\n private setPropertyTarget(newTarget: Component | null) {\n var oldTarget = this.propertyTarget\n\n if (oldTarget) {\n oldTarget.off('change', this.onChangedByScene, this)\n }\n if (newTarget) {\n newTarget.on('change', this.onChangedByScene, this)\n }\n\n this.propertyTarget = newTarget\n }\n\n private setBounds(bounds: BOUNDS) {\n this.bounds = {\n left: bounds.left,\n top: bounds.top,\n width: Math.round(bounds.width),\n height: Math.round(bounds.height)\n }\n }\n\n private is3dish(selected: Component[]) {\n if (selected.length === 0) return false\n const first = selected[0]\n return first?.is3dish?.() || first?.isRootModel?.()\n }\n\n private isRootModel(selected: Component[]) {\n return selected.length > 0 && selected[0]?.isRootModel?.()\n }\n\n private isLine(selected: Component[]) {\n var isLine = false\n\n for (var i = 0; i < selected.length; i++) {\n var comp = selected[i]\n\n if (!comp.isLine || !comp.isLine()) {\n isLine = false\n return isLine\n }\n\n isLine = true\n }\n\n return isLine\n }\n}\n"]}
|
|
@@ -201,8 +201,11 @@ EventHandlersMapper.styles = [
|
|
|
201
201
|
padding: 3px 6px;
|
|
202
202
|
font-size: 13px;
|
|
203
203
|
border-radius: 4px;
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
/* #fff 와 rgba(0,0,0,.15) 는 다크에서 흰 칸 + 안 보이는 테두리가 된다.
|
|
205
|
+
프레임워크의 --input-field-* 가족을 쓰고, 없을 때도 자립하도록 md 토큰을 기본값으로 둔다. */
|
|
206
|
+
border: var(--input-field-border, 1px solid var(--md-sys-color-outline));
|
|
207
|
+
background: var(--input-field-background, var(--md-sys-color-surface-container-highest));
|
|
208
|
+
color: var(--input-field-color, var(--md-sys-color-on-surface));
|
|
206
209
|
}
|
|
207
210
|
|
|
208
211
|
ox-input-code {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-handlers-mapper.js","sourceRoot":"","sources":["../../../../src/property-panel/event-handlers/event-handlers-mapper.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * Single EventHandler editor — trigger / action / (code 또는 target+value).\n *\n * 'script' 도 action 의 한 값. action='script' 시 code 필드를, 다른 declarative\n * action 시 target+value 필드를 노출. action 미선택 (= '') 시 no-op.\n */\n\nimport '@operato/input/ox-input-code.js'\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { property } from 'lit/decorators.js'\n\nimport type { Scene } from '@hatiolab/things-scene'\n\n/** EventHandler model — things-scene 의 EventHandler interface 와 호환. */\nexport type EventHandlerSpec = {\n trigger: string // click | dblclick | mouseenter | mouseleave | longpress | mousedown | mouseup\n action?: string // '' | 'script' | 'data-toggle' | 'data-set' | 'emphasize' | ...\n code?: string // action='script' 시 JS 코드\n target?: string // selector\n value?: any\n options?: Record<string, any>\n disabled?: boolean\n}\n\nconst TRIGGERS = [\n 'click', 'dblclick',\n 'mouseenter', 'mouseleave',\n 'longpress',\n 'mousedown', 'mouseup'\n]\n\n/** 'script' 는 action 의 한 값. */\nconst ACTIONS = [\n '',\n 'script',\n 'data-toggle',\n 'data-tristate',\n 'data-spreading',\n 'data-set',\n 'partial-data-set',\n 'value-set',\n 'partial-value-set',\n 'info-window',\n 'emphasize'\n]\n\nexport class EventHandlersMapper extends LitElement {\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 10px 10px 10px;\n /* 카드 외곽 — tabs (위) + toolbar (중간) + mapper (아래) 가 하나의 카드.\n * 위쪽 1px 는 toolbar 와의 internal divider. radius 6px 으로 통일. */\n border: 1px solid rgba(0, 0, 0, 0.18);\n border-radius: 0 0 6px 6px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n line-height: 2;\n background: var(--md-sys-color-surface, #fff);\n min-width: 0;\n width: 100%;\n box-sizing: border-box;\n overflow: hidden;\n flex: 1;\n min-height: 0;\n }\n\n [script-field] {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex: 1;\n margin-top: 6px;\n padding-top: 10px;\n /* trigger/action/target/value 입력 묶음과 code editor 사이의 구분선.\n * dashed 대신 solid + 옅은 opacity 로 modern. */\n border-top: 1px solid rgba(0, 0, 0, 0.08);\n }\n\n [script-field] > .script-header {\n display: flex;\n align-items: baseline;\n gap: 8px;\n margin-bottom: 6px;\n font-size: 10.5px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.6px;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n }\n\n [script-field] > .script-header small {\n font-weight: 400;\n text-transform: none;\n letter-spacing: 0;\n opacity: 0.65;\n font-size: 10.5px;\n }\n\n .row {\n display: grid;\n grid-template-columns: 80px 1fr;\n gap: 6px;\n align-items: center;\n min-width: 0;\n }\n\n .row > select,\n .row > input[type='text'] {\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n padding: 3px 6px;\n font-size: 13px;\n border-radius: 4px;\n border: 1px solid rgba(0, 0, 0, 0.15);\n background: #fff;\n }\n\n ox-input-code {\n /* display 는 *지정하지 않는다* — outside selector 가 ox-input-code 의\n * :host { display: flex; flex-direction: column } 를 덮어쓰면, 그 안의\n * .cm-editor { flex: 1 } 가 작동을 멈춰 텍스트 라인 1개 크기로 축소된다. */\n width: 100%;\n max-width: 100%;\n min-width: 0;\n box-sizing: border-box;\n overflow: hidden;\n min-height: 180px;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 4px;\n /* popup 모드에서 script-field 의 남은 세로 공간을 모두 차지 — 아래 빈공간 X.\n * inline 모드에서는 script-field 가 grow 하지 않으므로 min-height 180px 만 사용. */\n flex: 1;\n }\n\n label {\n font-size: 12px;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n }\n `\n ]\n\n @property({ type: Object }) handler: EventHandlerSpec = { trigger: 'click', action: '' }\n @property({ type: Object }) scene?: Scene\n\n render() {\n const h = this.handler || { trigger: 'click', action: '' }\n return html`\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.trigger\">Trigger</ox-i18n></label>\n <select\n .value=${h.trigger || 'click'}\n @change=${(e: Event) => this._update('trigger', (e.target as HTMLSelectElement).value)}\n >\n ${TRIGGERS.map(t => html`<option value=${t} ?selected=${h.trigger === t}>${t}</option>`)}\n </select>\n </div>\n\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.action\">Action</ox-i18n></label>\n <select\n .value=${h.action ?? ''}\n @change=${(e: Event) => this._update('action', (e.target as HTMLSelectElement).value)}\n >\n ${ACTIONS.map(a => html`<option value=${a} ?selected=${(h.action ?? '') === a}>${a || '(none)'}</option>`)}\n </select>\n </div>\n\n ${h.action === 'script'\n ? this._renderScriptFields(h)\n : (h.action ? this._renderActionFields(h) : null)}\n `\n }\n\n private _renderScriptFields(h: EventHandlerSpec) {\n return html`\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.target\">Target</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"optional selector (#id, .class, ...)\"\n .value=${h.target || ''}\n @change=${(e: Event) => this._update('target', (e.target as HTMLInputElement).value)}\n />\n </div>\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.value\">Value</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"optional · script 안 value 변수로 전달\"\n .value=${h.value !== undefined ? String(h.value) : ''}\n @change=${(e: Event) => this._update('value', (e.target as HTMLInputElement).value)}\n />\n </div>\n <div script-field>\n <div class=\"script-header\">\n <ox-i18n msgid=\"label.script\">Script</ox-i18n>\n <small>vars: component, scene, event, targets, value · await 지원</small>\n </div>\n <ox-input-code\n mode=\"javascript\"\n .value=${h.code || ''}\n @change=${(e: any) => this._update('code', e.detail?.value ?? e.target?.value ?? '')}\n ></ox-input-code>\n </div>\n `\n }\n\n private _renderActionFields(h: EventHandlerSpec) {\n return html`\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.target\">Target</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"selector (#id, .class, ...)\"\n .value=${h.target || ''}\n @change=${(e: Event) => this._update('target', (e.target as HTMLInputElement).value)}\n />\n </div>\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.value\">Value</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"value or accessor\"\n .value=${h.value !== undefined ? String(h.value) : ''}\n @change=${(e: Event) => this._update('value', (e.target as HTMLInputElement).value)}\n />\n </div>\n `\n }\n\n private _update(key: keyof EventHandlerSpec, value: any) {\n const next: EventHandlerSpec = { ...(this.handler || { trigger: 'click', action: '' }), [key]: value }\n // action 전환 시 비-script 진입은 code 정리. script 진입 시 target/value 는\n // 의미가 호환 (target=findAll selector, value=script 입력) 이라 유지.\n if (key === 'action' && value !== 'script') {\n delete next.code\n }\n this.handler = next\n this.dispatchEvent(\n new CustomEvent('value-change', {\n bubbles: true,\n composed: true,\n detail: { handler: next, changed: { [key]: value } }\n })\n )\n }\n}\n\ncustomElements.define('event-handlers-mapper', EventHandlersMapper)\n"]}
|
|
1
|
+
{"version":3,"file":"event-handlers-mapper.js","sourceRoot":"","sources":["../../../../src/property-panel/event-handlers/event-handlers-mapper.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n *\n * Single EventHandler editor — trigger / action / (code 또는 target+value).\n *\n * 'script' 도 action 의 한 값. action='script' 시 code 필드를, 다른 declarative\n * action 시 target+value 필드를 노출. action 미선택 (= '') 시 no-op.\n */\n\nimport '@operato/input/ox-input-code.js'\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { property } from 'lit/decorators.js'\n\nimport type { Scene } from '@hatiolab/things-scene'\n\n/** EventHandler model — things-scene 의 EventHandler interface 와 호환. */\nexport type EventHandlerSpec = {\n trigger: string // click | dblclick | mouseenter | mouseleave | longpress | mousedown | mouseup\n action?: string // '' | 'script' | 'data-toggle' | 'data-set' | 'emphasize' | ...\n code?: string // action='script' 시 JS 코드\n target?: string // selector\n value?: any\n options?: Record<string, any>\n disabled?: boolean\n}\n\nconst TRIGGERS = [\n 'click', 'dblclick',\n 'mouseenter', 'mouseleave',\n 'longpress',\n 'mousedown', 'mouseup'\n]\n\n/** 'script' 는 action 의 한 값. */\nconst ACTIONS = [\n '',\n 'script',\n 'data-toggle',\n 'data-tristate',\n 'data-spreading',\n 'data-set',\n 'partial-data-set',\n 'value-set',\n 'partial-value-set',\n 'info-window',\n 'emphasize'\n]\n\nexport class EventHandlersMapper extends LitElement {\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 10px 10px 10px;\n /* 카드 외곽 — tabs (위) + toolbar (중간) + mapper (아래) 가 하나의 카드.\n * 위쪽 1px 는 toolbar 와의 internal divider. radius 6px 으로 통일. */\n border: 1px solid rgba(0, 0, 0, 0.18);\n border-radius: 0 0 6px 6px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n line-height: 2;\n background: var(--md-sys-color-surface, #fff);\n min-width: 0;\n width: 100%;\n box-sizing: border-box;\n overflow: hidden;\n flex: 1;\n min-height: 0;\n }\n\n [script-field] {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex: 1;\n margin-top: 6px;\n padding-top: 10px;\n /* trigger/action/target/value 입력 묶음과 code editor 사이의 구분선.\n * dashed 대신 solid + 옅은 opacity 로 modern. */\n border-top: 1px solid rgba(0, 0, 0, 0.08);\n }\n\n [script-field] > .script-header {\n display: flex;\n align-items: baseline;\n gap: 8px;\n margin-bottom: 6px;\n font-size: 10.5px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.6px;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n }\n\n [script-field] > .script-header small {\n font-weight: 400;\n text-transform: none;\n letter-spacing: 0;\n opacity: 0.65;\n font-size: 10.5px;\n }\n\n .row {\n display: grid;\n grid-template-columns: 80px 1fr;\n gap: 6px;\n align-items: center;\n min-width: 0;\n }\n\n .row > select,\n .row > input[type='text'] {\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n padding: 3px 6px;\n font-size: 13px;\n border-radius: 4px;\n /* #fff 와 rgba(0,0,0,.15) 는 다크에서 흰 칸 + 안 보이는 테두리가 된다.\n 프레임워크의 --input-field-* 가족을 쓰고, 없을 때도 자립하도록 md 토큰을 기본값으로 둔다. */\n border: var(--input-field-border, 1px solid var(--md-sys-color-outline));\n background: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\n }\n\n ox-input-code {\n /* display 는 *지정하지 않는다* — outside selector 가 ox-input-code 의\n * :host { display: flex; flex-direction: column } 를 덮어쓰면, 그 안의\n * .cm-editor { flex: 1 } 가 작동을 멈춰 텍스트 라인 1개 크기로 축소된다. */\n width: 100%;\n max-width: 100%;\n min-width: 0;\n box-sizing: border-box;\n overflow: hidden;\n min-height: 180px;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 4px;\n /* popup 모드에서 script-field 의 남은 세로 공간을 모두 차지 — 아래 빈공간 X.\n * inline 모드에서는 script-field 가 grow 하지 않으므로 min-height 180px 만 사용. */\n flex: 1;\n }\n\n label {\n font-size: 12px;\n color: var(--md-sys-color-on-surface-variant, #49454f);\n }\n `\n ]\n\n @property({ type: Object }) handler: EventHandlerSpec = { trigger: 'click', action: '' }\n @property({ type: Object }) scene?: Scene\n\n render() {\n const h = this.handler || { trigger: 'click', action: '' }\n return html`\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.trigger\">Trigger</ox-i18n></label>\n <select\n .value=${h.trigger || 'click'}\n @change=${(e: Event) => this._update('trigger', (e.target as HTMLSelectElement).value)}\n >\n ${TRIGGERS.map(t => html`<option value=${t} ?selected=${h.trigger === t}>${t}</option>`)}\n </select>\n </div>\n\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.action\">Action</ox-i18n></label>\n <select\n .value=${h.action ?? ''}\n @change=${(e: Event) => this._update('action', (e.target as HTMLSelectElement).value)}\n >\n ${ACTIONS.map(a => html`<option value=${a} ?selected=${(h.action ?? '') === a}>${a || '(none)'}</option>`)}\n </select>\n </div>\n\n ${h.action === 'script'\n ? this._renderScriptFields(h)\n : (h.action ? this._renderActionFields(h) : null)}\n `\n }\n\n private _renderScriptFields(h: EventHandlerSpec) {\n return html`\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.target\">Target</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"optional selector (#id, .class, ...)\"\n .value=${h.target || ''}\n @change=${(e: Event) => this._update('target', (e.target as HTMLInputElement).value)}\n />\n </div>\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.value\">Value</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"optional · script 안 value 변수로 전달\"\n .value=${h.value !== undefined ? String(h.value) : ''}\n @change=${(e: Event) => this._update('value', (e.target as HTMLInputElement).value)}\n />\n </div>\n <div script-field>\n <div class=\"script-header\">\n <ox-i18n msgid=\"label.script\">Script</ox-i18n>\n <small>vars: component, scene, event, targets, value · await 지원</small>\n </div>\n <ox-input-code\n mode=\"javascript\"\n .value=${h.code || ''}\n @change=${(e: any) => this._update('code', e.detail?.value ?? e.target?.value ?? '')}\n ></ox-input-code>\n </div>\n `\n }\n\n private _renderActionFields(h: EventHandlerSpec) {\n return html`\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.target\">Target</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"selector (#id, .class, ...)\"\n .value=${h.target || ''}\n @change=${(e: Event) => this._update('target', (e.target as HTMLInputElement).value)}\n />\n </div>\n <div class=\"row\">\n <label><ox-i18n msgid=\"label.value\">Value</ox-i18n></label>\n <input type=\"text\"\n placeholder=\"value or accessor\"\n .value=${h.value !== undefined ? String(h.value) : ''}\n @change=${(e: Event) => this._update('value', (e.target as HTMLInputElement).value)}\n />\n </div>\n `\n }\n\n private _update(key: keyof EventHandlerSpec, value: any) {\n const next: EventHandlerSpec = { ...(this.handler || { trigger: 'click', action: '' }), [key]: value }\n // action 전환 시 비-script 진입은 code 정리. script 진입 시 target/value 는\n // 의미가 호환 (target=findAll selector, value=script 입력) 이라 유지.\n if (key === 'action' && value !== 'script') {\n delete next.code\n }\n this.handler = next\n this.dispatchEvent(\n new CustomEvent('value-change', {\n bubbles: true,\n composed: true,\n detail: { handler: next, changed: { [key]: value } }\n })\n )\n }\n}\n\ncustomElements.define('event-handlers-mapper', EventHandlersMapper)\n"]}
|
|
@@ -294,7 +294,8 @@ SceneInspector.styles = [
|
|
|
294
294
|
background-color: var(--scene-inspector-name-background-color);
|
|
295
295
|
border-radius: var(--border-radius);
|
|
296
296
|
padding: 0 4px;
|
|
297
|
-
|
|
297
|
+
/* 칩 배경이 브랜드색에서 표면색으로 바뀌었으므로 #fff 고정은 라이트에서 읽히지 않는다 */
|
|
298
|
+
color: var(--scene-inspector-name-color, var(--md-sys-color-on-surface));
|
|
298
299
|
font-size: 0.8em;
|
|
299
300
|
}
|
|
300
301
|
|
|
@@ -373,7 +374,7 @@ SceneInspector.styles = [
|
|
|
373
374
|
font-size: 0.72em;
|
|
374
375
|
font-weight: normal;
|
|
375
376
|
background: var(--scene-inspector-name-background-color);
|
|
376
|
-
color:
|
|
377
|
+
color: var(--scene-inspector-name-color, var(--md-sys-color-on-surface));
|
|
377
378
|
border-radius: 999px;
|
|
378
379
|
padding: 0 6px;
|
|
379
380
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspector.js","sourceRoot":"","sources":["../../../../src/property-panel/inspector/inspector.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * SceneInspector — 속성패널 inspector 탭 호스트.\n *\n * 얇은 호스트: 검색 상태 + 클릭 위임만 담당하고, scene 상호작용은 InspectorController,\n * 렌더는 뷰 렌더러(현재 트리)로 위임한다. (아키텍처 리팩터 P0 — 동작은 기존과 동일)\n */\nimport '@operato/input/ox-input-search.js'\n\nimport { css, html, LitElement, nothing, PropertyValues, TemplateResult } from 'lit'\nimport { property, state } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { Component, Scene } from '@hatiolab/things-scene'\nimport { InspectorStyles } from '@operato/styles/inspector-styles.js'\n\nimport { parseQuery } from './inspector-model.js'\nimport { InspectorController } from './inspector-controller.js'\nimport { countMatches, renderInspectorTree } from './inspector-tree-view.js'\nimport { renderInspectorGroupView } from './inspector-group-view.js'\n\ntype InspectorViewMode = 'tree' | 'group'\n\n/** 뷰 모드 선호를 세션 간 유지(localStorage). 전역 사용자 선호. */\nconst VIEW_MODE_STORAGE_KEY = 'scene-inspector.view-mode'\n\nfunction readStoredViewMode(): InspectorViewMode {\n try {\n const v = localStorage.getItem(VIEW_MODE_STORAGE_KEY)\n return v === 'group' || v === 'tree' ? v : 'tree'\n } catch {\n return 'tree'\n }\n}\n\nfunction writeStoredViewMode(mode: InspectorViewMode): void {\n try {\n localStorage.setItem(VIEW_MODE_STORAGE_KEY, mode)\n } catch {\n /* localStorage 미가용(SSR/프라이빗 등) — 무시 */\n }\n}\n\nexport class SceneInspector extends LitElement {\n static styles = [\n InspectorStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n\n color: var(--scene-inspector-color);\n }\n\n ox-input-search {\n --input-search-padding: 3px;\n --input-search-focus-border-bottom: none;\n --input-search-font: normal 16px var(--theme-font);\n\n border-radius: 999px;\n box-sizing: border-box;\n border: 1px solid var(--md-sys-color-on-secondary-container);\n padding: 0 10px;\n margin: 5px;\n }\n\n div[result] {\n flex: 1;\n\n display: flex;\n flex-direction: column;\n\n overflow-y: auto;\n }\n\n .component {\n display: block;\n overflow: hidden;\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n font-size: 14px;\n }\n\n .component[selected] {\n background-color: var(--scene-inspector-selected-background-color);\n border-top: var(--scene-inspector-selected-border);\n border-bottom: var(--scene-inspector-selected-border);\n }\n [selected] .type {\n font-weight: bold;\n }\n\n span,\n i {\n display: inline-block;\n }\n\n span.type {\n text-overflow: ellipses;\n }\n\n span.name {\n background-color: var(--scene-inspector-name-background-color);\n border-radius: var(--border-radius);\n padding: 0 4px;\n color: #fff;\n font-size: 0.8em;\n }\n\n .eye {\n margin: 0 0 0 4px;\n vertical-align: middle;\n opacity: 0.7;\n font-size: 1em;\n width: 1em;\n color: var(--scene-inspector-eye-icon-color);\n cursor: pointer;\n }\n\n .lock {\n margin: 0;\n vertical-align: middle;\n opacity: 0.3;\n font-size: 1em;\n width: 1em;\n cursor: pointer;\n }\n .lock[locked] {\n opacity: 0.9;\n color: var(--md-sys-color-error, #d9534f);\n }\n\n pre {\n display: inline;\n /* 트리 인덴트(수직라인까지의 간격) 최소화 — depth 당 좁은 폭 */\n font-size: 0.6em;\n margin: 0;\n }\n\n /* 뷰 모드 툴바 */\n div[toolbar] {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px 4px;\n }\n div[toolbar] .spacer {\n flex: 1;\n }\n div[toolbar] md-icon {\n cursor: pointer;\n opacity: 0.5;\n font-size: 18px;\n border-radius: 4px;\n padding: 2px;\n }\n div[toolbar] md-icon:hover {\n opacity: 0.9;\n }\n div[toolbar] md-icon[selected] {\n opacity: 1;\n color: var(--md-sys-color-primary, #4a9eff);\n background: rgba(0, 0, 0, 0.05);\n }\n\n /* 타입 그룹 뷰 */\n .group-header {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n padding: 2px 0;\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n }\n .group-header:hover {\n background: rgba(0, 0, 0, 0.03);\n }\n .group-count {\n margin-left: auto;\n font-size: 0.72em;\n font-weight: normal;\n background: var(--scene-inspector-name-background-color);\n color: #fff;\n border-radius: 999px;\n padding: 0 6px;\n }\n .group-hidden,\n .group-dim {\n font-size: 1em;\n opacity: 0.35;\n cursor: pointer;\n }\n .group-hidden {\n margin: 0 2px 0 6px;\n }\n .group-dim {\n margin: 0 4px 0 2px;\n }\n .group-hidden:hover,\n .group-dim:hover {\n opacity: 0.8;\n }\n .group-header[hidden-group] .group-hidden,\n .group-header[dimmed] .group-dim {\n opacity: 0.95;\n color: var(--md-sys-color-primary, #4a9eff);\n }\n .group-header[dimmed] .group-type {\n opacity: 0.45;\n }\n .group-member {\n padding-left: 8px;\n }\n .muted {\n opacity: 0.4;\n font-style: italic;\n }\n /* 검색 매치 하이라이트 */\n mark {\n background: var(--md-sys-color-tertiary-container, #ffe082);\n color: inherit;\n border-radius: 2px;\n padding: 0 1px;\n }\n `\n ]\n\n @property({ type: Object }) scene?: Scene\n\n @state() private searchText: string = ''\n @state() private viewMode: InspectorViewMode = readStoredViewMode()\n\n private controller = new InspectorController()\n private show: boolean = false\n /** reveal(스크롤) 판정용 직전 선택. */\n private _lastSelected: Component[] = []\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.controller.unbind()\n }\n\n render() {\n return html`\n <ox-input-search\n .placeholder=${i18next.t('text.search with type, id or tag')}\n autofocus\n @change=${(e: Event) => (this.searchText = ((e.target as HTMLInputElement).value || '').toLowerCase())}\n ></ox-input-search>\n\n <div toolbar>\n <md-icon class=\"view-btn\" ?selected=${this.viewMode === 'tree'} title=\"Tree view\" @click=${() => this._setViewMode('tree')}>account_tree</md-icon>\n <md-icon class=\"view-btn\" ?selected=${this.viewMode === 'group'} title=\"Group by type\" @click=${() => this._setViewMode('group')}>category</md-icon>\n <span class=\"spacer\"></span>\n ${this.viewMode === 'group'\n ? html`\n <md-icon title=\"Expand all groups\" @click=${this._expandGroups}>unfold_more</md-icon>\n <md-icon title=\"Collapse to group level\" @click=${this._collapseGroups}>unfold_less</md-icon>\n `\n : nothing}\n </div>\n\n <div result>${this._renderResult()}</div>\n `\n }\n\n private _renderResult(): TemplateResult {\n if (!this.scene) return html``\n\n const terms = parseQuery(this.searchText)\n\n if (this.viewMode === 'group') {\n return renderInspectorGroupView(this.controller.index, {\n selected: this.controller.selected,\n terms,\n isGroupExpanded: (type: string) => this.controller.isGroupExpanded(type),\n isTypeDimmed: (type: string) => this.controller.isTypeDimmed(type),\n isTypeHidden: (type: string) => this.controller.isTypeHidden(type)\n })\n }\n\n return renderInspectorTree(this.scene.root, {\n selected: this.controller.selected,\n terms,\n count: countMatches(this.scene.root, terms),\n isExtended: (c: Component) => this.controller.isExtended(c),\n nodeHandleClass: (c: Component) => this.controller.nodeHandleClass(c)\n })\n }\n\n private _setViewMode(mode: InspectorViewMode) {\n if (this.viewMode === mode) return\n this.viewMode = mode\n writeStoredViewMode(mode) // 세션 간 유지\n }\n\n private _collapseGroups = () => {\n this.controller.collapseAllGroups()\n this.requestUpdate()\n }\n\n private _expandGroups = () => {\n const index = this.controller.index\n if (index) this.controller.expandAllGroups([...index.byType.keys()])\n this.requestUpdate()\n }\n\n firstUpdated() {\n dispatchEvent(new Event('resize'))\n\n this.renderRoot.addEventListener('click', this._onclick.bind(this) as EventListener)\n this.renderRoot.addEventListener('dblclick', this._ondblclick.bind(this) as EventListener)\n }\n\n /** 외부에서 강제 재렌더용(기존 공개 메쏘드 유지). */\n refresh() {\n this.requestUpdate()\n }\n\n updated(change: PropertyValues<this>) {\n if (change.has('scene')) {\n this.controller.bind(this.scene, () => this.requestUpdate())\n }\n this._maybeReveal()\n }\n\n /**\n * 선택이 바뀌면(캔버스 등) 해당 항목을 리스트에 노출한다 — 그룹 뷰면 그룹을 펼친 뒤 스크롤.\n * scrollIntoView({block:'nearest'}) 라 이미 보이는 경우(인스펙터 클릭 등)엔 사실상 no-op.\n */\n private _maybeReveal() {\n const selected = this.controller.selected\n const changed =\n selected.length !== this._lastSelected.length || selected.some((c, i) => c !== this._lastSelected[i])\n if (!changed) return\n\n if (selected.length === 0) {\n this._lastSelected = selected\n return\n }\n\n if (this.viewMode === 'group') {\n const types = [...new Set(selected.map(c => c.state.type))]\n if (types.some(t => !this.controller.isGroupExpanded(t))) {\n this.controller.expandAllGroups(types)\n return // _lastSelected 미갱신 → 다음 updated 에서 changed 유지 → 그때 스크롤\n }\n }\n\n this._lastSelected = selected\n this.updateComplete.then(() => {\n const el = this.renderRoot.querySelector('.component[selected]') as HTMLElement | null\n el?.scrollIntoView({ block: 'nearest' })\n })\n }\n\n /** 이벤트 타겟에서 상위로 올라가며 `.component` 를 가진 요소를 찾는다. */\n private _resolveComponent(e: MouseEvent): Component | undefined {\n let targetElement = e.target as HTMLElement & { component: Component }\n let component: Component | undefined\n\n while (!component && targetElement) {\n component = targetElement.component\n\n if (component) break\n\n targetElement = targetElement.parentNode as HTMLElement & { component: Component }\n\n if (!targetElement || targetElement === this.renderRoot) break\n }\n\n return component\n }\n\n _onclick(e: MouseEvent) {\n e.stopPropagation()\n\n const target = e.target as HTMLElement\n\n // 그룹 hidden 아이콘 클릭 → 그룹(타입) 전원 hidden 토글 (헤더 펼침보다 먼저 처리)\n if (target.classList.contains('group-hidden')) {\n const type = target.closest?.('[data-group-type]')?.getAttribute('data-group-type')\n if (type != null) this.controller.toggleTypeHidden(type)\n this.requestUpdate()\n return\n }\n\n // 그룹 dim-out 아이콘 클릭 → 그룹(타입) 포커스 dim 토글 (헤더 펼침보다 먼저 처리)\n if (target.classList.contains('group-dim')) {\n const type = target.closest?.('[data-group-type]')?.getAttribute('data-group-type')\n if (type != null) this.controller.toggleTypeDim(type)\n this.requestUpdate()\n return\n }\n\n // 타입 그룹 헤더 클릭 → 그룹 펼침/접힘 (멤버 행은 헤더의 자손이 아니므로 여기 안 걸림)\n const header = target.closest?.('[data-group-type]') as HTMLElement | null\n if (header) {\n const type = header.getAttribute('data-group-type')\n if (type != null) this.controller.toggleGroup(type)\n this.requestUpdate()\n return\n }\n\n const classList = (e.target as HTMLElement).classList\n const component = this._resolveComponent(e)\n\n if (component) {\n if (classList.contains('eye')) {\n this.controller.toggleHidden(component)\n } else if (classList.contains('lock')) {\n this.controller.toggleLocked(component)\n } else if (classList.contains('extended') || classList.contains('collapsed')) {\n this.controller.toggleExtended(component)\n }\n\n // shift = 범위(볼륨) 선택/블록 해제, ctrl/cmd = 개별 토글, 그 외 = 단일 선택\n if (e.shiftKey) {\n this.controller.selectRange(component, parseQuery(this.searchText))\n } else {\n this.controller.select(component, e.metaKey || e.ctrlKey)\n }\n } else {\n if (classList.contains('inspector')) {\n this.show = !this.show\n this.style.height = this.show ? '100%' : ''\n }\n }\n\n this.requestUpdate()\n }\n\n _ondblclick(e: MouseEvent) {\n e.stopPropagation()\n\n const component = this._resolveComponent(e)\n\n if (component) {\n if (component.isContainer()) {\n this.controller.toggleExtended(component)\n }\n // 캔버스를 해당 컴포넌트로 센터 이동(줌).\n this.controller.centerTo(component)\n }\n\n this.requestUpdate()\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"inspector.js","sourceRoot":"","sources":["../../../../src/property-panel/inspector/inspector.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * SceneInspector — 속성패널 inspector 탭 호스트.\n *\n * 얇은 호스트: 검색 상태 + 클릭 위임만 담당하고, scene 상호작용은 InspectorController,\n * 렌더는 뷰 렌더러(현재 트리)로 위임한다. (아키텍처 리팩터 P0 — 동작은 기존과 동일)\n */\nimport '@operato/input/ox-input-search.js'\n\nimport { css, html, LitElement, nothing, PropertyValues, TemplateResult } from 'lit'\nimport { property, state } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { Component, Scene } from '@hatiolab/things-scene'\nimport { InspectorStyles } from '@operato/styles/inspector-styles.js'\n\nimport { parseQuery } from './inspector-model.js'\nimport { InspectorController } from './inspector-controller.js'\nimport { countMatches, renderInspectorTree } from './inspector-tree-view.js'\nimport { renderInspectorGroupView } from './inspector-group-view.js'\n\ntype InspectorViewMode = 'tree' | 'group'\n\n/** 뷰 모드 선호를 세션 간 유지(localStorage). 전역 사용자 선호. */\nconst VIEW_MODE_STORAGE_KEY = 'scene-inspector.view-mode'\n\nfunction readStoredViewMode(): InspectorViewMode {\n try {\n const v = localStorage.getItem(VIEW_MODE_STORAGE_KEY)\n return v === 'group' || v === 'tree' ? v : 'tree'\n } catch {\n return 'tree'\n }\n}\n\nfunction writeStoredViewMode(mode: InspectorViewMode): void {\n try {\n localStorage.setItem(VIEW_MODE_STORAGE_KEY, mode)\n } catch {\n /* localStorage 미가용(SSR/프라이빗 등) — 무시 */\n }\n}\n\nexport class SceneInspector extends LitElement {\n static styles = [\n InspectorStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n\n color: var(--scene-inspector-color);\n }\n\n ox-input-search {\n --input-search-padding: 3px;\n --input-search-focus-border-bottom: none;\n --input-search-font: normal 16px var(--theme-font);\n\n border-radius: 999px;\n box-sizing: border-box;\n border: 1px solid var(--md-sys-color-on-secondary-container);\n padding: 0 10px;\n margin: 5px;\n }\n\n div[result] {\n flex: 1;\n\n display: flex;\n flex-direction: column;\n\n overflow-y: auto;\n }\n\n .component {\n display: block;\n overflow: hidden;\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n font-size: 14px;\n }\n\n .component[selected] {\n background-color: var(--scene-inspector-selected-background-color);\n border-top: var(--scene-inspector-selected-border);\n border-bottom: var(--scene-inspector-selected-border);\n }\n [selected] .type {\n font-weight: bold;\n }\n\n span,\n i {\n display: inline-block;\n }\n\n span.type {\n text-overflow: ellipses;\n }\n\n span.name {\n background-color: var(--scene-inspector-name-background-color);\n border-radius: var(--border-radius);\n padding: 0 4px;\n /* 칩 배경이 브랜드색에서 표면색으로 바뀌었으므로 #fff 고정은 라이트에서 읽히지 않는다 */\n color: var(--scene-inspector-name-color, var(--md-sys-color-on-surface));\n font-size: 0.8em;\n }\n\n .eye {\n margin: 0 0 0 4px;\n vertical-align: middle;\n opacity: 0.7;\n font-size: 1em;\n width: 1em;\n color: var(--scene-inspector-eye-icon-color);\n cursor: pointer;\n }\n\n .lock {\n margin: 0;\n vertical-align: middle;\n opacity: 0.3;\n font-size: 1em;\n width: 1em;\n cursor: pointer;\n }\n .lock[locked] {\n opacity: 0.9;\n color: var(--md-sys-color-error, #d9534f);\n }\n\n pre {\n display: inline;\n /* 트리 인덴트(수직라인까지의 간격) 최소화 — depth 당 좁은 폭 */\n font-size: 0.6em;\n margin: 0;\n }\n\n /* 뷰 모드 툴바 */\n div[toolbar] {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px 4px;\n }\n div[toolbar] .spacer {\n flex: 1;\n }\n div[toolbar] md-icon {\n cursor: pointer;\n opacity: 0.5;\n font-size: 18px;\n border-radius: 4px;\n padding: 2px;\n }\n div[toolbar] md-icon:hover {\n opacity: 0.9;\n }\n div[toolbar] md-icon[selected] {\n opacity: 1;\n color: var(--md-sys-color-primary, #4a9eff);\n background: rgba(0, 0, 0, 0.05);\n }\n\n /* 타입 그룹 뷰 */\n .group-header {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n padding: 2px 0;\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n }\n .group-header:hover {\n background: rgba(0, 0, 0, 0.03);\n }\n .group-count {\n margin-left: auto;\n font-size: 0.72em;\n font-weight: normal;\n background: var(--scene-inspector-name-background-color);\n color: var(--scene-inspector-name-color, var(--md-sys-color-on-surface));\n border-radius: 999px;\n padding: 0 6px;\n }\n .group-hidden,\n .group-dim {\n font-size: 1em;\n opacity: 0.35;\n cursor: pointer;\n }\n .group-hidden {\n margin: 0 2px 0 6px;\n }\n .group-dim {\n margin: 0 4px 0 2px;\n }\n .group-hidden:hover,\n .group-dim:hover {\n opacity: 0.8;\n }\n .group-header[hidden-group] .group-hidden,\n .group-header[dimmed] .group-dim {\n opacity: 0.95;\n color: var(--md-sys-color-primary, #4a9eff);\n }\n .group-header[dimmed] .group-type {\n opacity: 0.45;\n }\n .group-member {\n padding-left: 8px;\n }\n .muted {\n opacity: 0.4;\n font-style: italic;\n }\n /* 검색 매치 하이라이트 */\n mark {\n background: var(--md-sys-color-tertiary-container, #ffe082);\n color: inherit;\n border-radius: 2px;\n padding: 0 1px;\n }\n `\n ]\n\n @property({ type: Object }) scene?: Scene\n\n @state() private searchText: string = ''\n @state() private viewMode: InspectorViewMode = readStoredViewMode()\n\n private controller = new InspectorController()\n private show: boolean = false\n /** reveal(스크롤) 판정용 직전 선택. */\n private _lastSelected: Component[] = []\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.controller.unbind()\n }\n\n render() {\n return html`\n <ox-input-search\n .placeholder=${i18next.t('text.search with type, id or tag')}\n autofocus\n @change=${(e: Event) => (this.searchText = ((e.target as HTMLInputElement).value || '').toLowerCase())}\n ></ox-input-search>\n\n <div toolbar>\n <md-icon class=\"view-btn\" ?selected=${this.viewMode === 'tree'} title=\"Tree view\" @click=${() => this._setViewMode('tree')}>account_tree</md-icon>\n <md-icon class=\"view-btn\" ?selected=${this.viewMode === 'group'} title=\"Group by type\" @click=${() => this._setViewMode('group')}>category</md-icon>\n <span class=\"spacer\"></span>\n ${this.viewMode === 'group'\n ? html`\n <md-icon title=\"Expand all groups\" @click=${this._expandGroups}>unfold_more</md-icon>\n <md-icon title=\"Collapse to group level\" @click=${this._collapseGroups}>unfold_less</md-icon>\n `\n : nothing}\n </div>\n\n <div result>${this._renderResult()}</div>\n `\n }\n\n private _renderResult(): TemplateResult {\n if (!this.scene) return html``\n\n const terms = parseQuery(this.searchText)\n\n if (this.viewMode === 'group') {\n return renderInspectorGroupView(this.controller.index, {\n selected: this.controller.selected,\n terms,\n isGroupExpanded: (type: string) => this.controller.isGroupExpanded(type),\n isTypeDimmed: (type: string) => this.controller.isTypeDimmed(type),\n isTypeHidden: (type: string) => this.controller.isTypeHidden(type)\n })\n }\n\n return renderInspectorTree(this.scene.root, {\n selected: this.controller.selected,\n terms,\n count: countMatches(this.scene.root, terms),\n isExtended: (c: Component) => this.controller.isExtended(c),\n nodeHandleClass: (c: Component) => this.controller.nodeHandleClass(c)\n })\n }\n\n private _setViewMode(mode: InspectorViewMode) {\n if (this.viewMode === mode) return\n this.viewMode = mode\n writeStoredViewMode(mode) // 세션 간 유지\n }\n\n private _collapseGroups = () => {\n this.controller.collapseAllGroups()\n this.requestUpdate()\n }\n\n private _expandGroups = () => {\n const index = this.controller.index\n if (index) this.controller.expandAllGroups([...index.byType.keys()])\n this.requestUpdate()\n }\n\n firstUpdated() {\n dispatchEvent(new Event('resize'))\n\n this.renderRoot.addEventListener('click', this._onclick.bind(this) as EventListener)\n this.renderRoot.addEventListener('dblclick', this._ondblclick.bind(this) as EventListener)\n }\n\n /** 외부에서 강제 재렌더용(기존 공개 메쏘드 유지). */\n refresh() {\n this.requestUpdate()\n }\n\n updated(change: PropertyValues<this>) {\n if (change.has('scene')) {\n this.controller.bind(this.scene, () => this.requestUpdate())\n }\n this._maybeReveal()\n }\n\n /**\n * 선택이 바뀌면(캔버스 등) 해당 항목을 리스트에 노출한다 — 그룹 뷰면 그룹을 펼친 뒤 스크롤.\n * scrollIntoView({block:'nearest'}) 라 이미 보이는 경우(인스펙터 클릭 등)엔 사실상 no-op.\n */\n private _maybeReveal() {\n const selected = this.controller.selected\n const changed =\n selected.length !== this._lastSelected.length || selected.some((c, i) => c !== this._lastSelected[i])\n if (!changed) return\n\n if (selected.length === 0) {\n this._lastSelected = selected\n return\n }\n\n if (this.viewMode === 'group') {\n const types = [...new Set(selected.map(c => c.state.type))]\n if (types.some(t => !this.controller.isGroupExpanded(t))) {\n this.controller.expandAllGroups(types)\n return // _lastSelected 미갱신 → 다음 updated 에서 changed 유지 → 그때 스크롤\n }\n }\n\n this._lastSelected = selected\n this.updateComplete.then(() => {\n const el = this.renderRoot.querySelector('.component[selected]') as HTMLElement | null\n el?.scrollIntoView({ block: 'nearest' })\n })\n }\n\n /** 이벤트 타겟에서 상위로 올라가며 `.component` 를 가진 요소를 찾는다. */\n private _resolveComponent(e: MouseEvent): Component | undefined {\n let targetElement = e.target as HTMLElement & { component: Component }\n let component: Component | undefined\n\n while (!component && targetElement) {\n component = targetElement.component\n\n if (component) break\n\n targetElement = targetElement.parentNode as HTMLElement & { component: Component }\n\n if (!targetElement || targetElement === this.renderRoot) break\n }\n\n return component\n }\n\n _onclick(e: MouseEvent) {\n e.stopPropagation()\n\n const target = e.target as HTMLElement\n\n // 그룹 hidden 아이콘 클릭 → 그룹(타입) 전원 hidden 토글 (헤더 펼침보다 먼저 처리)\n if (target.classList.contains('group-hidden')) {\n const type = target.closest?.('[data-group-type]')?.getAttribute('data-group-type')\n if (type != null) this.controller.toggleTypeHidden(type)\n this.requestUpdate()\n return\n }\n\n // 그룹 dim-out 아이콘 클릭 → 그룹(타입) 포커스 dim 토글 (헤더 펼침보다 먼저 처리)\n if (target.classList.contains('group-dim')) {\n const type = target.closest?.('[data-group-type]')?.getAttribute('data-group-type')\n if (type != null) this.controller.toggleTypeDim(type)\n this.requestUpdate()\n return\n }\n\n // 타입 그룹 헤더 클릭 → 그룹 펼침/접힘 (멤버 행은 헤더의 자손이 아니므로 여기 안 걸림)\n const header = target.closest?.('[data-group-type]') as HTMLElement | null\n if (header) {\n const type = header.getAttribute('data-group-type')\n if (type != null) this.controller.toggleGroup(type)\n this.requestUpdate()\n return\n }\n\n const classList = (e.target as HTMLElement).classList\n const component = this._resolveComponent(e)\n\n if (component) {\n if (classList.contains('eye')) {\n this.controller.toggleHidden(component)\n } else if (classList.contains('lock')) {\n this.controller.toggleLocked(component)\n } else if (classList.contains('extended') || classList.contains('collapsed')) {\n this.controller.toggleExtended(component)\n }\n\n // shift = 범위(볼륨) 선택/블록 해제, ctrl/cmd = 개별 토글, 그 외 = 단일 선택\n if (e.shiftKey) {\n this.controller.selectRange(component, parseQuery(this.searchText))\n } else {\n this.controller.select(component, e.metaKey || e.ctrlKey)\n }\n } else {\n if (classList.contains('inspector')) {\n this.show = !this.show\n this.style.height = this.show ? '100%' : ''\n }\n }\n\n this.requestUpdate()\n }\n\n _ondblclick(e: MouseEvent) {\n e.stopPropagation()\n\n const component = this._resolveComponent(e)\n\n if (component) {\n if (component.isContainer()) {\n this.controller.toggleExtended(component)\n }\n // 캔버스를 해당 컴포넌트로 센터 이동(줌).\n this.controller.centerTo(component)\n }\n\n this.requestUpdate()\n }\n}\n"]}
|