feat(sw): viewer header logo menu and file list; simplify SW Storybook

SW Viewer Header (approved optional props, public export names unchanged at 170):
- SWViewerHeader: logoMenu, documents, activeDocumentId, onDocumentSelect,
  onDocumentOpen, documentOpenLabel; measured responsive toggles (full ->
  icons -> More) with the SWHeader More / file-list styling.
- SW AI guides, ExampleH, viewer/text-weight/theme-inheritance tests and the
  logo-document-menu tests with their QA record.

SW Storybook cleanup (Story IDs and test fixtures kept):
- Sidebar: Overview -> "SW Header" (first), 전체 Footer -> "SW Footer",
  Footer/Viewer responsive pages -> "Responsive More", Viewer content-variants
  -> "Settings Popup" (merges the settings popup page).
- Hidden by the verification tag (direct URL kept): Header Dropdown,
  Document Open, Interactive States, Document Dropdown, Hidden Sub Controls,
  Home Menu standalone; Footer tool groups / custom content / parts;
  SW/Theme; Viewer menu groups / item states / variable size / settings
  panel / Header + Dropdown.
- Removed demo UI: Overview size and callback output (demo data and
  selection only with &verification), Home Menu Figma node table and width
  demo, Header Responsive More reduced to 1440 / 768 / 390, Popup reopen
  button, property-panel comparison captions, Viewer context-menu demo
  (&verification only).
- Tests: storybook-inventory records the hidden and renamed Stories;
  overview / range-cross-browser / viewer open the demo controls with
  &verification; integrated-popup reloads the Story instead of the reopen
  button; dropdown-consolidation asserts Dropdown is hidden from the sidebar
  and still opens by its direct URL.
This commit is contained in:
shkim4 committed 2026-10-21 14:52:46 +09:00
1 parent 46119eacb5
commit 23d522c136
57 files changed
+2623 -333

No files matched your search

