@stackone/malachite 2.76.0 → 2.77.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"SidePanel.js","sources":["../../src/components/SidePanel.tsx"],"sourcesContent":["import React, { useEffect, useId, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { ShadowRootWrapper } from '../ShadowRootWrapper';\nimport type { AlertType } from './Alert';\nimport CalloutStrip from './CalloutStrip';\nimport ArrowLeftIcon from './CustomIcons/ArrowLeftIcon';\nimport DismissIcon from './CustomIcons/DismissIcon';\nimport type { RenderLink } from './PageLayout/types';\nimport EllipsisText from './Typography/EllipsisText';\nimport { OVERLAY_ICON_BUTTON_CLASSES, useOverlayBehaviour } from './overlayBehaviour';\nimport { OVERLAY_ELEVATED_ATTR, overlayIdentity } from './portalOverlay';\n\nexport interface SidePanelProps {\n open: boolean;\n /** Dismiss handler — close button, ESC, or overlay click. */\n onClose: () => void;\n title: React.ReactNode;\n subtitle?: React.ReactNode;\n /** `below` stacks the subtitle under the title, so neither truncates the other. */\n subtitlePlacement?: 'inline' | 'below';\n /** Content at the end of the title row, after the subtitle (e.g. a status tag). */\n titleExtra?: React.ReactNode;\n /** Block paragraph below the title row, spanning the header width. Announced as the panel's description. */\n description?: React.ReactNode;\n /** Pinned between the header and the scrolling body. */\n toolbar?: React.ReactNode;\n /** Divider under the header. Default: false. */\n headerDivider?: boolean;\n /** Leading header slot, typically a logo or icon. */\n icon?: React.ReactNode;\n /** Content pinned right in the header, before the close button. */\n headerExtra?: React.ReactNode;\n footer?: React.ReactNode;\n /** Renders a back button before the header icon. */\n onBack?: () => void;\n /** Maximum panel width. Default: 1080. */\n maxWidth?: number | string;\n /** Accessible dialog name; required when `title` isn't a plain string. */\n 'aria-label'?: string;\n /** ID of a visible element naming the dialog (alternative to `aria-label`). */\n 'aria-labelledby'?: string;\n /** Whether the body gets default padding. Default: true. */\n bodyPadding?: boolean;\n /** `above-app-overlays` lifts the overlay stack clear of chrome pinned near max-int. */\n stacking?: 'default' | 'above-app-overlays';\n /** Full-width Alert bands at the top of the body, above `children`, in order. Unaffected by\n * `bodyPadding`, and announced ahead of `description`. The message is markdown. */\n callouts?: { type?: AlertType; message: string }[];\n /** Routes root-relative links in `callouts` through the app's router; see `MarkdownBlock`. */\n renderLink?: RenderLink;\n children: React.ReactNode;\n}\n\nconst TRANSITION_MS = 200;\n\nconst toCssSize = (value: number | string): string =>\n typeof value === 'number' ? `${value}px` : value;\n\n/**\n * A right-hand slide-in overlay panel (drawer) for inspecting or editing an entity\n * without leaving the page. Portals to `document.body`, locks background scroll and\n * traps keyboard focus while open (restoring both on close), dismisses on ESC\n * (topmost open overlay only), overlay click or the close button, and exposes header\n * (back/icon/title/subtitle/extra), scrollable body and optional footer slots.\n */\nconst SidePanel: React.FC<SidePanelProps> = ({\n open,\n onClose,\n title,\n subtitle,\n subtitlePlacement = 'inline',\n titleExtra,\n description,\n toolbar,\n headerDivider = false,\n icon,\n headerExtra,\n footer,\n onBack,\n maxWidth = 1080,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n bodyPadding = true,\n stacking = 'default',\n callouts,\n renderLink,\n children,\n}) => {\n const calloutId = useId();\n const descriptionId = useId();\n const [isVisible, setIsVisible] = useState(false);\n const [isMounted, setIsMounted] = useState(false);\n const frameRef = useRef<number | null>(null);\n const unmountTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const hostRef = useRef<HTMLDivElement | null>(null);\n const {\n ref: handlePanelRef,\n isTopmost,\n zIndex,\n elevated,\n } = useOverlayBehaviour({\n open,\n onClose,\n elevated: stacking === 'above-app-overlays',\n hostRef,\n });\n\n useEffect(() => {\n if (open) {\n setIsMounted(true);\n if (unmountTimerRef.current) {\n clearTimeout(unmountTimerRef.current);\n unmountTimerRef.current = null;\n }\n frameRef.current = requestAnimationFrame(() => {\n frameRef.current = null;\n setIsVisible(true);\n });\n } else {\n setIsVisible(false);\n unmountTimerRef.current = setTimeout(() => {\n unmountTimerRef.current = null;\n setIsMounted(false);\n }, TRANSITION_MS);\n }\n return () => {\n if (frameRef.current !== null) {\n cancelAnimationFrame(frameRef.current);\n frameRef.current = null;\n }\n if (unmountTimerRef.current) {\n clearTimeout(unmountTimerRef.current);\n unmountTimerRef.current = null;\n }\n };\n }, [open]);\n\n if (!isMounted) {\n return null;\n }\n\n const shownCallouts = callouts?.filter((callout) => callout.message.trim());\n const hasCallouts = !!shownCallouts?.length;\n const paddedBody = bodyPadding ? (\n <div className={hasCallouts ? 'p-[28px]' : 'py-[28px]'}>{children}</div>\n ) : (\n children\n );\n\n const subtitleBelow = subtitlePlacement === 'below' && !!subtitle;\n const titleNode = (\n <span\n className={subtitleBelow ? 'flex min-w-0 shrink' : 'flex min-w-0 max-w-full shrink-0'}\n >\n <EllipsisText\n tooltip={title}\n tooltipWhenTruncated\n tooltipPlacement=\"bottom\"\n className=\"min-w-0 text-[length:var(--malachite-font-size-15,15px)] font-semibold\"\n >\n {title}\n </EllipsisText>\n </span>\n );\n const subtitleNode = subtitle ? (\n <EllipsisText\n tooltip={subtitle}\n tooltipWhenTruncated\n tooltipPlacement=\"bottom\"\n color=\"secondary\"\n className=\"min-w-0 text-[13px]\"\n >\n {subtitle}\n </EllipsisText>\n ) : null;\n const titleExtraNode = titleExtra ? (\n <span className=\"flex shrink-0 items-center\">{titleExtra}</span>\n ) : null;\n const headerExtraNode = headerExtra ? (\n <span className=\"flex shrink-0 items-baseline\">{headerExtra}</span>\n ) : null;\n\n return createPortal(\n // Host carries the stacking context — ShadowRootWrapper's `isolation` would\n // otherwise flatten our z-index and let sticky page elements paint over us.\n <ShadowRootWrapper\n hostRef={hostRef}\n hostStyle={{ position: 'relative', zIndex }}\n hostProps={\n elevated\n ? { ...overlayIdentity('sidepanel'), [OVERLAY_ELEVATED_ATTR]: '' }\n : overlayIdentity('sidepanel')\n }\n >\n <div className=\"fixed inset-0 z-[1000]\">\n <div\n aria-hidden=\"true\"\n onClick={onClose}\n className={`absolute inset-0 bg-[var(--malachite-overlay-scrim)] transition-opacity duration-200 ease-in-out ${\n isVisible ? 'opacity-100' : 'opacity-0'\n }`}\n />\n <div\n ref={handlePanelRef}\n role=\"dialog\"\n // Only the topmost overlay may claim it, or a screen reader can still\n // browse the one underneath.\n aria-modal={isTopmost ? 'true' : undefined}\n aria-describedby={\n [\n ...(shownCallouts ?? []).map((_, index) => `${calloutId}-${index}`),\n description ? descriptionId : null,\n ]\n .filter(Boolean)\n .join(' ') || undefined\n }\n tabIndex={-1}\n // Prefer aria-labelledby; only fall back to aria-label when it's absent.\n aria-label={\n ariaLabelledBy\n ? undefined\n : (ariaLabel ?? (typeof title === 'string' ? title : undefined))\n }\n aria-labelledby={ariaLabelledBy}\n className=\"absolute top-[8px] right-[8px] bottom-[8px] flex flex-col overflow-hidden rounded-[20px] border border-solid border-[var(--malachite-card-border)] bg-[var(--malachite-card-background)] text-[var(--malachite-card-foreground)] outline-none [box-shadow:var(--malachite-shadow-lg)]\"\n style={{\n width: `min(calc(100% - 16px), ${toCssSize(maxWidth)})`,\n // Inline: Tailwind translate utilities silently break inside\n // shadow roots (see CLAUDE.md Shadow DOM caveats).\n transform: isVisible ? 'translateX(0)' : 'translateX(calc(100% + 16px))',\n transition: `transform ${TRANSITION_MS}ms ease-in-out`,\n }}\n >\n <div\n className={`flex flex-col gap-[20px] px-[20px] py-[18px] ${\n headerDivider\n ? 'border-0 border-b border-solid border-[var(--malachite-card-border)]'\n : ''\n }`}\n >\n <div className=\"flex items-center gap-[12px]\">\n {onBack ? (\n <button\n type=\"button\"\n aria-label=\"Back\"\n onClick={onBack}\n className={OVERLAY_ICON_BUTTON_CLASSES}\n >\n <ArrowLeftIcon size={16} />\n </button>\n ) : null}\n {icon ? (\n <span className=\"flex shrink-0 items-center\">{icon}</span>\n ) : null}\n {subtitleBelow ? (\n <div className=\"flex min-w-0 flex-1 items-center gap-[12px]\">\n <div className=\"flex min-w-0 flex-1 flex-col gap-[2px]\">\n <div className=\"flex min-w-0 items-baseline gap-[8px]\">\n {titleNode}\n {titleExtraNode}\n </div>\n {subtitleNode}\n </div>\n {headerExtraNode}\n </div>\n ) : (\n <div className=\"flex min-w-0 flex-1 items-baseline gap-[12px]\">\n <div className=\"flex min-w-0 flex-1 items-baseline gap-[8px]\">\n <div className=\"flex min-w-0 items-baseline gap-[8px]\">\n {titleNode}\n {subtitleNode}\n </div>\n {titleExtraNode}\n </div>\n {headerExtraNode}\n </div>\n )}\n <button\n type=\"button\"\n aria-label=\"Close\"\n onClick={onClose}\n className={OVERLAY_ICON_BUTTON_CLASSES}\n >\n <DismissIcon size={10} />\n </button>\n </div>\n {description ? (\n <div\n id={descriptionId}\n className=\"text-[13px] leading-[1.6] text-[var(--malachite-text-secondary)]\"\n >\n {description}\n </div>\n ) : null}\n </div>\n {toolbar ? (\n <div className=\"shrink-0 px-[16px] pt-[16px] pb-[8px]\">{toolbar}</div>\n ) : null}\n {/* Callouts move the horizontal padding inward so their strips run full-bleed. */}\n <div\n className={`min-h-0 flex-1 overflow-y-auto ${bodyPadding && !hasCallouts ? 'px-[28px]' : ''}`}\n >\n {shownCallouts?.map((callout, index) => (\n <CalloutStrip\n key={index}\n id={`${calloutId}-${index}`}\n type={callout.type}\n message={callout.message}\n renderLink={renderLink}\n />\n ))}\n {paddedBody}\n </div>\n {footer ? (\n <div className=\"border-0 border-t border-solid border-[var(--malachite-card-border)] px-[24px] py-[16px]\">\n {footer}\n </div>\n ) : null}\n </div>\n </div>\n </ShadowRootWrapper>,\n document.body,\n );\n};\n\nexport default SidePanel;\nexport { SidePanel };\n"],"names":["TRANSITION_MS","toCssSize","value","SidePanel","open","onClose","title","subtitle","subtitlePlacement","titleExtra","description","toolbar","headerDivider","icon","headerExtra","footer","onBack","maxWidth","ariaLabel","ariaLabelledBy","bodyPadding","stacking","callouts","renderLink","children","calloutId","useId","descriptionId","isVisible","setIsVisible","useState","isMounted","setIsMounted","frameRef","useRef","unmountTimerRef","hostRef","handlePanelRef","isTopmost","zIndex","elevated","useOverlayBehaviour","useEffect","shownCallouts","callout","hasCallouts","paddedBody","jsx","subtitleBelow","titleNode","EllipsisText","subtitleNode","titleExtraNode","headerExtraNode","createPortal","ShadowRootWrapper","overlayIdentity","OVERLAY_ELEVATED_ATTR","jsxs","_","index","OVERLAY_ICON_BUTTON_CLASSES","ArrowLeftIcon","DismissIcon","CalloutStrip"],"mappings":"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"}
1
+ {"version":3,"file":"SidePanel.js","sources":["../../src/components/SidePanel.tsx"],"sourcesContent":["import React, { useEffect, useId, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { ShadowRootWrapper } from '../ShadowRootWrapper';\nimport type { AlertType } from './Alert';\nimport CalloutStrip from './CalloutStrip';\nimport ArrowLeftIcon from './CustomIcons/ArrowLeftIcon';\nimport DismissIcon from './CustomIcons/DismissIcon';\nimport type { RenderLink } from './PageLayout/types';\nimport EllipsisText from './Typography/EllipsisText';\nimport { OVERLAY_ICON_BUTTON_CLASSES, useOverlayBehaviour } from './overlayBehaviour';\nimport { OVERLAY_ELEVATED_ATTR, overlayIdentity } from './portalOverlay';\n\nexport interface SidePanelProps {\n open: boolean;\n /** Dismiss handler — close button, ESC, or overlay click. */\n onClose: () => void;\n title: React.ReactNode;\n subtitle?: React.ReactNode;\n /** `below` stacks the subtitle under the title, so neither truncates the other. */\n subtitlePlacement?: 'inline' | 'below';\n /** Content at the end of the title row, after the subtitle (e.g. a status tag). */\n titleExtra?: React.ReactNode;\n /** Block paragraph below the title row, spanning the header width. Announced as the panel's description. */\n description?: React.ReactNode;\n /** Pinned between the header and the scrolling body. */\n toolbar?: React.ReactNode;\n /** Divider under the header. Default: false. */\n headerDivider?: boolean;\n /** Leading header slot, typically a logo or icon. */\n icon?: React.ReactNode;\n /** Content pinned right in the header, before the close button. */\n headerExtra?: React.ReactNode;\n footer?: React.ReactNode;\n /** Renders a back button before the header icon. */\n onBack?: () => void;\n /** Maximum panel width. Default: 1080. */\n maxWidth?: number | string;\n /** Accessible dialog name; required when `title` isn't a plain string. */\n 'aria-label'?: string;\n /** ID of a visible element naming the dialog (alternative to `aria-label`). */\n 'aria-labelledby'?: string;\n /** Whether the body gets default padding. Default: true. */\n bodyPadding?: boolean;\n /** `above-app-overlays` lifts the overlay stack clear of chrome pinned near max-int. */\n stacking?: 'default' | 'above-app-overlays';\n /** Full-width Alert bands at the top of the body, above `children`, in order. Unaffected by\n * `bodyPadding`, and announced ahead of `description`. The message is markdown. */\n callouts?: { type?: AlertType; message: string }[];\n /** Routes root-relative links in `callouts` through the app's router; see `MarkdownBlock`. */\n renderLink?: RenderLink;\n /** A full-height column opened to the left, as wide again as `maxWidth`. The panel's own\n * header, body and footer keep their width and place; the aside's header and footer line\n * up with them. The aside is never narrower than 360px: a window too narrow for both\n * columns scrolls the panel sideways. */\n aside?: SidePanelAside;\n children: React.ReactNode;\n}\n\nexport interface SidePanelAside {\n /** Shown in the aside's header, and names the column for assistive tech. */\n title: string;\n /** Content pinned right in the aside's header (e.g. a stepper). */\n headerExtra?: React.ReactNode;\n /** Pinned at the bottom, level with the panel's footer. */\n footer?: React.ReactNode;\n /** Whether the aside's body gets default padding, as the panel's `bodyPadding`. Default: true. */\n bodyPadding?: boolean;\n children: React.ReactNode;\n}\n\nconst TRANSITION_MS = 200;\nconst ASIDE_MIN_WIDTH = '360px';\n\ninterface ScrollingBodyProps {\n bodyPadding: boolean;\n callouts?: { type?: AlertType; message: string }[];\n /** Prefix for each callout's id, which the panel names in `aria-describedby`. */\n calloutId?: string;\n renderLink?: RenderLink;\n children: React.ReactNode;\n}\n\nconst ScrollingBody: React.FC<ScrollingBodyProps> = ({\n bodyPadding,\n callouts,\n calloutId,\n renderLink,\n children,\n}) => {\n const hasCallouts = !!callouts?.length;\n\n // Callouts move the horizontal padding inward so their strips run full-bleed.\n return (\n <div\n className={`min-h-0 flex-1 overflow-y-auto ${bodyPadding && !hasCallouts ? 'px-[28px]' : ''}`}\n >\n {callouts?.map((callout, index) => (\n <CalloutStrip\n key={index}\n id={calloutId ? `${calloutId}-${index}` : undefined}\n type={callout.type}\n message={callout.message}\n renderLink={renderLink}\n />\n ))}\n {bodyPadding ? (\n <div className={hasCallouts ? 'p-[28px]' : 'py-[28px]'}>{children}</div>\n ) : (\n children\n )}\n </div>\n );\n};\n\nconst toCssSize = (value: number | string): string =>\n typeof value === 'number' ? `${value}px` : value;\n\n/**\n * A right-hand slide-in overlay panel (drawer) for inspecting or editing an entity\n * without leaving the page. Portals to `document.body`, locks background scroll and\n * traps keyboard focus while open (restoring both on close), dismisses on ESC\n * (topmost open overlay only), overlay click or the close button, and exposes header\n * (back/icon/title/subtitle/extra), scrollable body and optional footer slots.\n */\nconst SidePanel: React.FC<SidePanelProps> = ({\n open,\n onClose,\n title,\n subtitle,\n subtitlePlacement = 'inline',\n titleExtra,\n description,\n toolbar,\n headerDivider = false,\n icon,\n headerExtra,\n footer,\n onBack,\n maxWidth = 1080,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n bodyPadding = true,\n stacking = 'default',\n callouts,\n renderLink,\n aside,\n children,\n}) => {\n const calloutId = useId();\n const descriptionId = useId();\n const [isVisible, setIsVisible] = useState(false);\n const [isMounted, setIsMounted] = useState(false);\n const frameRef = useRef<number | null>(null);\n const unmountTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const hostRef = useRef<HTMLDivElement | null>(null);\n const {\n ref: handlePanelRef,\n isTopmost,\n zIndex,\n elevated,\n } = useOverlayBehaviour({\n open,\n onClose,\n elevated: stacking === 'above-app-overlays',\n hostRef,\n });\n\n useEffect(() => {\n if (open) {\n setIsMounted(true);\n if (unmountTimerRef.current) {\n clearTimeout(unmountTimerRef.current);\n unmountTimerRef.current = null;\n }\n frameRef.current = requestAnimationFrame(() => {\n frameRef.current = null;\n setIsVisible(true);\n });\n } else {\n setIsVisible(false);\n unmountTimerRef.current = setTimeout(() => {\n unmountTimerRef.current = null;\n setIsMounted(false);\n }, TRANSITION_MS);\n }\n return () => {\n if (frameRef.current !== null) {\n cancelAnimationFrame(frameRef.current);\n frameRef.current = null;\n }\n if (unmountTimerRef.current) {\n clearTimeout(unmountTimerRef.current);\n unmountTimerRef.current = null;\n }\n };\n }, [open]);\n\n if (!isMounted) {\n return null;\n }\n\n const shownCallouts = callouts?.filter((callout) => callout.message.trim());\n const panelColumnWidth = `calc(${toCssSize(maxWidth)} - 2px)`;\n const dividerBelow = headerDivider\n ? 'border-0 border-b border-solid border-[var(--malachite-card-border)]'\n : '';\n // Each column is a subgrid of the panel's three rows, so the aside's header and footer\n // stay level with the panel's whatever their content. The panel's own column is always the\n // second, so opening or closing the aside never remounts it or reflows it mid-transition:\n // without an aside the first column is simply empty and narrows to nothing.\n const asideColumn = aside ? (\n <aside\n aria-label={aside.title}\n className=\"col-start-1 row-span-3 grid min-w-0 grid-cols-[minmax(0,1fr)] grid-rows-subgrid overflow-hidden border-0 border-r border-solid border-[var(--malachite-card-border)]\"\n >\n <div\n className={`flex min-w-0 items-center gap-[8px] px-[20px] py-[18px] ${dividerBelow}`}\n >\n <span className=\"flex min-w-0 shrink-0\">\n <EllipsisText\n tooltip={aside.title}\n tooltipWhenTruncated\n tooltipPlacement=\"bottom\"\n className=\"min-w-0 text-[length:var(--malachite-font-size-15,15px)] font-semibold\"\n >\n {aside.title}\n </EllipsisText>\n </span>\n {aside.headerExtra ? (\n <span className=\"ml-auto flex shrink-0 items-center\">{aside.headerExtra}</span>\n ) : null}\n </div>\n <div className={`flex min-h-0 flex-col ${aside.footer ? '' : 'row-span-2'}`}>\n <ScrollingBody bodyPadding={aside.bodyPadding ?? true}>\n {aside.children}\n </ScrollingBody>\n </div>\n {aside.footer ? (\n <div className=\"border-0 border-t border-solid border-[var(--malachite-card-border)] px-[24px] py-[16px]\">\n {aside.footer}\n </div>\n ) : null}\n </aside>\n ) : null;\n\n const subtitleBelow = subtitlePlacement === 'below' && !!subtitle;\n const titleNode = (\n <span\n className={subtitleBelow ? 'flex min-w-0 shrink' : 'flex min-w-0 max-w-full shrink-0'}\n >\n <EllipsisText\n tooltip={title}\n tooltipWhenTruncated\n tooltipPlacement=\"bottom\"\n className=\"min-w-0 text-[length:var(--malachite-font-size-15,15px)] font-semibold\"\n >\n {title}\n </EllipsisText>\n </span>\n );\n const subtitleNode = subtitle ? (\n <EllipsisText\n tooltip={subtitle}\n tooltipWhenTruncated\n tooltipPlacement=\"bottom\"\n color=\"secondary\"\n className=\"min-w-0 text-[13px]\"\n >\n {subtitle}\n </EllipsisText>\n ) : null;\n const titleExtraNode = titleExtra ? (\n <span className=\"flex shrink-0 items-center\">{titleExtra}</span>\n ) : null;\n const headerExtraNode = headerExtra ? (\n <span className=\"flex shrink-0 items-baseline\">{headerExtra}</span>\n ) : null;\n\n return createPortal(\n // Host carries the stacking context — ShadowRootWrapper's `isolation` would\n // otherwise flatten our z-index and let sticky page elements paint over us.\n <ShadowRootWrapper\n hostRef={hostRef}\n hostStyle={{ position: 'relative', zIndex }}\n hostProps={\n elevated\n ? { ...overlayIdentity('sidepanel'), [OVERLAY_ELEVATED_ATTR]: '' }\n : overlayIdentity('sidepanel')\n }\n >\n <div className=\"fixed inset-0 z-[1000]\">\n <div\n aria-hidden=\"true\"\n onClick={onClose}\n className={`absolute inset-0 bg-[var(--malachite-overlay-scrim)] transition-opacity duration-200 ease-in-out ${\n isVisible ? 'opacity-100' : 'opacity-0'\n }`}\n />\n <div\n ref={handlePanelRef}\n role=\"dialog\"\n // Only the topmost overlay may claim it, or a screen reader can still\n // browse the one underneath.\n aria-modal={isTopmost ? 'true' : undefined}\n aria-describedby={\n [\n ...(shownCallouts ?? []).map((_, index) => `${calloutId}-${index}`),\n description ? descriptionId : null,\n ]\n .filter(Boolean)\n .join(' ') || undefined\n }\n tabIndex={-1}\n // Prefer aria-labelledby; only fall back to aria-label when it's absent.\n aria-label={\n ariaLabelledBy\n ? undefined\n : (ariaLabel ?? (typeof title === 'string' ? title : undefined))\n }\n aria-labelledby={ariaLabelledBy}\n className={`absolute top-[8px] right-[8px] bottom-[8px] grid grid-rows-[auto_minmax(0,1fr)_auto] overflow-x-auto overflow-y-hidden rounded-[20px] border border-solid border-[var(--malachite-card-border)] bg-[var(--malachite-card-background)] text-[var(--malachite-card-foreground)] outline-none [box-shadow:var(--malachite-shadow-lg)]`}\n style={{\n width: `min(calc(100% - 16px), ${aside ? `calc(2 * ${toCssSize(maxWidth)})` : toCssSize(maxWidth)})`,\n // Inline: Tailwind translate utilities silently break inside\n // shadow roots (see CLAUDE.md Shadow DOM caveats).\n transform: isVisible ? 'translateX(0)' : 'translateX(calc(100% + 16px))',\n transition: `transform ${TRANSITION_MS}ms ease-in-out, width ${TRANSITION_MS}ms ease-in-out, grid-template-columns ${TRANSITION_MS}ms ease-in-out`,\n // The panel's own column is the width it has without an aside: `maxWidth`\n // less the 1px border either side, so opening the aside never widens it.\n // With an aside neither column gives way, so a narrow window scrolls the\n // panel instead. Both templates share one shape so the minimums animate\n // with the width rather than overflowing mid-transition.\n gridTemplateColumns: aside\n ? `minmax(${ASIDE_MIN_WIDTH}, 1fr) minmax(${panelColumnWidth}, ${panelColumnWidth})`\n : `minmax(0px, 1fr) minmax(0px, ${panelColumnWidth})`,\n }}\n >\n {asideColumn}\n {/* An explicit column: an implicit one is sized to its widest content and overflows the panel. */}\n <div className=\"col-start-2 row-span-3 grid min-w-0 grid-cols-[minmax(0,1fr)] grid-rows-subgrid\">\n <div\n className={`flex flex-col gap-[20px] px-[20px] py-[18px] ${dividerBelow}`}\n >\n <div className=\"flex items-center gap-[12px]\">\n {onBack ? (\n <button\n type=\"button\"\n aria-label=\"Back\"\n onClick={onBack}\n className={OVERLAY_ICON_BUTTON_CLASSES}\n >\n <ArrowLeftIcon size={16} />\n </button>\n ) : null}\n {icon ? (\n <span className=\"flex shrink-0 items-center\">{icon}</span>\n ) : null}\n {subtitleBelow ? (\n <div className=\"flex min-w-0 flex-1 items-center gap-[12px]\">\n <div className=\"flex min-w-0 flex-1 flex-col gap-[2px]\">\n <div className=\"flex min-w-0 items-baseline gap-[8px]\">\n {titleNode}\n {titleExtraNode}\n </div>\n {subtitleNode}\n </div>\n {headerExtraNode}\n </div>\n ) : (\n <div className=\"flex min-w-0 flex-1 items-baseline gap-[12px]\">\n <div className=\"flex min-w-0 flex-1 items-baseline gap-[8px]\">\n <div className=\"flex min-w-0 items-baseline gap-[8px]\">\n {titleNode}\n {subtitleNode}\n </div>\n {titleExtraNode}\n </div>\n {headerExtraNode}\n </div>\n )}\n <button\n type=\"button\"\n aria-label=\"Close\"\n onClick={onClose}\n className={OVERLAY_ICON_BUTTON_CLASSES}\n >\n <DismissIcon size={10} />\n </button>\n </div>\n {description ? (\n <div\n id={descriptionId}\n className=\"text-[13px] leading-[1.6] text-[var(--malachite-text-secondary)]\"\n >\n {description}\n </div>\n ) : null}\n </div>\n {/* The toolbar shares the body's row, so an aside's header and footer stay level. */}\n <div className={`flex min-h-0 flex-col ${footer ? '' : 'row-span-2'}`}>\n {toolbar ? (\n <div className=\"shrink-0 px-[16px] pt-[16px] pb-[8px]\">\n {toolbar}\n </div>\n ) : null}\n <ScrollingBody\n bodyPadding={bodyPadding}\n callouts={shownCallouts}\n calloutId={calloutId}\n renderLink={renderLink}\n >\n {children}\n </ScrollingBody>\n </div>\n {footer ? (\n <div className=\"border-0 border-t border-solid border-[var(--malachite-card-border)] px-[24px] py-[16px]\">\n {footer}\n </div>\n ) : null}\n </div>\n </div>\n </div>\n </ShadowRootWrapper>,\n document.body,\n );\n};\n\nexport default SidePanel;\nexport { SidePanel };\n"],"names":["TRANSITION_MS","ASIDE_MIN_WIDTH","ScrollingBody","bodyPadding","callouts","calloutId","renderLink","children","hasCallouts","jsxs","callout","index","jsx","CalloutStrip","toCssSize","value","SidePanel","open","onClose","title","subtitle","subtitlePlacement","titleExtra","description","toolbar","headerDivider","icon","headerExtra","footer","onBack","maxWidth","ariaLabel","ariaLabelledBy","stacking","aside","useId","descriptionId","isVisible","setIsVisible","useState","isMounted","setIsMounted","frameRef","useRef","unmountTimerRef","hostRef","handlePanelRef","isTopmost","zIndex","elevated","useOverlayBehaviour","useEffect","shownCallouts","panelColumnWidth","dividerBelow","asideColumn","EllipsisText","subtitleBelow","titleNode","subtitleNode","titleExtraNode","headerExtraNode","createPortal","ShadowRootWrapper","overlayIdentity","OVERLAY_ELEVATED_ATTR","_","OVERLAY_ICON_BUTTON_CLASSES","ArrowLeftIcon","DismissIcon"],"mappings":"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"}
@@ -1,40 +1,106 @@
1
- import { jsx as e } from "react/jsx-runtime";
2
- const f = ({
3
- steps: i,
4
- active: t,
5
- onChange: o,
6
- className: p = "",
1
+ import { jsx as n } from "react/jsx-runtime";
2
+ import { FOCUS_RING as h } from "./focusRing.js";
3
+ const N = "var(--malachite-accent-foreground)", d = "color-mix(in srgb, var(--malachite-accent-foreground), white 50%)", C = "var(--malachite-card-border)", b = (o, e) => o === e ? N : o < e ? d : C, x = ({ label: o, isCurrent: e, onClick: s, className: a, style: c }) => s ? /* @__PURE__ */ n(
4
+ "button",
5
+ {
6
+ type: "button",
7
+ "aria-label": o,
8
+ "aria-current": e ? "step" : void 0,
9
+ onClick: s,
10
+ className: `${a} cursor-pointer p-0 ${h}`,
11
+ style: c
12
+ }
13
+ ) : /* @__PURE__ */ n("span", { className: a, style: c }), k = (o, e, s) => {
14
+ const a = o.map(
15
+ (l, t) => o.slice(0, t).reduce((i, p) => i + Math.max(0, p.steps), 0)
16
+ ), c = o.findIndex(
17
+ (l, t) => e >= a[t] && e < a[t] + l.steps
18
+ );
19
+ return o.map((l, t) => {
20
+ const i = a[t];
21
+ if (l.steps <= 0)
22
+ return null;
23
+ if (t !== c) {
24
+ const p = t < c;
25
+ return /* @__PURE__ */ n(
26
+ x,
27
+ {
28
+ label: `Go to ${l.label}`,
29
+ isCurrent: !1,
30
+ onClick: s && (() => s(i)),
31
+ className: "h-[8px] w-[16px] shrink-0 rounded-[3px] border-2 border-solid",
32
+ style: {
33
+ borderColor: p ? d : C,
34
+ backgroundColor: p ? d : "transparent"
35
+ }
36
+ },
37
+ t
38
+ );
39
+ }
40
+ return /* @__PURE__ */ n(
41
+ "div",
42
+ {
43
+ className: "mx-[6px] flex min-w-0 flex-1 gap-[4px] first:ml-0 last:mr-0",
44
+ children: Array.from({ length: Math.max(0, l.steps) }, (p, u) => {
45
+ const r = i + u;
46
+ return /* @__PURE__ */ n(
47
+ x,
48
+ {
49
+ label: `Go to step ${r + 1}`,
50
+ isCurrent: r === e,
51
+ onClick: s && (() => s(r)),
52
+ className: "h-[8px] min-w-0 flex-1 rounded-[3px] border-0",
53
+ style: { backgroundColor: b(r, e) }
54
+ },
55
+ r
56
+ );
57
+ })
58
+ },
59
+ t
60
+ );
61
+ });
62
+ }, y = ({
63
+ steps: o,
64
+ groups: e,
65
+ active: s,
66
+ onChange: a,
67
+ className: c = "",
7
68
  "aria-label": l,
8
- "aria-labelledby": s
69
+ "aria-labelledby": t
9
70
  }) => {
10
- const m = Array.from({ length: Math.max(0, i) }, (r, a) => a);
11
- return /* @__PURE__ */ e(
12
- "div",
13
- {
14
- role: !!(l ?? s) ? "group" : void 0,
15
- "aria-label": l,
16
- "aria-labelledby": s,
17
- className: `flex gap-[4px] ${p}`,
18
- children: m.map((r) => {
19
- const a = r === t, u = r < t, c = a ? "var(--malachite-accent-foreground)" : u ? "color-mix(in srgb, var(--malachite-accent-foreground), white 50%)" : "var(--malachite-card-border)", n = `h-[8px] rounded-full transition-all ${a ? "w-[28px]" : "w-[16px]"}`;
20
- return o ? /* @__PURE__ */ e(
21
- "button",
22
- {
23
- type: "button",
24
- "aria-label": `Go to step ${r + 1}`,
25
- "aria-current": a ? "step" : void 0,
26
- onClick: () => o(r),
27
- className: `${n} border-0 p-0 cursor-pointer`,
28
- style: { backgroundColor: c }
29
- },
30
- r
31
- ) : /* @__PURE__ */ e("span", { className: n, style: { backgroundColor: c } }, r);
32
- })
33
- }
34
- );
71
+ const p = {
72
+ role: !!(l ?? t) ? "group" : void 0,
73
+ "aria-label": l,
74
+ "aria-labelledby": t
75
+ };
76
+ if (e)
77
+ return /* @__PURE__ */ n("div", { ...p, className: `flex w-full items-center gap-[4px] ${c}`, children: k(e, s, a) });
78
+ const u = Array.from({ length: Math.max(0, o) }, (r, m) => m);
79
+ return /* @__PURE__ */ n("div", { ...p, className: `flex gap-[4px] ${c}`, children: u.map((r) => {
80
+ const m = r === s, f = `h-[8px] rounded-full transition-all ${m ? "w-[28px]" : "w-[16px]"}`;
81
+ return a ? /* @__PURE__ */ n(
82
+ "button",
83
+ {
84
+ type: "button",
85
+ "aria-label": `Go to step ${r + 1}`,
86
+ "aria-current": m ? "step" : void 0,
87
+ onClick: () => a(r),
88
+ className: `${f} border-0 p-0 cursor-pointer`,
89
+ style: { backgroundColor: b(r, s) }
90
+ },
91
+ r
92
+ ) : /* @__PURE__ */ n(
93
+ "span",
94
+ {
95
+ className: f,
96
+ style: { backgroundColor: b(r, s) }
97
+ },
98
+ r
99
+ );
100
+ }) });
35
101
  };
36
102
  export {
37
- f as Stepper,
38
- f as default
103
+ y as Stepper,
104
+ y as default
39
105
  };
40
106
  //# sourceMappingURL=Stepper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Stepper.js","sources":["../../src/components/Stepper.tsx"],"sourcesContent":["import React from 'react';\n\nexport interface StepperProps {\n /** Total number of steps. */\n steps: number;\n /** Active step index (0-based). */\n active: number;\n /** When provided, each step becomes a clickable control. */\n onChange?: (index: number) => void;\n className?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n}\n\nconst Stepper: React.FC<StepperProps> = ({\n steps,\n active,\n onChange,\n className = '',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n}) => {\n const segments = Array.from({ length: Math.max(0, steps) }, (_, index) => index);\n // A role is what makes aria-label apply at all, but an unnamed group is pure noise\n // in the a11y tree, so the role only appears alongside a name. progressbar would be\n // wrong either way: its contents are presentational and cannot expose the step buttons.\n const isNamed = Boolean(ariaLabel ?? ariaLabelledBy);\n\n return (\n <div\n role={isNamed ? 'group' : undefined}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n className={`flex gap-[4px] ${className}`}\n >\n {segments.map((index) => {\n const isActive = index === active;\n const isCompleted = index < active;\n // Completed steps get a lightened accent, the active step the full accent,\n // upcoming steps a neutral border colour.\n const backgroundColor = isActive\n ? 'var(--malachite-accent-foreground)'\n : isCompleted\n ? 'color-mix(in srgb, var(--malachite-accent-foreground), white 50%)'\n : 'var(--malachite-card-border)';\n const base = `h-[8px] rounded-full transition-all ${isActive ? 'w-[28px]' : 'w-[16px]'}`;\n\n if (!onChange) {\n return <span key={index} className={base} style={{ backgroundColor }} />;\n }\n\n return (\n <button\n key={index}\n type=\"button\"\n aria-label={`Go to step ${index + 1}`}\n aria-current={isActive ? 'step' : undefined}\n onClick={() => onChange(index)}\n className={`${base} border-0 p-0 cursor-pointer`}\n style={{ backgroundColor }}\n />\n );\n })}\n </div>\n );\n};\n\nexport default Stepper;\nexport { Stepper };\n"],"names":["Stepper","steps","active","onChange","className","ariaLabel","ariaLabelledBy","segments","_","index","jsx","isActive","isCompleted","backgroundColor","base"],"mappings":";AAcA,MAAMA,IAAkC,CAAC;AAAA,EACrC,OAAAC;AAAA,EACA,QAAAC;AAAA,EACA,UAAAC;AAAA,EACA,WAAAC,IAAY;AAAA,EACZ,cAAcC;AAAA,EACd,mBAAmBC;AACvB,MAAM;AACF,QAAMC,IAAW,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAGN,CAAK,EAAA,GAAK,CAACO,GAAGC,MAAUA,CAAK;AAM/E,SACI,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACG,MAJQ,GAAQL,KAAaC,KAIb,UAAU;AAAA,MAC1B,cAAYD;AAAA,MACZ,mBAAiBC;AAAA,MACjB,WAAW,kBAAkBF,CAAS;AAAA,MAErC,UAAAG,EAAS,IAAI,CAACE,MAAU;AACrB,cAAME,IAAWF,MAAUP,GACrBU,IAAcH,IAAQP,GAGtBW,IAAkBF,IAClB,uCACAC,IACE,sEACA,gCACFE,IAAO,uCAAuCH,IAAW,aAAa,UAAU;AAEtF,eAAKR,IAKD,gBAAAO;AAAA,UAAC;AAAA,UAAA;AAAA,YAEG,MAAK;AAAA,YACL,cAAY,cAAcD,IAAQ,CAAC;AAAA,YACnC,gBAAcE,IAAW,SAAS;AAAA,YAClC,SAAS,MAAMR,EAASM,CAAK;AAAA,YAC7B,WAAW,GAAGK,CAAI;AAAA,YAClB,OAAO,EAAE,iBAAAD,EAAA;AAAA,UAAgB;AAAA,UANpBJ;AAAA,QAAA,IALF,gBAAAC,EAAC,UAAiB,WAAWI,GAAM,OAAO,EAAE,iBAAAD,OAAjCJ,CAAoD;AAAA,MAc9E,CAAC;AAAA,IAAA;AAAA,EAAA;AAGb;"}
1
+ {"version":3,"file":"Stepper.js","sources":["../../src/components/Stepper.tsx"],"sourcesContent":["import React from 'react';\nimport { FOCUS_RING } from './focusRing';\n\ninterface StepperBaseProps {\n /** Active step index (0-based), counted across every group when grouped. */\n active: number;\n /** When provided, each step becomes a clickable control. */\n onChange?: (index: number) => void;\n className?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n}\n\nexport interface StepperProps extends StepperBaseProps {\n /** Total number of steps. */\n steps: number;\n groups?: never;\n}\n\nexport interface StepperGroup {\n /** Names the group's folded mark, e.g. a section title. */\n label: string;\n steps: number;\n}\n\n/**\n * Steps in consecutive groups. The active step's group opens into one bar per step across\n * the full width, and every other group folds to a short bar. Folded bars keep a fixed width\n * and the open group shares what is left, so allow about 20px per other group and 12px per\n * step in the open group.\n */\nexport interface GroupedStepperProps extends StepperBaseProps {\n groups: StepperGroup[];\n steps?: never;\n}\n\n// Completed steps get a lightened accent, the active step the full accent,\n// upcoming steps a neutral border colour.\nconst ACTIVE_COLOR = 'var(--malachite-accent-foreground)';\nconst COMPLETED_COLOR = 'color-mix(in srgb, var(--malachite-accent-foreground), white 50%)';\nconst UPCOMING_COLOR = 'var(--malachite-card-border)';\n\nconst stepColor = (index: number, active: number): string =>\n index === active ? ACTIVE_COLOR : index < active ? COMPLETED_COLOR : UPCOMING_COLOR;\n\ninterface MarkProps {\n label: string;\n isCurrent: boolean;\n onClick?: () => void;\n className: string;\n style: React.CSSProperties;\n}\n\nconst Mark: React.FC<MarkProps> = ({ label, isCurrent, onClick, className, style }) =>\n onClick ? (\n <button\n type=\"button\"\n aria-label={label}\n aria-current={isCurrent ? 'step' : undefined}\n onClick={onClick}\n className={`${className} cursor-pointer p-0 ${FOCUS_RING}`}\n style={style}\n />\n ) : (\n <span className={className} style={style} />\n );\n\nconst groupedMarks = (\n groups: StepperGroup[],\n active: number,\n onChange: ((index: number) => void) | undefined,\n): React.ReactNode[] => {\n const starts = groups.map((_, index) =>\n groups.slice(0, index).reduce((total, group) => total + Math.max(0, group.steps), 0),\n );\n const currentGroup = groups.findIndex(\n (group, index) => active >= starts[index] && active < starts[index] + group.steps,\n );\n\n return groups.map((group, groupIndex) => {\n const start = starts[groupIndex];\n if (group.steps <= 0) {\n return null;\n }\n if (groupIndex !== currentGroup) {\n const isDone = groupIndex < currentGroup;\n return (\n <Mark\n key={groupIndex}\n label={`Go to ${group.label}`}\n isCurrent={false}\n onClick={onChange && (() => onChange(start))}\n className=\"h-[8px] w-[16px] shrink-0 rounded-[3px] border-2 border-solid\"\n style={{\n borderColor: isDone ? COMPLETED_COLOR : UPCOMING_COLOR,\n backgroundColor: isDone ? COMPLETED_COLOR : 'transparent',\n }}\n />\n );\n }\n\n return (\n <div\n key={groupIndex}\n className=\"mx-[6px] flex min-w-0 flex-1 gap-[4px] first:ml-0 last:mr-0\"\n >\n {Array.from({ length: Math.max(0, group.steps) }, (_, offset) => {\n const index = start + offset;\n return (\n <Mark\n key={index}\n label={`Go to step ${index + 1}`}\n isCurrent={index === active}\n onClick={onChange && (() => onChange(index))}\n className=\"h-[8px] min-w-0 flex-1 rounded-[3px] border-0\"\n style={{ backgroundColor: stepColor(index, active) }}\n />\n );\n })}\n </div>\n );\n });\n};\n\nconst Stepper: React.FC<StepperProps | GroupedStepperProps> = ({\n steps,\n groups,\n active,\n onChange,\n className = '',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n}) => {\n // A role is what makes aria-label apply at all, but an unnamed group is pure noise\n // in the a11y tree, so the role only appears alongside a name. progressbar would be\n // wrong either way: its contents are presentational and cannot expose the step buttons.\n const isNamed = Boolean(ariaLabel ?? ariaLabelledBy);\n const groupProps = {\n role: isNamed ? 'group' : undefined,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n };\n\n if (groups) {\n return (\n <div {...groupProps} className={`flex w-full items-center gap-[4px] ${className}`}>\n {groupedMarks(groups, active, onChange)}\n </div>\n );\n }\n\n const segments = Array.from({ length: Math.max(0, steps) }, (_, index) => index);\n\n return (\n <div {...groupProps} className={`flex gap-[4px] ${className}`}>\n {segments.map((index) => {\n const isActive = index === active;\n const base = `h-[8px] rounded-full transition-all ${isActive ? 'w-[28px]' : 'w-[16px]'}`;\n\n if (!onChange) {\n return (\n <span\n key={index}\n className={base}\n style={{ backgroundColor: stepColor(index, active) }}\n />\n );\n }\n\n return (\n <button\n key={index}\n type=\"button\"\n aria-label={`Go to step ${index + 1}`}\n aria-current={isActive ? 'step' : undefined}\n onClick={() => onChange(index)}\n className={`${base} border-0 p-0 cursor-pointer`}\n style={{ backgroundColor: stepColor(index, active) }}\n />\n );\n })}\n </div>\n );\n};\n\nexport default Stepper;\nexport { Stepper };\n"],"names":["ACTIVE_COLOR","COMPLETED_COLOR","UPCOMING_COLOR","stepColor","index","active","Mark","label","isCurrent","onClick","className","style","jsx","FOCUS_RING","groupedMarks","groups","onChange","starts","_","total","group","currentGroup","groupIndex","start","isDone","offset","Stepper","steps","ariaLabel","ariaLabelledBy","groupProps","segments","isActive","base"],"mappings":"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"}
@@ -35,7 +35,7 @@ const f = {
35
35
  onClick: (e) => {
36
36
  e.stopPropagation(), a(r.id);
37
37
  },
38
- className: "pointer-events-auto flex max-w-[480px] cursor-pointer items-start gap-[8px] rounded-[var(--malachite-radius-xxxl,16px)] bg-[var(--malachite-tooltip-background,rgb(59,59,59))] px-[12px] py-[8px] text-[length:var(--malachite-font-size-medium,14px)] leading-[20px] text-[var(--malachite-tooltip-foreground,rgb(255,255,255))] [box-shadow:var(--malachite-tooltip-shadow,0px_0px_10px_0px_rgba(0,0,0,0.2))] animate-[malachite-toast-in_150ms_ease-out] motion-reduce:animate-none",
38
+ className: "pointer-events-auto flex max-w-[480px] cursor-pointer items-start gap-[8px] rounded-xl bg-[var(--malachite-tooltip-background,rgb(59,59,59))] px-[12px] py-[8px] text-[length:var(--malachite-font-size-medium,14px)] leading-[20px] text-[var(--malachite-tooltip-foreground,rgb(255,255,255))] [box-shadow:var(--malachite-tooltip-shadow,0px_0px_10px_0px_rgba(0,0,0,0.2))] animate-[malachite-toast-in_150ms_ease-out] motion-reduce:animate-none",
39
39
  style: { overflowWrap: "anywhere" },
40
40
  children: [
41
41
  /* @__PURE__ */ l(d, { size: 16, className: `mt-[2px] shrink-0 ${f[r.variant]}` }),
@@ -1 +1 @@
1
- {"version":3,"file":"ToastItem.js","sources":["../../src/components/ToastItem.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport CheckCircleFilled from './CustomIcons/CheckCircleFilled';\nimport ErrorCircleFilled from './CustomIcons/ErrorCircleFilled';\nimport type { QueuedToast, ToastVariant } from './toastTypes';\n\n// The plain status tokens fall under 3:1 on the pill; the mix clears it in both themes.\nconst ICON_COLOR: Record<ToastVariant, string> = {\n success:\n 'text-[var(--malachite-text-success,rgb(2,122,66))] supports-[color:color-mix(in_srgb,red,red)]:text-[color-mix(in_srgb,var(--malachite-text-success,rgb(2,122,66))_70%,var(--malachite-tooltip-foreground,rgb(255,255,255)))]',\n error: 'text-[var(--malachite-text-destructive,rgb(237,53,54))] supports-[color:color-mix(in_srgb,red,red)]:text-[color-mix(in_srgb,var(--malachite-text-destructive,rgb(237,53,54))_70%,var(--malachite-tooltip-foreground,rgb(255,255,255)))]',\n};\n\ninterface ToastItemProps {\n toast: QueuedToast;\n duration: number;\n onDismiss: (id: number) => void;\n}\n\nconst ToastItem: React.FC<ToastItemProps> = ({ toast, duration, onDismiss }) => {\n const [paused, setPaused] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n const remaining = useRef(duration);\n\n // React's onPointerEnter never fires across this portal's shadow boundary.\n useEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n const pause = () => setPaused(true);\n const resume = () => setPaused(false);\n el.addEventListener('pointerenter', pause);\n el.addEventListener('pointerleave', resume);\n return () => {\n el.removeEventListener('pointerenter', pause);\n el.removeEventListener('pointerleave', resume);\n };\n }, []);\n\n useEffect(() => {\n if (paused) {\n return;\n }\n const startedAt = Date.now();\n const timeout = setTimeout(() => onDismiss(toast.id), remaining.current);\n return () => {\n clearTimeout(timeout);\n remaining.current = Math.max(0, remaining.current - (Date.now() - startedAt));\n };\n }, [paused, onDismiss, toast.id]);\n\n const Icon = toast.variant === 'error' ? ErrorCircleFilled : CheckCircleFilled;\n\n return (\n // aria-hidden: the provider's live regions are the announced copy.\n <div\n ref={ref}\n aria-hidden=\"true\"\n data-toast={toast.variant}\n onClick={(event) => {\n // A portal bubbles through the React tree, to the provider's ancestors.\n event.stopPropagation();\n onDismiss(toast.id);\n }}\n className=\"pointer-events-auto flex max-w-[480px] cursor-pointer items-start gap-[8px] rounded-[var(--malachite-radius-xxxl,16px)] bg-[var(--malachite-tooltip-background,rgb(59,59,59))] px-[12px] py-[8px] text-[length:var(--malachite-font-size-medium,14px)] leading-[20px] text-[var(--malachite-tooltip-foreground,rgb(255,255,255))] [box-shadow:var(--malachite-tooltip-shadow,0px_0px_10px_0px_rgba(0,0,0,0.2))] animate-[malachite-toast-in_150ms_ease-out] motion-reduce:animate-none\"\n style={{ overflowWrap: 'anywhere' }}\n >\n <Icon size={16} className={`mt-[2px] shrink-0 ${ICON_COLOR[toast.variant]}`} />\n <span>{toast.message}</span>\n </div>\n );\n};\n\nexport default ToastItem;\n"],"names":["ICON_COLOR","ToastItem","toast","duration","onDismiss","paused","setPaused","useState","ref","useRef","remaining","useEffect","el","pause","resume","startedAt","timeout","Icon","ErrorCircleFilled","CheckCircleFilled","jsxs","event","jsx"],"mappings":";;;;AAMA,MAAMA,IAA2C;AAAA,EAC7C,SACI;AAAA,EACJ,OAAO;AACX,GAQMC,IAAsC,CAAC,EAAE,OAAAC,GAAO,UAAAC,GAAU,WAAAC,QAAgB;AAC5E,QAAM,CAACC,GAAQC,CAAS,IAAIC,EAAS,EAAK,GACpCC,IAAMC,EAAuB,IAAI,GACjCC,IAAYD,EAAON,CAAQ;AAGjC,EAAAQ,EAAU,MAAM;AACZ,UAAMC,IAAKJ,EAAI;AACf,QAAI,CAACI;AACD;AAEJ,UAAMC,IAAQ,MAAMP,EAAU,EAAI,GAC5BQ,IAAS,MAAMR,EAAU,EAAK;AACpC,WAAAM,EAAG,iBAAiB,gBAAgBC,CAAK,GACzCD,EAAG,iBAAiB,gBAAgBE,CAAM,GACnC,MAAM;AACT,MAAAF,EAAG,oBAAoB,gBAAgBC,CAAK,GAC5CD,EAAG,oBAAoB,gBAAgBE,CAAM;AAAA,IACjD;AAAA,EACJ,GAAG,CAAA,CAAE,GAELH,EAAU,MAAM;AACZ,QAAIN;AACA;AAEJ,UAAMU,IAAY,KAAK,IAAA,GACjBC,IAAU,WAAW,MAAMZ,EAAUF,EAAM,EAAE,GAAGQ,EAAU,OAAO;AACvE,WAAO,MAAM;AACT,mBAAaM,CAAO,GACpBN,EAAU,UAAU,KAAK,IAAI,GAAGA,EAAU,WAAW,KAAK,IAAA,IAAQK,EAAU;AAAA,IAChF;AAAA,EACJ,GAAG,CAACV,GAAQD,GAAWF,EAAM,EAAE,CAAC;AAEhC,QAAMe,IAAOf,EAAM,YAAY,UAAUgB,IAAoBC;AAE7D;AAAA;AAAA,IAEI,gBAAAC;AAAA,MAAC;AAAA,MAAA;AAAA,QACG,KAAAZ;AAAA,QACA,eAAY;AAAA,QACZ,cAAYN,EAAM;AAAA,QAClB,SAAS,CAACmB,MAAU;AAEhB,UAAAA,EAAM,gBAAA,GACNjB,EAAUF,EAAM,EAAE;AAAA,QACtB;AAAA,QACA,WAAU;AAAA,QACV,OAAO,EAAE,cAAc,WAAA;AAAA,QAEvB,UAAA;AAAA,UAAA,gBAAAoB,EAACL,GAAA,EAAK,MAAM,IAAI,WAAW,qBAAqBjB,EAAWE,EAAM,OAAO,CAAC,GAAA,CAAI;AAAA,UAC7E,gBAAAoB,EAAC,QAAA,EAAM,UAAApB,EAAM,QAAA,CAAQ;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA;AAGjC;"}
1
+ {"version":3,"file":"ToastItem.js","sources":["../../src/components/ToastItem.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport CheckCircleFilled from './CustomIcons/CheckCircleFilled';\nimport ErrorCircleFilled from './CustomIcons/ErrorCircleFilled';\nimport type { QueuedToast, ToastVariant } from './toastTypes';\n\n// The plain status tokens fall under 3:1 on the pill; the mix clears it in both themes.\nconst ICON_COLOR: Record<ToastVariant, string> = {\n success:\n 'text-[var(--malachite-text-success,rgb(2,122,66))] supports-[color:color-mix(in_srgb,red,red)]:text-[color-mix(in_srgb,var(--malachite-text-success,rgb(2,122,66))_70%,var(--malachite-tooltip-foreground,rgb(255,255,255)))]',\n error: 'text-[var(--malachite-text-destructive,rgb(237,53,54))] supports-[color:color-mix(in_srgb,red,red)]:text-[color-mix(in_srgb,var(--malachite-text-destructive,rgb(237,53,54))_70%,var(--malachite-tooltip-foreground,rgb(255,255,255)))]',\n};\n\ninterface ToastItemProps {\n toast: QueuedToast;\n duration: number;\n onDismiss: (id: number) => void;\n}\n\nconst ToastItem: React.FC<ToastItemProps> = ({ toast, duration, onDismiss }) => {\n const [paused, setPaused] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n const remaining = useRef(duration);\n\n // React's onPointerEnter never fires across this portal's shadow boundary.\n useEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n const pause = () => setPaused(true);\n const resume = () => setPaused(false);\n el.addEventListener('pointerenter', pause);\n el.addEventListener('pointerleave', resume);\n return () => {\n el.removeEventListener('pointerenter', pause);\n el.removeEventListener('pointerleave', resume);\n };\n }, []);\n\n useEffect(() => {\n if (paused) {\n return;\n }\n const startedAt = Date.now();\n const timeout = setTimeout(() => onDismiss(toast.id), remaining.current);\n return () => {\n clearTimeout(timeout);\n remaining.current = Math.max(0, remaining.current - (Date.now() - startedAt));\n };\n }, [paused, onDismiss, toast.id]);\n\n const Icon = toast.variant === 'error' ? ErrorCircleFilled : CheckCircleFilled;\n\n return (\n // aria-hidden: the provider's live regions are the announced copy.\n <div\n ref={ref}\n aria-hidden=\"true\"\n data-toast={toast.variant}\n onClick={(event) => {\n // A portal bubbles through the React tree, to the provider's ancestors.\n event.stopPropagation();\n onDismiss(toast.id);\n }}\n className=\"pointer-events-auto flex max-w-[480px] cursor-pointer items-start gap-[8px] rounded-xl bg-[var(--malachite-tooltip-background,rgb(59,59,59))] px-[12px] py-[8px] text-[length:var(--malachite-font-size-medium,14px)] leading-[20px] text-[var(--malachite-tooltip-foreground,rgb(255,255,255))] [box-shadow:var(--malachite-tooltip-shadow,0px_0px_10px_0px_rgba(0,0,0,0.2))] animate-[malachite-toast-in_150ms_ease-out] motion-reduce:animate-none\"\n style={{ overflowWrap: 'anywhere' }}\n >\n <Icon size={16} className={`mt-[2px] shrink-0 ${ICON_COLOR[toast.variant]}`} />\n <span>{toast.message}</span>\n </div>\n );\n};\n\nexport default ToastItem;\n"],"names":["ICON_COLOR","ToastItem","toast","duration","onDismiss","paused","setPaused","useState","ref","useRef","remaining","useEffect","el","pause","resume","startedAt","timeout","Icon","ErrorCircleFilled","CheckCircleFilled","jsxs","event","jsx"],"mappings":";;;;AAMA,MAAMA,IAA2C;AAAA,EAC7C,SACI;AAAA,EACJ,OAAO;AACX,GAQMC,IAAsC,CAAC,EAAE,OAAAC,GAAO,UAAAC,GAAU,WAAAC,QAAgB;AAC5E,QAAM,CAACC,GAAQC,CAAS,IAAIC,EAAS,EAAK,GACpCC,IAAMC,EAAuB,IAAI,GACjCC,IAAYD,EAAON,CAAQ;AAGjC,EAAAQ,EAAU,MAAM;AACZ,UAAMC,IAAKJ,EAAI;AACf,QAAI,CAACI;AACD;AAEJ,UAAMC,IAAQ,MAAMP,EAAU,EAAI,GAC5BQ,IAAS,MAAMR,EAAU,EAAK;AACpC,WAAAM,EAAG,iBAAiB,gBAAgBC,CAAK,GACzCD,EAAG,iBAAiB,gBAAgBE,CAAM,GACnC,MAAM;AACT,MAAAF,EAAG,oBAAoB,gBAAgBC,CAAK,GAC5CD,EAAG,oBAAoB,gBAAgBE,CAAM;AAAA,IACjD;AAAA,EACJ,GAAG,CAAA,CAAE,GAELH,EAAU,MAAM;AACZ,QAAIN;AACA;AAEJ,UAAMU,IAAY,KAAK,IAAA,GACjBC,IAAU,WAAW,MAAMZ,EAAUF,EAAM,EAAE,GAAGQ,EAAU,OAAO;AACvE,WAAO,MAAM;AACT,mBAAaM,CAAO,GACpBN,EAAU,UAAU,KAAK,IAAI,GAAGA,EAAU,WAAW,KAAK,IAAA,IAAQK,EAAU;AAAA,IAChF;AAAA,EACJ,GAAG,CAACV,GAAQD,GAAWF,EAAM,EAAE,CAAC;AAEhC,QAAMe,IAAOf,EAAM,YAAY,UAAUgB,IAAoBC;AAE7D;AAAA;AAAA,IAEI,gBAAAC;AAAA,MAAC;AAAA,MAAA;AAAA,QACG,KAAAZ;AAAA,QACA,eAAY;AAAA,QACZ,cAAYN,EAAM;AAAA,QAClB,SAAS,CAACmB,MAAU;AAEhB,UAAAA,EAAM,gBAAA,GACNjB,EAAUF,EAAM,EAAE;AAAA,QACtB;AAAA,QACA,WAAU;AAAA,QACV,OAAO,EAAE,cAAc,WAAA;AAAA,QAEvB,UAAA;AAAA,UAAA,gBAAAoB,EAACL,GAAA,EAAK,MAAM,IAAI,WAAW,qBAAqBjB,EAAWE,EAAM,OAAO,CAAC,GAAA,CAAI;AAAA,UAC7E,gBAAAoB,EAAC,QAAA,EAAM,UAAApB,EAAM,QAAA,CAAQ;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA;AAGjC;"}
package/dist/index.d.ts CHANGED
@@ -2026,6 +2026,17 @@ export declare function getCurrentThemeMode(): 'light' | 'dark';
2026
2026
 
2027
2027
  export declare const GlobeIcon: React.FC<IconProps_58>;
2028
2028
 
2029
+ /**
2030
+ * Steps in consecutive groups. The active step's group opens into one bar per step across
2031
+ * the full width, and every other group folds to a short bar. Folded bars keep a fixed width
2032
+ * and the open group shares what is left, so allow about 20px per other group and 12px per
2033
+ * step in the open group.
2034
+ */
2035
+ export declare interface GroupedStepperProps extends StepperBaseProps {
2036
+ groups: StepperGroup[];
2037
+ steps?: never;
2038
+ }
2039
+
2029
2040
  export declare const IconBadge: default_2.FC<IconBadgeProps>;
2030
2041
 
2031
2042
  export declare interface IconBadgeProps {
@@ -3660,7 +3671,8 @@ export declare interface ModalProps {
3660
3671
  children: default_2.ReactNode;
3661
3672
  }
3662
3673
 
3663
- export declare type ModalSize = 'small' | 'medium' | 'large';
3674
+ /** `fullscreen` fills the window, inside the overlay's margin, in both directions. */
3675
+ export declare type ModalSize = 'small' | 'medium' | 'large' | 'fullscreen';
3664
3676
 
3665
3677
  export declare type ModalSurface = 'card' | 'none';
3666
3678
 
@@ -4363,6 +4375,11 @@ export declare type RightSideContent = {
4363
4375
  type?: 'link';
4364
4376
  text: string;
4365
4377
  href: string;
4378
+ } | {
4379
+ /** An action on the form itself, such as showing a panel of help beside it. */
4380
+ type: 'button';
4381
+ text: string;
4382
+ onClick: () => void;
4366
4383
  };
4367
4384
 
4368
4385
  declare const Row: default_2.FC<KeyValueListRowProps>;
@@ -4723,6 +4740,18 @@ export declare interface SidebarSwitcherProps extends SidebarButtonAttributes {
4723
4740
  */
4724
4741
  export declare const SidePanel: default_2.FC<SidePanelProps>;
4725
4742
 
4743
+ export declare interface SidePanelAside {
4744
+ /** Shown in the aside's header, and names the column for assistive tech. */
4745
+ title: string;
4746
+ /** Content pinned right in the aside's header (e.g. a stepper). */
4747
+ headerExtra?: default_2.ReactNode;
4748
+ /** Pinned at the bottom, level with the panel's footer. */
4749
+ footer?: default_2.ReactNode;
4750
+ /** Whether the aside's body gets default padding, as the panel's `bodyPadding`. Default: true. */
4751
+ bodyPadding?: boolean;
4752
+ children: default_2.ReactNode;
4753
+ }
4754
+
4726
4755
  export declare const SidePanelNavFooter: default_2.FC<SidePanelNavFooterProps>;
4727
4756
 
4728
4757
  export declare interface SidePanelNavFooterProps {
@@ -4775,6 +4804,11 @@ export declare interface SidePanelProps {
4775
4804
  }[];
4776
4805
  /** Routes root-relative links in `callouts` through the app's router; see `MarkdownBlock`. */
4777
4806
  renderLink?: RenderLink;
4807
+ /** A full-height column opened to the left, as wide again as `maxWidth`. The panel's own
4808
+ * header, body and footer keep their width and place; the aside's header and footer line
4809
+ * up with them. The aside is never narrower than 360px: a window too narrow for both
4810
+ * columns scrolls the panel sideways. */
4811
+ aside?: SidePanelAside;
4778
4812
  children: default_2.ReactNode;
4779
4813
  }
4780
4814
 
@@ -4984,12 +5018,10 @@ export declare type StatusPillTone = 'success' | 'neutral' | 'warning' | 'error'
4984
5018
 
4985
5019
  export declare const StepCurrentIcon: React.FC<IconProps_104>;
4986
5020
 
4987
- export declare const Stepper: default_2.FC<StepperProps>;
5021
+ export declare const Stepper: default_2.FC<StepperProps | GroupedStepperProps>;
4988
5022
 
4989
- export declare interface StepperProps {
4990
- /** Total number of steps. */
4991
- steps: number;
4992
- /** Active step index (0-based). */
5023
+ declare interface StepperBaseProps {
5024
+ /** Active step index (0-based), counted across every group when grouped. */
4993
5025
  active: number;
4994
5026
  /** When provided, each step becomes a clickable control. */
4995
5027
  onChange?: (index: number) => void;
@@ -4998,6 +5030,18 @@ export declare interface StepperProps {
4998
5030
  'aria-labelledby'?: string;
4999
5031
  }
5000
5032
 
5033
+ export declare interface StepperGroup {
5034
+ /** Names the group's folded mark, e.g. a section title. */
5035
+ label: string;
5036
+ steps: number;
5037
+ }
5038
+
5039
+ export declare interface StepperProps extends StepperBaseProps {
5040
+ /** Total number of steps. */
5041
+ steps: number;
5042
+ groups?: never;
5043
+ }
5044
+
5001
5045
  export declare interface StepTabItem {
5002
5046
  key: string;
5003
5047
  label: string;