feat(sw): implement property field interaction variants

- Property panel rows follow Figma 26:12388 ~ 26:12649: 28px fields with
  Default / Hover border / numeric editing On (#0F0D0A) / dropdown On (#211D13)
- Layer, line style and color fields open the shared header dropdown contents
  (SWHeaderLayerRows, SWHeaderLineStyleColumns, SWHeaderColorPanelBody)
- Additive API: property.picker, property.decimals, panel menus
- Property Fields story and browser test (77 checks)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shkim4andClaude Opus 5.5 committed 2026-10-19 09:38:31 +09:00
1 parent 724c51732b
commit 0029cef60c
23 files changed
+1907 -189

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

+813
View File
@@ -0,0 +1,813 @@
{
"label": "run4",
"checks": [
{
"name": "Default layer-mixed: 200 x 28, radius 2, no border / surface",
"status": "PASS",
"data": {
"x": 97,
"y": 371.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"borderTopLeftRadius": "2px"
}
},
{
"name": "Default layer: 200 x 28, radius 2, no border / surface",
"status": "PASS",
"data": {
"x": 97,
"y": 403.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"borderTopLeftRadius": "2px"
}
},
{
"name": "Default color: 200 x 28, radius 2, no border / surface",
"status": "PASS",
"data": {
"x": 97,
"y": 435.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"borderTopLeftRadius": "2px"
}
},
{
"name": "Default line: 200 x 28, radius 2, no border / surface",
"status": "PASS",
"data": {
"x": 97,
"y": 467.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"borderTopLeftRadius": "2px"
}
},
{
"name": "Default scale: 200 x 28, radius 2, no border / surface",
"status": "PASS",
"data": {
"x": 97,
"y": 531.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"borderTopLeftRadius": "2px"
}
},
{
"name": "Default link: 200 x 28, radius 2, no border / surface",
"status": "PASS",
"data": {
"x": 97,
"y": 595.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"borderTopLeftRadius": "2px"
}
},
{
"name": "Default thickness: 200 x 28, radius 2, no border / surface",
"status": "PASS",
"data": {
"x": 97,
"y": 627.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"borderTopLeftRadius": "2px"
}
},
{
"name": "Default material: 200 x 28, radius 2, no border / surface",
"status": "PASS",
"data": {
"x": 97,
"y": 659.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"borderTopLeftRadius": "2px"
}
},
{
"name": "Default weight: compact 28px hug field (pl 6 / pr 4 incl. border), no border",
"status": "PASS",
"data": {
"x": 97,
"y": 499.5,
"w": 39.23,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"paddingLeft": "5px",
"paddingRight": "3px"
}
},
{
"name": "Default opacity: compact 28px hug field (pl 6 / pr 4 incl. border), no border",
"status": "PASS",
"data": {
"x": 97,
"y": 563.5,
"w": 39.23,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"paddingLeft": "5px",
"paddingRight": "3px"
}
},
{
"name": "Typography: value #E60403 14px, unit #000 12px",
"status": "PASS",
"data": {
"value": [
"rgb(230, 4, 3)",
"14px"
],
"unit": [
"rgb(0, 0, 0)",
"12px"
]
}
},
{
"name": "Hover layer-mixed: rgba(33,29,19,.4) border, 28px kept, value not moved",
"status": "PASS",
"data": {
"x": 97,
"y": 371.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(33, 29, 19, 0.4)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"before": [
105,
377.5
],
"after": [
105,
377.5
]
}
},
{
"name": "Mouse Leave layer-mixed: back to Default",
"status": "PASS",
"data": {
"x": 97,
"y": 371.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)"
}
},
{
"name": "Hover layer: rgba(33,29,19,.4) border, 28px kept, value not moved",
"status": "PASS",
"data": {
"x": 97,
"y": 403.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(33, 29, 19, 0.4)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"before": [
105,
409.5
],
"after": [
105,
409.5
]
}
},
{
"name": "Mouse Leave layer: back to Default",
"status": "PASS",
"data": {
"x": 97,
"y": 403.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)"
}
},
{
"name": "Hover color: rgba(33,29,19,.4) border, 28px kept, value not moved, divider shown",
"status": "PASS",
"data": {
"x": 98,
"y": 436.5,
"w": 114,
"h": 26,
"borderTopColor": "rgba(33, 29, 19, 0.4)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"before": [
105,
436.5
],
"after": [
105,
436.5
],
"borderRightColor": "rgba(33, 29, 19, 0.4)"
}
},
{
"name": "Mouse Leave color: back to Default",
"status": "PASS",
"data": {
"x": 97,
"y": 435.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)"
}
},
{
"name": "Hover line: rgba(33,29,19,.4) border, 28px kept, value not moved",
"status": "PASS",
"data": {
"x": 97,
"y": 467.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(33, 29, 19, 0.4)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"before": [
105,
473.5
],
"after": [
105,
473.5
]
}
},
{
"name": "Mouse Leave line: back to Default",
"status": "PASS",
"data": {
"x": 97,
"y": 467.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)"
}
},
{
"name": "Hover scale: rgba(33,29,19,.4) border, 28px kept, value not moved",
"status": "PASS",
"data": {
"x": 97,
"y": 531.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(33, 29, 19, 0.4)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"before": [
105,
532.5
],
"after": [
105,
532.5
]
}
},
{
"name": "Mouse Leave scale: back to Default",
"status": "PASS",
"data": {
"x": 97,
"y": 531.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)"
}
},
{
"name": "Hover link: rgba(33,29,19,.4) border, 28px kept, value not moved",
"status": "PASS",
"data": {
"x": 97,
"y": 595.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(33, 29, 19, 0.4)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"before": [
105,
596.5
],
"after": [
105,
596.5
]
}
},
{
"name": "Mouse Leave link: back to Default",
"status": "PASS",
"data": {
"x": 97,
"y": 595.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)"
}
},
{
"name": "Hover thickness: rgba(33,29,19,.4) border, 28px kept, value not moved",
"status": "PASS",
"data": {
"x": 97,
"y": 627.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(33, 29, 19, 0.4)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"before": [
105,
628.5
],
"after": [
105,
628.5
]
}
},
{
"name": "Mouse Leave thickness: back to Default",
"status": "PASS",
"data": {
"x": 97,
"y": 627.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)"
}
},
{
"name": "Hover material: rgba(33,29,19,.4) border, 28px kept, value not moved",
"status": "PASS",
"data": {
"x": 97,
"y": 659.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(33, 29, 19, 0.4)",
"backgroundColor": "rgba(0, 0, 0, 0)",
"before": [
105,
660.5
],
"after": [
105,
660.5
]
}
},
{
"name": "Mouse Leave material: back to Default",
"status": "PASS",
"data": {
"x": 97,
"y": 659.5,
"w": 200,
"h": 28,
"borderTopColor": "rgba(0, 0, 0, 0)"
}
},
{
"name": "Hover / Leave weight compact",
"status": "PASS",
"data": {
"hover": "rgba(33, 29, 19, 0.4)",
"leave": "rgba(0, 0, 0, 0)"
}
},
{
"name": "Hover / Leave opacity compact",
"status": "PASS",
"data": {
"hover": "rgba(33, 29, 19, 0.4)",
"leave": "rgba(0, 0, 0, 0)"
}
},
{
"name": "On(편집) scale: #0F0D0A, border, unit #FFF, focused input with red caret + outline",
"status": "PASS",
"data": {
"x": 97,
"y": 531.5,
"w": 200,
"h": 28,
"backgroundColor": "rgb(15, 13, 10)",
"borderTopColor": "rgba(33, 29, 19, 0.4)",
"unit": "rgb(255, 255, 255)",
"focused": true,
"caret": "rgb(230, 4, 3)",
"shadow": true
}
},
{
"name": "On(편집) thickness: #0F0D0A, border, unit #FFF, focused input with red caret + outline",
"status": "PASS",
"data": {
"x": 97,
"y": 627.5,
"w": 200,
"h": 28,
"backgroundColor": "rgb(15, 13, 10)",
"borderTopColor": "rgba(33, 29, 19, 0.4)",
"unit": "rgb(255, 255, 255)",
"focused": true,
"caret": "rgb(230, 4, 3)",
"shadow": true
}
},
{
"name": "Enter: 선축적 2 → validated, 4 decimals committed",
"status": "PASS",
"data": "property(scale, 2.0000)"
},
{
"name": "Escape: draft cancelled (2.0000 restored, no callback)",
"status": "PASS",
"data": "2.0000"
},
{
"name": "On(편집) 선가중치 compact: #0F0D0A",
"status": "PASS",
"data": {
"x": 97,
"y": 499.5,
"w": 43.03,
"h": 28,
"backgroundColor": "rgb(15, 13, 10)"
}
},
{
"name": "Invalid 선가중치 -1 (min 0): restored, no callback",
"status": "PASS",
"data": "0.25"
},
{
"name": "선가중치 0.5 committed",
"status": "PASS",
"data": "property(scale, 2.0000) → property(weight, 0.5)"
},
{
"name": "하이퍼링크 focus: no On surface (Figma has no On), editable",
"status": "PASS",
"data": {
"x": 97,
"y": 595.5,
"w": 200,
"h": 28,
"backgroundColor": "rgba(0, 0, 0, 0)",
"borderTopColor": "rgba(33, 29, 19, 0.4)"
}
},
{
"name": "하이퍼링크 commit via host callback",
"status": "PASS",
"data": "property(scale, 2.0000) → property(weight, 0.5) → property(link, http://a)"
},
{
"name": "재료: read-only, no On",
"status": "PASS",
"data": {
"x": 97,
"y": 659.5,
"w": 200,
"h": 28,
"backgroundColor": "rgba(0, 0, 0, 0)"
}
},
{
"name": "Layer On: #211D13 field, 234 wide list, right edges meet, starts at field bottom",
"status": "PASS",
"data": {
"f": {
"x": 97,
"y": 403.5,
"w": 200,
"h": 28,
"backgroundColor": "rgb(33, 29, 19)",
"borderTopColor": "rgba(33, 29, 19, 0.4)"
},
"d": {
"x": 63,
"y": 431.5,
"w": 234,
"h": 250
}
}
},
{
"name": "Layer list rows 30px, footer 40px, height = rows*30+40",
"status": "PASS",
"data": {
"rows": 7,
"row": 30,
"footer": 40,
"h": 250
}
},
{
"name": "Layer current row marked (0_Layer_Cludmark)",
"status": "PASS",
"data": null
},
{
"name": "Bulb toggle: callback only, list stays open, row not selected",
"status": "PASS",
"data": "property(scale, 2.0000) → property(weight, 0.5) → property(link, http://a) → layer.visible(Sample-Wall, true)"
},
{
"name": "Row select: property(layer, CC-D GNL), closes, field shows value",
"status": "PASS",
"data": "property(scale, 2.0000) → property(weight, 0.5) → property(link, http://a) → layer.visible(Sample-Wall, true) → property(layer, CC-D GNL)"
},
{
"name": "Close keeps the value and focus returns to the field",
"status": "PASS",
"data": null
},
{
"name": "다양함: text 다양함, no current row in list",
"status": "PASS",
"data": null
},
{
"name": "Escape closes the layer list; panel stays",
"status": "PASS",
"data": null
},
{
"name": "Host data: 다양함 → 단일 값 (0_Layer_Cludmark)",
"status": "PASS",
"data": "0_Layer_Cludmark"
},
{
"name": "External value change shown (CC-D GNL)",
"status": "PASS",
"data": null
},
{
"name": "Keyboard: ArrowDown opens + focuses list, Escape closes to trigger",
"status": "PASS",
"data": null
},
{
"name": "Outside click closes",
"status": "PASS",
"data": null
},
{
"name": "Line style On: #211D13, (ByL) #D4CBBD, 3 columns, right edges meet (or shifted to the 4px screen edge when no room)",
"status": "PASS",
"data": {
"f": {
"x": 97,
"y": 485.5,
"w": 200,
"h": 28,
"backgroundColor": "rgb(33, 29, 19)"
},
"d": {
"x": 4,
"y": 513.5,
"w": 471.19,
"h": 190
},
"cols": [
187.19,
142,
142
]
}
},
{
"name": "Line style drop 466 x 190 (type column grows only for wider font names)",
"status": "PASS",
"data": {
"w": 471.19,
"h": 190,
"cols": [
187.19,
142,
142
]
}
},
{
"name": "Weight column: callback only, stays open",
"status": "PASS",
"data": "property(scale, 2.0000) → property(weight, 0.5) → property(link, http://a) → layer.visible(Sample-Wall, true) → property(layer, CC-D GNL) → lineWeight(w-byblock)"
},
{
"name": "Line type select: property(line, CENTER), closes, preview = CENTER pattern (4 segments)",
"status": "PASS",
"data": {
"segs": 4
}
},
{
"name": "Figma sample 255,259,0 shown as host text, swatch from host",
"status": "PASS",
"data": null
},
{
"name": "Color On: dropdown 192 wide, left edges meet, RGB area #211D13 + divider",
"status": "PASS",
"data": {
"f": {
"x": 97,
"y": 435.5,
"w": 200,
"h": 28
},
"d": {
"x": 97,
"y": 463.5,
"w": 192,
"h": 158
},
"left": {
"x": 98,
"y": 436.5,
"w": 114,
"h": 26,
"backgroundColor": "rgb(33, 29, 19)",
"borderRightColor": "rgba(33, 29, 19, 0.4)"
}
}
},
{
"name": "Color dropdown height 158 (Figma)",
"status": "PASS",
"data": 158
},
{
"name": "ByBlock: callback, required single choice (ByBlock pressed, ByLayer not)",
"status": "PASS",
"data": null
},
{
"name": "Clicking the pressed mode is a no-op",
"status": "PASS",
"data": null
},
{
"name": "Chip: property(color, 241,4,4)",
"status": "PASS",
"data": "property(scale, 2.0000) → property(weight, 0.5) → property(link, http://a) → layer.visible(Sample-Wall, true) → property(layer, CC-D GNL) → lineWeight(w-byblock) → property(line, CENTER) → colorMode(byBlock) → property(color, 241,4,4)"
},
{
"name": "Out-of-range 255,259,0 rejected (no callback, restored)",
"status": "PASS",
"data": "241,4,4"
},
{
"name": "Valid RGB committed normalized (1,2,3)",
"status": "PASS",
"data": "property(scale, 2.0000) → property(weight, 0.5) → property(link, http://a) → layer.visible(Sample-Wall, true) → property(layer, CC-D GNL) → lineWeight(w-byblock) → property(line, CENTER) → colorMode(byBlock) → property(color, 241,4,4) → property(color, 1,2,3)"
},
{
"name": "btn_colorize: host callback, dropdown closes",
"status": "PASS",
"data": null
},
{
"name": "Field RGB 300,0,0 rejected",
"status": "PASS",
"data": "1,2,3"
},
{
"name": "Escape #1 cancels draft, dropdown still open",
"status": "PASS",
"data": null
},
{
"name": "Escape #2 closes the dropdown, panel stays",
"status": "PASS",
"data": null
},
{
"name": "ReadOnly 두께: no On; ReadOnly 레이어: no dropdown",
"status": "PASS",
"data": {
"x": 97,
"y": 647,
"w": 200,
"h": 28,
"backgroundColor": "rgba(0, 0, 0, 0)"
}
},
{
"name": "ReadOnly keeps Hover border",
"status": "PASS",
"data": null
},
{
"name": "Disabled 색상: no dropdown; Disabled 선축적: no hover border",
"status": "PASS",
"data": null
},
{
"name": "External value / unit props: 선축적 2.5000, 두께 cm",
"status": "PASS",
"data": null
},
{
"name": "Dynamic rows: added at the end, removed row gone, order kept",
"status": "PASS",
"data": [
"layer-mixed",
"layer",
"color",
"line",
"weight",
"scale",
"opacity",
"thickness",
"material",
"extra-10"
]
},
{
"name": "Without menus: line types from options only (1 column), no crash",
"status": "PASS",
"data": 1
},
{
"name": "Pinned Hover specimen uses the same border",
"status": "PASS",
"data": "rgba(33, 29, 19, 0.4)"
},
{
"name": "4 panel states: 314 x 256 / 361 / 658 / 754",
"status": "PASS",
"data": [
[
"none",
314,
256
],
[
"all",
314,
361
],
[
"circle",
314,
658
],
[
"expanded",
314,
754
]
]
},
{
"name": "Header-hosted panel: layer list attached; Escape closes list only",
"status": "PASS",
"data": {
"hosted": {
"right": 0,
"top": 0,
"panelOpen": true
},
"stillOpen": 1
}
},
{
"name": "no console / page errors",
"status": "PASS",
"data": []
}
],
"errors": []
}
+3
View File
@@ -502,6 +502,9 @@ function DrawingHeader() {
- `toolActive/onToolActiveChange/toolLabel`로 내부 아이콘을 연결합니다. 현재 의미는 호스트가 정하는 선택 도구 상태 통지이며 CAD 기능을 내장하지 않습니다.
- 단독 사용: `<SWTheme><SWHeaderPropertyPanel ... onClose={close} /></SWTheme>`. 단독 배치는 호스트의 `style`로 지정할 수 있습니다.
- 동적 패널 입력은 `SWHeaderInput commitOnUnmount={false}`를 사용합니다. 기존 Input의 기본 `true` 동작은 유지합니다.
- 속성 필드 상태(2026-10-19, Figma 26:12388 ~ 26:12649): 공통 행은 200 × 28 필드(선가중치처럼 단위 없는 숫자와 투명도 값은 41px 내외 Hug)입니다. Default는 값만, Hover는 `rgba(33,29,19,.4)` 테두리, 숫자 편집 On은 `#0F0D0A` + 흰 단위, Dropdown On은 `#211D13`입니다. 하이퍼링크(text)·재료(computed)는 Figma에 On이 없어 Default/Hover만 적용합니다. [검증 기록](./sw-property-fields-verification-2026-10-19.md).
- 필드 Dropdown: `property.picker`(`"layer" | "lineStyle" | "color" | false`)와 패널 `menus`(`SWHeaderPropertyMenus`: layers·toggle Callback, lineTypes·lineWeights·lineScale, colors·colorMode·onColorPick 등)를 전달합니다. `type: "color"`는 색상, `type: "select"` + `linePreview`는 선스타일 Dropdown이 기본이며 레이어는 `picker: "layer"`를 지정합니다. 목록 선택은 해당 속성의 `onPropertyChange`(값 = 행 id)로 확정되고, 레이어 Toggle·가중치·스케일·ByLayer/ByBlock·색상 추출·상세는 `menus` Callback만 호출합니다. `menus`가 없으면 `options`로 목록을 만듭니다. `mixed`는 `다양함`으로 표시하며 현재 행을 표시하지 않습니다.
- `decimals`를 지정한 숫자는 확정값을 고정 소수 자릿수로 정규화합니다(예: 선축적 `2` → `2.0000`). 색상 입력은 기존 0~255 검증을 유지합니다.
- K 가이드는 별도 데이터를 요구하지 않습니다. 기존 K 버튼 Hover/Focus로 원본 이미지가 Pan/Zoom 반복되고, 작은 화면의 More에서도 같은 미리보기가 보입니다. Reduced Motion·Escape·재진입 초기화가 적용됩니다. 클릭은 기존 `onPopupGuide`만 호출합니다.
- 색상 모드의 null은 직접 색상 의미를 보존하고, 필수 모드 표시는 마지막 유효 모드(처음 ByLayer)를 유지합니다. 선택된 모드를 재클릭해도 onColorModeChange가 중복 호출되지 않습니다.
- 신규 Story: Property Panel / Property Panel Variants / K Guide / Additional Hover. 기존 17개 Story를 유지합니다.
@@ -0,0 +1,44 @@
# SW 속성창 입력·선택 필드 검증 (2026-10-19)
Figma `rj6wpsfk24lOWvWiVRAANP`의 속성 필드 13개 노드를 기존 `SWHeaderPropertyPanel` 행에 반영했습니다. 팝업은 새로 만들지 않았고, Header의 레이어·색상·선스타일 Dropdown 본문을 공유 컴포넌트로 분리해 재사용했습니다.
## 구현 범위
| Figma | 항목 | Default / Hover | On |
|---|---|---|---|
| 26:12388 / 26:12404 | 선스타일 | 값 + `(ByL)` + 선택한 선 패턴 | `#211D13`, `(ByL)` `#D4CBBD`, 3열 Dropdown(우측 정렬) |
| 26:12250 / 26:12265 | 레이어 — 다양함 | `다양함` | `#211D13`, 234px 레이어 목록(현재 행 없음) |
| 26:12260 / 26:12291 | 레이어 — 단일 값 | 레이어 이름 | `#211D13`, 234px 레이어 목록(현재 행 강조) |
| 26:12314 | 선축적 | 값 + `(LTscale:1)` | 편집 `#0F0D0A`, 단위 흰색, 빨간 caret |
| 26:12649 | 선가중치 · 투명도 | 41px 내외 Hug(pl 6 / pr 4) | 편집 `#0F0D0A` |
| 26:12170 / 26:12184 | 색상 | RGB + 70 × 16 색상칩, Hover 시 구분선 | RGB 영역 `#211D13`, 192px 색상 Dropdown(좌측 정렬) |
| 26:12583 | 하이퍼링크 | `--` | Figma On 없음, Hover 모양만 유지 |
| 26:12590 | 두께 | 값 + `mm` | 편집 `#0F0D0A`, 단위 흰색 |
| 26:12574 | 재료 | 값 + `(ByL)` | Figma On 없음(computed, 읽기 전용) |
공통: 높이 28px, radius 2px, Hover `1px rgba(33,29,19,.4)`. 테두리는 Default에서도 투명 1px로 존재해 Hover/On에서 값이 움직이지 않습니다.
## 실행한 검증
| 항목 | 결과 | 근거 |
|---|---|---|
| 필드 인터랙션 (Chrome, Playwright) | 77/77 PASS | `packages/sw/tests/property-fields.browser.mjs`, `property-fields-qa/run4-result.json` |
| 기존 Header·Dropdown 회귀(detail-buttons verify) | 257 checks PASS, 33 stories | 기존 baseline 대비, 산출물은 임시 폴더 |
| followup-four | 39/39 PASS | 기존 스크립트 재실행 |
| property-close | 6/6 PASS | 기존 스크립트 재실행 |
| 속성창 4상태 크기 | 314 × 256 / 361 / 658 / 754 | 위 인터랙션 테스트 마지막 단계 |
| SW Typecheck / Build, 루트 앱 Typecheck, Storybook Build | PASS | 임시 폴더로 빌드, `storybook-static`은 변경하지 않음 |
## Figma와 남은 차이
- 글꼴: Figma Dotum과 달리 Noto Sans KR은 폭이 넓습니다. 선스타일 필드는 이름이 잘리지 않도록 선 미리보기를 68px에서 최소 40px까지 줄입니다. Dropdown의 선 종류 열은 기존 Header 정책대로 182px보다 넓어질 수 있습니다(측정 187px).
- 값 굵기: 기존 속성창 기준 500을 유지했습니다(Figma 레이어명은 Dotum Regular이고 컴포넌트명에 bold가 붙어 있습니다).
- Figma에서는 Hover 시 테두리 때문에 값이 1px 이동하지만, 요청에 따라 구현은 이동하지 않습니다. 색상칩의 오른쪽 여백은 Default 기준(6px)입니다.
- 열린 Dropdown은 화면 여유가 없으면 화면 안쪽(4px)으로 이동하거나 필드 위로 열립니다.
- 하이퍼링크는 편집 가능하지만 Figma에 On이 없으므로 포커스 중에도 Hover 테두리만 표시합니다.
## NOT VERIFIED
- Firefox: 이번 필드 변경은 Chrome에서만 실행했습니다. Firefox의 `field-sizing` 미지원 경로(Hug 폭 계산)는 실행하지 않았습니다.
- 실제 CAD 호스트 연동(색상 추출, 레이어 상세 등)은 Callback 호출까지만 확인했습니다.
- Header 77개 구성 시나리오와 10개 폭 비교는 이번 변경 범위가 아니어서 다시 실행하지 않고 기존 PASS 근거를 재사용합니다.