+125
View File
@@ -114,6 +114,131 @@ button.hm-sw-viewer-header__brand { text-align: left; }
}
.hm-sw-viewer-header__document-name { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
/* Logo menu (2026-10-21): the brand cap is the trigger of the SWHeader `...` menu (SWHeaderMore, sw.css). The wrapper
takes the cap's place in the row but is not positioned: the cap keeps its own layer (z-index 2 over the file name,
like the SWHeader logo), and the menu is placed by the header container itself — flush under the header's bottom
border (100% of its padding box + its 1px border) at the left edge of the cap, at any header height or width. */
.hm-sw-viewer-header__logo-menu.hm-sw-header__more {
position: static;
display: flex;
flex: 0 0 auto;
height: calc(var(--hm-sw-viewer-height) - 2px);
margin-left: 0;
}
.hm-sw-viewer-header__logo-menu > .hm-sw-header__more-menu {
top: calc(100% + 1px);
left: 0;
font-size: var(--hm-sw-header-font-size);
font-weight: 400;
line-height: normal;
}
/* Logo menu (2026-10-21): the size, spacing and type of the viewer header dropdowns (SWViewerMenu); its colors are
those of the SWHeader `...` menu it is (sw.css .hm-sw-header__more-menu: face, border, shadow, hover); its place
stays flush under the header. Only this menu: the document list stays the SWHeader file-name list. */
.hm-sw-viewer-header__logo-menu > .hm-sw-header__more-menu {
min-width: 160px;
padding: 4px;
border-width: 1px;
border-radius: var(--hm-sw-viewer-radius);
color: var(--hm-sw-header-text-color);
font-size: 14px;
font-weight: 400;
line-height: 20px;
}
.hm-sw-viewer-header__logo-menu .hm-sw-header__more-item {
gap: 8px;
min-height: var(--hm-sw-viewer-menu-item-height);
padding: 0 12px;
color: inherit;
}
.hm-sw-viewer-header__logo-menu .hm-sw-header__more-item:is(:hover, :focus):not(:disabled) { background: var(--hm-sw-header-hover); outline: none; }
.hm-sw-viewer-header__logo-menu .hm-sw-header__more-item:disabled { opacity: 0.5; }
/* Title = SWViewerMenu label, directly above its items (one group, no divider); icons = SWViewerMenu icon slot. */
.hm-sw-viewer-header__logo-menu .hm-sw-header__more-title { min-height: 0; padding: 6px 12px; font-size: 12px; font-weight: 500; line-height: 18px; }
/* Document list (2026-10-21): the SWHeader file-name button and dropdown (SWHeaderDocumentTitle) fill the well.
The button draws the well (border, gradient), sized to the viewer row; the text keeps the viewer inset. */
.hm-sw-viewer-header__document[data-list] {
--hm-sw-header-bar-height: calc(var(--hm-sw-viewer-height) - 2px);
padding: 0;
border: 0;
background: none;
font-size: var(--hm-sw-header-font-size);
line-height: normal;
}
/* Header Open (2026-10-21, `onDocumentOpen`): SWHeader's documentOpenPlacement="header" surface (SWToolButton
appearance="document", the file-name surface at its content width) right after the logo, sized to the viewer row,
with the label after a folder icon (no border, no face). Icon only when the header has no room (measured,
SWViewerHeader); the file name after it has no left padding (2026-10-21 user request). */
.hm-sw-viewer-header > .hm-sw-header__document[data-tool] {
--hm-sw-header-bar-height: calc(var(--hm-sw-viewer-height) - 2px);
font-size: var(--hm-sw-header-font-size);
line-height: normal;
}
.hm-sw-viewer-header .hm-sw-viewer-header__open { padding: 0 12px 0 16px; }
.hm-sw-viewer-header__open .hm-sw-header__document-name { display: inline-flex; align-items: center; gap: 8px; overflow: visible; }
.hm-sw-viewer-header__open-icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: var(--hm-sw-viewer-button-height);
height: var(--hm-sw-viewer-button-height);
/* 2026-10-21 user request: no border and no face (transparent); the icon keeps its color, size and place. */
border: 0;
background: transparent;
box-shadow: none;
text-shadow: none;
}
.hm-sw-viewer-header__open-icon > svg { width: var(--hm-sw-viewer-icon-size); height: var(--hm-sw-viewer-icon-size); }
.hm-sw-viewer-header > .hm-sw-header__document[data-tool] + .hm-sw-viewer-header__document { padding-left: 0; }
.hm-sw-viewer-header__document[data-list] > .hm-sw-header__document { flex: 1 1 auto; min-width: 0; }
/* No file open button before it: the file name starts 10px after the slanted logo edge (at mid height), as in
SWHeader (text 10px after its home slant; measured 10.5px here with 18px). */
.hm-sw-viewer-header__document[data-list] .hm-sw-header__document-button { padding: 0 16px 0 18px; }
/* After the file open button (2026-10-21 user decision): 8px in. */
.hm-sw-viewer-header > .hm-sw-header__document[data-tool] + .hm-sw-viewer-header__document[data-list] .hm-sw-header__document-button { padding: 0 16px 0 8px; }
/* The list (SWHeader face, placement and rows unchanged) is never narrower than the file-name button it opens from:
its outer width is at least the button's (100% of the wrapper, plus the two borders the list starts left of it) and
at least the SWHeader list width; longer rows widen it. min-width wins over the SWHeader max-width. */
.hm-sw-viewer-header__document[data-list] .hm-sw-header__document-menu {
width: max-content;
min-width: max(
calc(var(--hm-sw-header-document-menu-width) + 2 * var(--hm-sw-header-document-menu-border-width)),
calc(100% + 2 * var(--hm-sw-header-document-menu-border-width))
);
}
/* Viewer header dropdown colors (2026-10-21): the menus of the header — menu buttons, More and their submenus — use the
colors of the SWHeader `...` menu (sw.css .hm-sw-header__more-menu): its face and shadow (fixed values there), the
object border token, the header text, secondary text at 80% (as its values) and the white 8% row hover with the text
unchanged. Size, spacing and type stay SWViewerMenu. A standalone SWViewerMenu (context menu) and the document list
keep their own colors. */
.hm-sw-viewer-header .hm-sw-viewer-menu[data-surface] {
border-color: var(--hm-sw-header-object-border-color);
background: #0e3d2f;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
color: var(--hm-sw-header-text-color);
}
.hm-sw-viewer-header .hm-sw-viewer-menu .hm-sw-viewer-menu__item:is(:focus, :hover, [data-state="open"]):not([aria-disabled]) {
background: var(--hm-sw-header-hover);
color: inherit;
}
.hm-sw-viewer-header .hm-sw-viewer-menu :is(.hm-sw-viewer-menu__label, .hm-sw-viewer-menu__shortcut, .hm-sw-viewer-menu__chevron) { color: inherit; opacity: 0.8; }
.hm-sw-viewer-header .hm-sw-viewer-menu .hm-sw-viewer-menu__separator { background: var(--hm-sw-header-document-menu-divider-color); }
/* Measured responsive steps (2026-10-21, SWViewerHeader measureRoom): set only inside one layout pass, never painted.
Every part at its natural width: the file name at its text width up to its usual flex basis, the object surface
neither stretched nor shrunk, the trailing area without its auto margin. */
.hm-sw-viewer-header[data-measuring] .hm-sw-viewer-header__document { flex: 0 0 auto; width: max-content; max-width: clamp(140px, 25cqw, 360px); }
.hm-sw-viewer-header[data-measuring] .hm-sw-viewer-header__object { flex: 0 0 auto; }
.hm-sw-viewer-header[data-measuring] .hm-sw-viewer-header__actions { margin-left: 0; }
/* While the logo menu or the document list is open, the header takes the viewer dropdown layer (SWViewerMenu, 60) so
the parts hanging below it stay above SWViewerPanel popups (41+). Inside, the SWHeader order is kept: the logo cap
(z-index 2) over the file name and its list (z-index 1), so an open list never covers the logo. Closed: no layer. */
.hm-sw-viewer-header:has(> .hm-sw-viewer-header__logo-menu > .hm-sw-header__more-menu, > .hm-sw-viewer-header__document .hm-sw-header__document-menu) { z-index: 60; }
.hm-sw-viewer-header__object {
display: flex;
align-items: center;
+32
View File
@@ -48,6 +48,10 @@
--hm-sw-header-document-menu-inset-shadow: inset 0 4px 8px 0 rgba(0, 0, 0, 0.4);
--hm-sw-header-document-menu-divider-color: rgba(194, 217, 210, 0.2);
--hm-sw-header-document-list-max-height: 320px;
/* Document list scrollbar (2026-10-21): the Storybook sidebar scrollbar (.storybook/manager.css, #BDDFD4 thumb at
50% / 80% on hover, 6px, round, transparent track). */
--hm-sw-header-document-scrollbar-thumb: rgba(189, 223, 212, 0.5);
--hm-sw-header-document-scrollbar-thumb-hover: rgba(189, 223, 212, 0.8);
--hm-sw-header-document-action-background: rgba(182, 208, 201, 0.2);
--hm-sw-header-document-action-color: rgba(0, 0, 0, 0.7);
/* Figma hover variants: list 26:10680, + New 26:10762, Open 26:10805 */
@@ -422,6 +426,18 @@
overflow-y: auto;
box-shadow: var(--hm-sw-header-document-menu-inset-shadow);
}
/* Scrollbar (2026-10-21): like the Storybook sidebar — 6px, round thumb, transparent track 3px in from top and bottom,
thumb 50% (80% on the thumb). Shown whenever the list scrolls: Chromium does not repaint a custom scrollbar on the
list's own :hover, so a hover-only thumb could stay hidden. Chromium / Safari draw it with ::-webkit-scrollbar;
Firefox, which has no such pseudo-elements, uses the SW standard thin scrollbar with the same colors. Only the
scrollbar changes: list size, rows and scrolling stay as they are. */
@supports not selector(::-webkit-scrollbar) {
.hm-sw-header__document-list { scrollbar-width: thin; scrollbar-color: var(--hm-sw-header-document-scrollbar-thumb) transparent; }
}
.hm-sw-header__document-list::-webkit-scrollbar { width: 6px; background: transparent; }
.hm-sw-header__document-list::-webkit-scrollbar-track { margin: 3px 0; background: transparent; }
.hm-sw-header__document-list::-webkit-scrollbar-thumb { border-radius: 9px; background: var(--hm-sw-header-document-scrollbar-thumb); }
.hm-sw-header__document-list::-webkit-scrollbar-thumb:hover { background: var(--hm-sw-header-document-scrollbar-thumb-hover); }
/* Row = select button + optional Close Document button (siblings). Hover and the current
document are styled on the row so the overlay stays while the pointer is on the X button. */
.hm-sw-header__document-row {
@@ -1310,6 +1326,22 @@ div.hm-sw-header__menu-row { justify-content: space-between; gap: 8px; padding-l
.hm-sw-header__more-item[role="menuitemcheckbox"]::after { content: ""; width: 14px; text-align: center; }
.hm-sw-header__more-item[aria-checked="true"]::after { content: "✓"; }
.hm-sw-header__more-value { font-size: var(--hm-sw-header-label-size); opacity: 0.8; }
/* Optional title and item icons (SWViewerHeader logo menu, 2026-10-21). The title is rendered only with at least one
item and has no divider (title + items are one group); the `...` menu of SWHeader passes neither and is unchanged. */
.hm-sw-header__more-title {
display: flex;
align-items: center;
min-height: 32px;
padding: 0 10px;
font-weight: 700;
white-space: nowrap;
opacity: 0.8;
}
.hm-sw-header__more-item:has(> .hm-sw-header__more-icon) { justify-content: flex-start; }
.hm-sw-header__more-icon { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 16px; width: 16px; height: 16px; }
.hm-sw-header__more-icon > svg { width: 16px; height: 16px; }
.hm-sw-header__more-item:has(> .hm-sw-header__more-icon) > .hm-sw-header__more-value { margin-left: auto; }
.hm-sw-header__more-menu[data-align="start"] { right: auto; left: 0; }
/* Window area: print / guide pill, window move, minimize, close. */
.hm-sw-header__window {