chore(sw): organize Storybook components and documentation
First-pass cleanup only; no design, behavior, public API or Story ID change. - Stories: correct stale Text Stroke / Colorize / "latest" texts, reuse parseSWHeaderColor, derive the field fixture from PROPERTY_FIXTURE - Comments: date the "Latest request" notes in sw.css and the color menu - Docs: fix removed text-stroke tokens in spec/usage/README, add spec §18 (property fields), current-state index banners, Storybook 33 pointer - Add docs/sw/sw-first-pass-cleanup-review-2026-10-19.md (Story table, structure, SAFE / REVIEW list for the second pass) Checks: SW + app typecheck, SW build, Storybook build, 33 Story IDs unchanged, property fields 77/77, detail-buttons verify 257 checks, 111 doc links 0 broken. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0029cef60c
commit
92830fe11d
7 files changed
+267
-39
No files matched your search
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState, type ReactNode } from "react"
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite"
|
||||
import {
|
||||
SWHeader, SWHeaderColorControl, SWHeaderLayerControl, SWHeaderLineStyle, SWHeaderTextStyle, SWHeaderDimensionStyle,
|
||||
SWHeaderProperties, SWHeaderWindowControls, SWTheme, SWHeaderLayerMenu, SWHeaderInput, swHeaderLinePatterns, SWHeaderPropertyPanel, SWHeaderPropertyBar,
|
||||
SWHeaderProperties, SWHeaderWindowControls, SWTheme, SWHeaderLayerMenu, SWHeaderInput, parseSWHeaderColor, swHeaderLinePatterns, SWHeaderPropertyPanel, SWHeaderPropertyBar,
|
||||
type SWHeaderColorMode, type SWHeaderColorOption, type SWHeaderColorValue, type SWHeaderDimensionStyleOption,
|
||||
type SWHeaderDocument, type SWHeaderLayer, type SWHeaderLineStyleValue, type SWHeaderLineType, type SWHeaderLineWeight,
|
||||
type SWHeaderMenuId, type SWHeaderProps, type SWHeaderTextStyleOption, type SWHeaderProperty, type SWHeaderPropertyGroup, type SWHeaderPropertySelection, type SWHeaderPropertyMenus, type SWHeaderControlConfig, type SWHeaderHideableControlId, type SWHeaderPropertyShapeContent,
|
||||
@@ -852,13 +852,6 @@ const FIGMA_DIMENSION_STYLES: SWHeaderDimensionStyleOption[] = [
|
||||
|
||||
type StyleHostProps = Partial<SWHeaderProps> & { onLog?: (line: string) => void; showState?: boolean; initialColorMode?: SWHeaderColorMode | null }
|
||||
|
||||
/** `#rrggbb` → `r,g,b` (sample data helper; other CSS colors are passed through). */
|
||||
function hexToRgbText(color: string) {
|
||||
const match = /^#([0-9a-f]{6})$/i.exec(color)
|
||||
if (!match) return color
|
||||
return [0, 2, 4].map(index => parseInt(match[1].slice(index, index + 2), 16)).join(",")
|
||||
}
|
||||
|
||||
/**
|
||||
* Host-owned field data: the SW program keeps color, line / text / dimension style and the lists. The
|
||||
* header only reports; this example writes each report back (an explicit color clears ByLayer / ByBlock,
|
||||
@@ -891,7 +884,7 @@ function StyleHeader({ onLog, showState, initialColorMode = null, ...props }: St
|
||||
colorMode={colorMode}
|
||||
onColorOpen={() => log("onColorOpen")}
|
||||
onColorMenuOpenChange={open => log(`onColorMenuOpenChange(${open})`)}
|
||||
onColorSelect={option => { log(`onColorSelect(${option.id})`); setColor({ value: option.value ?? hexToRgbText(option.color), swatch: option.color }); setColorMode(null) }}
|
||||
onColorSelect={option => { log(`onColorSelect(${option.id})`); setColor({ value: option.value ?? parseSWHeaderColor(option.color)?.value ?? option.color, swatch: option.color }); setColorMode(null) }}
|
||||
onColorModeChange={mode => { log(`onColorModeChange(${mode})`); setColorMode(mode); setColor(current => ({ ...current, value: mode === "byLayer" ? "ByLayer" : "ByBlock" })) }}
|
||||
onColorInput={value => { log(`onColorInput(${value.value})`); setColor({ value: value.value, swatch: value.hex }); setColorMode(null) }}
|
||||
onColorPick={() => log("onColorPick")}
|
||||
@@ -980,7 +973,7 @@ export const ColorDropdown: Story = {
|
||||
<StorySection title="ByLayer On (Figma on 26:12671)" description="colorMode='byLayer': #62533D 배경 + 밝은 inner shadow. 텍스트 #D4CBBD + 검정 Stroke. ByBlock은 Default(rgba(98,83,61,.3) + 어두운 inner shadow).">
|
||||
<MenuFrame width={1920}><StyleHeader initialColorMode="byLayer" colorMenuOpen /></MenuFrame>
|
||||
</StorySection>
|
||||
<StorySection title="내부 버튼 Hover (고정) — 최신 예외 정책" description="ByLayer / ByBlock 글자 #FF5C00. 선택 배경과 Inset Shadow는 유지합니다. 색상 상세는 배경 .25·radius 25·Hover 아이콘으로 변합니다. 색상 칩·추출 버튼은 기존 정책을 유지합니다.">
|
||||
<StorySection title="내부 버튼 Hover (고정) — 2026-10-16 추가 수정 정책" description="ByLayer / ByBlock 글자 #FF5C00. 선택 배경과 Inset Shadow는 유지합니다. 색상 상세는 배경 .25·radius 25·Hover 아이콘으로 변합니다. 색상 칩은 Hover가 없고, 색상 추출은 btn_colorize Hover(26:11305)입니다.">
|
||||
<MenuFrame width={1920}><PinHover selectors={MENU_BUTTON_HOVER}><StyleHeader colorMenuOpen /></PinHover></MenuFrame>
|
||||
</StorySection>
|
||||
<StorySection title="RGB 입력 Hover (고정)" description="입력은 수정 가능함을 알리는 Figma Hover를 유지합니다(header/menu/색상/input hover: 1px rgba(197,177,158,.2)). 편집 중(Focus)에는 값 영역이 검정이 되고 커서는 #E60403입니다 — 아래 실제 예제에서 클릭해 확인합니다.">
|
||||
@@ -1022,7 +1015,7 @@ export const LineStyleDropdown: Story = {
|
||||
<StorySection title="현재 행 Hover (고정)" description="현재 선 종류(Continuous)는 Hover 및 Mouse Leave 뒤에도 갈색 배경·주황 선·이름을 유지합니다.">
|
||||
<MenuFrame width={1920}><PinHover selectors={[".hm-sw-header__menu-row[data-active]"]}><StyleHeader lineStyleMenuOpen /></PinHover></MenuFrame>
|
||||
</StorySection>
|
||||
<StorySection title="상세 버튼 Hover (고정)" description="최신 요청에 따라 선스타일 상세의 Figma 배경 .25·radius 25·Hover 아이콘을 적용합니다.">
|
||||
<StorySection title="상세 버튼 Hover (고정)" description="2026-10-16 추가 수정 기준으로 선스타일 상세의 Figma 배경 .25·radius 25·Hover 아이콘을 적용합니다.">
|
||||
<MenuFrame width={1920}><PinHover selectors={MENU_BUTTON_HOVER}><StyleHeader lineStyleMenuOpen /></PinHover></MenuFrame>
|
||||
</StorySection>
|
||||
<StorySection title="선 종류 · 가중치 · 스케일 선택 Callback" description="선 종류 → onLineTypeSelect(가중치·스케일 유지), 가중치 → onLineWeightSelect(선 종류·스케일 유지), User 입력 → onLineWeightValueChange(0 이상), 스케일 입력 → onLineScaleChange(0 초과). 입력은 Enter/Focus 이동으로 확정, Escape는 취소. ↓↑로 항목 이동.">
|
||||
@@ -1108,7 +1101,7 @@ export const TextStroke: Story = {
|
||||
<StoryPageHeader
|
||||
kicker={KICKER}
|
||||
title="Text Stroke"
|
||||
description="Figma 26:8944와 메뉴의 텍스트 Stroke(모두 OUTSIDE)를 복원했습니다: 14 · 16px = 검정 1.3px + 자기 색 0.3px, 12px 라벨 = 검정 1px, '… 상세' = 자기 색 0.4px(검정 없음). 글꼴·크기·굵기·자간·기준선·말줄임은 그대로이며 Hover/Selected는 채움색만 바뀝니다."
|
||||
description="Figma 26:8944와 메뉴의 텍스트 Stroke(모두 OUTSIDE) 중 검정 외곽선을 text-shadow로 표시합니다: 14 · 16px 값 = 검정 1.3px, 12px 라벨 = 검정 1px. 자기 색 Stroke(0.3px · '… 상세' 0.4px)는 2026-10-16 추가 수정으로 제거했습니다. 글꼴·크기·굵기·자간·기준선·말줄임은 그대로이며 Hover/Selected는 채움색만 바뀝니다."
|
||||
/>
|
||||
<StorySection title="구현 (1920)">
|
||||
<Frame width={1920}><SWHeader {...figmaArgs} {...callbacks} /></Frame>
|
||||
@@ -1800,19 +1793,12 @@ const FIELD_NODES = [
|
||||
{ node: "26:12590", name: "두께", states: "Default · Hover · On(편집)" },
|
||||
{ node: "26:12574", name: "재료", states: "Default · Hover (Figma On 없음)" },
|
||||
]
|
||||
const FIELD_FIXTURE: SWHeaderProperty[] = [
|
||||
{ id: "layer-mixed", name: "레이어", value: "0_Layer_Cludmark", mixed: true, type: "text", groupId: "common", picker: "layer" },
|
||||
{ id: "layer", name: "레이어", value: "0_Layer_Cludmark", type: "text", groupId: "common", picker: "layer" },
|
||||
// Figma sample text 255,259,0 is out of range: shown as host text only, never accepted as input.
|
||||
{ id: "color", name: "색상", value: "255,259,0", swatch: "#ffe500", type: "color", groupId: "common" },
|
||||
{ id: "line", name: "선스타일", value: "Continuous", type: "select", groupId: "common", unit: "(ByL)", linePreview: true, options: PROPERTY_LINE_TYPES.map(({ id, name }) => ({ id, name })) },
|
||||
{ id: "weight", name: "선가중치", value: "0.25", type: "number", groupId: "common", min: 0 },
|
||||
{ id: "scale", name: "선축적", value: "1.0000", type: "number", groupId: "common", min: .0001, decimals: 4, unit: "(LTscale:1)" },
|
||||
{ id: "opacity", name: "투명도", value: "0.25", type: "slider", groupId: "common", min: 0, max: 1, step: .01, annotation: "(ByL)", unit: "%" },
|
||||
{ id: "link", name: "하이퍼링크", value: "--", type: "text", groupId: "common" },
|
||||
{ id: "thickness", name: "두께", value: "0.25", type: "number", groupId: "common", unit: "mm", min: 0 },
|
||||
{ id: "material", name: "재료", value: "-", type: "computed", groupId: "common", unit: "(ByL)" },
|
||||
]
|
||||
// The common rows of PROPERTY_FIXTURE plus a mixed layer row. Figma sample text 255,259,0 is out of range:
|
||||
// shown as host text only, never accepted as input.
|
||||
const FIELD_FIXTURE: SWHeaderProperty[] = PROPERTY_FIXTURE.filter(item => item.groupId === "common").flatMap(item =>
|
||||
item.id === "layer" ? [{ ...item, id: "layer-mixed", mixed: true }, item]
|
||||
: item.id === "color" ? [{ ...item, value: "255,259,0", swatch: "#ffe500" }]
|
||||
: [item])
|
||||
const FIELD_HOVER = [".hm-sw-property__value[data-field]", ".hm-sw-property__compact"]
|
||||
function FieldPanel({ title, properties, onChange, menus }: { title: string; properties: SWHeaderProperty[]; onChange?: (property: SWHeaderProperty, value: string) => void; menus?: SWHeaderPropertyMenus }) {
|
||||
return <SWTheme><SWHeaderPropertyPanel selection={{ id: title, title, kind: "none" }} properties={properties} groups={[{ id: "common" }]} onPropertyChange={onChange} menus={menus} /></SWTheme>
|
||||
|
||||
Reference in new issue
Block a user