@namroodinc/liquid-editorial 0.1.9 → 0.1.11
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/README.md +13 -1
- package/UPGRADING.md +56 -0
- package/dist/components/AppShell/index.cjs +1 -1
- package/dist/components/AppShell/index.cjs.map +1 -1
- package/dist/components/AppShell/index.d.cts.map +1 -1
- package/dist/components/AppShell/index.d.ts.map +1 -1
- package/dist/components/AppShell/index.js +1 -0
- package/dist/components/AppShell/index.js.map +1 -1
- package/dist/components/Badge/style.css +24 -21
- package/dist/components/Drawer/index.cjs +1 -1
- package/dist/components/Drawer/index.cjs.map +1 -1
- package/dist/components/Drawer/index.d.cts +3 -2
- package/dist/components/Drawer/index.d.cts.map +1 -1
- package/dist/components/Drawer/index.d.ts +3 -2
- package/dist/components/Drawer/index.d.ts.map +1 -1
- package/dist/components/Drawer/index.js +11 -10
- package/dist/components/Drawer/index.js.map +1 -1
- package/dist/components/Drawer/style.css +37 -79
- package/dist/components/Field/index.cjs +1 -1
- package/dist/components/Field/index.cjs.map +1 -1
- package/dist/components/Field/index.d.cts +2 -1
- package/dist/components/Field/index.d.cts.map +1 -1
- package/dist/components/Field/index.d.ts +2 -1
- package/dist/components/Field/index.d.ts.map +1 -1
- package/dist/components/Field/index.js +2 -2
- package/dist/components/Field/index.js.map +1 -1
- package/dist/components/SelectionCard/index.cjs +1 -1
- package/dist/components/SelectionCard/index.cjs.map +1 -1
- package/dist/components/SelectionCard/index.d.cts +4 -1
- package/dist/components/SelectionCard/index.d.cts.map +1 -1
- package/dist/components/SelectionCard/index.d.ts +4 -1
- package/dist/components/SelectionCard/index.d.ts.map +1 -1
- package/dist/components/SelectionCard/index.js +3 -3
- package/dist/components/SelectionCard/index.js.map +1 -1
- package/dist/tokens.css +15 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,6 +29,8 @@ export function ArticleForm() {
|
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
`Field.hint` accepts a string or React node, such as `hint={<>Use a <strong>short headline</strong>.</>}`. Hints are associated with the control through `aria-describedby`; an error replaces the hint while present.
|
|
33
|
+
|
|
32
34
|
Every family also has a direct entry point:
|
|
33
35
|
|
|
34
36
|
```tsx
|
|
@@ -47,6 +49,12 @@ Pending Buttons replace custom glyphs with the shared spinner. Button-backed sel
|
|
|
47
49
|
|
|
48
50
|
If an application only needs the design tokens, import `@namroodinc/liquid-editorial/tokens.css`. A small global normalization is available separately at `@namroodinc/liquid-editorial/reset.css`; it is never included automatically.
|
|
49
51
|
|
|
52
|
+
## Badges
|
|
53
|
+
|
|
54
|
+
`Badge` is a non-interactive editorial label. Use `shape="status"` with one of the six publication tones, `shape="tag"` (the default) for metadata, and `shape="count"` for totals. Status labels and tags display uppercase with 4px corners; status labels add 6px square markers. Counts keep 12px untracked, tabular numerals; zero renders nothing and values above 99 display `99+`. The original child text remains unchanged in the markup.
|
|
55
|
+
|
|
56
|
+
Use `appearance="outline"` (the default) on board and selection cards, `appearance="soft"` in dense tables and toolbar counts, and `appearance="solid"` for the one state a row or tile emphasizes, such as a Cover asset badge. Soft badges have no visible stroke. Tags inside sentences also display uppercase, including path labels such as `/news`.
|
|
57
|
+
|
|
50
58
|
## Content models
|
|
51
59
|
|
|
52
60
|
`EntryEditor` renders editable fields in the order defined by a JSON model:
|
|
@@ -124,7 +132,11 @@ The optional `actions` render function receives `"topnav"` or `"drawer"`. Return
|
|
|
124
132
|
|
|
125
133
|
The drawer traps focus and locks body scroll. Escape, Close, the scrim, and navigation links dismiss it and return focus to the hamburger. A popup inside it, such as a Menu in the actions footer, closes first on Escape. Modifier-key clicks that open a link elsewhere leave the drawer open. Widening the shell to desktop closes it and moves focus to the active desktop link when focus was inside the drawer. Reduced motion skips the transition.
|
|
126
134
|
|
|
127
|
-
|
|
135
|
+
All `Drawer` variants have square corners and slide fully in and out through their anchored edge, with a fading scrim. Use `side="end"` (the default) for the right edge in LTR, `side="start"` for the left, and `side="bottom"` for a bottom sheet. Start and end mirror in RTL, including when passing `dir="rtl"` directly to Drawer. Motion uses `--duration-panel` and `--ease-panel`; reduced motion skips the transition. Closing immediately makes the exiting panel inert and accessibility-hidden, restores focus and body scroll, and removes the panel when the transition finishes. Tests checking actual DOM removal should await it rather than expect immediate unmounting. Keep Drawer mounted while changing `isOpen` so it can animate its exit.
|
|
136
|
+
|
|
137
|
+
Side drawers have one outer border facing the page: the left border for a panel entering from the right, or the right border for one entering from the left. They have no outer top or bottom border. This applies to both variants and mirrors in RTL. Internal header/footer separators remain.
|
|
138
|
+
|
|
139
|
+
AppShell uses the shared `Drawer` with `variant="navigation"` and `side="end"`. For other navigation compositions, use the same variant for a compact width, header and footer. Drawer supports a React-node `title` and `closeLabel`; supply `aria-label` for a dialog name independent of its visual brand. By default the drawer portals to `document.body` and covers the viewport; for a navigation drawer, pass a positioned element as `portalContainer` to keep it inside that element. Default drawers retain their standard widths, shadow, and header/footer spacing. Both variants use the same slide, exit handling, and focus protection.
|
|
128
140
|
|
|
129
141
|
## Image cropping
|
|
130
142
|
|
package/UPGRADING.md
CHANGED
|
@@ -2,6 +2,62 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.1.11
|
|
6
|
+
|
|
7
|
+
### Changes
|
|
8
|
+
|
|
9
|
+
- `SelectionCardGroup` now accepts an optional `gap` prop, passed to its shared Grid to control spacing between cards in both axes. The default is now `md`, increased from the previous fixed `sm` spacing.
|
|
10
|
+
- `Badge` now follows the editorial-label handoff: 11px bold uppercase status labels and tags, 4px corners, 6px square status markers, and 24px default height across appearances. Soft badges lose their visible border. Counts retain 12px untracked tabular numerals with a 22px minimum width and 6px inline padding. Draft soft fill now uses a defined surface token; archived text uses a stronger muted-text mix.
|
|
11
|
+
- All `Drawer` variants now have square corners and share full-distance slide-in/out transitions and a fading scrim. `start`/`end` mirror in RTL; `bottom` remains supported and slides vertically. Side drawers have only the vertical outer border facing the page, with no outer top or bottom border. Default drawers now retain an inert, accessibility-hidden panel during exit, as navigation drawers already did. AppShell continues to compose the shared Drawer and explicitly uses `side="end"`.
|
|
12
|
+
|
|
13
|
+
### Adoption
|
|
14
|
+
|
|
15
|
+
Pass a Grid spacing step, for example `<SelectionCardGroup label="Distribution channels" gap="lg">…</SelectionCardGroup>`. Supported values are `none`, `xs`, `sm`, `md`, `lg`, `xl`, and `2xl`. Omit `gap` to use `md`, or pass `gap="sm"` to retain the previous spacing.
|
|
16
|
+
|
|
17
|
+
Badge props and imports need no changes; retain the aggregate stylesheet import. Prefer `outline` for board and selection cards, `soft` for table states and toolbar counts, and `solid` for a single emphasized state such as Cover. Review custom CSS and layouts that assume pill corners, round markers, mixed-case tags, or the old spacing. Inline tags such as `/news` now display uppercase. The new scale tokens are `--badge-radius`, `--badge-font-size`, `--badge-line-height`, `--badge-tracking`, `--badge-pad-block`, `--badge-gap`, `--badge-marker-size`, `--badge-count-min`, and `--badge-count-pad-inline`; use scoped overrides when needed.
|
|
18
|
+
|
|
19
|
+
Drawer props and imports need no changes. Keep Drawer mounted and control visibility with `isOpen`; conditional unmounting skips its exit. Use `side="start"` or `side="end"` for horizontal drawers and `side="bottom"` for sheets. Remove local corner-radius, short-entrance-animation, and page-scroll workarounds superseded by the shared behavior. Tests that check removal should await the panel or scrim leaving the DOM; role queries stop finding it immediately on close. Continue using `variant="navigation"` for compact navigation content. Customize motion with the existing `--duration-panel` and `--ease-panel` tokens.
|
|
20
|
+
|
|
21
|
+
Remove local overrides that only hide a side drawer's outer top/bottom borders; the shared stylesheet now handles that. Review custom frame styles that assume a border around three edges.
|
|
22
|
+
|
|
23
|
+
### Compatibility
|
|
24
|
+
|
|
25
|
+
The prop uses the existing `GapSize` type and Grid styles. Groups without an explicit gap now have more space between cards; pass `gap="sm"` where the previous density is required. Legend spacing and column behavior are unchanged. No dependency or version changes are required; the release workflow owns version increments.
|
|
26
|
+
|
|
27
|
+
Badge's shapes, appearances, six tones, forwarded ref, data attributes, and `.ds-badge__dot` hook remain supported. Uppercase is a CSS treatment; child text is unchanged. Zero counts remain hidden and values above 99 still display `99+`. Default badge height is 24px at a 16px root font size and text scales with the root font. Shared `--status-draft-soft` and `--status-archived-text` corrections also affect custom status tabs or other consumers of those aliases. Theme palettes and dependencies are unchanged.
|
|
28
|
+
|
|
29
|
+
Drawer's public types, sizes, default `side="end"`, and variants remain supported. Standard widths, border weights, shadows, and header/footer spacing are retained; navigation keeps its compact geometry and shell containment. The intentional behavior change is delayed DOM removal for default drawers. Focus restoration and body-scroll restoration still happen when `isOpen` becomes false, and closed panels cannot receive interaction while exiting. Default initial focus stays on the first control; navigation still focuses Close. Both now use `preventScroll` for initial and restored focus. A native `dir` prop also reaches the scrim, keeping logical-edge alignment consistent with the panel. Reduced motion finishes exit without a visible transition. No dependency or version changes are needed.
|
|
30
|
+
|
|
31
|
+
The side-drawer border adjustment changes only the outer frame for `start`/`end`: the page-facing border retains `--frame-width` and `--frame-color`. Header/footer separators and bottom-sheet framing are retained. Logical border properties keep the remaining vertical border correct in RTL.
|
|
32
|
+
|
|
33
|
+
### Verification
|
|
34
|
+
|
|
35
|
+
Run the consuming project's typecheck, tests, and build. Compare a group without `gap` to one with `gap="md"`, confirm `gap="sm"` restores the previous density, and check custom spacing between columns and rows, including when the group collapses to one column. The library's `CustomGap` SelectionCard story offers a selector for every supported gap.
|
|
36
|
+
|
|
37
|
+
Review Badge's `VariantsAndStates` story in light, dark, cherry-glaze, and black-hawaii: all three appearances should share a height, status markers should be square, draft soft should have a fill, and archived text should use the stronger ink. Check count values 0, 3, 42, and 128 (hidden, 3, 42, and 99+). Inspect table status columns and toolbar counts, board and selection cards (including inline tags), Cover asset badges, EntryPicker and SideNav counts, and any custom status tabs that consume the corrected aliases. Check narrow layouts and custom token overrides, and run the consuming project's normal checks against the packed build.
|
|
38
|
+
|
|
39
|
+
Exercise the Drawer `Start`, `End`, and `Bottom` stories and AppShell navigation in every theme, LTR/RTL, narrow/wide viewports, and reduced motion. Confirm square corners and a full slide through the anchored edge on open and close, with stationary page/shell backgrounds and no horizontal scrolling. Check long content scrolls inside the panel while header/footer stay usable. Test Close, scrim and Escape dismissal, nested popup Escape handling, Tab/Shift+Tab, first-control versus navigation Close focus, focus/body-scroll restoration, rapid reopening, transition completion, and zero-duration exit. Verify AppShell still contains the shared navigation drawer and closes correctly at its desktop breakpoint. Run the consuming project's typecheck, tests, and build; update any tests or code that assume immediate DOM removal.
|
|
40
|
+
|
|
41
|
+
For both Drawer variants, check that `start`/`end` panels have zero outer top/bottom borders and exactly one vertical frame border facing the page, in LTR and RTL. Confirm internal header/footer separators and the Bottom story's existing frame still render correctly.
|
|
42
|
+
|
|
43
|
+
## 0.1.10
|
|
44
|
+
|
|
45
|
+
### Changes
|
|
46
|
+
|
|
47
|
+
- `Field.hint` now accepts `ReactNode`, including strings, formatted text, and links. Falsy conditional hints are omitted without leaving a stray `0`.
|
|
48
|
+
|
|
49
|
+
### Adoption
|
|
50
|
+
|
|
51
|
+
Existing string hints need no changes. Pass inline JSX for richer guidance, for example `hint={<>Use a <strong>short headline</strong>.</>}`. Continue spreading Field's render props onto its control to retain accessible hint associations.
|
|
52
|
+
|
|
53
|
+
### Compatibility
|
|
54
|
+
|
|
55
|
+
This widens the optional hint type without changing the hint's styling, wrapper, or ID association. Errors still replace hints and mark the control invalid. Falsy hints remain hidden. No dependency, stylesheet, or version changes are required; the release workflow owns version increments.
|
|
56
|
+
|
|
57
|
+
### Verification
|
|
58
|
+
|
|
59
|
+
Run the consuming project's typecheck, tests, and build. Check string and JSX hints, confirm the control's `aria-describedby` points to the displayed hint, and verify that an error hides either hint and becomes the control's description. Check that a falsy conditional hint leaves no text or description reference. The library's `RichHint` Field story demonstrates formatted content.
|
|
60
|
+
|
|
5
61
|
## 0.1.9
|
|
6
62
|
|
|
7
63
|
### Changes
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../Button/index.cjs"),t=require("../Drawer/index.cjs"),n=require("../SideNav/index.cjs"),r=require("../TopNav/index.cjs");let i=require("react"),a=require("react/jsx-runtime");var o=(0,i.forwardRef)(function({brand:o,navigation:s,actions:c,navigationLabel:l=`Main`,className:u,children:d,...f},p){let[m,h]=(0,i.useState)(null),[g,_]=(0,i.useState)(!1),v=(0,i.useRef)(null),y=(0,i.useRef)(null),b=(0,i.useRef)(!1),x=(0,i.useId)(),S=(0,i.useCallback)(e=>{h(e),typeof p==`function`?p(e):p&&(p.current=e)},[p]);(0,i.useEffect)(()=>{if(!m)return;let e=e=>{e<768||(m.querySelector(`[data-state='open'] [role='dialog']`)?.contains(document.activeElement)&&(b.current=!0),_(!1))};if(e(m.getBoundingClientRect().width),typeof ResizeObserver>`u`)return;let t=new ResizeObserver(([t])=>{t&&e(t.contentRect.width)});return t.observe(m),()=>{t.disconnect()}},[m]),(0,i.useEffect)(()=>{!g&&b.current&&(b.current=!1,(v.current?.querySelector(`[aria-current='page']`)??v.current?.querySelector(`a[href]`)??y.current)?.focus())},[g]);let C=c?.(`drawer`);return(0,a.jsxs)(`div`,{...f,ref:S,className:[`ds-appshell`,u].filter(Boolean).join(` `),children:[(0,a.jsxs)(`div`,{className:`ds-appshell__frame`,"aria-hidden":g||void 0,ref:e=>{e&&(e.inert=g)},children:[(0,a.jsx)(r.TopNav,{brand:o,actions:(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(`div`,{className:`ds-appshell__actions`,children:c?.(`topnav`)}),(0,a.jsx)(e.Button,{variant:`simple`,size:`lg`,isIconOnly:!0,iconStart:`menu`,className:`ds-appshell__toggle`,"aria-label":`Open navigation`,"aria-expanded":g,"aria-controls":x,onClick:()=>_(!0)})]})}),(0,a.jsxs)(`div`,{className:`ds-appshell__body`,children:[(0,a.jsx)(n.SideNav,{ref:v,className:`ds-appshell__desktop-nav`,label:l,children:s}),(0,a.jsx)(`main`,{ref:y,className:`ds-appshell__main`,tabIndex:-1,children:d})]})]}),(0,a.jsx)(t.Drawer,{id:x,isOpen:g,onClose:()=>_(!1),variant:`navigation`,portalContainer:m,title:o,"aria-label":`Navigation`,closeLabel:`Close navigation`,footer:C?(0,a.jsx)(n.SideNavFooter,{children:C}):void 0,children:(0,a.jsx)(n.SideNav,{className:`ds-appshell__mobile-nav`,label:l,onClick:e=>{let t=e.target instanceof Element?e.target.closest(`a[href]`):null,n=e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||t?.target===`_blank`;t&&!n&&_(!1)},children:s})})]})});exports.AppShell=o;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../Button/index.cjs"),t=require("../Drawer/index.cjs"),n=require("../SideNav/index.cjs"),r=require("../TopNav/index.cjs");let i=require("react"),a=require("react/jsx-runtime");var o=(0,i.forwardRef)(function({brand:o,navigation:s,actions:c,navigationLabel:l=`Main`,className:u,children:d,...f},p){let[m,h]=(0,i.useState)(null),[g,_]=(0,i.useState)(!1),v=(0,i.useRef)(null),y=(0,i.useRef)(null),b=(0,i.useRef)(!1),x=(0,i.useId)(),S=(0,i.useCallback)(e=>{h(e),typeof p==`function`?p(e):p&&(p.current=e)},[p]);(0,i.useEffect)(()=>{if(!m)return;let e=e=>{e<768||(m.querySelector(`[data-state='open'] [role='dialog']`)?.contains(document.activeElement)&&(b.current=!0),_(!1))};if(e(m.getBoundingClientRect().width),typeof ResizeObserver>`u`)return;let t=new ResizeObserver(([t])=>{t&&e(t.contentRect.width)});return t.observe(m),()=>{t.disconnect()}},[m]),(0,i.useEffect)(()=>{!g&&b.current&&(b.current=!1,(v.current?.querySelector(`[aria-current='page']`)??v.current?.querySelector(`a[href]`)??y.current)?.focus())},[g]);let C=c?.(`drawer`);return(0,a.jsxs)(`div`,{...f,ref:S,className:[`ds-appshell`,u].filter(Boolean).join(` `),children:[(0,a.jsxs)(`div`,{className:`ds-appshell__frame`,"aria-hidden":g||void 0,ref:e=>{e&&(e.inert=g)},children:[(0,a.jsx)(r.TopNav,{brand:o,actions:(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(`div`,{className:`ds-appshell__actions`,children:c?.(`topnav`)}),(0,a.jsx)(e.Button,{variant:`simple`,size:`lg`,isIconOnly:!0,iconStart:`menu`,className:`ds-appshell__toggle`,"aria-label":`Open navigation`,"aria-expanded":g,"aria-controls":x,onClick:()=>_(!0)})]})}),(0,a.jsxs)(`div`,{className:`ds-appshell__body`,children:[(0,a.jsx)(n.SideNav,{ref:v,className:`ds-appshell__desktop-nav`,label:l,children:s}),(0,a.jsx)(`main`,{ref:y,className:`ds-appshell__main`,tabIndex:-1,children:d})]})]}),(0,a.jsx)(t.Drawer,{id:x,isOpen:g,onClose:()=>_(!1),variant:`navigation`,side:`end`,portalContainer:m,title:o,"aria-label":`Navigation`,closeLabel:`Close navigation`,footer:C?(0,a.jsx)(n.SideNavFooter,{children:C}):void 0,children:(0,a.jsx)(n.SideNav,{className:`ds-appshell__mobile-nav`,label:l,onClick:e=>{let t=e.target instanceof Element?e.target.closest(`a[href]`):null,n=e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||t?.target===`_blank`;t&&!n&&_(!1)},children:s})})]})});exports.AppShell=o;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["forwardRef","useState","useRef","useId","useCallback","TopNav","_Fragment","Button","SideNav","Drawer","SideNavFooter","require_components_Button_index","require_components_Drawer_index","require_components_SideNav_index","require_components_TopNav_index","react","react_jsx_runtime","AppShell","rest","shell","setShell","isOpen","setOpen","desktopNavRef","mainRef","restoreDesktopFocus","drawerId","shellRef","node","ref","measure","width","observer","entry","drawerActions","actions","className","navigationLabel","navigation","brand","link","event","opensElsewhere"],"sources":["../../../src/components/AppShell/index.tsx"],"sourcesContent":["import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\nimport { Drawer } from \"../Drawer\";\nimport { Button } from \"../Button\";\nimport { SideNav, SideNavFooter } from \"../SideNav\";\nimport { TopNav } from \"../TopNav\";\nimport \"./style.css\";\n\nexport interface AppShellProps extends HTMLAttributes<HTMLDivElement> {\n /** Product brand, shared by the top bar and mobile drawer header. */\n brand: ReactNode;\n /** SideNavSection/SideNavItem content. The application owns routing and selection. */\n navigation: ReactNode;\n /** One slot with a compact top-bar and expanded drawer presentation. */\n actions?: (location: \"topnav\" | \"drawer\") => ReactNode;\n navigationLabel?: string;\n /** Rendered inside the shell's scrolling main landmark. */\n children?: ReactNode;\n}\n\n/** Full-width product bar above a desktop rail, or a mobile navigation drawer. */\nexport const AppShell = forwardRef<HTMLDivElement, AppShellProps>(function AppShell(\n { brand, navigation, actions, navigationLabel = \"Main\", className, children, ...rest },\n ref,\n) {\n const [shell, setShell] = useState<HTMLDivElement | null>(null);\n const [isOpen, setOpen] = useState(false);\n const desktopNavRef = useRef<HTMLElement | null>(null);\n const mainRef = useRef<HTMLElement | null>(null);\n const restoreDesktopFocus = useRef(false);\n const drawerId = useId();\n const shellRef = useCallback((node: HTMLDivElement | null) => {\n setShell(node);\n if (typeof ref === \"function\") ref(node);\n else if (ref) ref.current = node;\n }, [ref]);\n\n useEffect(() => {\n if (!shell) return;\n const measure = (width: number) => {\n if (width < 768) return;\n const focusInDrawer = shell.querySelector(\"[data-state='open'] [role='dialog']\")?.contains(document.activeElement);\n if (focusInDrawer) restoreDesktopFocus.current = true;\n setOpen(false);\n };\n measure(shell.getBoundingClientRect().width);\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(([entry]) => {\n if (entry) measure(entry.contentRect.width);\n });\n observer.observe(shell);\n return () => {\n observer.disconnect();\n };\n }, [shell]);\n\n useEffect(() => {\n if (isOpen || !restoreDesktopFocus.current) return;\n restoreDesktopFocus.current = false;\n const destination = desktopNavRef.current?.querySelector<HTMLElement>(\"[aria-current='page']\")\n ?? desktopNavRef.current?.querySelector<HTMLElement>(\"a[href]\");\n (destination ?? mainRef.current)?.focus();\n }, [isOpen]);\n\n const drawerActions = actions?.(\"drawer\");\n\n return (\n <div {...rest} ref={shellRef} className={[\"ds-appshell\", className].filter(Boolean).join(\" \")}>\n <div\n className=\"ds-appshell__frame\"\n aria-hidden={isOpen || undefined}\n ref={(node) => { if (node) node.inert = isOpen; }}\n >\n <TopNav\n brand={brand}\n actions={<>\n <div className=\"ds-appshell__actions\">{actions?.(\"topnav\")}</div>\n <Button\n variant=\"simple\"\n size=\"lg\"\n isIconOnly\n iconStart=\"menu\"\n className=\"ds-appshell__toggle\"\n aria-label=\"Open navigation\"\n aria-expanded={isOpen}\n aria-controls={drawerId}\n onClick={() => setOpen(true)}\n />\n </>}\n />\n <div className=\"ds-appshell__body\">\n <SideNav ref={desktopNavRef} className=\"ds-appshell__desktop-nav\" label={navigationLabel}>\n {navigation}\n </SideNav>\n <main ref={mainRef} className=\"ds-appshell__main\" tabIndex={-1}>{children}</main>\n </div>\n </div>\n <Drawer\n id={drawerId}\n isOpen={isOpen}\n onClose={() => setOpen(false)}\n variant=\"navigation\"\n portalContainer={shell}\n title={brand}\n aria-label=\"Navigation\"\n closeLabel=\"Close navigation\"\n footer={drawerActions ? <SideNavFooter>{drawerActions}</SideNavFooter> : undefined}\n >\n <SideNav\n className=\"ds-appshell__mobile-nav\"\n label={navigationLabel}\n onClick={(event) => {\n const link = event.target instanceof Element ? event.target.closest<HTMLAnchorElement>(\"a[href]\") : null;\n // Opening a link elsewhere (modifier keys, another button, a new tab) leaves this page in place.\n const opensElsewhere = event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey\n || event.altKey || link?.target === \"_blank\";\n if (link && !opensElsewhere) setOpen(false);\n }}\n >\n {navigation}\n </SideNav>\n </Drawer>\n </div>\n );\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["forwardRef","useState","useRef","useId","useCallback","TopNav","_Fragment","Button","SideNav","Drawer","SideNavFooter","require_components_Button_index","require_components_Drawer_index","require_components_SideNav_index","require_components_TopNav_index","react","react_jsx_runtime","AppShell","rest","shell","setShell","isOpen","setOpen","desktopNavRef","mainRef","restoreDesktopFocus","drawerId","shellRef","node","ref","measure","width","observer","entry","drawerActions","actions","className","navigationLabel","navigation","brand","link","event","opensElsewhere"],"sources":["../../../src/components/AppShell/index.tsx"],"sourcesContent":["import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\nimport { Drawer } from \"../Drawer\";\nimport { Button } from \"../Button\";\nimport { SideNav, SideNavFooter } from \"../SideNav\";\nimport { TopNav } from \"../TopNav\";\nimport \"./style.css\";\n\nexport interface AppShellProps extends HTMLAttributes<HTMLDivElement> {\n /** Product brand, shared by the top bar and mobile drawer header. */\n brand: ReactNode;\n /** SideNavSection/SideNavItem content. The application owns routing and selection. */\n navigation: ReactNode;\n /** One slot with a compact top-bar and expanded drawer presentation. */\n actions?: (location: \"topnav\" | \"drawer\") => ReactNode;\n navigationLabel?: string;\n /** Rendered inside the shell's scrolling main landmark. */\n children?: ReactNode;\n}\n\n/** Full-width product bar above a desktop rail, or a mobile navigation drawer. */\nexport const AppShell = forwardRef<HTMLDivElement, AppShellProps>(function AppShell(\n { brand, navigation, actions, navigationLabel = \"Main\", className, children, ...rest },\n ref,\n) {\n const [shell, setShell] = useState<HTMLDivElement | null>(null);\n const [isOpen, setOpen] = useState(false);\n const desktopNavRef = useRef<HTMLElement | null>(null);\n const mainRef = useRef<HTMLElement | null>(null);\n const restoreDesktopFocus = useRef(false);\n const drawerId = useId();\n const shellRef = useCallback((node: HTMLDivElement | null) => {\n setShell(node);\n if (typeof ref === \"function\") ref(node);\n else if (ref) ref.current = node;\n }, [ref]);\n\n useEffect(() => {\n if (!shell) return;\n const measure = (width: number) => {\n if (width < 768) return;\n const focusInDrawer = shell.querySelector(\"[data-state='open'] [role='dialog']\")?.contains(document.activeElement);\n if (focusInDrawer) restoreDesktopFocus.current = true;\n setOpen(false);\n };\n measure(shell.getBoundingClientRect().width);\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(([entry]) => {\n if (entry) measure(entry.contentRect.width);\n });\n observer.observe(shell);\n return () => {\n observer.disconnect();\n };\n }, [shell]);\n\n useEffect(() => {\n if (isOpen || !restoreDesktopFocus.current) return;\n restoreDesktopFocus.current = false;\n const destination = desktopNavRef.current?.querySelector<HTMLElement>(\"[aria-current='page']\")\n ?? desktopNavRef.current?.querySelector<HTMLElement>(\"a[href]\");\n (destination ?? mainRef.current)?.focus();\n }, [isOpen]);\n\n const drawerActions = actions?.(\"drawer\");\n\n return (\n <div {...rest} ref={shellRef} className={[\"ds-appshell\", className].filter(Boolean).join(\" \")}>\n <div\n className=\"ds-appshell__frame\"\n aria-hidden={isOpen || undefined}\n ref={(node) => { if (node) node.inert = isOpen; }}\n >\n <TopNav\n brand={brand}\n actions={<>\n <div className=\"ds-appshell__actions\">{actions?.(\"topnav\")}</div>\n <Button\n variant=\"simple\"\n size=\"lg\"\n isIconOnly\n iconStart=\"menu\"\n className=\"ds-appshell__toggle\"\n aria-label=\"Open navigation\"\n aria-expanded={isOpen}\n aria-controls={drawerId}\n onClick={() => setOpen(true)}\n />\n </>}\n />\n <div className=\"ds-appshell__body\">\n <SideNav ref={desktopNavRef} className=\"ds-appshell__desktop-nav\" label={navigationLabel}>\n {navigation}\n </SideNav>\n <main ref={mainRef} className=\"ds-appshell__main\" tabIndex={-1}>{children}</main>\n </div>\n </div>\n <Drawer\n id={drawerId}\n isOpen={isOpen}\n onClose={() => setOpen(false)}\n variant=\"navigation\"\n side=\"end\"\n portalContainer={shell}\n title={brand}\n aria-label=\"Navigation\"\n closeLabel=\"Close navigation\"\n footer={drawerActions ? <SideNavFooter>{drawerActions}</SideNavFooter> : undefined}\n >\n <SideNav\n className=\"ds-appshell__mobile-nav\"\n label={navigationLabel}\n onClick={(event) => {\n const link = event.target instanceof Element ? event.target.closest<HTMLAnchorElement>(\"a[href]\") : null;\n // Opening a link elsewhere (modifier keys, another button, a new tab) leaves this page in place.\n const opensElsewhere = event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey\n || event.altKey || link?.target === \"_blank\";\n if (link && !opensElsewhere) setOpen(false);\n }}\n >\n {navigation}\n </SideNav>\n </Drawer>\n </div>\n );\n});\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/components/AppShell/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,cAAc,EACnB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAKf,OAAO,aAAa,CAAC;AAErB,MAAM,WAAW,aAAc,SAAQ,cAAc,CAAC,cAAc,CAAC;IACnE,qEAAqE;IACrE,KAAK,EAAE,SAAS,CAAC;IACjB,sFAAsF;IACtF,UAAU,EAAE,SAAS,CAAC;IACtB,wEAAwE;IACxE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,KAAK,SAAS,CAAC;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,kFAAkF;AAClF,eAAO,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/components/AppShell/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,cAAc,EACnB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAKf,OAAO,aAAa,CAAC;AAErB,MAAM,WAAW,aAAc,SAAQ,cAAc,CAAC,cAAc,CAAC;IACnE,qEAAqE;IACrE,KAAK,EAAE,SAAS,CAAC;IACjB,sFAAsF;IACtF,UAAU,EAAE,SAAS,CAAC;IACtB,wEAAwE;IACxE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,KAAK,SAAS,CAAC;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,kFAAkF;AAClF,eAAO,MAAM,QAAQ,0GAwGnB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/AppShell/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,cAAc,EACnB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAKf,OAAO,aAAa,CAAC;AAErB,MAAM,WAAW,aAAc,SAAQ,cAAc,CAAC,cAAc,CAAC;IACnE,qEAAqE;IACrE,KAAK,EAAE,SAAS,CAAC;IACjB,sFAAsF;IACtF,UAAU,EAAE,SAAS,CAAC;IACtB,wEAAwE;IACxE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,KAAK,SAAS,CAAC;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,kFAAkF;AAClF,eAAO,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/AppShell/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,cAAc,EACnB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAKf,OAAO,aAAa,CAAC;AAErB,MAAM,WAAW,aAAc,SAAQ,cAAc,CAAC,cAAc,CAAC;IACnE,qEAAqE;IACrE,KAAK,EAAE,SAAS,CAAC;IACjB,sFAAsF;IACtF,UAAU,EAAE,SAAS,CAAC;IACtB,wEAAwE;IACxE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,KAAK,SAAS,CAAC;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,kFAAkF;AAClF,eAAO,MAAM,QAAQ,0GAwGnB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["_Fragment","Fragment$1","AppShell","forwardRef","rest","shell","setShell","useState","isOpen","setOpen","desktopNavRef","useRef","mainRef","restoreDesktopFocus","drawerId","useId","shellRef","useCallback","node","ref","useEffect","measure","width","observer","entry","drawerActions","actions","className","TopNav","Button","SideNav","navigationLabel","navigation","Drawer","brand","SideNavFooter","link","event","opensElsewhere"],"sources":["../../../src/components/AppShell/index.tsx"],"sourcesContent":["import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\nimport { Drawer } from \"../Drawer\";\nimport { Button } from \"../Button\";\nimport { SideNav, SideNavFooter } from \"../SideNav\";\nimport { TopNav } from \"../TopNav\";\nimport \"./style.css\";\n\nexport interface AppShellProps extends HTMLAttributes<HTMLDivElement> {\n /** Product brand, shared by the top bar and mobile drawer header. */\n brand: ReactNode;\n /** SideNavSection/SideNavItem content. The application owns routing and selection. */\n navigation: ReactNode;\n /** One slot with a compact top-bar and expanded drawer presentation. */\n actions?: (location: \"topnav\" | \"drawer\") => ReactNode;\n navigationLabel?: string;\n /** Rendered inside the shell's scrolling main landmark. */\n children?: ReactNode;\n}\n\n/** Full-width product bar above a desktop rail, or a mobile navigation drawer. */\nexport const AppShell = forwardRef<HTMLDivElement, AppShellProps>(function AppShell(\n { brand, navigation, actions, navigationLabel = \"Main\", className, children, ...rest },\n ref,\n) {\n const [shell, setShell] = useState<HTMLDivElement | null>(null);\n const [isOpen, setOpen] = useState(false);\n const desktopNavRef = useRef<HTMLElement | null>(null);\n const mainRef = useRef<HTMLElement | null>(null);\n const restoreDesktopFocus = useRef(false);\n const drawerId = useId();\n const shellRef = useCallback((node: HTMLDivElement | null) => {\n setShell(node);\n if (typeof ref === \"function\") ref(node);\n else if (ref) ref.current = node;\n }, [ref]);\n\n useEffect(() => {\n if (!shell) return;\n const measure = (width: number) => {\n if (width < 768) return;\n const focusInDrawer = shell.querySelector(\"[data-state='open'] [role='dialog']\")?.contains(document.activeElement);\n if (focusInDrawer) restoreDesktopFocus.current = true;\n setOpen(false);\n };\n measure(shell.getBoundingClientRect().width);\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(([entry]) => {\n if (entry) measure(entry.contentRect.width);\n });\n observer.observe(shell);\n return () => {\n observer.disconnect();\n };\n }, [shell]);\n\n useEffect(() => {\n if (isOpen || !restoreDesktopFocus.current) return;\n restoreDesktopFocus.current = false;\n const destination = desktopNavRef.current?.querySelector<HTMLElement>(\"[aria-current='page']\")\n ?? desktopNavRef.current?.querySelector<HTMLElement>(\"a[href]\");\n (destination ?? mainRef.current)?.focus();\n }, [isOpen]);\n\n const drawerActions = actions?.(\"drawer\");\n\n return (\n <div {...rest} ref={shellRef} className={[\"ds-appshell\", className].filter(Boolean).join(\" \")}>\n <div\n className=\"ds-appshell__frame\"\n aria-hidden={isOpen || undefined}\n ref={(node) => { if (node) node.inert = isOpen; }}\n >\n <TopNav\n brand={brand}\n actions={<>\n <div className=\"ds-appshell__actions\">{actions?.(\"topnav\")}</div>\n <Button\n variant=\"simple\"\n size=\"lg\"\n isIconOnly\n iconStart=\"menu\"\n className=\"ds-appshell__toggle\"\n aria-label=\"Open navigation\"\n aria-expanded={isOpen}\n aria-controls={drawerId}\n onClick={() => setOpen(true)}\n />\n </>}\n />\n <div className=\"ds-appshell__body\">\n <SideNav ref={desktopNavRef} className=\"ds-appshell__desktop-nav\" label={navigationLabel}>\n {navigation}\n </SideNav>\n <main ref={mainRef} className=\"ds-appshell__main\" tabIndex={-1}>{children}</main>\n </div>\n </div>\n <Drawer\n id={drawerId}\n isOpen={isOpen}\n onClose={() => setOpen(false)}\n variant=\"navigation\"\n portalContainer={shell}\n title={brand}\n aria-label=\"Navigation\"\n closeLabel=\"Close navigation\"\n footer={drawerActions ? <SideNavFooter>{drawerActions}</SideNavFooter> : undefined}\n >\n <SideNav\n className=\"ds-appshell__mobile-nav\"\n label={navigationLabel}\n onClick={(event) => {\n const link = event.target instanceof Element ? event.target.closest<HTMLAnchorElement>(\"a[href]\") : null;\n // Opening a link elsewhere (modifier keys, another button, a new tab) leaves this page in place.\n const opensElsewhere = event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey\n || event.altKey || link?.target === \"_blank\";\n if (link && !opensElsewhere) setOpen(false);\n }}\n >\n {navigation}\n </SideNav>\n </Drawer>\n </div>\n );\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["_Fragment","Fragment$1","AppShell","forwardRef","rest","shell","setShell","useState","isOpen","setOpen","desktopNavRef","useRef","mainRef","restoreDesktopFocus","drawerId","useId","shellRef","useCallback","node","ref","useEffect","measure","width","observer","entry","drawerActions","actions","className","TopNav","Button","SideNav","navigationLabel","navigation","Drawer","brand","SideNavFooter","link","event","opensElsewhere"],"sources":["../../../src/components/AppShell/index.tsx"],"sourcesContent":["import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\nimport { Drawer } from \"../Drawer\";\nimport { Button } from \"../Button\";\nimport { SideNav, SideNavFooter } from \"../SideNav\";\nimport { TopNav } from \"../TopNav\";\nimport \"./style.css\";\n\nexport interface AppShellProps extends HTMLAttributes<HTMLDivElement> {\n /** Product brand, shared by the top bar and mobile drawer header. */\n brand: ReactNode;\n /** SideNavSection/SideNavItem content. The application owns routing and selection. */\n navigation: ReactNode;\n /** One slot with a compact top-bar and expanded drawer presentation. */\n actions?: (location: \"topnav\" | \"drawer\") => ReactNode;\n navigationLabel?: string;\n /** Rendered inside the shell's scrolling main landmark. */\n children?: ReactNode;\n}\n\n/** Full-width product bar above a desktop rail, or a mobile navigation drawer. */\nexport const AppShell = forwardRef<HTMLDivElement, AppShellProps>(function AppShell(\n { brand, navigation, actions, navigationLabel = \"Main\", className, children, ...rest },\n ref,\n) {\n const [shell, setShell] = useState<HTMLDivElement | null>(null);\n const [isOpen, setOpen] = useState(false);\n const desktopNavRef = useRef<HTMLElement | null>(null);\n const mainRef = useRef<HTMLElement | null>(null);\n const restoreDesktopFocus = useRef(false);\n const drawerId = useId();\n const shellRef = useCallback((node: HTMLDivElement | null) => {\n setShell(node);\n if (typeof ref === \"function\") ref(node);\n else if (ref) ref.current = node;\n }, [ref]);\n\n useEffect(() => {\n if (!shell) return;\n const measure = (width: number) => {\n if (width < 768) return;\n const focusInDrawer = shell.querySelector(\"[data-state='open'] [role='dialog']\")?.contains(document.activeElement);\n if (focusInDrawer) restoreDesktopFocus.current = true;\n setOpen(false);\n };\n measure(shell.getBoundingClientRect().width);\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(([entry]) => {\n if (entry) measure(entry.contentRect.width);\n });\n observer.observe(shell);\n return () => {\n observer.disconnect();\n };\n }, [shell]);\n\n useEffect(() => {\n if (isOpen || !restoreDesktopFocus.current) return;\n restoreDesktopFocus.current = false;\n const destination = desktopNavRef.current?.querySelector<HTMLElement>(\"[aria-current='page']\")\n ?? desktopNavRef.current?.querySelector<HTMLElement>(\"a[href]\");\n (destination ?? mainRef.current)?.focus();\n }, [isOpen]);\n\n const drawerActions = actions?.(\"drawer\");\n\n return (\n <div {...rest} ref={shellRef} className={[\"ds-appshell\", className].filter(Boolean).join(\" \")}>\n <div\n className=\"ds-appshell__frame\"\n aria-hidden={isOpen || undefined}\n ref={(node) => { if (node) node.inert = isOpen; }}\n >\n <TopNav\n brand={brand}\n actions={<>\n <div className=\"ds-appshell__actions\">{actions?.(\"topnav\")}</div>\n <Button\n variant=\"simple\"\n size=\"lg\"\n isIconOnly\n iconStart=\"menu\"\n className=\"ds-appshell__toggle\"\n aria-label=\"Open navigation\"\n aria-expanded={isOpen}\n aria-controls={drawerId}\n onClick={() => setOpen(true)}\n />\n </>}\n />\n <div className=\"ds-appshell__body\">\n <SideNav ref={desktopNavRef} className=\"ds-appshell__desktop-nav\" label={navigationLabel}>\n {navigation}\n </SideNav>\n <main ref={mainRef} className=\"ds-appshell__main\" tabIndex={-1}>{children}</main>\n </div>\n </div>\n <Drawer\n id={drawerId}\n isOpen={isOpen}\n onClose={() => setOpen(false)}\n variant=\"navigation\"\n side=\"end\"\n portalContainer={shell}\n title={brand}\n aria-label=\"Navigation\"\n closeLabel=\"Close navigation\"\n footer={drawerActions ? <SideNavFooter>{drawerActions}</SideNavFooter> : undefined}\n >\n <SideNav\n className=\"ds-appshell__mobile-nav\"\n label={navigationLabel}\n onClick={(event) => {\n const link = event.target instanceof Element ? event.target.closest<HTMLAnchorElement>(\"a[href]\") : null;\n // Opening a link elsewhere (modifier keys, another button, a new tab) leaves this page in place.\n const opensElsewhere = event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey\n || event.altKey || link?.target === \"_blank\";\n if (link && !opensElsewhere) setOpen(false);\n }}\n >\n {navigation}\n </SideNav>\n </Drawer>\n </div>\n );\n});\n"],"mappings":"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"}
|
|
@@ -1,38 +1,38 @@
|
|
|
1
1
|
@layer components {
|
|
2
2
|
.ds-badge {
|
|
3
|
-
/* Three inks per badge: the stroke/text tone, the soft fill it sits on,
|
|
4
|
-
* and the solid pair. A tone sets all four; appearance picks which two
|
|
5
|
-
* are used. Nothing else in the component knows a colour. */
|
|
6
3
|
--badge-ink: var(--color-text-muted);
|
|
7
4
|
--badge-soft: var(--color-surface-subtle);
|
|
8
5
|
--badge-solid: var(--color-text);
|
|
9
6
|
--badge-on-solid: var(--color-surface);
|
|
7
|
+
--badge-marker: currentColor;
|
|
10
8
|
|
|
11
9
|
display: inline-flex;
|
|
12
10
|
align-items: center;
|
|
13
|
-
gap: var(--
|
|
14
|
-
padding:
|
|
11
|
+
gap: var(--badge-gap);
|
|
12
|
+
padding: var(--badge-pad-block) var(--space-2);
|
|
15
13
|
border: var(--badge-border-width) solid transparent;
|
|
16
|
-
border-radius: var(--radius
|
|
17
|
-
font-
|
|
14
|
+
border-radius: var(--badge-radius);
|
|
15
|
+
font-family: var(--font-sans);
|
|
16
|
+
font-size: var(--badge-font-size);
|
|
18
17
|
font-weight: var(--font-weight-bold);
|
|
19
|
-
line-height: var(--line-height
|
|
18
|
+
line-height: var(--badge-line-height);
|
|
19
|
+
letter-spacing: var(--badge-tracking);
|
|
20
|
+
text-transform: uppercase;
|
|
20
21
|
white-space: nowrap;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
.
|
|
24
|
-
padding: 2px 10px;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/* A count reads as one glyph: never narrower than it is tall. */
|
|
24
|
+
/* Counts keep their numeric treatment instead of label tracking and case. */
|
|
28
25
|
.ds-badge[data-shape="count"] {
|
|
29
26
|
justify-content: center;
|
|
30
|
-
min-inline-size: var(--
|
|
31
|
-
padding-inline: var(--
|
|
27
|
+
min-inline-size: var(--badge-count-min);
|
|
28
|
+
padding-inline: var(--badge-count-pad-inline);
|
|
29
|
+
font-size: var(--font-size-xs);
|
|
30
|
+
letter-spacing: normal;
|
|
31
|
+
text-transform: none;
|
|
32
32
|
font-variant-numeric: tabular-nums;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
/*
|
|
35
|
+
/* Keep the same border width and height for every appearance. */
|
|
36
36
|
.ds-badge[data-appearance="outline"] {
|
|
37
37
|
background: transparent;
|
|
38
38
|
border-color: var(--badge-ink);
|
|
@@ -40,8 +40,9 @@
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
.ds-badge[data-appearance="soft"] {
|
|
43
|
+
--badge-marker: var(--badge-soft-marker, currentColor);
|
|
43
44
|
background: var(--badge-soft);
|
|
44
|
-
border-color:
|
|
45
|
+
border-color: transparent;
|
|
45
46
|
color: var(--badge-ink);
|
|
46
47
|
}
|
|
47
48
|
|
|
@@ -52,16 +53,18 @@
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
.ds-badge__dot {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
flex: none;
|
|
57
|
+
inline-size: var(--badge-marker-size);
|
|
58
|
+
block-size: var(--badge-marker-size);
|
|
59
|
+
border-radius: 1px;
|
|
60
|
+
background: var(--badge-marker);
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
/* ── Publication tones ───────────────────────────────────────────────── */
|
|
62
64
|
.ds-badge[data-tone="draft"] {
|
|
63
65
|
--badge-ink: var(--status-draft-text);
|
|
64
66
|
--badge-soft: var(--status-draft-soft);
|
|
67
|
+
--badge-soft-marker: var(--status-draft);
|
|
65
68
|
--badge-solid: var(--color-text-muted);
|
|
66
69
|
--badge-on-solid: var(--color-surface);
|
|
67
70
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../Button/index.cjs"),t=require("../../chunks/mergeRefs-Bc3qS0Tt.cjs");let n=require("react"),r=require("react-dom"),i=require("react/jsx-runtime");function a(e,t
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../Button/index.cjs"),t=require("../../chunks/mergeRefs-Bc3qS0Tt.cjs");let n=require("react"),r=require("react-dom"),i=require("react/jsx-runtime");function a(e,t){let[r,i]=(0,n.useState)(e),[a,o]=(0,n.useState)(!1);return r!==e&&(i(e),o(!e)),(0,n.useEffect)(()=>{if(!a||e)return;let n=getComputedStyle(t.current).transitionDuration.split(`,`),r=Math.max(0,...n.map(e=>parseFloat(e)*(e.trim().endsWith(`ms`)?1:1e3)||0)),i=window.setTimeout(()=>o(!1),r);return()=>window.clearTimeout(i)},[a,e,t]),[a,o]}var o=(0,n.forwardRef)(function({isOpen:o,onClose:s,title:c,description:l,side:u=`end`,size:d=`md`,variant:f=`default`,closeLabel:p=`Close`,portalContainer:m,footer:h,className:g,children:_,...v},y){let b=(0,n.useRef)(null),x=(0,n.useRef)(null),S=(0,n.useRef)(null),C=(0,n.useRef)(s),w=(0,n.useId)(),T=(0,n.useId)(),[E,D]=a(o,x);return(0,n.useEffect)(()=>{C.current=s},[s]),(0,n.useEffect)(()=>{if(!o)return;let e=document.activeElement,t={preventScroll:!0},n=`:is(button, [href], input, select, textarea, [tabindex]):not(:disabled):not([tabindex='-1'])`;((f===`navigation`?S.current:b.current?.querySelector(n))??b.current)?.focus(t);let r=e=>{if(e.key===`Escape`){if(e.defaultPrevented)return;e.preventDefault(),C.current();return}if(e.key!==`Tab`)return;let t=Array.from(b.current?.querySelectorAll(n)??[]);if(t.length===0)return;let r=t[0],i=t[t.length-1];e.shiftKey&&(document.activeElement===r||!b.current?.contains(document.activeElement))?(e.preventDefault(),i.focus()):!e.shiftKey&&(document.activeElement===i||!b.current?.contains(document.activeElement))&&(e.preventDefault(),r.focus())};window.addEventListener(`keydown`,r);let i=document.body.style.overflow;return document.body.style.overflow=`hidden`,()=>{window.removeEventListener(`keydown`,r),document.body.style.overflow=i,e?.focus(t)}},[o,f,m]),!o&&!E||typeof document>`u`?null:(0,r.createPortal)((0,i.jsx)(`div`,{ref:e=>{x.current=e,e&&(e.inert=!o)},className:`ds-drawer-scrim`,dir:v.dir,"data-side":u,"data-variant":f,"data-contained":m?``:void 0,"data-state":o?`open`:`closed`,"aria-hidden":!o||void 0,onTransitionEnd:e=>{e.target===e.currentTarget&&e.propertyName===`opacity`&&!o&&D(!1)},onMouseDown:e=>o&&e.target===e.currentTarget&&C.current(),children:(0,i.jsxs)(`div`,{...v,ref:t.t(b,y),role:`dialog`,"aria-modal":`true`,"aria-labelledby":v[`aria-label`]?void 0:v[`aria-labelledby`]??w,"aria-describedby":l?T:void 0,tabIndex:-1,"data-side":u,"data-size":d,"data-variant":f,className:[`ds-drawer`,g].filter(Boolean).join(` `),children:[(0,i.jsxs)(`header`,{className:`ds-drawer__head`,children:[(0,i.jsxs)(`div`,{className:`ds-drawer__titles`,children:[(0,i.jsx)(`h2`,{id:w,className:`ds-drawer__title`,children:c}),l&&(0,i.jsx)(`p`,{id:T,className:`ds-drawer__description`,children:l})]}),(0,i.jsx)(e.Button,{ref:S,variant:`simple`,size:f===`navigation`?`lg`:`sm`,isIconOnly:!0,iconStart:`x`,className:f===`navigation`?`ds-drawer__close`:void 0,"aria-label":p,onClick:()=>C.current()})]}),(0,i.jsx)(`div`,{className:`ds-drawer__body`,children:_}),h&&(0,i.jsx)(`footer`,{className:`ds-drawer__footer`,children:h})]})}),m??document.body)});exports.Drawer=o;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["useState","forwardRef","useRef","useId","createPortal","mergeRefs","Button","require_components_Button_index","require_mergeRefs","react","react_dom","react_jsx_runtime","useDrawerExit","previousOpen","setPreviousOpen","isOpen","exiting","setExiting","animated","durations","scrimRef","duration","value","timeout","Drawer","rest","panelRef","closeRef","onCloseRef","onClose","titleId","descriptionId","variant","previous","focusOptions","selector","onKeyDown","event","focusable","first","last","overflow","portalContainer","node","side","ref","description","size","className","title","closeLabel","footer"],"sources":["../../../src/components/Drawer/index.tsx"],"sourcesContent":["import { forwardRef, useEffect, useId, useRef, useState, type HTMLAttributes, type ReactNode, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { Button } from \"../Button\";\nimport { mergeRefs } from \"../../internal/mergeRefs\";\nimport \"./style.css\";\n\nexport type DrawerSide = \"end\" | \"start\" | \"bottom\";\nexport type DrawerSize = \"md\" | \"wide\";\n\nexport interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n isOpen: boolean;\n /** Escape, the scrim and the close button all call this. */\n onClose: () => void;\n title: ReactNode;\n description?: string;\n /** Which edge the panel is anchored to. */\n side?: DrawerSide;\n size?: DrawerSize;\n /** Navigation uses a compact, square panel and animates its exit. */\n variant?: \"default\" | \"navigation\";\n /** Accessible name of the dismiss button. */\n closeLabel?: string;\n /**\n * Defaults to document.body, covering the viewport. A navigation drawer\n * portaled into a positioned element, such as an app shell, stays inside it.\n */\n portalContainer?: HTMLElement | null;\n /** Actions, right-aligned. The confirming action goes last. */\n footer?: ReactNode;\n children?: ReactNode;\n}\n\nfunction useDrawerExit(isOpen: boolean, animated: boolean, scrimRef: RefObject<HTMLDivElement | null>) {\n const [previousOpen, setPreviousOpen] = useState(isOpen);\n const [exiting, setExiting] = useState(false);\n\n if (previousOpen !== isOpen) {\n setPreviousOpen(isOpen);\n setExiting(!isOpen && animated);\n }\n\n useEffect(() => {\n if (!exiting || isOpen) return;\n // Read the actual transition, including token overrides and reduced motion.\n const durations = getComputedStyle(scrimRef.current!).transitionDuration.split(\",\");\n const duration = Math.max(0, ...durations.map((value) =>\n parseFloat(value) * (value.trim().endsWith(\"ms\") ? 1 : 1000) || 0,\n ));\n const timeout = window.setTimeout(() => setExiting(false), duration);\n return () => window.clearTimeout(timeout);\n }, [exiting, isOpen, scrimRef]);\n\n return [exiting, setExiting] as const;\n}\n\n/**\n * A side panel for work that belongs beside the page rather than on top of it\n * — editing one record, a filter set, a revision history. Same frame, rules\n * and footer band as `Modal`; focus is trapped while open.\n */\nexport const Drawer = forwardRef<HTMLDivElement, DrawerProps>(function Drawer(\n {\n isOpen,\n onClose,\n title,\n description,\n side = \"end\",\n size = \"md\",\n variant = \"default\",\n closeLabel = \"Close\",\n portalContainer,\n footer,\n className,\n children,\n ...rest\n },\n ref,\n) {\n const panelRef = useRef<HTMLDivElement | null>(null);\n const scrimRef = useRef<HTMLDivElement | null>(null);\n const closeRef = useRef<HTMLButtonElement | null>(null);\n const onCloseRef = useRef(onClose);\n const titleId = useId();\n const descriptionId = useId();\n const [exiting, setExiting] = useDrawerExit(isOpen, variant === \"navigation\", scrimRef);\n\n useEffect(() => {\n onCloseRef.current = onClose;\n }, [onClose]);\n\n useEffect(() => {\n if (!isOpen) return;\n\n const previous = document.activeElement as HTMLElement | null;\n // Navigation starts off-screen; focusing it must not scroll the shell with it.\n const focusOptions = variant === \"navigation\" ? { preventScroll: true } : undefined;\n const selector = \":is(button, [href], input, select, textarea, [tabindex]):not(:disabled):not([tabindex='-1'])\";\n const initialFocus = variant === \"navigation\" ? closeRef.current : panelRef.current?.querySelector<HTMLElement>(selector);\n (initialFocus ?? panelRef.current)?.focus(focusOptions);\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n // A popup inside the panel (Menu, Select, DateField) claims its Escape.\n if (event.defaultPrevented) return;\n event.preventDefault();\n onCloseRef.current();\n return;\n }\n if (event.key !== \"Tab\") return;\n const focusable = Array.from(\n panelRef.current?.querySelectorAll<HTMLElement>(\n selector,\n ) ?? [],\n );\n if (focusable.length === 0) return;\n const first = focusable[0]!;\n const last = focusable[focusable.length - 1]!;\n if (event.shiftKey && (document.activeElement === first || !panelRef.current?.contains(document.activeElement))) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && (document.activeElement === last || !panelRef.current?.contains(document.activeElement))) {\n event.preventDefault();\n first.focus();\n }\n };\n\n // On window, so document-level popup handlers inside the panel run first.\n window.addEventListener(\"keydown\", onKeyDown);\n const overflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n return () => {\n window.removeEventListener(\"keydown\", onKeyDown);\n document.body.style.overflow = overflow;\n previous?.focus(focusOptions);\n };\n }, [isOpen, variant, portalContainer]);\n\n if ((!isOpen && !exiting) || typeof document === \"undefined\") return null;\n\n return createPortal(\n <div\n ref={(node) => {\n scrimRef.current = node;\n if (node) node.inert = !isOpen;\n }}\n className=\"ds-drawer-scrim\"\n data-side={side}\n data-variant={variant}\n data-contained={portalContainer ? \"\" : undefined}\n data-state={isOpen ? \"open\" : \"closed\"}\n aria-hidden={!isOpen || undefined}\n onTransitionEnd={(event) => {\n if (event.target === event.currentTarget && event.propertyName === \"opacity\" && !isOpen) setExiting(false);\n }}\n onMouseDown={(event) => isOpen && event.target === event.currentTarget && onCloseRef.current()}\n >\n <div\n {...rest}\n ref={mergeRefs(panelRef, ref)}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={rest[\"aria-label\"] ? undefined : (rest[\"aria-labelledby\"] ?? titleId)}\n aria-describedby={description ? descriptionId : undefined}\n tabIndex={-1}\n data-side={side}\n data-size={size}\n data-variant={variant}\n className={[\"ds-drawer\", className].filter(Boolean).join(\" \")}\n >\n <header className=\"ds-drawer__head\">\n <div className=\"ds-drawer__titles\">\n <h2 id={titleId} className=\"ds-drawer__title\">\n {title}\n </h2>\n {description && (\n <p id={descriptionId} className=\"ds-drawer__description\">\n {description}\n </p>\n )}\n </div>\n <Button\n ref={closeRef}\n variant=\"simple\"\n size={variant === \"navigation\" ? \"lg\" : \"sm\"}\n isIconOnly\n iconStart=\"x\"\n className={variant === \"navigation\" ? \"ds-drawer__close\" : undefined}\n aria-label={closeLabel}\n onClick={() => onCloseRef.current()}\n />\n </header>\n <div className=\"ds-drawer__body\">{children}</div>\n {footer && <footer className=\"ds-drawer__footer\">{footer}</footer>}\n </div>\n </div>,\n portalContainer ?? document.body,\n );\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["useState","forwardRef","useRef","useId","createPortal","mergeRefs","Button","require_components_Button_index","require_mergeRefs","react","react_dom","react_jsx_runtime","useDrawerExit","previousOpen","setPreviousOpen","isOpen","exiting","setExiting","durations","scrimRef","duration","value","timeout","Drawer","rest","panelRef","closeRef","onCloseRef","onClose","titleId","descriptionId","previous","focusOptions","selector","variant","onKeyDown","event","focusable","first","last","overflow","portalContainer","node","side","ref","description","size","className","title","closeLabel","footer"],"sources":["../../../src/components/Drawer/index.tsx"],"sourcesContent":["import { forwardRef, useEffect, useId, useRef, useState, type HTMLAttributes, type ReactNode, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { Button } from \"../Button\";\nimport { mergeRefs } from \"../../internal/mergeRefs\";\nimport \"./style.css\";\n\nexport type DrawerSide = \"end\" | \"start\" | \"bottom\";\nexport type DrawerSize = \"md\" | \"wide\";\n\nexport interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n isOpen: boolean;\n /** Escape, the scrim and the close button all call this. */\n onClose: () => void;\n title: ReactNode;\n description?: string;\n /** Which edge the panel is anchored to. */\n side?: DrawerSide;\n size?: DrawerSize;\n /** Navigation uses a compact panel, header and footer. Both variants slide in and out. */\n variant?: \"default\" | \"navigation\";\n /** Accessible name of the dismiss button. */\n closeLabel?: string;\n /**\n * Defaults to document.body, covering the viewport. A navigation drawer\n * portaled into a positioned element, such as an app shell, stays inside it.\n */\n portalContainer?: HTMLElement | null;\n /** Actions, right-aligned. The confirming action goes last. */\n footer?: ReactNode;\n children?: ReactNode;\n}\n\nfunction useDrawerExit(isOpen: boolean, scrimRef: RefObject<HTMLDivElement | null>) {\n const [previousOpen, setPreviousOpen] = useState(isOpen);\n const [exiting, setExiting] = useState(false);\n\n if (previousOpen !== isOpen) {\n setPreviousOpen(isOpen);\n setExiting(!isOpen);\n }\n\n useEffect(() => {\n if (!exiting || isOpen) return;\n // Read the actual transition, including token overrides and reduced motion.\n const durations = getComputedStyle(scrimRef.current!).transitionDuration.split(\",\");\n const duration = Math.max(0, ...durations.map((value) =>\n parseFloat(value) * (value.trim().endsWith(\"ms\") ? 1 : 1000) || 0,\n ));\n const timeout = window.setTimeout(() => setExiting(false), duration);\n return () => window.clearTimeout(timeout);\n }, [exiting, isOpen, scrimRef]);\n\n return [exiting, setExiting] as const;\n}\n\n/**\n * A side panel for work that belongs beside the page rather than on top of it\n * — editing one record, a filter set, a revision history. Same frame, rules\n * and footer band as `Modal`, with square corners and a full slide through\n * its anchored edge. Focus is trapped while open.\n */\nexport const Drawer = forwardRef<HTMLDivElement, DrawerProps>(function Drawer(\n {\n isOpen,\n onClose,\n title,\n description,\n side = \"end\",\n size = \"md\",\n variant = \"default\",\n closeLabel = \"Close\",\n portalContainer,\n footer,\n className,\n children,\n ...rest\n },\n ref,\n) {\n const panelRef = useRef<HTMLDivElement | null>(null);\n const scrimRef = useRef<HTMLDivElement | null>(null);\n const closeRef = useRef<HTMLButtonElement | null>(null);\n const onCloseRef = useRef(onClose);\n const titleId = useId();\n const descriptionId = useId();\n const [exiting, setExiting] = useDrawerExit(isOpen, scrimRef);\n\n useEffect(() => {\n onCloseRef.current = onClose;\n }, [onClose]);\n\n useEffect(() => {\n if (!isOpen) return;\n\n const previous = document.activeElement as HTMLElement | null;\n // Panels start off-screen; focusing them must not scroll the page with them.\n const focusOptions = { preventScroll: true };\n const selector = \":is(button, [href], input, select, textarea, [tabindex]):not(:disabled):not([tabindex='-1'])\";\n const initialFocus = variant === \"navigation\" ? closeRef.current : panelRef.current?.querySelector<HTMLElement>(selector);\n (initialFocus ?? panelRef.current)?.focus(focusOptions);\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n // A popup inside the panel (Menu, Select, DateField) claims its Escape.\n if (event.defaultPrevented) return;\n event.preventDefault();\n onCloseRef.current();\n return;\n }\n if (event.key !== \"Tab\") return;\n const focusable = Array.from(\n panelRef.current?.querySelectorAll<HTMLElement>(\n selector,\n ) ?? [],\n );\n if (focusable.length === 0) return;\n const first = focusable[0]!;\n const last = focusable[focusable.length - 1]!;\n if (event.shiftKey && (document.activeElement === first || !panelRef.current?.contains(document.activeElement))) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && (document.activeElement === last || !panelRef.current?.contains(document.activeElement))) {\n event.preventDefault();\n first.focus();\n }\n };\n\n // On window, so document-level popup handlers inside the panel run first.\n window.addEventListener(\"keydown\", onKeyDown);\n const overflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n return () => {\n window.removeEventListener(\"keydown\", onKeyDown);\n document.body.style.overflow = overflow;\n previous?.focus(focusOptions);\n };\n }, [isOpen, variant, portalContainer]);\n\n if ((!isOpen && !exiting) || typeof document === \"undefined\") return null;\n\n return createPortal(\n <div\n ref={(node) => {\n scrimRef.current = node;\n if (node) node.inert = !isOpen;\n }}\n className=\"ds-drawer-scrim\"\n dir={rest.dir}\n data-side={side}\n data-variant={variant}\n data-contained={portalContainer ? \"\" : undefined}\n data-state={isOpen ? \"open\" : \"closed\"}\n aria-hidden={!isOpen || undefined}\n onTransitionEnd={(event) => {\n if (event.target === event.currentTarget && event.propertyName === \"opacity\" && !isOpen) setExiting(false);\n }}\n onMouseDown={(event) => isOpen && event.target === event.currentTarget && onCloseRef.current()}\n >\n <div\n {...rest}\n ref={mergeRefs(panelRef, ref)}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={rest[\"aria-label\"] ? undefined : (rest[\"aria-labelledby\"] ?? titleId)}\n aria-describedby={description ? descriptionId : undefined}\n tabIndex={-1}\n data-side={side}\n data-size={size}\n data-variant={variant}\n className={[\"ds-drawer\", className].filter(Boolean).join(\" \")}\n >\n <header className=\"ds-drawer__head\">\n <div className=\"ds-drawer__titles\">\n <h2 id={titleId} className=\"ds-drawer__title\">\n {title}\n </h2>\n {description && (\n <p id={descriptionId} className=\"ds-drawer__description\">\n {description}\n </p>\n )}\n </div>\n <Button\n ref={closeRef}\n variant=\"simple\"\n size={variant === \"navigation\" ? \"lg\" : \"sm\"}\n isIconOnly\n iconStart=\"x\"\n className={variant === \"navigation\" ? \"ds-drawer__close\" : undefined}\n aria-label={closeLabel}\n onClick={() => onCloseRef.current()}\n />\n </header>\n <div className=\"ds-drawer__body\">{children}</div>\n {footer && <footer className=\"ds-drawer__footer\">{footer}</footer>}\n </div>\n </div>,\n portalContainer ?? document.body,\n );\n});\n"],"mappings":"A,wOAgCA,SAASY,EAAc,EAAiB,EAA4C,CAClF,GAAM,CAACC,EAAcC,IAAA,EAAmBd,EAAAA,SAAAA,CAASe,CAAM,EACjD,CAACC,EAASC,IAAA,EAAcjB,EAAAA,SAAAA,CAAS,EAAK,EAkB5C,OAhBIa,IAAiBE,IACnBD,EAAgBC,CAAM,EACtBE,EAAW,CAACF,CAAM,IAGpB,EAAAN,EAAA,UAAA,KAAgB,CACd,GAAI,CAACO,GAAWD,EAAQ,OAExB,IAAMG,EAAY,iBAAiBC,EAAS,OAAQ,CAAC,CAAC,mBAAmB,MAAM,GAAG,EAC5EC,EAAW,KAAK,IAAI,EAAG,GAAGF,EAAU,IAAK,GAC7C,WAAWG,CAAK,GAAKA,EAAM,KAAK,CAAC,CAAC,SAAS,IAAI,EAAI,EAAI,MAAS,CAClE,CAAC,EACKC,EAAU,OAAO,eAAiBL,EAAW,EAAK,EAAGG,CAAQ,EACnE,UAAa,OAAO,aAAaE,CAAO,CAC1C,EAAG,CAACN,EAASD,EAAQI,CAAQ,CAAC,EAEvB,CAACH,EAASC,CAAU,CAC7B,CAQA,IAAaM,GAAA,EAAStB,EAAAA,WAAAA,CAAwC,SAC5D,CACE,SACA,UACA,QACA,cACA,OAAO,MACP,OAAO,KACP,UAAU,UACV,aAAa,QACb,kBACA,SACA,YACA,WACA,GAAGuB,GAEL,EACA,CACA,IAAMC,GAAA,EAAWvB,EAAAA,OAAAA,CAA8B,IAAI,EAC7CiB,GAAA,EAAWjB,EAAAA,OAAAA,CAA8B,IAAI,EAC7CwB,GAAA,EAAWxB,EAAAA,OAAAA,CAAiC,IAAI,EAChDyB,GAAA,EAAazB,EAAAA,OAAAA,CAAO0B,CAAO,EAC3BC,GAAA,EAAU1B,EAAAA,MAAAA,CAAM,EAChB2B,GAAA,EAAgB3B,EAAAA,MAAAA,CAAM,EACtB,CAACa,EAASC,GAAcL,EAAcG,EAAQI,CAAQ,EAwD5D,OAtDA,EAAAV,EAAA,UAAA,KAAgB,CACd,EAAW,QAAUmB,CACvB,EAAG,CAACA,CAAO,CAAC,GAEZ,EAAAnB,EAAA,UAAA,KAAgB,CACd,GAAI,CAACM,EAAQ,OAEb,IAAMgB,EAAW,SAAS,cAEpBC,EAAe,CAAE,cAAe,EAAK,EACrCC,EAAW,iGACIC,IAAY,aAAeR,EAAS,QAAUD,EAAS,SAAS,cAA2BQ,CAAQ,IACvGR,EAAS,QAAA,EAAU,MAAMO,CAAY,EAEtD,IAAMG,EAAa,GAAyB,CAC1C,GAAIC,EAAM,MAAQ,SAAU,CAE1B,GAAIA,EAAM,iBAAkB,OAC5BA,EAAM,eAAe,EACrBT,EAAW,QAAQ,EACnB,MACF,CACA,GAAIS,EAAM,MAAQ,MAAO,OACzB,IAAMC,EAAY,MAAM,KACtBZ,EAAS,SAAS,iBAChBQ,CACF,GAAK,CAAC,CACR,EACA,GAAII,EAAU,SAAW,EAAG,OAC5B,IAAMC,EAAQD,EAAU,GAClBE,EAAOF,EAAUA,EAAU,OAAS,GACtCD,EAAM,WAAa,SAAS,gBAAkBE,GAAS,CAACb,EAAS,SAAS,SAAS,SAAS,aAAa,IAC3GW,EAAM,eAAe,EACrBG,EAAK,MAAM,GACF,CAACH,EAAM,WAAa,SAAS,gBAAkBG,GAAQ,CAACd,EAAS,SAAS,SAAS,SAAS,aAAa,KAClHW,EAAM,eAAe,EACrBE,EAAM,MAAM,EAEhB,EAGA,OAAO,iBAAiB,UAAWH,CAAS,EAC5C,IAAMK,EAAW,SAAS,KAAK,MAAM,SAGrC,MAFA,UAAS,KAAK,MAAM,SAAW,aAElB,CACX,OAAO,oBAAoB,UAAWL,CAAS,EAC/C,SAAS,KAAK,MAAM,SAAWK,EAC/BT,GAAU,MAAMC,CAAY,CAC9B,CACF,EAAG,CAACjB,EAAQmB,EAASO,CAAe,CAAC,EAEhC,CAAC1B,GAAU,CAACC,GAAY,OAAO,SAAa,IAAoB,MAErE,EAAOZ,EAAAA,aAAAA,EACL,EAAAO,EAAA,IAAA,CAAC,MAAD,CACE,IAAM,GAAS,CACb,EAAS,QAAU+B,EACfA,IAAM,EAAK,MAAQ,CAAC3B,EAC1B,EACA,UAAU,kBACV,IAAKS,EAAK,IACV,YAAWmB,EACX,eAAcT,EACd,iBAAgBO,EAAkB,GAAK,IAAA,GACvC,aAAY1B,EAAS,OAAS,SAC9B,cAAa,CAACA,GAAU,IAAA,GACxB,gBAAkB,GAAU,CACtBqB,EAAM,SAAWA,EAAM,eAAiBA,EAAM,eAAiB,WAAa,CAACrB,GAAQE,EAAW,EAAK,CAC3G,EACA,YAAc,GAAUF,GAAUqB,EAAM,SAAWA,EAAM,eAAiBT,EAAW,QAAQ,EAE7F,UAAA,EAAAhB,EAAA,KAAA,CAAC,MAAD,CACE,GAAIa,EACJ,IAAKnB,EAAAA,EAAUoB,EAAUmB,CAAG,EAC5B,KAAK,SACL,aAAW,OACX,kBAAiBpB,EAAK,cAAgB,IAAA,GAAaA,EAAK,oBAAsBK,EAC9E,mBAAkBgB,EAAcf,EAAgB,IAAA,GAChD,SAAU,GACV,YAAWa,EACX,YAAWG,EACX,eAAcZ,EACd,UAAW,CAAC,YAAaa,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,EAX9D,SAAA,EAaE,EAAApC,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAU,kBAAlB,SAAA,EACE,EAAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,oBAAf,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC,KAAD,CAAI,GAAIkB,EAAS,UAAU,mBACxB,SAAAmB,CACC,CAAA,EACHH,IACC,EAAAlC,EAAA,IAAA,CAAC,IAAD,CAAG,GAAImB,EAAe,UAAU,yBAC7B,SAAAe,CACA,CAAA,CAEF,CACL,CAAA,GAAA,EAAAlC,EAAA,IAAA,CAACL,EAAAA,OAAD,CACE,IAAKoB,EACL,QAAQ,SACR,KAAMQ,IAAY,aAAe,KAAO,KACxC,WAAA,GACA,UAAU,IACV,UAAWA,IAAY,aAAe,mBAAqB,IAAA,GAC3D,aAAYe,EACZ,YAAetB,EAAW,QAAQ,CACnC,CAAA,CACK,C,IACR,EAAAhB,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,kBAAmB,UAAc,CAAA,EAC/CuC,IAAU,EAAAvC,EAAA,IAAA,CAAC,SAAD,CAAQ,UAAU,oBAAqB,SAAAuC,CAAe,CAAA,CAC9D,C,EACF,CAAA,EACLT,GAAmB,SAAS,IAC9B,CACF,CAAC,E"}
|
|
@@ -10,7 +10,7 @@ export interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, "title
|
|
|
10
10
|
/** Which edge the panel is anchored to. */
|
|
11
11
|
side?: DrawerSide;
|
|
12
12
|
size?: DrawerSize;
|
|
13
|
-
/** Navigation uses a compact,
|
|
13
|
+
/** Navigation uses a compact panel, header and footer. Both variants slide in and out. */
|
|
14
14
|
variant?: "default" | "navigation";
|
|
15
15
|
/** Accessible name of the dismiss button. */
|
|
16
16
|
closeLabel?: string;
|
|
@@ -26,7 +26,8 @@ export interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, "title
|
|
|
26
26
|
/**
|
|
27
27
|
* A side panel for work that belongs beside the page rather than on top of it
|
|
28
28
|
* — editing one record, a filter set, a revision history. Same frame, rules
|
|
29
|
-
* and footer band as `Modal
|
|
29
|
+
* and footer band as `Modal`, with square corners and a full slide through
|
|
30
|
+
* its anchored edge. Focus is trapped while open.
|
|
30
31
|
*/
|
|
31
32
|
export declare const Drawer: import("react").ForwardRefExoticComponent<DrawerProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
32
33
|
//# sourceMappingURL=index.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/components/Drawer/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkD,KAAK,cAAc,EAAE,KAAK,SAAS,EAAkB,MAAM,OAAO,CAAC;AAI5H,OAAO,aAAa,CAAC;AAErB,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AACpD,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,MAAM,CAAC;AAEvC,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAChF,MAAM,EAAE,OAAO,CAAC;IAChB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,
|
|
1
|
+
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/components/Drawer/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkD,KAAK,cAAc,EAAE,KAAK,SAAS,EAAkB,MAAM,OAAO,CAAC;AAI5H,OAAO,aAAa,CAAC;AAErB,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AACpD,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,MAAM,CAAC;AAEvC,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAChF,MAAM,EAAE,OAAO,CAAC;IAChB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,0FAA0F;IAC1F,OAAO,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IACnC,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,eAAe,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACrC,+DAA+D;IAC/D,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAyBD;;;;;GAKG;AACH,eAAO,MAAM,MAAM,wGA2IjB,CAAC"}
|
|
@@ -10,7 +10,7 @@ export interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, "title
|
|
|
10
10
|
/** Which edge the panel is anchored to. */
|
|
11
11
|
side?: DrawerSide;
|
|
12
12
|
size?: DrawerSize;
|
|
13
|
-
/** Navigation uses a compact,
|
|
13
|
+
/** Navigation uses a compact panel, header and footer. Both variants slide in and out. */
|
|
14
14
|
variant?: "default" | "navigation";
|
|
15
15
|
/** Accessible name of the dismiss button. */
|
|
16
16
|
closeLabel?: string;
|
|
@@ -26,7 +26,8 @@ export interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, "title
|
|
|
26
26
|
/**
|
|
27
27
|
* A side panel for work that belongs beside the page rather than on top of it
|
|
28
28
|
* — editing one record, a filter set, a revision history. Same frame, rules
|
|
29
|
-
* and footer band as `Modal
|
|
29
|
+
* and footer band as `Modal`, with square corners and a full slide through
|
|
30
|
+
* its anchored edge. Focus is trapped while open.
|
|
30
31
|
*/
|
|
31
32
|
export declare const Drawer: import("react").ForwardRefExoticComponent<DrawerProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
32
33
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Drawer/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkD,KAAK,cAAc,EAAE,KAAK,SAAS,EAAkB,MAAM,OAAO,CAAC;AAI5H,OAAO,aAAa,CAAC;AAErB,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AACpD,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,MAAM,CAAC;AAEvC,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAChF,MAAM,EAAE,OAAO,CAAC;IAChB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Drawer/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkD,KAAK,cAAc,EAAE,KAAK,SAAS,EAAkB,MAAM,OAAO,CAAC;AAI5H,OAAO,aAAa,CAAC;AAErB,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AACpD,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,MAAM,CAAC;AAEvC,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAChF,MAAM,EAAE,OAAO,CAAC;IAChB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,0FAA0F;IAC1F,OAAO,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IACnC,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,eAAe,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACrC,+DAA+D;IAC/D,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAyBD;;;;;GAKG;AACH,eAAO,MAAM,MAAM,wGA2IjB,CAAC"}
|
|
@@ -4,25 +4,25 @@ import { forwardRef as n, useEffect as r, useId as i, useRef as a, useState as o
|
|
|
4
4
|
import { createPortal as s } from "react-dom";
|
|
5
5
|
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/Drawer/index.tsx
|
|
7
|
-
function u(e, t
|
|
8
|
-
let [
|
|
9
|
-
return
|
|
10
|
-
if (!
|
|
11
|
-
let
|
|
7
|
+
function u(e, t) {
|
|
8
|
+
let [n, i] = o(e), [a, s] = o(!1);
|
|
9
|
+
return n !== e && (i(e), s(!e)), r(() => {
|
|
10
|
+
if (!a || e) return;
|
|
11
|
+
let n = getComputedStyle(t.current).transitionDuration.split(","), r = Math.max(0, ...n.map((e) => parseFloat(e) * (e.trim().endsWith("ms") ? 1 : 1e3) || 0)), i = window.setTimeout(() => s(!1), r);
|
|
12
12
|
return () => window.clearTimeout(i);
|
|
13
13
|
}, [
|
|
14
|
-
|
|
14
|
+
a,
|
|
15
15
|
e,
|
|
16
|
-
|
|
17
|
-
]), [
|
|
16
|
+
t
|
|
17
|
+
]), [a, s];
|
|
18
18
|
}
|
|
19
19
|
var d = n(function({ isOpen: n, onClose: o, title: d, description: f, side: p = "end", size: m = "md", variant: h = "default", closeLabel: g = "Close", portalContainer: _, footer: v, className: y, children: b, ...x }, S) {
|
|
20
|
-
let C = a(null), w = a(null), T = a(null), E = a(o), D = i(), O = i(), [k, A] = u(n,
|
|
20
|
+
let C = a(null), w = a(null), T = a(null), E = a(o), D = i(), O = i(), [k, A] = u(n, w);
|
|
21
21
|
return r(() => {
|
|
22
22
|
E.current = o;
|
|
23
23
|
}, [o]), r(() => {
|
|
24
24
|
if (!n) return;
|
|
25
|
-
let e = document.activeElement, t =
|
|
25
|
+
let e = document.activeElement, t = { preventScroll: !0 }, r = ":is(button, [href], input, select, textarea, [tabindex]):not(:disabled):not([tabindex='-1'])";
|
|
26
26
|
((h === "navigation" ? T.current : C.current?.querySelector(r)) ?? C.current)?.focus(t);
|
|
27
27
|
let i = (e) => {
|
|
28
28
|
if (e.key === "Escape") {
|
|
@@ -50,6 +50,7 @@ var d = n(function({ isOpen: n, onClose: o, title: d, description: f, side: p =
|
|
|
50
50
|
w.current = e, e && (e.inert = !n);
|
|
51
51
|
},
|
|
52
52
|
className: "ds-drawer-scrim",
|
|
53
|
+
dir: x.dir,
|
|
53
54
|
"data-side": p,
|
|
54
55
|
"data-variant": h,
|
|
55
56
|
"data-contained": _ ? "" : void 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["useDrawerExit","previousOpen","setPreviousOpen","useState","isOpen","exiting","setExiting","animated","useEffect","durations","scrimRef","duration","value","timeout","Drawer","forwardRef","rest","panelRef","useRef","closeRef","onCloseRef","onClose","titleId","useId","descriptionId","variant","previous","focusOptions","selector","onKeyDown","event","focusable","first","last","overflow","portalContainer","createPortal","node","side","mergeRefs","ref","description","size","className","title","Button","closeLabel","footer"],"sources":["../../../src/components/Drawer/index.tsx"],"sourcesContent":["import { forwardRef, useEffect, useId, useRef, useState, type HTMLAttributes, type ReactNode, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { Button } from \"../Button\";\nimport { mergeRefs } from \"../../internal/mergeRefs\";\nimport \"./style.css\";\n\nexport type DrawerSide = \"end\" | \"start\" | \"bottom\";\nexport type DrawerSize = \"md\" | \"wide\";\n\nexport interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n isOpen: boolean;\n /** Escape, the scrim and the close button all call this. */\n onClose: () => void;\n title: ReactNode;\n description?: string;\n /** Which edge the panel is anchored to. */\n side?: DrawerSide;\n size?: DrawerSize;\n /** Navigation uses a compact, square panel and animates its exit. */\n variant?: \"default\" | \"navigation\";\n /** Accessible name of the dismiss button. */\n closeLabel?: string;\n /**\n * Defaults to document.body, covering the viewport. A navigation drawer\n * portaled into a positioned element, such as an app shell, stays inside it.\n */\n portalContainer?: HTMLElement | null;\n /** Actions, right-aligned. The confirming action goes last. */\n footer?: ReactNode;\n children?: ReactNode;\n}\n\nfunction useDrawerExit(isOpen: boolean, animated: boolean, scrimRef: RefObject<HTMLDivElement | null>) {\n const [previousOpen, setPreviousOpen] = useState(isOpen);\n const [exiting, setExiting] = useState(false);\n\n if (previousOpen !== isOpen) {\n setPreviousOpen(isOpen);\n setExiting(!isOpen && animated);\n }\n\n useEffect(() => {\n if (!exiting || isOpen) return;\n // Read the actual transition, including token overrides and reduced motion.\n const durations = getComputedStyle(scrimRef.current!).transitionDuration.split(\",\");\n const duration = Math.max(0, ...durations.map((value) =>\n parseFloat(value) * (value.trim().endsWith(\"ms\") ? 1 : 1000) || 0,\n ));\n const timeout = window.setTimeout(() => setExiting(false), duration);\n return () => window.clearTimeout(timeout);\n }, [exiting, isOpen, scrimRef]);\n\n return [exiting, setExiting] as const;\n}\n\n/**\n * A side panel for work that belongs beside the page rather than on top of it\n * — editing one record, a filter set, a revision history. Same frame, rules\n * and footer band as `Modal`; focus is trapped while open.\n */\nexport const Drawer = forwardRef<HTMLDivElement, DrawerProps>(function Drawer(\n {\n isOpen,\n onClose,\n title,\n description,\n side = \"end\",\n size = \"md\",\n variant = \"default\",\n closeLabel = \"Close\",\n portalContainer,\n footer,\n className,\n children,\n ...rest\n },\n ref,\n) {\n const panelRef = useRef<HTMLDivElement | null>(null);\n const scrimRef = useRef<HTMLDivElement | null>(null);\n const closeRef = useRef<HTMLButtonElement | null>(null);\n const onCloseRef = useRef(onClose);\n const titleId = useId();\n const descriptionId = useId();\n const [exiting, setExiting] = useDrawerExit(isOpen, variant === \"navigation\", scrimRef);\n\n useEffect(() => {\n onCloseRef.current = onClose;\n }, [onClose]);\n\n useEffect(() => {\n if (!isOpen) return;\n\n const previous = document.activeElement as HTMLElement | null;\n // Navigation starts off-screen; focusing it must not scroll the shell with it.\n const focusOptions = variant === \"navigation\" ? { preventScroll: true } : undefined;\n const selector = \":is(button, [href], input, select, textarea, [tabindex]):not(:disabled):not([tabindex='-1'])\";\n const initialFocus = variant === \"navigation\" ? closeRef.current : panelRef.current?.querySelector<HTMLElement>(selector);\n (initialFocus ?? panelRef.current)?.focus(focusOptions);\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n // A popup inside the panel (Menu, Select, DateField) claims its Escape.\n if (event.defaultPrevented) return;\n event.preventDefault();\n onCloseRef.current();\n return;\n }\n if (event.key !== \"Tab\") return;\n const focusable = Array.from(\n panelRef.current?.querySelectorAll<HTMLElement>(\n selector,\n ) ?? [],\n );\n if (focusable.length === 0) return;\n const first = focusable[0]!;\n const last = focusable[focusable.length - 1]!;\n if (event.shiftKey && (document.activeElement === first || !panelRef.current?.contains(document.activeElement))) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && (document.activeElement === last || !panelRef.current?.contains(document.activeElement))) {\n event.preventDefault();\n first.focus();\n }\n };\n\n // On window, so document-level popup handlers inside the panel run first.\n window.addEventListener(\"keydown\", onKeyDown);\n const overflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n return () => {\n window.removeEventListener(\"keydown\", onKeyDown);\n document.body.style.overflow = overflow;\n previous?.focus(focusOptions);\n };\n }, [isOpen, variant, portalContainer]);\n\n if ((!isOpen && !exiting) || typeof document === \"undefined\") return null;\n\n return createPortal(\n <div\n ref={(node) => {\n scrimRef.current = node;\n if (node) node.inert = !isOpen;\n }}\n className=\"ds-drawer-scrim\"\n data-side={side}\n data-variant={variant}\n data-contained={portalContainer ? \"\" : undefined}\n data-state={isOpen ? \"open\" : \"closed\"}\n aria-hidden={!isOpen || undefined}\n onTransitionEnd={(event) => {\n if (event.target === event.currentTarget && event.propertyName === \"opacity\" && !isOpen) setExiting(false);\n }}\n onMouseDown={(event) => isOpen && event.target === event.currentTarget && onCloseRef.current()}\n >\n <div\n {...rest}\n ref={mergeRefs(panelRef, ref)}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={rest[\"aria-label\"] ? undefined : (rest[\"aria-labelledby\"] ?? titleId)}\n aria-describedby={description ? descriptionId : undefined}\n tabIndex={-1}\n data-side={side}\n data-size={size}\n data-variant={variant}\n className={[\"ds-drawer\", className].filter(Boolean).join(\" \")}\n >\n <header className=\"ds-drawer__head\">\n <div className=\"ds-drawer__titles\">\n <h2 id={titleId} className=\"ds-drawer__title\">\n {title}\n </h2>\n {description && (\n <p id={descriptionId} className=\"ds-drawer__description\">\n {description}\n </p>\n )}\n </div>\n <Button\n ref={closeRef}\n variant=\"simple\"\n size={variant === \"navigation\" ? \"lg\" : \"sm\"}\n isIconOnly\n iconStart=\"x\"\n className={variant === \"navigation\" ? \"ds-drawer__close\" : undefined}\n aria-label={closeLabel}\n onClick={() => onCloseRef.current()}\n />\n </header>\n <div className=\"ds-drawer__body\">{children}</div>\n {footer && <footer className=\"ds-drawer__footer\">{footer}</footer>}\n </div>\n </div>,\n portalContainer ?? document.body,\n );\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["useDrawerExit","previousOpen","setPreviousOpen","useState","isOpen","exiting","setExiting","useEffect","durations","scrimRef","duration","value","timeout","Drawer","forwardRef","rest","panelRef","useRef","closeRef","onCloseRef","onClose","titleId","useId","descriptionId","previous","focusOptions","selector","variant","onKeyDown","event","focusable","first","last","overflow","portalContainer","createPortal","node","side","mergeRefs","ref","description","size","className","title","Button","closeLabel","footer"],"sources":["../../../src/components/Drawer/index.tsx"],"sourcesContent":["import { forwardRef, useEffect, useId, useRef, useState, type HTMLAttributes, type ReactNode, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { Button } from \"../Button\";\nimport { mergeRefs } from \"../../internal/mergeRefs\";\nimport \"./style.css\";\n\nexport type DrawerSide = \"end\" | \"start\" | \"bottom\";\nexport type DrawerSize = \"md\" | \"wide\";\n\nexport interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n isOpen: boolean;\n /** Escape, the scrim and the close button all call this. */\n onClose: () => void;\n title: ReactNode;\n description?: string;\n /** Which edge the panel is anchored to. */\n side?: DrawerSide;\n size?: DrawerSize;\n /** Navigation uses a compact panel, header and footer. Both variants slide in and out. */\n variant?: \"default\" | \"navigation\";\n /** Accessible name of the dismiss button. */\n closeLabel?: string;\n /**\n * Defaults to document.body, covering the viewport. A navigation drawer\n * portaled into a positioned element, such as an app shell, stays inside it.\n */\n portalContainer?: HTMLElement | null;\n /** Actions, right-aligned. The confirming action goes last. */\n footer?: ReactNode;\n children?: ReactNode;\n}\n\nfunction useDrawerExit(isOpen: boolean, scrimRef: RefObject<HTMLDivElement | null>) {\n const [previousOpen, setPreviousOpen] = useState(isOpen);\n const [exiting, setExiting] = useState(false);\n\n if (previousOpen !== isOpen) {\n setPreviousOpen(isOpen);\n setExiting(!isOpen);\n }\n\n useEffect(() => {\n if (!exiting || isOpen) return;\n // Read the actual transition, including token overrides and reduced motion.\n const durations = getComputedStyle(scrimRef.current!).transitionDuration.split(\",\");\n const duration = Math.max(0, ...durations.map((value) =>\n parseFloat(value) * (value.trim().endsWith(\"ms\") ? 1 : 1000) || 0,\n ));\n const timeout = window.setTimeout(() => setExiting(false), duration);\n return () => window.clearTimeout(timeout);\n }, [exiting, isOpen, scrimRef]);\n\n return [exiting, setExiting] as const;\n}\n\n/**\n * A side panel for work that belongs beside the page rather than on top of it\n * — editing one record, a filter set, a revision history. Same frame, rules\n * and footer band as `Modal`, with square corners and a full slide through\n * its anchored edge. Focus is trapped while open.\n */\nexport const Drawer = forwardRef<HTMLDivElement, DrawerProps>(function Drawer(\n {\n isOpen,\n onClose,\n title,\n description,\n side = \"end\",\n size = \"md\",\n variant = \"default\",\n closeLabel = \"Close\",\n portalContainer,\n footer,\n className,\n children,\n ...rest\n },\n ref,\n) {\n const panelRef = useRef<HTMLDivElement | null>(null);\n const scrimRef = useRef<HTMLDivElement | null>(null);\n const closeRef = useRef<HTMLButtonElement | null>(null);\n const onCloseRef = useRef(onClose);\n const titleId = useId();\n const descriptionId = useId();\n const [exiting, setExiting] = useDrawerExit(isOpen, scrimRef);\n\n useEffect(() => {\n onCloseRef.current = onClose;\n }, [onClose]);\n\n useEffect(() => {\n if (!isOpen) return;\n\n const previous = document.activeElement as HTMLElement | null;\n // Panels start off-screen; focusing them must not scroll the page with them.\n const focusOptions = { preventScroll: true };\n const selector = \":is(button, [href], input, select, textarea, [tabindex]):not(:disabled):not([tabindex='-1'])\";\n const initialFocus = variant === \"navigation\" ? closeRef.current : panelRef.current?.querySelector<HTMLElement>(selector);\n (initialFocus ?? panelRef.current)?.focus(focusOptions);\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n // A popup inside the panel (Menu, Select, DateField) claims its Escape.\n if (event.defaultPrevented) return;\n event.preventDefault();\n onCloseRef.current();\n return;\n }\n if (event.key !== \"Tab\") return;\n const focusable = Array.from(\n panelRef.current?.querySelectorAll<HTMLElement>(\n selector,\n ) ?? [],\n );\n if (focusable.length === 0) return;\n const first = focusable[0]!;\n const last = focusable[focusable.length - 1]!;\n if (event.shiftKey && (document.activeElement === first || !panelRef.current?.contains(document.activeElement))) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && (document.activeElement === last || !panelRef.current?.contains(document.activeElement))) {\n event.preventDefault();\n first.focus();\n }\n };\n\n // On window, so document-level popup handlers inside the panel run first.\n window.addEventListener(\"keydown\", onKeyDown);\n const overflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n return () => {\n window.removeEventListener(\"keydown\", onKeyDown);\n document.body.style.overflow = overflow;\n previous?.focus(focusOptions);\n };\n }, [isOpen, variant, portalContainer]);\n\n if ((!isOpen && !exiting) || typeof document === \"undefined\") return null;\n\n return createPortal(\n <div\n ref={(node) => {\n scrimRef.current = node;\n if (node) node.inert = !isOpen;\n }}\n className=\"ds-drawer-scrim\"\n dir={rest.dir}\n data-side={side}\n data-variant={variant}\n data-contained={portalContainer ? \"\" : undefined}\n data-state={isOpen ? \"open\" : \"closed\"}\n aria-hidden={!isOpen || undefined}\n onTransitionEnd={(event) => {\n if (event.target === event.currentTarget && event.propertyName === \"opacity\" && !isOpen) setExiting(false);\n }}\n onMouseDown={(event) => isOpen && event.target === event.currentTarget && onCloseRef.current()}\n >\n <div\n {...rest}\n ref={mergeRefs(panelRef, ref)}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={rest[\"aria-label\"] ? undefined : (rest[\"aria-labelledby\"] ?? titleId)}\n aria-describedby={description ? descriptionId : undefined}\n tabIndex={-1}\n data-side={side}\n data-size={size}\n data-variant={variant}\n className={[\"ds-drawer\", className].filter(Boolean).join(\" \")}\n >\n <header className=\"ds-drawer__head\">\n <div className=\"ds-drawer__titles\">\n <h2 id={titleId} className=\"ds-drawer__title\">\n {title}\n </h2>\n {description && (\n <p id={descriptionId} className=\"ds-drawer__description\">\n {description}\n </p>\n )}\n </div>\n <Button\n ref={closeRef}\n variant=\"simple\"\n size={variant === \"navigation\" ? \"lg\" : \"sm\"}\n isIconOnly\n iconStart=\"x\"\n className={variant === \"navigation\" ? \"ds-drawer__close\" : undefined}\n aria-label={closeLabel}\n onClick={() => onCloseRef.current()}\n />\n </header>\n <div className=\"ds-drawer__body\">{children}</div>\n {footer && <footer className=\"ds-drawer__footer\">{footer}</footer>}\n </div>\n </div>,\n portalContainer ?? document.body,\n );\n});\n"],"mappings":"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"}
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
inset: 0;
|
|
5
5
|
z-index: 60;
|
|
6
6
|
display: flex;
|
|
7
|
+
overflow: clip;
|
|
7
8
|
background: var(--scrim);
|
|
8
|
-
|
|
9
|
+
opacity: 1;
|
|
10
|
+
transition: opacity var(--duration-panel) var(--ease-panel);
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
.ds-drawer-scrim[data-side="end"] {
|
|
@@ -22,32 +24,35 @@
|
|
|
22
24
|
|
|
23
25
|
.ds-drawer {
|
|
24
26
|
--field-bg: var(--color-surface);
|
|
27
|
+
--drawer-hidden-transform: translateX(100%);
|
|
25
28
|
|
|
29
|
+
box-sizing: border-box;
|
|
26
30
|
display: flex;
|
|
27
31
|
flex-direction: column;
|
|
32
|
+
min-block-size: 0;
|
|
28
33
|
background: var(--color-surface);
|
|
29
34
|
border: var(--frame-width) solid var(--frame-color);
|
|
35
|
+
border-radius: 0;
|
|
30
36
|
box-shadow: var(--shadow-lg);
|
|
37
|
+
transform: none;
|
|
38
|
+
transition: transform var(--duration-panel) var(--ease-panel);
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
.ds-drawer[data-side="end"],
|
|
34
42
|
.ds-drawer[data-side="start"] {
|
|
35
43
|
block-size: 100dvh;
|
|
36
44
|
inline-size: 100%;
|
|
45
|
+
border-block: 0;
|
|
37
46
|
}
|
|
38
47
|
|
|
39
48
|
.ds-drawer[data-side="end"] {
|
|
40
|
-
border-start-start-radius: var(--overlay-radius);
|
|
41
|
-
border-end-start-radius: var(--overlay-radius);
|
|
42
49
|
border-inline-end: 0;
|
|
43
|
-
animation: ds-drawer-in-end var(--duration-panel) var(--ease-panel);
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
.ds-drawer[data-side="start"] {
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
--drawer-hidden-transform: translateX(-100%);
|
|
54
|
+
|
|
49
55
|
border-inline-start: 0;
|
|
50
|
-
animation: ds-drawer-in-start var(--duration-panel) var(--ease-panel);
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
.ds-drawer[data-side="end"][data-size="md"],
|
|
@@ -61,12 +66,34 @@
|
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
.ds-drawer[data-side="bottom"] {
|
|
69
|
+
--drawer-hidden-transform: translateY(100%);
|
|
70
|
+
|
|
64
71
|
inline-size: 100%;
|
|
65
72
|
max-block-size: calc(100dvh - var(--space-12));
|
|
66
|
-
border-start-start-radius: var(--overlay-radius);
|
|
67
|
-
border-start-end-radius: var(--overlay-radius);
|
|
68
73
|
border-block-end: 0;
|
|
69
|
-
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* A closed panel leaves through its own edge, mirrored for right-to-left. */
|
|
77
|
+
.ds-drawer[data-side="end"]:dir(rtl) {
|
|
78
|
+
--drawer-hidden-transform: translateX(-100%);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.ds-drawer[data-side="start"]:dir(rtl) {
|
|
82
|
+
--drawer-hidden-transform: translateX(100%);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.ds-drawer-scrim[data-state="closed"] {
|
|
86
|
+
opacity: 0;
|
|
87
|
+
pointer-events: none;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.ds-drawer-scrim[data-state="closed"] .ds-drawer {
|
|
91
|
+
transform: var(--drawer-hidden-transform);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@starting-style {
|
|
95
|
+
.ds-drawer-scrim[data-state="open"] { opacity: 0; }
|
|
96
|
+
.ds-drawer { transform: var(--drawer-hidden-transform); }
|
|
70
97
|
}
|
|
71
98
|
|
|
72
99
|
.ds-drawer__head {
|
|
@@ -117,13 +144,6 @@
|
|
|
117
144
|
border-block-start: var(--rule-width) solid var(--rule-color);
|
|
118
145
|
}
|
|
119
146
|
|
|
120
|
-
.ds-drawer-scrim[data-variant="navigation"] {
|
|
121
|
-
overflow: clip;
|
|
122
|
-
opacity: 1;
|
|
123
|
-
animation: none;
|
|
124
|
-
transition: opacity var(--duration-panel) var(--ease-panel);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
147
|
/* Portaled into a positioned container, navigation covers only that box. */
|
|
128
148
|
.ds-drawer-scrim[data-variant="navigation"][data-contained] {
|
|
129
149
|
position: absolute;
|
|
@@ -131,15 +151,7 @@
|
|
|
131
151
|
}
|
|
132
152
|
|
|
133
153
|
.ds-drawer[data-variant="navigation"] {
|
|
134
|
-
--drawer-hidden-transform: translateX(100%);
|
|
135
|
-
|
|
136
|
-
box-sizing: border-box;
|
|
137
|
-
min-block-size: 0;
|
|
138
|
-
border-radius: 0;
|
|
139
154
|
box-shadow: none;
|
|
140
|
-
transform: none;
|
|
141
|
-
animation: none;
|
|
142
|
-
transition: transform var(--duration-panel) var(--ease-panel);
|
|
143
155
|
}
|
|
144
156
|
|
|
145
157
|
.ds-drawer[data-variant="navigation"]:is([data-side="end"], [data-side="start"]) {
|
|
@@ -149,43 +161,14 @@
|
|
|
149
161
|
border: 0;
|
|
150
162
|
}
|
|
151
163
|
|
|
152
|
-
/* A closed panel leaves through its own edge, mirrored for right-to-left. */
|
|
153
164
|
.ds-drawer[data-variant="navigation"][data-side="end"] {
|
|
154
165
|
border-inline-start: var(--frame-width) solid var(--frame-color);
|
|
155
166
|
}
|
|
156
167
|
|
|
157
168
|
.ds-drawer[data-variant="navigation"][data-side="start"] {
|
|
158
|
-
--drawer-hidden-transform: translateX(-100%);
|
|
159
|
-
|
|
160
169
|
border-inline-end: var(--frame-width) solid var(--frame-color);
|
|
161
170
|
}
|
|
162
171
|
|
|
163
|
-
.ds-drawer[data-variant="navigation"][data-side="end"]:dir(rtl) {
|
|
164
|
-
--drawer-hidden-transform: translateX(-100%);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
.ds-drawer[data-variant="navigation"][data-side="start"]:dir(rtl) {
|
|
168
|
-
--drawer-hidden-transform: translateX(100%);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
.ds-drawer[data-variant="navigation"][data-side="bottom"] {
|
|
172
|
-
--drawer-hidden-transform: translateY(100%);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
.ds-drawer-scrim[data-variant="navigation"][data-state="closed"] {
|
|
176
|
-
opacity: 0;
|
|
177
|
-
pointer-events: none;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
.ds-drawer-scrim[data-variant="navigation"][data-state="closed"] .ds-drawer {
|
|
181
|
-
transform: var(--drawer-hidden-transform);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
@starting-style {
|
|
185
|
-
.ds-drawer-scrim[data-variant="navigation"][data-state="open"] { opacity: 0; }
|
|
186
|
-
.ds-drawer[data-variant="navigation"] { transform: var(--drawer-hidden-transform); }
|
|
187
|
-
}
|
|
188
|
-
|
|
189
172
|
.ds-drawer[data-variant="navigation"] .ds-drawer__head {
|
|
190
173
|
box-sizing: border-box;
|
|
191
174
|
flex: none;
|
|
@@ -219,34 +202,9 @@
|
|
|
219
202
|
background: var(--color-surface);
|
|
220
203
|
}
|
|
221
204
|
|
|
222
|
-
@keyframes ds-drawer-in-end {
|
|
223
|
-
from {
|
|
224
|
-
translate: var(--space-8) 0;
|
|
225
|
-
opacity: 0%;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
@keyframes ds-drawer-in-start {
|
|
230
|
-
from {
|
|
231
|
-
translate: calc(-1 * var(--space-8)) 0;
|
|
232
|
-
opacity: 0%;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
@keyframes ds-drawer-in-bottom {
|
|
237
|
-
from {
|
|
238
|
-
translate: 0 var(--space-8);
|
|
239
|
-
opacity: 0%;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
205
|
@media (prefers-reduced-motion: reduce) {
|
|
244
206
|
.ds-drawer-scrim,
|
|
245
207
|
.ds-drawer {
|
|
246
|
-
animation: none;
|
|
247
|
-
}
|
|
248
|
-
.ds-drawer-scrim[data-variant="navigation"],
|
|
249
|
-
.ds-drawer[data-variant="navigation"] {
|
|
250
208
|
transition-duration: 0s;
|
|
251
209
|
}
|
|
252
210
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});/* empty css */let e=require("react"),t=require("react/jsx-runtime");function n({label:n,hint:r,error:i,isRequired:a,isGroup:o=!1,children:s}){let c=(0,e.useId)(),l=`${c}-hint`,u=`${c}-error`,d=i?u:r?l:void 0,f=`${c}-label`,p=o?`fieldset`:`div`,m=(0,t.jsxs)(t.Fragment,{children:[n,a&&(0,t.jsx)(`span`,{className:`ds-field__required`,"aria-hidden":`true`,children:`*`})]});return(0,t.jsxs)(p,{className:`ds-field`,children:[o?(0,t.jsx)(`legend`,{id:f,className:`ds-field__label`,children:m}):(0,t.jsx)(`label`,{id:f,className:`ds-field__label`,htmlFor:c,children:m}),s({id:c,"aria-describedby":d,"aria-labelledby":f,"aria-invalid":i?!0:void 0,required:a}),r&&!i
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});/* empty css */let e=require("react"),t=require("react/jsx-runtime");function n({label:n,hint:r,error:i,isRequired:a,isGroup:o=!1,children:s}){let c=(0,e.useId)(),l=`${c}-hint`,u=`${c}-error`,d=i?u:r?l:void 0,f=`${c}-label`,p=o?`fieldset`:`div`,m=(0,t.jsxs)(t.Fragment,{children:[n,a&&(0,t.jsx)(`span`,{className:`ds-field__required`,"aria-hidden":`true`,children:`*`})]});return(0,t.jsxs)(p,{className:`ds-field`,children:[o?(0,t.jsx)(`legend`,{id:f,className:`ds-field__label`,children:m}):(0,t.jsx)(`label`,{id:f,className:`ds-field__label`,htmlFor:c,children:m}),s({id:c,"aria-describedby":d,"aria-labelledby":f,"aria-invalid":i?!0:void 0,required:a}),r&&!i?(0,t.jsx)(`span`,{className:`ds-field__hint`,id:l,children:r}):null,i&&(0,t.jsx)(`span`,{className:`ds-field__error`,id:u,children:i})]})}exports.Field=n;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["useId","_Fragment","react","react_jsx_runtime","Field","id","hintId","errorId","describedBy","error","hint","labelId","Wrapper","isGroup","labelContent","label","isRequired","children"],"sources":["../../../src/components/Field/index.tsx"],"sourcesContent":["import { useId, type ReactNode } from \"react\";\nimport \"../Field/style.css\";\n\nexport type FieldSize = \"sm\" | \"md\" | \"lg\";\n\nexport interface FieldProps {\n label: string;\n hint?:
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["useId","_Fragment","react","react_jsx_runtime","Field","id","hintId","errorId","describedBy","error","hint","labelId","Wrapper","isGroup","labelContent","label","isRequired","children"],"sources":["../../../src/components/Field/index.tsx"],"sourcesContent":["import { useId, type ReactNode } from \"react\";\nimport \"../Field/style.css\";\n\nexport type FieldSize = \"sm\" | \"md\" | \"lg\";\n\nexport interface FieldProps {\n label: string;\n /** Supporting text or inline content, hidden while an error is present. */\n hint?: ReactNode;\n /** Present means invalid: the message renders and the control is marked. */\n error?: string;\n isRequired?: boolean;\n /** Use a legend for a field containing a group of controls. */\n isGroup?: boolean;\n /** Receives the ids and invalid flag the control must carry. */\n children: (props: {\n id: string;\n \"aria-describedby\"?: string;\n \"aria-labelledby\": string;\n \"aria-invalid\"?: true;\n required?: boolean;\n }) => ReactNode;\n}\n\n/** Label, hint, error and required marker around any control wearing the field skin. */\nexport function Field({ label, hint, error, isRequired, isGroup = false, children }: FieldProps) {\n const id = useId();\n const hintId = `${id}-hint`;\n const errorId = `${id}-error`;\n const describedBy = error ? errorId : hint ? hintId : undefined;\n const labelId = `${id}-label`;\n const Wrapper = isGroup ? \"fieldset\" : \"div\";\n const labelContent = <>{label}{isRequired && <span className=\"ds-field__required\" aria-hidden=\"true\">*</span>}</>;\n\n return (\n <Wrapper className=\"ds-field\">\n {isGroup\n ? <legend id={labelId} className=\"ds-field__label\">{labelContent}</legend>\n : <label id={labelId} className=\"ds-field__label\" htmlFor={id}>{labelContent}</label>}\n {children({\n id,\n \"aria-describedby\": describedBy,\n \"aria-labelledby\": labelId,\n \"aria-invalid\": error ? true : undefined,\n required: isRequired,\n })}\n {hint && !error ? (\n <span className=\"ds-field__hint\" id={hintId}>\n {hint}\n </span>\n ) : null}\n {error && (\n <span className=\"ds-field__error\" id={errorId}>\n {error}\n </span>\n )}\n </Wrapper>\n );\n}\n"],"mappings":"A,oKAyBA,SAAgBI,EAAM,CAAE,QAAO,OAAM,QAAO,aAAY,UAAU,GAAO,YAAwB,CAC/F,IAAMC,GAAA,EAAKL,EAAAA,MAAAA,CAAM,EACXM,EAAS,GAAGD,EAAG,OACfE,EAAU,GAAGF,EAAG,QAChBG,EAAcC,EAAQF,EAAUG,EAAOJ,EAAS,IAAA,GAChDK,EAAU,GAAGN,EAAG,QAChBO,EAAUC,EAAU,WAAa,MACjCC,GAAe,EAAAX,EAAA,KAAA,CAAAF,EAAAA,SAAA,CAAA,SAAA,CAAGc,EAAOC,IAAc,EAAAb,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,qBAAqB,cAAY,OAAO,SAAA,GAAO,CAAA,CAAI,CAAA,CAAA,EAEhH,OACE,EAAAA,EAAA,KAAA,CAACS,EAAD,CAAS,UAAU,WAAnB,SAAA,CACGC,GACG,EAAAV,EAAA,IAAA,CAAC,SAAD,CAAQ,GAAIQ,EAAS,UAAU,kBAAmB,SAAAG,CAAqB,CAAA,GACvE,EAAAX,EAAA,IAAA,CAAC,QAAD,CAAO,GAAIQ,EAAS,UAAU,kBAAkB,QAASN,EAAK,SAAAS,CAAoB,CAAA,EACrFG,EAAS,CACR,KACA,mBAAoBT,EACpB,kBAAmBG,EACnB,eAAgBF,EAAQ,GAAO,IAAA,GAC/B,SAAUO,CACZ,CAAC,EACAN,GAAQ,CAACD,GACR,EAAAN,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,iBAAiB,GAAIG,EAClC,SAAAI,CACG,CAAA,EACJ,KACHD,IACC,EAAAN,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,kBAAkB,GAAII,EACnC,SAAAE,CACG,CAAA,CAED,C,EAEb,C"}
|
|
@@ -2,7 +2,8 @@ import { type ReactNode } from "react";
|
|
|
2
2
|
export type FieldSize = "sm" | "md" | "lg";
|
|
3
3
|
export interface FieldProps {
|
|
4
4
|
label: string;
|
|
5
|
-
|
|
5
|
+
/** Supporting text or inline content, hidden while an error is present. */
|
|
6
|
+
hint?: ReactNode;
|
|
6
7
|
/** Present means invalid: the message renders and the control is marked. */
|
|
7
8
|
error?: string;
|
|
8
9
|
isRequired?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/components/Field/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,oBAAoB,CAAC;AAE5B,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/components/Field/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,oBAAoB,CAAC;AAE5B,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gEAAgE;IAChE,QAAQ,EAAE,CAAC,KAAK,EAAE;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,cAAc,CAAC,EAAE,IAAI,CAAC;QACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,KAAK,SAAS,CAAC;CACjB;AAED,wFAAwF;AACxF,wBAAgB,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,OAAe,EAAE,QAAQ,EAAE,EAAE,UAAU,+BAiC9F"}
|
|
@@ -2,7 +2,8 @@ import { type ReactNode } from "react";
|
|
|
2
2
|
export type FieldSize = "sm" | "md" | "lg";
|
|
3
3
|
export interface FieldProps {
|
|
4
4
|
label: string;
|
|
5
|
-
|
|
5
|
+
/** Supporting text or inline content, hidden while an error is present. */
|
|
6
|
+
hint?: ReactNode;
|
|
6
7
|
/** Present means invalid: the message renders and the control is marked. */
|
|
7
8
|
error?: string;
|
|
8
9
|
isRequired?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Field/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,oBAAoB,CAAC;AAE5B,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Field/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,oBAAoB,CAAC;AAE5B,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gEAAgE;IAChE,QAAQ,EAAE,CAAC,KAAK,EAAE;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,cAAc,CAAC,EAAE,IAAI,CAAC;QACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,KAAK,SAAS,CAAC;CACjB;AAED,wFAAwF;AACxF,wBAAgB,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,OAAe,EAAE,QAAQ,EAAE,EAAE,UAAU,+BAiC9F"}
|
|
@@ -28,11 +28,11 @@ function i({ label: i, hint: a, error: o, isRequired: s, isGroup: c = !1, childr
|
|
|
28
28
|
"aria-invalid": o ? !0 : void 0,
|
|
29
29
|
required: s
|
|
30
30
|
}),
|
|
31
|
-
a && !o
|
|
31
|
+
a && !o ? /* @__PURE__ */ n("span", {
|
|
32
32
|
className: "ds-field__hint",
|
|
33
33
|
id: d,
|
|
34
34
|
children: a
|
|
35
|
-
}),
|
|
35
|
+
}) : null,
|
|
36
36
|
o && /* @__PURE__ */ n("span", {
|
|
37
37
|
className: "ds-field__error",
|
|
38
38
|
id: f,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["_Fragment","Fragment$1","Field","id","useId","hintId","errorId","describedBy","error","hint","labelId","Wrapper","isGroup","labelContent","label","isRequired","children"],"sources":["../../../src/components/Field/index.tsx"],"sourcesContent":["import { useId, type ReactNode } from \"react\";\nimport \"../Field/style.css\";\n\nexport type FieldSize = \"sm\" | \"md\" | \"lg\";\n\nexport interface FieldProps {\n label: string;\n hint?:
|
|
1
|
+
{"version":3,"file":"index.js","names":["_Fragment","Fragment$1","Field","id","useId","hintId","errorId","describedBy","error","hint","labelId","Wrapper","isGroup","labelContent","label","isRequired","children"],"sources":["../../../src/components/Field/index.tsx"],"sourcesContent":["import { useId, type ReactNode } from \"react\";\nimport \"../Field/style.css\";\n\nexport type FieldSize = \"sm\" | \"md\" | \"lg\";\n\nexport interface FieldProps {\n label: string;\n /** Supporting text or inline content, hidden while an error is present. */\n hint?: ReactNode;\n /** Present means invalid: the message renders and the control is marked. */\n error?: string;\n isRequired?: boolean;\n /** Use a legend for a field containing a group of controls. */\n isGroup?: boolean;\n /** Receives the ids and invalid flag the control must carry. */\n children: (props: {\n id: string;\n \"aria-describedby\"?: string;\n \"aria-labelledby\": string;\n \"aria-invalid\"?: true;\n required?: boolean;\n }) => ReactNode;\n}\n\n/** Label, hint, error and required marker around any control wearing the field skin. */\nexport function Field({ label, hint, error, isRequired, isGroup = false, children }: FieldProps) {\n const id = useId();\n const hintId = `${id}-hint`;\n const errorId = `${id}-error`;\n const describedBy = error ? errorId : hint ? hintId : undefined;\n const labelId = `${id}-label`;\n const Wrapper = isGroup ? \"fieldset\" : \"div\";\n const labelContent = <>{label}{isRequired && <span className=\"ds-field__required\" aria-hidden=\"true\">*</span>}</>;\n\n return (\n <Wrapper className=\"ds-field\">\n {isGroup\n ? <legend id={labelId} className=\"ds-field__label\">{labelContent}</legend>\n : <label id={labelId} className=\"ds-field__label\" htmlFor={id}>{labelContent}</label>}\n {children({\n id,\n \"aria-describedby\": describedBy,\n \"aria-labelledby\": labelId,\n \"aria-invalid\": error ? true : undefined,\n required: isRequired,\n })}\n {hint && !error ? (\n <span className=\"ds-field__hint\" id={hintId}>\n {hint}\n </span>\n ) : null}\n {error && (\n <span className=\"ds-field__error\" id={errorId}>\n {error}\n </span>\n )}\n </Wrapper>\n );\n}\n"],"mappings":";;;;AAyBA,SAAgBE,EAAM,EAAE,UAAO,SAAM,UAAO,eAAY,aAAU,IAAO,eAAwB;CAC/F,IAAMC,IAAKC,EAAM,GACXC,IAAS,GAAGF,EAAG,QACfG,IAAU,GAAGH,EAAG,SAChBI,IAAcC,IAAQF,IAAUG,IAAOJ,IAAS,KAAA,GAChDK,IAAU,GAAGP,EAAG,SAChBQ,IAAUC,IAAU,aAAa,OACjCC,IAAe,kBAAAb,GAAA,EAAA,UAAA,CAAGc,GAAOC,KAAc,kBAAC,QAAD;EAAM,WAAU;EAAqB,eAAY;EAAO,UAAA;CAAO,CAAA,CAAI,EAAA,CAAA;CAEhH,OACE,kBAACJ,GAAD;EAAS,WAAU;EAAnB,UAAA;GACGC,IACG,kBAAC,UAAD;IAAQ,IAAIF;IAAS,WAAU;IAAmB,UAAAG;GAAqB,CAAA,IACvE,kBAAC,SAAD;IAAO,IAAIH;IAAS,WAAU;IAAkB,SAASP;IAAK,UAAAU;GAAoB,CAAA;GACrFG,EAAS;IACR;IACA,oBAAoBT;IACpB,mBAAmBG;IACnB,gBAAgBF,IAAQ,KAAO,KAAA;IAC/B,UAAUO;GACZ,CAAC;GACAN,KAAQ,CAACD,IACR,kBAAC,QAAD;IAAM,WAAU;IAAiB,IAAIH;IAClC,UAAAI;GACG,CAAA,IACJ;GACHD,KACC,kBAAC,QAAD;IAAM,WAAU;IAAkB,IAAIF;IACnC,UAAAE;GACG,CAAA;EAED;;AAEb"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../Grid/index.cjs");let t=require("react"),n=require("react/jsx-runtime");function r({kind:e,title:t,description:r,children:i,className:a,inputProps:o,inputRef:s}){return(0,n.jsxs)(`label`,{className:[`ds-selection-card`,a].filter(Boolean).join(` `),children:[(0,n.jsx)(`input`,{ref:s,type:e,"data-kind":e,className:`ds-selection-card__control`,...o}),(0,n.jsxs)(`span`,{className:`ds-selection-card__text`,children:[(0,n.jsx)(`span`,{className:`ds-selection-card__title`,children:t}),r&&(0,n.jsx)(`span`,{className:`ds-selection-card__description`,children:r}),i&&(0,n.jsx)(`span`,{className:`ds-selection-card__extra`,children:i})]})]})}var i=(0,t.forwardRef)(function({title:e,description:t,children:i,className:a,...o},s){return(0,n.jsx)(r,{kind:`checkbox`,title:e,description:t,className:a,inputProps:o,inputRef:s,children:i})}),a=(0,t.forwardRef)(function({title:e,description:t,children:i,className:a,...o},s){return(0,n.jsx)(r,{kind:`radio`,title:e,description:t,className:a,inputProps:o,inputRef:s,children:i})});function o({label:t,columns:r=2,children:
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../Grid/index.cjs");let t=require("react"),n=require("react/jsx-runtime");function r({kind:e,title:t,description:r,children:i,className:a,inputProps:o,inputRef:s}){return(0,n.jsxs)(`label`,{className:[`ds-selection-card`,a].filter(Boolean).join(` `),children:[(0,n.jsx)(`input`,{ref:s,type:e,"data-kind":e,className:`ds-selection-card__control`,...o}),(0,n.jsxs)(`span`,{className:`ds-selection-card__text`,children:[(0,n.jsx)(`span`,{className:`ds-selection-card__title`,children:t}),r&&(0,n.jsx)(`span`,{className:`ds-selection-card__description`,children:r}),i&&(0,n.jsx)(`span`,{className:`ds-selection-card__extra`,children:i})]})]})}var i=(0,t.forwardRef)(function({title:e,description:t,children:i,className:a,...o},s){return(0,n.jsx)(r,{kind:`checkbox`,title:e,description:t,className:a,inputProps:o,inputRef:s,children:i})}),a=(0,t.forwardRef)(function({title:e,description:t,children:i,className:a,...o},s){return(0,n.jsx)(r,{kind:`radio`,title:e,description:t,className:a,inputProps:o,inputRef:s,children:i})});function o({label:t,columns:r=2,gap:i=`md`,children:a}){return(0,n.jsxs)(`fieldset`,{className:`ds-selection-card-group`,children:[(0,n.jsx)(`legend`,{className:`ds-selection-card-group__label`,children:t}),(0,n.jsx)(e.Grid,{columns:r,gap:i,className:`ds-selection-card-group__grid`,children:a})]})}exports.CheckboxCard=i,exports.RadioCard=a,exports.SelectionCardGroup=o;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["forwardRef","Grid","require_components_Grid_index","react","react_jsx_runtime","CardBody","className","inputRef","kind","inputProps","title","description","children","CheckboxCard","rest","ref","RadioCard","SelectionCardGroup","label"],"sources":["../../../src/components/SelectionCard/index.tsx"],"sourcesContent":["import { forwardRef, type InputHTMLAttributes, type ReactNode } from \"react\";\nimport { Grid } from \"../Grid\";\nimport \"./style.css\";\n\ninterface SelectionCardBase {\n /** The commitment, in the user's words. */\n title: ReactNode;\n /** One line on what picking this means. Takes nodes, so a chip or a link can sit inline. */\n description?: ReactNode;\n /** Extra content under the description — a price, a sample, a badge. */\n children?: ReactNode;\n}\n\ntype NativeInput = Omit<\n InputHTMLAttributes<HTMLInputElement>,\n \"type\" | \"size\" | \"children\" | \"title\"\n>;\n\nfunction CardBody({\n kind,\n title,\n description,\n children,\n className,\n inputProps,\n inputRef,\n}: SelectionCardBase & {\n kind: \"checkbox\" | \"radio\";\n className?: string;\n inputProps: NativeInput;\n inputRef: React.Ref<HTMLInputElement>;\n}) {\n return (\n <label className={[\"ds-selection-card\", className].filter(Boolean).join(\" \")}>\n <input\n ref={inputRef}\n type={kind}\n data-kind={kind}\n className=\"ds-selection-card__control\"\n {...inputProps}\n />\n <span className=\"ds-selection-card__text\">\n <span className=\"ds-selection-card__title\">{title}</span>\n {description && <span className=\"ds-selection-card__description\">{description}</span>}\n {children && <span className=\"ds-selection-card__extra\">{children}</span>}\n </span>\n </label>\n );\n}\n\nexport interface CheckboxCardProps extends SelectionCardBase, NativeInput {}\n\n/**\n * A checkbox with room to explain itself. Same 20px box as the bare control —\n * the card is a bigger target, not a different decision.\n */\nexport const CheckboxCard = forwardRef<HTMLInputElement, CheckboxCardProps>(\n function CheckboxCard({ title, description, children, className, ...rest }, ref) {\n return (\n <CardBody\n kind=\"checkbox\"\n title={title}\n description={description}\n className={className}\n inputProps={rest}\n inputRef={ref}\n >\n {children}\n </CardBody>\n );\n },\n);\n\nexport interface RadioCardProps extends SelectionCardBase, NativeInput {}\n\n/** One of a set of mutually exclusive options, each with its own explanation. */\nexport const RadioCard = forwardRef<HTMLInputElement, RadioCardProps>(function RadioCard(\n { title, description, children, className, ...rest },\n ref,\n) {\n return (\n <CardBody\n kind=\"radio\"\n title={title}\n description={description}\n className={className}\n inputProps={rest}\n inputRef={ref}\n >\n {children}\n </CardBody>\n );\n});\n\nexport interface SelectionCardGroupProps {\n /** Group label — the question the cards answer. */\n label: string;\n /** Cards per row at full width. Steps down inside a narrow container. */\n columns?: 1 | 2 | 3;\n children: ReactNode;\n}\n\n/** A grid of selection cards under one legend, spaced by the shared Grid. */\nexport function SelectionCardGroup({ label, columns = 2, children }: SelectionCardGroupProps) {\n return (\n <fieldset className=\"ds-selection-card-group\">\n <legend className=\"ds-selection-card-group__label\">{label}</legend>\n <Grid columns={columns} gap
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["forwardRef","Grid","require_components_Grid_index","react","react_jsx_runtime","CardBody","className","inputRef","kind","inputProps","title","description","children","CheckboxCard","rest","ref","RadioCard","SelectionCardGroup","label"],"sources":["../../../src/components/SelectionCard/index.tsx"],"sourcesContent":["import { forwardRef, type InputHTMLAttributes, type ReactNode } from \"react\";\nimport { Grid, type GapSize } from \"../Grid\";\nimport \"./style.css\";\n\ninterface SelectionCardBase {\n /** The commitment, in the user's words. */\n title: ReactNode;\n /** One line on what picking this means. Takes nodes, so a chip or a link can sit inline. */\n description?: ReactNode;\n /** Extra content under the description — a price, a sample, a badge. */\n children?: ReactNode;\n}\n\ntype NativeInput = Omit<\n InputHTMLAttributes<HTMLInputElement>,\n \"type\" | \"size\" | \"children\" | \"title\"\n>;\n\nfunction CardBody({\n kind,\n title,\n description,\n children,\n className,\n inputProps,\n inputRef,\n}: SelectionCardBase & {\n kind: \"checkbox\" | \"radio\";\n className?: string;\n inputProps: NativeInput;\n inputRef: React.Ref<HTMLInputElement>;\n}) {\n return (\n <label className={[\"ds-selection-card\", className].filter(Boolean).join(\" \")}>\n <input\n ref={inputRef}\n type={kind}\n data-kind={kind}\n className=\"ds-selection-card__control\"\n {...inputProps}\n />\n <span className=\"ds-selection-card__text\">\n <span className=\"ds-selection-card__title\">{title}</span>\n {description && <span className=\"ds-selection-card__description\">{description}</span>}\n {children && <span className=\"ds-selection-card__extra\">{children}</span>}\n </span>\n </label>\n );\n}\n\nexport interface CheckboxCardProps extends SelectionCardBase, NativeInput {}\n\n/**\n * A checkbox with room to explain itself. Same 20px box as the bare control —\n * the card is a bigger target, not a different decision.\n */\nexport const CheckboxCard = forwardRef<HTMLInputElement, CheckboxCardProps>(\n function CheckboxCard({ title, description, children, className, ...rest }, ref) {\n return (\n <CardBody\n kind=\"checkbox\"\n title={title}\n description={description}\n className={className}\n inputProps={rest}\n inputRef={ref}\n >\n {children}\n </CardBody>\n );\n },\n);\n\nexport interface RadioCardProps extends SelectionCardBase, NativeInput {}\n\n/** One of a set of mutually exclusive options, each with its own explanation. */\nexport const RadioCard = forwardRef<HTMLInputElement, RadioCardProps>(function RadioCard(\n { title, description, children, className, ...rest },\n ref,\n) {\n return (\n <CardBody\n kind=\"radio\"\n title={title}\n description={description}\n className={className}\n inputProps={rest}\n inputRef={ref}\n >\n {children}\n </CardBody>\n );\n});\n\nexport interface SelectionCardGroupProps {\n /** Group label — the question the cards answer. */\n label: string;\n /** Cards per row at full width. Steps down inside a narrow container. */\n columns?: 1 | 2 | 3;\n /** Space between cards in both axes, using Grid's spacing scale. Defaults to `md`. */\n gap?: GapSize;\n children: ReactNode;\n}\n\n/** A grid of selection cards under one legend, spaced by the shared Grid. */\nexport function SelectionCardGroup({ label, columns = 2, gap = \"md\", children }: SelectionCardGroupProps) {\n return (\n <fieldset className=\"ds-selection-card-group\">\n <legend className=\"ds-selection-card-group__label\">{label}</legend>\n <Grid columns={columns} gap={gap} className=\"ds-selection-card-group__grid\">\n {children}\n </Grid>\n </fieldset>\n );\n}\n"],"mappings":"A,8JAkBA,SAASK,EAAS,CAChB,OACA,QACA,cACA,WACA,YACA,aACA,YAMC,CACD,OACE,EAAAD,EAAA,KAAA,CAAC,QAAD,CAAO,UAAW,CAAC,oBAAqBE,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,EAA3E,SAAA,EACE,EAAAF,EAAA,IAAA,CAAC,QAAD,CACE,IAAKG,EACL,KAAMC,EACN,YAAWA,EACX,UAAU,6BACV,GAAIC,CACL,CAAA,GACD,EAAAL,EAAA,KAAA,CAAC,OAAD,CAAM,UAAU,0BAAhB,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,2BAA4B,SAAAM,CAAY,CAAA,EACvDC,IAAe,EAAAP,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,iCAAkC,SAAAO,CAAkB,CAAA,EACnFC,IAAY,EAAAR,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,2BAA4B,UAAe,CAAA,CACpE,CACD,CAAA,CAAA,C,EAEX,CAQA,IAAaS,GAAA,EAAeb,EAAAA,WAAAA,CAC1B,SAAsB,CAAE,QAAO,cAAa,WAAU,YAAW,GAAGc,GAAQ,EAAK,CAC/E,OACE,EAAAV,EAAA,IAAA,CAACC,EAAD,CACE,KAAK,WACE,QACM,cACF,YACX,WAAYS,EACZ,SAAUC,EAET,UACO,CAAA,CAEd,CACF,EAKaC,GAAA,EAAYhB,EAAAA,WAAAA,CAA6C,SACpE,CAAE,QAAO,cAAa,WAAU,YAAW,GAAGc,GAC9C,EACA,CACA,OACE,EAAAV,EAAA,IAAA,CAACC,EAAD,CACE,KAAK,QACE,QACM,cACF,YACX,WAAYS,EACZ,SAAUC,EAET,UACO,CAAA,CAEd,CAAC,EAaD,SAAgBE,EAAmB,CAAE,QAAO,UAAU,EAAG,MAAM,KAAM,YAAqC,CACxG,OACE,EAAAb,EAAA,KAAA,CAAC,WAAD,CAAU,UAAU,0BAApB,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC,SAAD,CAAQ,UAAU,iCAAkC,SAAAc,CAAc,CAAA,GAClE,EAAAd,EAAA,IAAA,CAACH,EAAAA,KAAD,CAAe,UAAc,MAAK,UAAU,gCACzC,UACG,CAAA,CACE,C,EAEd,C"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type InputHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import { type GapSize } from "../Grid/index.cjs";
|
|
2
3
|
interface SelectionCardBase {
|
|
3
4
|
/** The commitment, in the user's words. */
|
|
4
5
|
title: ReactNode;
|
|
@@ -24,9 +25,11 @@ export interface SelectionCardGroupProps {
|
|
|
24
25
|
label: string;
|
|
25
26
|
/** Cards per row at full width. Steps down inside a narrow container. */
|
|
26
27
|
columns?: 1 | 2 | 3;
|
|
28
|
+
/** Space between cards in both axes, using Grid's spacing scale. Defaults to `md`. */
|
|
29
|
+
gap?: GapSize;
|
|
27
30
|
children: ReactNode;
|
|
28
31
|
}
|
|
29
32
|
/** A grid of selection cards under one legend, spaced by the shared Grid. */
|
|
30
|
-
export declare function SelectionCardGroup({ label, columns, children }: SelectionCardGroupProps): import("react").JSX.Element;
|
|
33
|
+
export declare function SelectionCardGroup({ label, columns, gap, children }: SelectionCardGroupProps): import("react").JSX.Element;
|
|
31
34
|
export {};
|
|
32
35
|
//# sourceMappingURL=index.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/components/SelectionCard/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/components/SelectionCard/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7E,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,aAAa,CAAC;AAErB,UAAU,iBAAiB;IACzB,2CAA2C;IAC3C,KAAK,EAAE,SAAS,CAAC;IACjB,4FAA4F;IAC5F,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,KAAK,WAAW,GAAG,IAAI,CACrB,mBAAmB,CAAC,gBAAgB,CAAC,EACrC,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,CACvC,CAAC;AAkCF,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB,EAAE,WAAW;CAAG;AAE5E;;;GAGG;AACH,eAAO,MAAM,YAAY,gHAexB,CAAC;AAEF,MAAM,WAAW,cAAe,SAAQ,iBAAiB,EAAE,WAAW;CAAG;AAEzE,iFAAiF;AACjF,eAAO,MAAM,SAAS,6GAgBpB,CAAC;AAEH,MAAM,WAAW,uBAAuB;IACtC,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACpB,sFAAsF;IACtF,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,OAAW,EAAE,GAAU,EAAE,QAAQ,EAAE,EAAE,uBAAuB,+BASvG"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type InputHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import { type GapSize } from "../Grid/index.js";
|
|
2
3
|
interface SelectionCardBase {
|
|
3
4
|
/** The commitment, in the user's words. */
|
|
4
5
|
title: ReactNode;
|
|
@@ -24,9 +25,11 @@ export interface SelectionCardGroupProps {
|
|
|
24
25
|
label: string;
|
|
25
26
|
/** Cards per row at full width. Steps down inside a narrow container. */
|
|
26
27
|
columns?: 1 | 2 | 3;
|
|
28
|
+
/** Space between cards in both axes, using Grid's spacing scale. Defaults to `md`. */
|
|
29
|
+
gap?: GapSize;
|
|
27
30
|
children: ReactNode;
|
|
28
31
|
}
|
|
29
32
|
/** A grid of selection cards under one legend, spaced by the shared Grid. */
|
|
30
|
-
export declare function SelectionCardGroup({ label, columns, children }: SelectionCardGroupProps): import("react").JSX.Element;
|
|
33
|
+
export declare function SelectionCardGroup({ label, columns, gap, children }: SelectionCardGroupProps): import("react").JSX.Element;
|
|
31
34
|
export {};
|
|
32
35
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SelectionCard/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SelectionCard/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7E,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,aAAa,CAAC;AAErB,UAAU,iBAAiB;IACzB,2CAA2C;IAC3C,KAAK,EAAE,SAAS,CAAC;IACjB,4FAA4F;IAC5F,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,KAAK,WAAW,GAAG,IAAI,CACrB,mBAAmB,CAAC,gBAAgB,CAAC,EACrC,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,CACvC,CAAC;AAkCF,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB,EAAE,WAAW;CAAG;AAE5E;;;GAGG;AACH,eAAO,MAAM,YAAY,gHAexB,CAAC;AAEF,MAAM,WAAW,cAAe,SAAQ,iBAAiB,EAAE,WAAW;CAAG;AAEzE,iFAAiF;AACjF,eAAO,MAAM,SAAS,6GAgBpB,CAAC;AAEH,MAAM,WAAW,uBAAuB;IACtC,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACpB,sFAAsF;IACtF,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,OAAW,EAAE,GAAU,EAAE,QAAQ,EAAE,EAAE,uBAAuB,+BASvG"}
|
|
@@ -51,7 +51,7 @@ var a = t(function({ title: e, description: t, children: r, className: a, ...o }
|
|
|
51
51
|
children: r
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
|
-
function s({ label: t, columns: i = 2,
|
|
54
|
+
function s({ label: t, columns: i = 2, gap: a = "md", children: o }) {
|
|
55
55
|
return /* @__PURE__ */ r("fieldset", {
|
|
56
56
|
className: "ds-selection-card-group",
|
|
57
57
|
children: [/* @__PURE__ */ n("legend", {
|
|
@@ -59,9 +59,9 @@ function s({ label: t, columns: i = 2, children: a }) {
|
|
|
59
59
|
children: t
|
|
60
60
|
}), /* @__PURE__ */ n(e, {
|
|
61
61
|
columns: i,
|
|
62
|
-
gap:
|
|
62
|
+
gap: a,
|
|
63
63
|
className: "ds-selection-card-group__grid",
|
|
64
|
-
children:
|
|
64
|
+
children: o
|
|
65
65
|
})]
|
|
66
66
|
});
|
|
67
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["CardBody","className","inputRef","kind","inputProps","title","description","children","CheckboxCard","forwardRef","rest","ref","RadioCard","SelectionCardGroup","label","Grid"],"sources":["../../../src/components/SelectionCard/index.tsx"],"sourcesContent":["import { forwardRef, type InputHTMLAttributes, type ReactNode } from \"react\";\nimport { Grid } from \"../Grid\";\nimport \"./style.css\";\n\ninterface SelectionCardBase {\n /** The commitment, in the user's words. */\n title: ReactNode;\n /** One line on what picking this means. Takes nodes, so a chip or a link can sit inline. */\n description?: ReactNode;\n /** Extra content under the description — a price, a sample, a badge. */\n children?: ReactNode;\n}\n\ntype NativeInput = Omit<\n InputHTMLAttributes<HTMLInputElement>,\n \"type\" | \"size\" | \"children\" | \"title\"\n>;\n\nfunction CardBody({\n kind,\n title,\n description,\n children,\n className,\n inputProps,\n inputRef,\n}: SelectionCardBase & {\n kind: \"checkbox\" | \"radio\";\n className?: string;\n inputProps: NativeInput;\n inputRef: React.Ref<HTMLInputElement>;\n}) {\n return (\n <label className={[\"ds-selection-card\", className].filter(Boolean).join(\" \")}>\n <input\n ref={inputRef}\n type={kind}\n data-kind={kind}\n className=\"ds-selection-card__control\"\n {...inputProps}\n />\n <span className=\"ds-selection-card__text\">\n <span className=\"ds-selection-card__title\">{title}</span>\n {description && <span className=\"ds-selection-card__description\">{description}</span>}\n {children && <span className=\"ds-selection-card__extra\">{children}</span>}\n </span>\n </label>\n );\n}\n\nexport interface CheckboxCardProps extends SelectionCardBase, NativeInput {}\n\n/**\n * A checkbox with room to explain itself. Same 20px box as the bare control —\n * the card is a bigger target, not a different decision.\n */\nexport const CheckboxCard = forwardRef<HTMLInputElement, CheckboxCardProps>(\n function CheckboxCard({ title, description, children, className, ...rest }, ref) {\n return (\n <CardBody\n kind=\"checkbox\"\n title={title}\n description={description}\n className={className}\n inputProps={rest}\n inputRef={ref}\n >\n {children}\n </CardBody>\n );\n },\n);\n\nexport interface RadioCardProps extends SelectionCardBase, NativeInput {}\n\n/** One of a set of mutually exclusive options, each with its own explanation. */\nexport const RadioCard = forwardRef<HTMLInputElement, RadioCardProps>(function RadioCard(\n { title, description, children, className, ...rest },\n ref,\n) {\n return (\n <CardBody\n kind=\"radio\"\n title={title}\n description={description}\n className={className}\n inputProps={rest}\n inputRef={ref}\n >\n {children}\n </CardBody>\n );\n});\n\nexport interface SelectionCardGroupProps {\n /** Group label — the question the cards answer. */\n label: string;\n /** Cards per row at full width. Steps down inside a narrow container. */\n columns?: 1 | 2 | 3;\n children: ReactNode;\n}\n\n/** A grid of selection cards under one legend, spaced by the shared Grid. */\nexport function SelectionCardGroup({ label, columns = 2, children }: SelectionCardGroupProps) {\n return (\n <fieldset className=\"ds-selection-card-group\">\n <legend className=\"ds-selection-card-group__label\">{label}</legend>\n <Grid columns={columns} gap
|
|
1
|
+
{"version":3,"file":"index.js","names":["CardBody","className","inputRef","kind","inputProps","title","description","children","CheckboxCard","forwardRef","rest","ref","RadioCard","SelectionCardGroup","label","Grid"],"sources":["../../../src/components/SelectionCard/index.tsx"],"sourcesContent":["import { forwardRef, type InputHTMLAttributes, type ReactNode } from \"react\";\nimport { Grid, type GapSize } from \"../Grid\";\nimport \"./style.css\";\n\ninterface SelectionCardBase {\n /** The commitment, in the user's words. */\n title: ReactNode;\n /** One line on what picking this means. Takes nodes, so a chip or a link can sit inline. */\n description?: ReactNode;\n /** Extra content under the description — a price, a sample, a badge. */\n children?: ReactNode;\n}\n\ntype NativeInput = Omit<\n InputHTMLAttributes<HTMLInputElement>,\n \"type\" | \"size\" | \"children\" | \"title\"\n>;\n\nfunction CardBody({\n kind,\n title,\n description,\n children,\n className,\n inputProps,\n inputRef,\n}: SelectionCardBase & {\n kind: \"checkbox\" | \"radio\";\n className?: string;\n inputProps: NativeInput;\n inputRef: React.Ref<HTMLInputElement>;\n}) {\n return (\n <label className={[\"ds-selection-card\", className].filter(Boolean).join(\" \")}>\n <input\n ref={inputRef}\n type={kind}\n data-kind={kind}\n className=\"ds-selection-card__control\"\n {...inputProps}\n />\n <span className=\"ds-selection-card__text\">\n <span className=\"ds-selection-card__title\">{title}</span>\n {description && <span className=\"ds-selection-card__description\">{description}</span>}\n {children && <span className=\"ds-selection-card__extra\">{children}</span>}\n </span>\n </label>\n );\n}\n\nexport interface CheckboxCardProps extends SelectionCardBase, NativeInput {}\n\n/**\n * A checkbox with room to explain itself. Same 20px box as the bare control —\n * the card is a bigger target, not a different decision.\n */\nexport const CheckboxCard = forwardRef<HTMLInputElement, CheckboxCardProps>(\n function CheckboxCard({ title, description, children, className, ...rest }, ref) {\n return (\n <CardBody\n kind=\"checkbox\"\n title={title}\n description={description}\n className={className}\n inputProps={rest}\n inputRef={ref}\n >\n {children}\n </CardBody>\n );\n },\n);\n\nexport interface RadioCardProps extends SelectionCardBase, NativeInput {}\n\n/** One of a set of mutually exclusive options, each with its own explanation. */\nexport const RadioCard = forwardRef<HTMLInputElement, RadioCardProps>(function RadioCard(\n { title, description, children, className, ...rest },\n ref,\n) {\n return (\n <CardBody\n kind=\"radio\"\n title={title}\n description={description}\n className={className}\n inputProps={rest}\n inputRef={ref}\n >\n {children}\n </CardBody>\n );\n});\n\nexport interface SelectionCardGroupProps {\n /** Group label — the question the cards answer. */\n label: string;\n /** Cards per row at full width. Steps down inside a narrow container. */\n columns?: 1 | 2 | 3;\n /** Space between cards in both axes, using Grid's spacing scale. Defaults to `md`. */\n gap?: GapSize;\n children: ReactNode;\n}\n\n/** A grid of selection cards under one legend, spaced by the shared Grid. */\nexport function SelectionCardGroup({ label, columns = 2, gap = \"md\", children }: SelectionCardGroupProps) {\n return (\n <fieldset className=\"ds-selection-card-group\">\n <legend className=\"ds-selection-card-group__label\">{label}</legend>\n <Grid columns={columns} gap={gap} className=\"ds-selection-card-group__grid\">\n {children}\n </Grid>\n </fieldset>\n );\n}\n"],"mappings":";;;;AAkBA,SAASA,EAAS,EAChB,SACA,UACA,gBACA,aACA,cACA,eACA,eAMC;CACD,OACE,kBAAC,SAAD;EAAO,WAAW,CAAC,qBAAqBC,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;EAA3E,UAAA,CACE,kBAAC,SAAD;GACE,KAAKC;GACL,MAAMC;GACN,aAAWA;GACX,WAAU;GACV,GAAIC;EACL,CAAA,GACD,kBAAC,QAAD;GAAM,WAAU;GAAhB,UAAA;IACE,kBAAC,QAAD;KAAM,WAAU;KAA4B,UAAAC;IAAY,CAAA;IACvDC,KAAe,kBAAC,QAAD;KAAM,WAAU;KAAkC,UAAAA;IAAkB,CAAA;IACnFC,KAAY,kBAAC,QAAD;KAAM,WAAU;KAA4B;IAAe,CAAA;GACpE;EACD,CAAA,CAAA;;AAEX;AAQA,IAAaC,IAAeC,EAC1B,SAAsB,EAAE,UAAO,gBAAa,aAAU,cAAW,GAAGC,KAAQ,GAAK;CAC/E,OACE,kBAACV,GAAD;EACE,MAAK;EACE;EACM;EACF;EACX,YAAYU;EACZ,UAAUC;EAET;CACO,CAAA;AAEd,CACF,GAKaC,IAAYH,EAA6C,SACpE,EAAE,UAAO,gBAAa,aAAU,cAAW,GAAGC,KAC9C,GACA;CACA,OACE,kBAACV,GAAD;EACE,MAAK;EACE;EACM;EACF;EACX,YAAYU;EACZ,UAAUC;EAET;CACO,CAAA;AAEd,CAAC;AAaD,SAAgBE,EAAmB,EAAE,UAAO,aAAU,GAAG,SAAM,MAAM,eAAqC;CACxG,OACE,kBAAC,YAAD;EAAU,WAAU;EAApB,UAAA,CACE,kBAAC,UAAD;GAAQ,WAAU;GAAkC,UAAAC;EAAc,CAAA,GAClE,kBAACC,GAAD;GAAe;GAAc;GAAK,WAAU;GACzC;EACG,CAAA,CACE;;AAEd"}
|
package/dist/tokens.css
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
--font-weight-semibold: 600;
|
|
13
13
|
--font-weight-bold: 700;
|
|
14
14
|
|
|
15
|
-
--font-size-xs: 0.75rem; /* 12 —
|
|
15
|
+
--font-size-xs: 0.75rem; /* 12 — badge counts, micro-labels */
|
|
16
16
|
--font-size-sm: 0.875rem; /* 14 — buttons, secondary cells */
|
|
17
17
|
--font-size-md: 1rem; /* 16 — body */
|
|
18
18
|
--font-size-md-lg: 1.0625rem; /* 17 — primary table cell */
|
|
@@ -117,11 +117,21 @@
|
|
|
117
117
|
/* Hard border. Buttons, fields, checkboxes and badges all carry a constant
|
|
118
118
|
* 2px stroke; state stays on the fill so nothing shifts on interaction.
|
|
119
119
|
* Deliberately thinner than --frame-width: the container must outrank its
|
|
120
|
-
* contents. Controls stroke in --control-border-color; badges
|
|
121
|
-
*
|
|
120
|
+
* contents. Controls stroke in --control-border-color; badges show their
|
|
121
|
+
* tone stroke on outline only, with transparent or fill-matched borders
|
|
122
|
+
* on soft and solid appearances. */
|
|
122
123
|
--control-border-width: 2px;
|
|
123
124
|
--control-border-color: var(--color-text);
|
|
124
125
|
--badge-border-width: 2px;
|
|
126
|
+
--badge-radius: var(--field-radius);
|
|
127
|
+
--badge-font-size: 0.6875rem; /* 11 */
|
|
128
|
+
--badge-line-height: 0.875rem; /* 14 */
|
|
129
|
+
--badge-tracking: var(--table-head-tracking);
|
|
130
|
+
--badge-pad-block: 3px;
|
|
131
|
+
--badge-gap: 6px;
|
|
132
|
+
--badge-marker-size: 6px;
|
|
133
|
+
--badge-count-min: 22px;
|
|
134
|
+
--badge-count-pad-inline: 6px;
|
|
125
135
|
|
|
126
136
|
--table-head-size: 0.8125rem; /* 13 */
|
|
127
137
|
--table-head-weight: var(--font-weight-bold);
|
|
@@ -245,7 +255,7 @@
|
|
|
245
255
|
redefining the semantic tones above. Six states, no seventh. */
|
|
246
256
|
--status-draft: var(--color-border-strong);
|
|
247
257
|
--status-draft-text: var(--color-text-muted);
|
|
248
|
-
--status-draft-soft: var(--color-surface-
|
|
258
|
+
--status-draft-soft: var(--color-surface-subtle);
|
|
249
259
|
--status-scheduled: var(--color-primary);
|
|
250
260
|
--status-scheduled-soft: var(--color-primary-soft);
|
|
251
261
|
--status-published: var(--color-success);
|
|
@@ -255,7 +265,7 @@
|
|
|
255
265
|
--status-unpublished: var(--color-danger);
|
|
256
266
|
--status-unpublished-soft: var(--color-danger-soft);
|
|
257
267
|
--status-archived: var(--color-border);
|
|
258
|
-
--status-archived-text: color-mix(in srgb, var(--color-text-muted)
|
|
268
|
+
--status-archived-text: color-mix(in srgb, var(--color-text-muted) 90%, var(--color-surface));
|
|
259
269
|
--status-archived-soft: transparent;
|
|
260
270
|
/* Status tabs are interactive, so each soft needs its two further steps.
|
|
261
271
|
* Four inherit their tone's ladder; draft and archived have no tone of
|