@stackone/malachite 2.51.0 → 2.51.2

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":"CardStrip.js","sources":["../../src/components/CardStrip.tsx"],"sourcesContent":["import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';\nimport ArrowLeftIcon from './CustomIcons/ArrowLeftIcon';\nimport ForwardArrowIcon from './CustomIcons/ForwardArrowIcon';\nimport PlusIcon from './CustomIcons/PlusIcon';\nimport { CONTENT_INSET } from './PageLayout/constants';\nimport {\n CARD_FLARE_PX,\n CARD_FLARE_TIP_PX,\n CARD_WIDTH_PX,\n FLARE_CLASS,\n STRIP_RULE_PX,\n} from './SelectableCard';\nimport { EDGE_EPSILON_PX, isCardInView, pageTargetFor, revealTargetFor } from './cardStripGeometry';\nimport { CardStripSlotContext, PINNED_SLOT, SCROLLED_SLOT } from './cardStripSlot';\nimport { FOCUS_RING } from './focusRing';\nimport { useAdoptedStyleSheet } from './useAdoptedStyleSheet';\n\nexport type CardStripAddTileEmphasis = 'primary' | 'secondary';\n\nexport interface CardStripAddTileProps {\n /** Visible label, e.g. \"Add Profile\". Also names the button for assistive tech. */\n label: string;\n /** Defaults to a plus. */\n icon?: React.ReactNode;\n onClick: () => void;\n disabled?: boolean;\n /** Leave unset: CardStrip picks `primary` while the strip is empty and `secondary` once cards exist. */\n emphasis?: CardStripAddTileEmphasis;\n className?: string;\n}\n\n/** Palette for the strip's rule. Pass the tone of whatever card is selected. */\nexport type CardStripTone = 'profile' | 'library' | 'editing';\n\nexport interface CardStripProps {\n /** The selected card's value, matched against each radio child's `data-value`. */\n value: string;\n /** Overrides the palette the strip reads off the selected card. See `CardStripTone`. */\n tone?: CardStripTone;\n onChange: (value: string) => void;\n /** Cards. Any element with `role=\"radio\"` and a `data-value` joins the group; an\n * add tile must be a direct child or it scrolls with the cards. */\n children: React.ReactNode;\n /** Pinned, non-scrolling slot before the cards (the Library card in the profile picker). */\n leading?: React.ReactNode;\n /** Circular scroll affordances, shown only on an edge that can actually scroll. */\n arrows?: boolean;\n /** Fade the cards out at any edge they scroll past. */\n fadeEdges?: boolean;\n /** Snap cards to the leading edge as the strip scrolls. */\n snap?: boolean;\n /** Run the bottom rule the full width of the page card while the cards stay on the gutter.\n * Only has an effect inside a layout that publishes its inset (`PageLayout`). */\n bleed?: boolean;\n /** Whether a selection change scrolls the selected card into view. The strip's opening\n * selection is placed instantly; later ones slide, unless motion is reduced. Set `false`\n * when the value comes from a URL, or a deep-linked strip below the fold jumps the page\n * on load. */\n scrollSelectedIntoView?: boolean;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n className?: string;\n style?: React.CSSProperties;\n}\n\n/** Arrow keys move AND select, per the WAI-ARIA radiogroup pattern. */\nconst NAV_KEYS = new Set(['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End']);\n\nconst RADIO_SELECTOR = '[role=\"radio\"]';\nconst SCROLLER_ATTR = 'data-card-strip-scroller';\nconst TROUGH_ATTR = 'data-card-strip-trough';\nconst ROOT_CLASS = 'malachite-card-strip';\nconst SCROLLER_CLASS = 'malachite-card-strip-scroller';\nconst FADE_CLASS = 'malachite-card-strip-fade';\nconst ADD_TILE_CLASS = 'malachite-card-strip-add-tile';\nconst ADD_SLOT_ATTR = 'data-card-strip-add-slot';\n\nconst ACTIONS_SELECTOR = '[data-selectable-card-actions]';\nconst BESIDE_RADIO_SELECTOR = '[data-selectable-card-link], [data-selectable-card-address]';\nconst FOCUSABLE_SELECTOR =\n 'a[href], area[href], button, input, select, textarea, iframe, [contenteditable=\"true\"], [tabindex]';\nconst PREV_TABINDEX_ATTR = 'data-card-strip-prev-tabindex';\n\nconst FADE_WIDTH_PX = 80;\n\nconst ARROW_GLYPH_PX = 11;\nconst GUTTER_REST_PX = 12;\n\nconst LIP_SHADOW = 'var(--malachite-shadow-default)';\nconst ARROW_BACKGROUND =\n 'var(--malachite-card-strip-arrow-background, var(--malachite-card-background, #ffffff))';\nconst ARROW_BORDER = 'var(--malachite-card-strip-arrow-border, #e8e8e8)';\nconst TROUGH_BACKGROUND = 'var(--malachite-card-strip-background, #f1f1f1)';\nconst CARD_CORNER = 'var(--malachite-radius-xxl, 14px)';\nconst CARD_RADIUS = `${CARD_CORNER} ${CARD_CORNER} 0 0`;\nconst TROUGH_CORNER = 'var(--malachite-radius-xxxl, 16px)';\nconst TROUGH_RADIUS = `${TROUGH_CORNER} ${TROUGH_CORNER} 0 0`;\nconst TROUGH_SHOULDER_PX = 4;\n// Fills the gap between the edge card and its neighbour, covering neither.\nconst ARROW_OFFSET_PX = GUTTER_REST_PX + TROUGH_SHOULDER_PX;\n// Any wider and the fade paints over the neighbouring slot.\nconst FADE_OVERHANG_PX = GUTTER_REST_PX;\nconst FADE_RAMP_PX = FADE_WIDTH_PX + (FADE_OVERHANG_PX - TROUGH_SHOULDER_PX);\nconst SLOT_WIDTH_PX = CARD_WIDTH_PX;\nconst TROUGH_FLOOR_PX = 2 * (GUTTER_REST_PX + TROUGH_SHOULDER_PX) + SLOT_WIDTH_PX;\nconst DIVIDER_COLOR = 'var(--malachite-card-strip-divider, #dddddd)';\nconst FADE_COLOR = 'var(--malachite-card-strip-fade, transparent)';\nconst ADD_HOVER_BACKGROUND = 'var(--malachite-selectable-card-hover-background, #e8e8e8)';\nconst ADD_PILL_BORDER = 'var(--malachite-card-strip-arrow-border, #e8e8e8)';\nconst ADD_PILL_BORDER_ACTIVE = 'var(--malachite-selectable-card-hover-rule, #bbbbbb)';\nconst ADD_PILL_SHADOW_DEEP = '0px 1px 0px 0px rgba(0, 0, 0, 0.4)';\nconst ADD_PILL_BACKGROUND = 'var(--malachite-card-background, #ffffff)';\nconst ADD_PILL_FOREGROUND = 'var(--malachite-text-primary, #0a0a0a)';\nconst ADD_PILL_PRIMARY_BACKGROUND =\n 'var(--malachite-card-strip-add-primary-background, var(--malachite-text-primary, #0a0a0a))';\nconst ADD_PILL_PRIMARY_FOREGROUND =\n 'var(--malachite-card-strip-add-primary-foreground, var(--malachite-card-background, #ffffff))';\nconst ADD_PILL_PRIMARY_SHADOW = '0px 1px 0px 0px rgba(10, 10, 10, 0.7)';\n\nconst BOTTOM_RULE_WEIGHT_PX = STRIP_RULE_PX;\n\nconst OVERFLOW_INSET_PX = 16;\n\nconst CLIP_SLACK_PX = 9999;\nconst FOCUS_RING_REACH_PX = 4;\n\n/** A sheet, not inline styles: these rules have to reach children this component does not render. */\nconst SCROLLER_CSS = `\n.${ROOT_CLASS} {\n clip-path: inset(-${CLIP_SLACK_PX}px -${CLIP_SLACK_PX}px 0);\n}\n.${ROOT_CLASS}:has(:focus-visible) {\n clip-path: inset(-${CLIP_SLACK_PX}px -${CLIP_SLACK_PX}px -${FOCUS_RING_REACH_PX}px);\n}\n.${SCROLLER_CLASS} {\n display: flex;\n flex-wrap: nowrap;\n overflow-x: auto;\n overflow-y: hidden;\n scroll-behavior: smooth;\n scrollbar-width: none;\n -ms-overflow-style: none;\n padding-block: ${OVERFLOW_INSET_PX}px;\n margin-block: -${OVERFLOW_INSET_PX}px;\n /* overflow-x clips both axes, so the end cards would lose a flare; the negative margin trades\n the padding back out and scroll-padding keeps the rest position at zero. */\n padding-inline: ${CARD_FLARE_PX}px;\n margin-inline: -${CARD_FLARE_PX}px;\n scroll-padding-inline: ${CARD_FLARE_PX}px;\n}\n/* A card focused at a faded edge has to stay readable, so the fade lifts entirely. */\n.${ROOT_CLASS}:has(:focus-visible) .${FADE_CLASS} {\n display: none;\n}\n.${SCROLLER_CLASS}::-webkit-scrollbar {\n display: none;\n}\n/* No bottom edge: the strip's rule is the baseline for the add tile as much as for a card. */\n.${ADD_TILE_CLASS} > * {\n background: ${TROUGH_BACKGROUND};\n border-radius: ${CARD_RADIUS};\n}\n.${ADD_TILE_CLASS}:hover:not(:has(button:disabled)) > * {\n background: ${ADD_HOVER_BACKGROUND};\n}\n.${ADD_TILE_CLASS} button:active:not(:disabled) {\n opacity: 0.8;\n}\n.${ADD_TILE_CLASS}:has(button:active) > * {\n background: ${TROUGH_BACKGROUND};\n}\n.${ADD_TILE_CLASS}[data-emphasis='secondary'] button {\n border-color: ${ADD_PILL_BORDER};\n box-shadow: ${LIP_SHADOW};\n}\n.${ADD_TILE_CLASS}[data-emphasis='secondary']:hover:not(:has(button:disabled)) button {\n border-color: ${ADD_PILL_BORDER_ACTIVE};\n box-shadow: ${ADD_PILL_SHADOW_DEEP};\n}\n.${ADD_TILE_CLASS}[data-emphasis='secondary'] button:active:not(:disabled) {\n border-color: ${ADD_PILL_BORDER_ACTIVE};\n box-shadow: ${ADD_PILL_SHADOW_DEEP};\n}\n.${ADD_TILE_CLASS}[data-emphasis='primary'] button {\n border-color: transparent;\n box-shadow: ${ADD_PILL_PRIMARY_SHADOW};\n}\n.${SCROLLER_CLASS} > * {\n flex: 0 0 auto;\n min-width: 0;\n}\n.${SCROLLER_CLASS} > * + * {\n border-left: 1px solid ${DIVIDER_COLOR};\n}\n.${SCROLLER_CLASS}[data-snap='true'] {\n scroll-snap-type: x proximity;\n}\n.${SCROLLER_CLASS}[data-snap='true'] > * {\n scroll-snap-align: start;\n}\n@media (prefers-reduced-motion: reduce) {\n .${SCROLLER_CLASS} {\n scroll-behavior: auto;\n }\n .${SCROLLER_CLASS}[data-snap='true'] {\n scroll-snap-type: none;\n }\n .${SCROLLER_CLASS}[data-snap='true'] > * {\n scroll-snap-align: none;\n }\n}`;\n\nconst STYLE_ID = 'malachite-card-strip-style';\n// Its own id: an older malachite copy on the page may already own STYLE_ID without these rules.\nconst ADD_SLOT_STYLE_ID = 'malachite-card-strip-add-slot';\n// In the track the trough is already the tile's shoulder, and it is no card to rule off.\nconst ADD_SLOT_CSS = `\n.${SCROLLER_CLASS} > [${ADD_SLOT_ATTR}] {\n display: flex;\n border-left: none;\n}\n[${ADD_SLOT_ATTR}] > .${ADD_TILE_CLASS} {\n height: 100% !important;\n padding: 0 !important;\n}\n`;\n\n/** `window.matchMedia` is absent under SSR and in older test environments. */\nconst prefersReducedMotion = (): boolean => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return true;\n }\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n};\n\nconst radioValue = (radio: HTMLElement): string | null =>\n radio.dataset.value ?? radio.getAttribute('value');\n\nconst isRadioDisabled = (radio: HTMLElement): boolean =>\n radio.hasAttribute('disabled') || radio.getAttribute('aria-disabled') === 'true';\n\nconst cardFor = (radio: HTMLElement): HTMLElement | null => {\n const card = radio.parentElement;\n return card && card.querySelectorAll(RADIO_SELECTOR).length === 1 ? card : null;\n};\n\nconst actionsFor = (radio: HTMLElement): HTMLElement | null =>\n cardFor(radio)?.querySelector<HTMLElement>(ACTIONS_SELECTOR) ?? null;\n\n/** A card is wider than its radio, so `closest` misses clicks on the count row. */\nconst radioForClick = (target: Element, root: Element): HTMLElement | null => {\n const direct = target.closest<HTMLElement>(RADIO_SELECTOR);\n if (direct) {\n return direct;\n }\n if (target.closest(ACTIONS_SELECTOR) || target.closest(BESIDE_RADIO_SELECTOR)) {\n return null;\n }\n let node: Element | null = target;\n while (node && root.contains(node)) {\n const radios: NodeListOf<HTMLElement> = node.querySelectorAll(RADIO_SELECTOR);\n if (radios.length === 1 && radios[0].parentElement === node) {\n return radios[0];\n }\n node = node.parentElement;\n }\n return null;\n};\n\n// The card's own controls beside its radio, which only the card holding the tab stop keeps.\nconst cardControls = (radio: HTMLElement): HTMLElement[] => {\n const actions = actionsFor(radio);\n const besideRadio = cardFor(radio)?.querySelectorAll<HTMLElement>(BESIDE_RADIO_SELECTOR);\n return [\n ...(actions ? Array.from(actions.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)) : []),\n ...(besideRadio ? Array.from(besideRadio) : []),\n ];\n};\n\nconst suppressTabStop = (element: HTMLElement): void => {\n if (!element.hasAttribute(PREV_TABINDEX_ATTR)) {\n element.setAttribute(PREV_TABINDEX_ATTR, element.getAttribute('tabindex') ?? '');\n }\n element.setAttribute('tabindex', '-1');\n};\n\nconst restoreTabStop = (element: HTMLElement): void => {\n if (!element.hasAttribute(PREV_TABINDEX_ATTR)) {\n return;\n }\n const previous = element.getAttribute(PREV_TABINDEX_ATTR);\n element.removeAttribute(PREV_TABINDEX_ATTR);\n if (previous) {\n element.setAttribute('tabindex', previous);\n } else {\n element.removeAttribute('tabindex');\n }\n};\n\nconst TONE_RULE: Record<CardStripTone, string> = {\n profile: 'var(--malachite-selectable-card-selected-border, #047b43)',\n library: 'var(--malachite-selectable-card-library-selected-border, #bbbbbb)',\n editing: 'var(--malachite-selectable-card-pending-foreground, #96580a)',\n};\nconst RULE_ROW_STYLE: React.CSSProperties = {\n backgroundRepeat: 'no-repeat',\n backgroundPosition: 'left bottom',\n backgroundSize: `100% ${BOTTOM_RULE_WEIGHT_PX}px`,\n};\n\n/** The rule runs under the flare's tip rather than butting against it, so antialiasing\n * cannot open the junction into a gap. */\nconst RULE_GAP_OVERLAP_PX = 0.5;\n\nconst FLARE_TIP_INSET_PX = CARD_FLARE_PX - CARD_FLARE_TIP_PX;\n\nconst flareTips = (card: HTMLElement): { left: number; right: number } | null => {\n const flare = card.querySelector(`:scope > .${FLARE_CLASS}`);\n if (!flare) {\n return null;\n }\n const { left, right } = flare.getBoundingClientRect();\n return { left: left + FLARE_TIP_INSET_PX, right: right - FLARE_TIP_INSET_PX };\n};\n\n/** The rule is cut where the selected card stands, never covered: the strip's clip lands on the\n * rule's own edge, so a cover takes the same partial alpha and the rule bleeds through it. */\nconst paintRule = (\n root: HTMLElement | null,\n scroller: HTMLElement | null,\n card: HTMLElement | null | undefined,\n tone: CardStripTone,\n): void => {\n if (!root) {\n return;\n }\n const colour = TONE_RULE[tone];\n const rootRect = root.getBoundingClientRect();\n const rootLeft = rootRect.left;\n const clip = card && scroller?.contains(card) ? scroller.getBoundingClientRect() : rootRect;\n const cardRect = card?.getBoundingClientRect();\n const tips = card ? flareTips(card) : null;\n const tipLeft = tips?.left ?? (cardRect ? cardRect.left - CARD_FLARE_TIP_PX : 0);\n const tipRight = tips?.right ?? (cardRect ? cardRect.right + CARD_FLARE_TIP_PX : 0);\n const from = cardRect ? Math.max(tipLeft, clip.left) + RULE_GAP_OVERLAP_PX - rootLeft : 0;\n const to = cardRect ? Math.min(tipRight, clip.right) - RULE_GAP_OVERLAP_PX - rootLeft : 0;\n\n root.style.backgroundImage =\n to > from\n ? `linear-gradient(to right, ${colour} 0 ${from}px, transparent ${from}px ${to}px, ${colour} ${to}px 100%)`\n : `linear-gradient(to right, ${colour} 0 100%)`;\n};\n\nconst BLEED_STYLE: React.CSSProperties = {\n marginInline: `calc(${CONTENT_INSET} * -1)`,\n paddingInline: CONTENT_INSET,\n};\n\n/** The rule's row is a margin, never padding: a background paints under its own padding, which\n * would hide the very line the troughs stand on. */\nconst TROUGH_SURFACE: React.CSSProperties = {\n background: TROUGH_BACKGROUND,\n borderRadius: TROUGH_RADIUS,\n paddingTop: `${TROUGH_SHOULDER_PX}px`,\n paddingInline: `${TROUGH_SHOULDER_PX}px`,\n marginBottom: `${BOTTOM_RULE_WEIGHT_PX}px`,\n};\nconst TROUGH_STYLE: React.CSSProperties = {\n ...TROUGH_SURFACE,\n marginInline: `${GUTTER_REST_PX}px`,\n};\nconst ADD_TILE_SHAPE_STYLE: React.CSSProperties = TROUGH_SURFACE;\n\nconst PINNED_SLOT_STYLE: React.CSSProperties = TROUGH_SURFACE;\n\nconst EMPTY_ADD_SLOT_STYLE: React.CSSProperties = {\n marginInlineStart: `${GUTTER_REST_PX}px`,\n};\n\nconst ADD_PILL_FILL: Record<CardStripAddTileEmphasis, React.CSSProperties> = {\n primary: {\n backgroundColor: ADD_PILL_PRIMARY_BACKGROUND,\n color: ADD_PILL_PRIMARY_FOREGROUND,\n },\n secondary: { backgroundColor: ADD_PILL_BACKGROUND, color: ADD_PILL_FOREGROUND },\n};\n\nconst AddTile: React.FC<CardStripAddTileProps> = ({\n label,\n icon,\n onClick,\n disabled = false,\n emphasis = 'secondary',\n className = '',\n}) => (\n <div\n className={ADD_TILE_CLASS}\n data-emphasis={emphasis}\n style={{\n ...ADD_TILE_SHAPE_STYLE,\n width: `${SLOT_WIDTH_PX}px`,\n height: `calc(100% - ${BOTTOM_RULE_WEIGHT_PX}px)`,\n }}\n >\n <div className=\"flex h-full items-center justify-center px-4\">\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n style={ADD_PILL_FILL[emphasis]}\n className={`${FOCUS_RING} inline-flex items-center justify-center gap-1.5 h-[var(--malachite-control-height-medium,32px)] w-[108px] px-2.5 rounded-xl border border-solid text-[length:var(--malachite-font-size-small,13px)] font-medium whitespace-nowrap ${\n disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'\n } ${className}`}\n >\n <span className=\"inline-flex h-[13px] w-[13px] shrink-0 items-center justify-center\">\n {icon ?? <PlusIcon size={13} />}\n </span>\n {label}\n </button>\n </div>\n </div>\n);\n\nconst isAddTile = (node: React.ReactNode): node is React.ReactElement<CardStripAddTileProps> =>\n React.isValidElement(node) && node.type === AddTile;\n\nconst hidesAddTile = (child: React.ReactNode): boolean =>\n React.isValidElement<{ children?: React.ReactNode }>(child) &&\n child.type === React.Fragment &&\n React.Children.toArray(child.props.children).some(isAddTile);\n\ninterface ArrowButtonProps {\n side: 'left' | 'right';\n onClick: () => void;\n}\n\nconst ArrowButton: React.FC<ArrowButtonProps> = ({ side, onClick }) => (\n <button\n type=\"button\"\n tabIndex={-1}\n aria-hidden=\"true\"\n aria-label={side === 'left' ? 'Scroll left' : 'Scroll right'}\n onClick={onClick}\n className=\"absolute top-1/2 -translate-y-1/2 z-10 inline-flex items-center justify-center w-5 h-5 rounded-lg cursor-pointer text-[var(--malachite-text-secondary)]\"\n style={{\n [side]: `-${ARROW_OFFSET_PX}px`,\n background: ARROW_BACKGROUND,\n border: `1px solid ${ARROW_BORDER}`,\n boxShadow: LIP_SHADOW,\n }}\n >\n {side === 'left' ? (\n <ArrowLeftIcon size={ARROW_GLYPH_PX} />\n ) : (\n <ForwardArrowIcon size={ARROW_GLYPH_PX} />\n )}\n </button>\n);\n\n/** A horizontally scrolling radiogroup of cards — the \"pick one of these\" strip. */\nconst CardStrip: React.FC<CardStripProps> & { AddTile: React.FC<CardStripAddTileProps> } = ({\n value,\n tone,\n onChange,\n children,\n leading,\n arrows = true,\n fadeEdges = true,\n snap = true,\n bleed = false,\n scrollSelectedIntoView = true,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n className = '',\n style,\n}) => {\n const rootRef = useRef<HTMLDivElement>(null);\n const pinnedRef = useRef<HTMLDivElement>(null);\n const scrollerRef = useRef<HTMLDivElement>(null);\n const hasPositioned = useRef(false);\n const [edges, setEdges] = useState({ canScrollLeft: false, canScrollRight: false });\n const [cramped, setCramped] = useState(false);\n const addHostRef = useRef<HTMLDivElement>(null);\n const addTileFocused = useRef(false);\n\n useAdoptedStyleSheet(rootRef, SCROLLER_CSS, STYLE_ID);\n useAdoptedStyleSheet(rootRef, ADD_SLOT_CSS, ADD_SLOT_STYLE_ID);\n\n const childList = React.Children.toArray(children);\n const cards = childList.filter((child) => !isAddTile(child));\n const wrappedAddTile = cards.some(hidesAddTile);\n const hasCards = cards.length > 0;\n\n const addTiles = childList\n .filter(isAddTile)\n .map((tile) =>\n tile.props.emphasis === undefined\n ? React.cloneElement(tile, { emphasis: hasCards ? 'secondary' : 'primary' })\n : tile,\n );\n\n const childCount = childList.length;\n const addTileCount = addTiles.length;\n const hasLeading = Boolean(leading);\n\n // With no room for a card between the pinned tiles, the add tile scrolls with the cards.\n // biome-ignore lint/correctness/useExhaustiveDependencies: a new pinned slot needs observing\n useLayoutEffect(() => {\n const root = rootRef.current;\n if (!root || !hasCards || addTileCount === 0) {\n setCramped(false);\n return;\n }\n const pinnedSlot = pinnedRef.current;\n const { paddingLeft, paddingRight } = getComputedStyle(root);\n const inset = (parseFloat(paddingLeft) || 0) + (parseFloat(paddingRight) || 0);\n let content = root.clientWidth - inset;\n let pinned = pinnedSlot?.offsetWidth ?? 0;\n const decide = (): void => {\n const needs = pinned + addTileCount * SLOT_WIDTH_PX + TROUGH_FLOOR_PX;\n setCramped(content > 0 && content < needs);\n };\n decide();\n // Sizes from the entries: reading layout here would force the layout the observer spares.\n const observer =\n typeof ResizeObserver !== 'undefined'\n ? new ResizeObserver((entries) => {\n for (const entry of entries ?? []) {\n if (entry.target === root) {\n content = entry.contentRect.width;\n } else {\n pinned =\n entry.borderBoxSize?.[0]?.inlineSize ??\n entry.contentRect.width + 2 * TROUGH_SHOULDER_PX;\n }\n }\n decide();\n })\n : null;\n observer?.observe(root);\n if (pinnedSlot) {\n observer?.observe(pinnedSlot);\n }\n return () => observer?.disconnect();\n }, [hasCards, addTileCount, hasLeading]);\n\n const addInTrack = cramped && hasCards;\n const trackAddTileFocus = useCallback((event: React.FocusEvent) => {\n const focused = event.type === 'focus' ? event.target : event.relatedTarget;\n addTileFocused.current = Boolean(\n focused instanceof Node && addHostRef.current?.contains(focused),\n );\n }, []);\n\n // The tile remounts wherever it moves, so focus has to follow it there. Removing a focused\n // element fires no blur, so the flag survives the move.\n // biome-ignore lint/correctness/useExhaustiveDependencies: the tile moving is the signal\n useLayoutEffect(() => {\n const host = addHostRef.current;\n const active = host ? (host.getRootNode() as Document | ShadowRoot).activeElement : null;\n if (addTileFocused.current && host && !(active && host.contains(active))) {\n host.querySelector('button')?.focus({ preventScroll: true });\n }\n }, [addInTrack]);\n\n const getRadios = useCallback((): HTMLElement[] => {\n const root = rootRef.current;\n if (!root) {\n return [];\n }\n return Array.from(root.querySelectorAll<HTMLElement>(RADIO_SELECTOR));\n }, []);\n\n // Read off the DOM rather than the children's keys: unkeyed cards reorder under positional\n // keys that never change. Attribute reads only, so this forces no layout.\n const radioOrder = useRef<string | null>(null);\n const [reorders, setReorders] = useState(0);\n useLayoutEffect(() => {\n const next = JSON.stringify(getRadios().map(radioValue));\n if (radioOrder.current !== null && radioOrder.current !== next) {\n setReorders((count) => count + 1);\n }\n radioOrder.current = next;\n });\n\n const getNavigableRadios = useCallback(\n (): HTMLElement[] => getRadios().filter((radio) => radioValue(radio) !== null),\n [getRadios],\n );\n\n const warned = useRef(new Set<string>());\n const warnOnce = useCallback((key: string, message: string): void => {\n if (warned.current.has(key)) {\n return;\n }\n warned.current.add(key);\n console.warn(message);\n }, []);\n\n const [derivedTone, setDerivedTone] = useState<CardStripTone>('profile');\n\n useLayoutEffect(() => {\n const selectedCard = getRadios()\n .find((radio) => radioValue(radio) === value)\n ?.parentElement?.getAttribute('data-variant');\n if (\n selectedCard === 'profile' ||\n selectedCard === 'library' ||\n selectedCard === 'editing'\n ) {\n setDerivedTone(selectedCard);\n }\n }, [getRadios, value]);\n\n useLayoutEffect(() => {\n const radios = getRadios();\n const navigable = getNavigableRadios();\n const selected = navigable.find((radio) => radioValue(radio) === value);\n const tabStop = selected ?? navigable.find((radio) => !isRadioDisabled(radio)) ?? null;\n\n radios.forEach((radio) => {\n const holdsTabStop = radio === tabStop;\n const nextTabIndex = holdsTabStop ? 0 : -1;\n if (radio.tabIndex !== nextTabIndex) {\n radio.tabIndex = nextTabIndex;\n }\n for (const control of cardControls(radio)) {\n if (holdsTabStop) {\n restoreTabStop(control);\n } else {\n suppressTabStop(control);\n }\n }\n });\n\n if (process.env.NODE_ENV === 'production') {\n return;\n }\n\n if (navigable.length !== radios.length) {\n warnOnce(\n 'missing-value',\n 'CardStrip: a child with role=\"radio\" has no `data-value`. It cannot be selected or reached by arrow keys, so it is excluded from the radio set. Give every card a `data-value`.',\n );\n }\n\n const values = new Set<string>();\n for (const radio of navigable) {\n const current = radioValue(radio);\n if (current === null) {\n continue;\n }\n if (values.has(current)) {\n warnOnce(\n `duplicate:${current}`,\n `CardStrip: two cards share the data-value \"${current}\". Both will read as checked and only the first is selectable — data-value must be unique.`,\n );\n }\n values.add(current);\n }\n\n const selectionSettled = value !== '';\n\n if (radios.length > 0 && selectionSettled && !values.has(value)) {\n warnOnce(\n `unmatched:${value}`,\n `CardStrip: value \"${value}\" matches no card's data-value, so the group reports no selection.`,\n );\n }\n\n const checked = radios.filter(\n (radio) => radio.getAttribute('aria-checked') === 'true',\n ).length;\n if (radios.length > 0 && selectionSettled && checked !== 1) {\n warnOnce(\n `checked:${checked}`,\n `CardStrip: ${checked} cards are aria-checked, but a radiogroup has exactly one. Drive each card's checked state from the strip's \\`value\\`.`,\n );\n }\n\n if (wrappedAddTile) {\n warnOnce(\n 'wrapped-add-tile',\n 'CardStrip: a CardStrip.AddTile inside a fragment is treated as a card and scrolls with them. The add tile must be a direct child of CardStrip.',\n );\n }\n });\n\n const selectedCard = useCallback((): HTMLElement | null => {\n const selected = getRadios().find((radio) => radioValue(radio) === value);\n return selected ? ((selected.closest('[role=\"radio\"]') ?? selected) as HTMLElement) : null;\n }, [getRadios, value]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: cards arriving is the signal\n useLayoutEffect(() => {\n if (!scrollSelectedIntoView) {\n return;\n }\n const scroller = scrollerRef.current;\n const card = selectedCard();\n // jsdom implements neither, and a strip that cannot scroll has nothing to bring into view.\n if (!scroller || !card || typeof scroller.scrollTo !== 'function') {\n return;\n }\n\n // The first placement is where the strip opens — a deep link, not a move — so it is\n // instant. Every later one is the selection travelling, and travel is legible only if\n // you can see it happen.\n const wasPositioned = hasPositioned.current;\n hasPositioned.current = true;\n\n const target = revealTargetFor(scroller, card, snap);\n if (target !== null) {\n scroller.scrollTo({\n left: target,\n behavior: wasPositioned && !prefersReducedMotion() ? 'smooth' : 'instant',\n });\n }\n }, [selectedCard, scrollSelectedIntoView, snap, childCount, reorders]);\n\n const ruleTone = tone ?? derivedTone;\n const callerPaintsBackground =\n style?.background !== undefined || style?.backgroundImage !== undefined;\n\n const measure = useCallback(() => {\n const scroller = scrollerRef.current;\n if (!callerPaintsBackground) {\n paintRule(\n rootRef.current,\n scroller,\n getRadios().find((radio) => radioValue(radio) === value)?.parentElement,\n ruleTone,\n );\n }\n if (!scroller) {\n return;\n }\n const { scrollWidth, clientWidth, scrollLeft } = scroller;\n const overflows = scrollWidth > clientWidth + EDGE_EPSILON_PX;\n const next = {\n canScrollLeft: overflows && scrollLeft > EDGE_EPSILON_PX,\n canScrollRight: overflows && scrollLeft + clientWidth < scrollWidth - EDGE_EPSILON_PX,\n };\n setEdges((previous) =>\n previous.canScrollLeft === next.canScrollLeft &&\n previous.canScrollRight === next.canScrollRight\n ? previous\n : next,\n );\n }, [callerPaintsBackground, getRadios, ruleTone, value]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: adding, removing or reordering cards moves the selected card, so re-measure on the order\n useLayoutEffect(() => {\n measure();\n\n const scroller = scrollerRef.current;\n if (!scroller) {\n return;\n }\n\n const selectedInView = (): boolean => {\n const card = selectedCard();\n return card !== null && isCardInView(scroller, card);\n };\n let wasInView = selectedInView();\n\n const onScroll = (): void => {\n measure();\n wasInView = selectedInView();\n };\n scroller.addEventListener('scroll', onScroll, { passive: true });\n\n // Bring back a selected card the resize cut off, unless the reader scrolled it away.\n // Next frame rather than in the callback, whose layout reads would force a layout.\n let frame = 0;\n const reveal = (): void => {\n const card = selectedCard();\n const canReveal =\n scrollSelectedIntoView && wasInView && typeof scroller.scrollTo === 'function';\n const target = canReveal && card ? revealTargetFor(scroller, card, snap) : null;\n if (target !== null) {\n scroller.scrollTo({ left: target, behavior: 'instant' });\n }\n wasInView = selectedInView();\n };\n const onResize = (): void => {\n measure();\n if (typeof requestAnimationFrame !== 'function') {\n reveal();\n return;\n }\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(reveal);\n };\n const observer =\n typeof ResizeObserver !== 'undefined' ? new ResizeObserver(onResize) : null;\n observer?.observe(scroller);\n\n return () => {\n scroller.removeEventListener('scroll', onScroll);\n observer?.disconnect();\n if (typeof cancelAnimationFrame === 'function') {\n cancelAnimationFrame(frame);\n }\n };\n }, [measure, selectedCard, scrollSelectedIntoView, snap, childCount, reorders]);\n\n const scrollByStep = useCallback(\n (direction: -1 | 1) => {\n const scroller = scrollerRef.current;\n if (!scroller || typeof scroller.scrollBy !== 'function') {\n return;\n }\n scroller.scrollBy({\n left: pageTargetFor(scroller, direction, snap) - scroller.scrollLeft,\n behavior: prefersReducedMotion() ? 'auto' : 'smooth',\n });\n },\n [snap],\n );\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n if (!NAV_KEYS.has(event.key)) {\n return;\n }\n\n const target = event.target;\n if (!(target instanceof Element) || !target.closest(RADIO_SELECTOR)) {\n return;\n }\n\n const enabled = getNavigableRadios().filter((radio) => !isRadioDisabled(radio));\n if (enabled.length === 0) {\n return;\n }\n\n event.preventDefault();\n\n const currentPosition = enabled.findIndex((radio) => radioValue(radio) === value);\n let nextPosition: number;\n if (event.key === 'Home') {\n nextPosition = 0;\n } else if (event.key === 'End') {\n nextPosition = enabled.length - 1;\n } else {\n const delta = event.key === 'ArrowLeft' || event.key === 'ArrowUp' ? -1 : 1;\n const from = currentPosition === -1 ? 0 : currentPosition;\n nextPosition = (from + delta + enabled.length) % enabled.length;\n }\n\n const next = enabled[nextPosition];\n const nextValue = radioValue(next);\n if (nextValue !== null && nextValue !== value) {\n onChange(nextValue);\n }\n next.focus();\n },\n [getNavigableRadios, onChange, value],\n );\n\n const handleClick = useCallback(\n (event: React.MouseEvent) => {\n const target = event.target;\n if (!(target instanceof Element) || target.closest(`.${ADD_TILE_CLASS}`)) {\n return;\n }\n const root = rootRef.current;\n if (!root) {\n return;\n }\n const radio = radioForClick(target, root);\n if (!radio || isRadioDisabled(radio)) {\n return;\n }\n const clicked = radioValue(radio);\n if (clicked !== null && clicked !== value) {\n onChange(clicked);\n }\n },\n [onChange, value],\n );\n\n const showLeftArrow = arrows && edges.canScrollLeft;\n const showRightArrow = arrows && edges.canScrollRight;\n\n const fadeLeft = fadeEdges && edges.canScrollLeft;\n const fadeRight = fadeEdges && edges.canScrollRight;\n\n return (\n <div\n ref={rootRef}\n onKeyDown={handleKeyDown}\n onFocus={trackAddTileFocus}\n onBlur={trackAddTileFocus}\n onClick={handleClick}\n className={`${ROOT_CLASS} flex items-stretch ${className}`}\n style={{\n ...RULE_ROW_STYLE,\n ...(bleed ? BLEED_STYLE : null),\n ...style,\n }}\n >\n <div\n role=\"radiogroup\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n className={hasCards ? 'flex items-stretch min-w-0' : 'flex items-stretch'}\n >\n {leading ? (\n <CardStripSlotContext.Provider value={PINNED_SLOT}>\n <div ref={pinnedRef} className=\"shrink-0\" style={PINNED_SLOT_STYLE}>\n {leading}\n </div>\n </CardStripSlotContext.Provider>\n ) : null}\n {hasCards ? (\n <div\n {...{ [TROUGH_ATTR]: '' }}\n className=\"relative min-w-0 shrink basis-auto\"\n style={TROUGH_STYLE}\n >\n <div\n ref={scrollerRef}\n tabIndex={-1}\n {...{ [SCROLLER_ATTR]: '' }}\n data-snap={snap ? 'true' : 'false'}\n className={SCROLLER_CLASS}\n >\n <CardStripSlotContext.Provider value={SCROLLED_SLOT}>\n {cards}\n </CardStripSlotContext.Provider>\n {addInTrack ? (\n <div ref={addHostRef} {...{ [ADD_SLOT_ATTR]: '' }}>\n {addTiles}\n </div>\n ) : null}\n </div>\n {showLeftArrow ? (\n <ArrowButton side=\"left\" onClick={() => scrollByStep(-1)} />\n ) : null}\n {showRightArrow ? (\n <ArrowButton side=\"right\" onClick={() => scrollByStep(1)} />\n ) : null}\n {fadeLeft || fadeRight ? (\n <div\n aria-hidden\n data-card-strip-fade=\"\"\n className={`${FADE_CLASS} absolute pointer-events-none`}\n style={{\n inset: `-${TROUGH_SHOULDER_PX}px -${FADE_OVERHANG_PX}px 0`,\n background: `linear-gradient(to right, ${fadeLeft ? FADE_COLOR : 'transparent'} 0, transparent ${FADE_RAMP_PX}px, transparent calc(100% - ${FADE_RAMP_PX}px), ${fadeRight ? FADE_COLOR : 'transparent'} 100%)`,\n }}\n />\n ) : null}\n </div>\n ) : null}\n </div>\n {addTiles.length > 0 && !addInTrack ? (\n <div\n ref={addHostRef}\n className=\"shrink-0\"\n style={!hasCards && leading ? EMPTY_ADD_SLOT_STYLE : undefined}\n >\n {addTiles}\n </div>\n ) : null}\n </div>\n );\n};\n\nCardStrip.AddTile = AddTile;\n\nexport default CardStrip;\nexport { CardStrip };\n"],"names":["NAV_KEYS","RADIO_SELECTOR","SCROLLER_ATTR","TROUGH_ATTR","ROOT_CLASS","SCROLLER_CLASS","FADE_CLASS","ADD_TILE_CLASS","ADD_SLOT_ATTR","ACTIONS_SELECTOR","BESIDE_RADIO_SELECTOR","FOCUSABLE_SELECTOR","PREV_TABINDEX_ATTR","FADE_WIDTH_PX","ARROW_GLYPH_PX","GUTTER_REST_PX","LIP_SHADOW","ARROW_BACKGROUND","ARROW_BORDER","TROUGH_BACKGROUND","CARD_CORNER","CARD_RADIUS","TROUGH_CORNER","TROUGH_RADIUS","TROUGH_SHOULDER_PX","ARROW_OFFSET_PX","FADE_OVERHANG_PX","FADE_RAMP_PX","SLOT_WIDTH_PX","CARD_WIDTH_PX","TROUGH_FLOOR_PX","DIVIDER_COLOR","FADE_COLOR","ADD_HOVER_BACKGROUND","ADD_PILL_BORDER","ADD_PILL_BORDER_ACTIVE","ADD_PILL_SHADOW_DEEP","ADD_PILL_BACKGROUND","ADD_PILL_FOREGROUND","ADD_PILL_PRIMARY_BACKGROUND","ADD_PILL_PRIMARY_FOREGROUND","ADD_PILL_PRIMARY_SHADOW","BOTTOM_RULE_WEIGHT_PX","STRIP_RULE_PX","OVERFLOW_INSET_PX","CLIP_SLACK_PX","FOCUS_RING_REACH_PX","SCROLLER_CSS","CARD_FLARE_PX","STYLE_ID","ADD_SLOT_STYLE_ID","ADD_SLOT_CSS","prefersReducedMotion","radioValue","radio","isRadioDisabled","cardFor","card","actionsFor","_a","radioForClick","target","root","direct","node","radios","cardControls","actions","besideRadio","suppressTabStop","element","restoreTabStop","previous","TONE_RULE","RULE_ROW_STYLE","RULE_GAP_OVERLAP_PX","FLARE_TIP_INSET_PX","CARD_FLARE_TIP_PX","flareTips","flare","FLARE_CLASS","left","right","paintRule","scroller","tone","colour","rootRect","rootLeft","clip","cardRect","tips","tipLeft","tipRight","from","to","BLEED_STYLE","CONTENT_INSET","TROUGH_SURFACE","TROUGH_STYLE","ADD_TILE_SHAPE_STYLE","PINNED_SLOT_STYLE","EMPTY_ADD_SLOT_STYLE","ADD_PILL_FILL","AddTile","label","icon","onClick","disabled","emphasis","className","jsx","jsxs","FOCUS_RING","PlusIcon","isAddTile","React","hidesAddTile","child","ArrowButton","side","ArrowLeftIcon","ForwardArrowIcon","CardStrip","value","onChange","children","leading","arrows","fadeEdges","snap","bleed","scrollSelectedIntoView","ariaLabel","ariaLabelledBy","style","rootRef","useRef","pinnedRef","scrollerRef","hasPositioned","edges","setEdges","useState","cramped","setCramped","addHostRef","addTileFocused","useAdoptedStyleSheet","childList","cards","wrappedAddTile","hasCards","addTiles","tile","childCount","addTileCount","useLayoutEffect","pinnedSlot","paddingLeft","paddingRight","inset","content","pinned","decide","needs","observer","entries","entry","_b","addInTrack","trackAddTileFocus","useCallback","event","focused","host","active","getRadios","radioOrder","reorders","setReorders","next","count","getNavigableRadios","warned","warnOnce","key","message","derivedTone","setDerivedTone","selectedCard","navigable","tabStop","holdsTabStop","nextTabIndex","control","values","current","selectionSettled","checked","selected","wasPositioned","revealTargetFor","ruleTone","callerPaintsBackground","measure","scrollWidth","clientWidth","scrollLeft","overflows","EDGE_EPSILON_PX","selectedInView","isCardInView","wasInView","onScroll","frame","reveal","onResize","scrollByStep","direction","pageTargetFor","handleKeyDown","enabled","currentPosition","nextPosition","delta","nextValue","handleClick","clicked","showLeftArrow","showRightArrow","fadeLeft","fadeRight","CardStripSlotContext","PINNED_SLOT","SCROLLED_SLOT"],"mappings":"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"}
1
+ {"version":3,"file":"CardStrip.js","sources":["../../src/components/CardStrip.tsx"],"sourcesContent":["import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';\nimport ArrowLeftIcon from './CustomIcons/ArrowLeftIcon';\nimport ForwardArrowIcon from './CustomIcons/ForwardArrowIcon';\nimport PlusIcon from './CustomIcons/PlusIcon';\nimport { CONTENT_INSET } from './PageLayout/constants';\nimport {\n CARD_FLARE_PX,\n CARD_FLARE_TIP_PX,\n CARD_WIDTH_PX,\n FLARE_CLASS,\n STRIP_RULE_PX,\n} from './SelectableCard';\nimport { EDGE_EPSILON_PX, isCardInView, pageTargetFor, revealTargetFor } from './cardStripGeometry';\nimport { createReaderPlace } from './cardStripReader';\nimport { CardStripSlotContext, PINNED_SLOT, SCROLLED_SLOT } from './cardStripSlot';\nimport { FOCUS_RING } from './focusRing';\nimport { useAdoptedStyleSheet } from './useAdoptedStyleSheet';\n\nexport type CardStripAddTileEmphasis = 'primary' | 'secondary';\n\nexport interface CardStripAddTileProps {\n /** Visible label, e.g. \"Add Profile\". Also names the button for assistive tech. */\n label: string;\n /** Defaults to a plus. */\n icon?: React.ReactNode;\n onClick: () => void;\n disabled?: boolean;\n /** Leave unset: CardStrip picks `primary` while the strip is empty and `secondary` once cards exist. */\n emphasis?: CardStripAddTileEmphasis;\n className?: string;\n}\n\n/** Palette for the strip's rule. Pass the tone of whatever card is selected. */\nexport type CardStripTone = 'profile' | 'library' | 'editing';\n\nexport interface CardStripProps {\n /** The selected card's value, matched against each radio child's `data-value`. */\n value: string;\n /** Overrides the palette the strip reads off the selected card. See `CardStripTone`. */\n tone?: CardStripTone;\n onChange: (value: string) => void;\n /** Cards. Any element with `role=\"radio\"` and a `data-value` joins the group; an\n * add tile must be a direct child or it scrolls with the cards. */\n children: React.ReactNode;\n /** Pinned, non-scrolling slot before the cards (the Library card in the profile picker).\n * Scrolls with the cards when the strip has no room for one beside it. */\n leading?: React.ReactNode;\n /** Circular scroll affordances, shown only on an edge that can actually scroll. */\n arrows?: boolean;\n /** Fade the cards out at any edge they scroll past. */\n fadeEdges?: boolean;\n /** Snap cards to the leading edge as the strip scrolls. */\n snap?: boolean;\n /** Run the bottom rule the full width of the page card while the cards stay on the gutter.\n * Only has an effect inside a layout that publishes its inset (`PageLayout`). */\n bleed?: boolean;\n /** Whether a selection change scrolls the selected card into view. The strip's opening\n * selection is placed instantly; later ones slide, unless motion is reduced. Set `false`\n * when the value comes from a URL, or a deep-linked strip below the fold jumps the page\n * on load. */\n scrollSelectedIntoView?: boolean;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n className?: string;\n style?: React.CSSProperties;\n}\n\n/** Arrow keys move AND select, per the WAI-ARIA radiogroup pattern. */\nconst NAV_KEYS = new Set(['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End']);\n\nconst RADIO_SELECTOR = '[role=\"radio\"]';\nconst SCROLLER_ATTR = 'data-card-strip-scroller';\nconst TROUGH_ATTR = 'data-card-strip-trough';\nconst ROOT_CLASS = 'malachite-card-strip';\nconst SCROLLER_CLASS = 'malachite-card-strip-scroller';\nconst FADE_CLASS = 'malachite-card-strip-fade';\nconst ADD_TILE_CLASS = 'malachite-card-strip-add-tile';\nconst ADD_SLOT_ATTR = 'data-card-strip-add-slot';\n\nconst ACTIONS_SELECTOR = '[data-selectable-card-actions]';\nconst BESIDE_RADIO_SELECTOR = '[data-selectable-card-link], [data-selectable-card-address]';\nconst FOCUSABLE_SELECTOR =\n 'a[href], area[href], button, input, select, textarea, iframe, [contenteditable=\"true\"], [tabindex]';\nconst PREV_TABINDEX_ATTR = 'data-card-strip-prev-tabindex';\n\nconst FADE_WIDTH_PX = 80;\n\nconst ARROW_GLYPH_PX = 11;\nconst GUTTER_REST_PX = 12;\n\nconst LIP_SHADOW = 'var(--malachite-shadow-default)';\nconst ARROW_BACKGROUND =\n 'var(--malachite-card-strip-arrow-background, var(--malachite-card-background, #ffffff))';\nconst ARROW_BORDER = 'var(--malachite-card-strip-arrow-border, #e8e8e8)';\nconst TROUGH_BACKGROUND = 'var(--malachite-card-strip-background, #f1f1f1)';\nconst CARD_CORNER = 'var(--malachite-radius-xxl, 14px)';\nconst CARD_RADIUS = `${CARD_CORNER} ${CARD_CORNER} 0 0`;\nconst TROUGH_CORNER = 'var(--malachite-radius-xxxl, 16px)';\nconst TROUGH_RADIUS = `${TROUGH_CORNER} ${TROUGH_CORNER} 0 0`;\nconst TROUGH_SHOULDER_PX = 4;\n// Fills the gap between the edge card and its neighbour, covering neither.\nconst ARROW_OFFSET_PX = GUTTER_REST_PX + TROUGH_SHOULDER_PX;\n// Any wider and the fade paints over the neighbouring slot.\nconst FADE_OVERHANG_PX = GUTTER_REST_PX;\nconst FADE_RAMP_PX = FADE_WIDTH_PX + (FADE_OVERHANG_PX - TROUGH_SHOULDER_PX);\nconst SLOT_WIDTH_PX = CARD_WIDTH_PX;\nconst TROUGH_FLOOR_PX = 2 * (GUTTER_REST_PX + TROUGH_SHOULDER_PX) + SLOT_WIDTH_PX;\nconst DIVIDER_COLOR = 'var(--malachite-card-strip-divider, #dddddd)';\nconst FADE_COLOR = 'var(--malachite-card-strip-fade, transparent)';\nconst ADD_HOVER_BACKGROUND = 'var(--malachite-selectable-card-hover-background, #e8e8e8)';\nconst ADD_PILL_BORDER = 'var(--malachite-card-strip-arrow-border, #e8e8e8)';\nconst ADD_PILL_BORDER_ACTIVE = 'var(--malachite-selectable-card-hover-rule, #bbbbbb)';\nconst ADD_PILL_SHADOW_DEEP = '0px 1px 0px 0px rgba(0, 0, 0, 0.4)';\nconst ADD_PILL_BACKGROUND = 'var(--malachite-card-background, #ffffff)';\nconst ADD_PILL_FOREGROUND = 'var(--malachite-text-primary, #0a0a0a)';\nconst ADD_PILL_PRIMARY_BACKGROUND =\n 'var(--malachite-card-strip-add-primary-background, var(--malachite-text-primary, #0a0a0a))';\nconst ADD_PILL_PRIMARY_FOREGROUND =\n 'var(--malachite-card-strip-add-primary-foreground, var(--malachite-card-background, #ffffff))';\nconst ADD_PILL_PRIMARY_SHADOW = '0px 1px 0px 0px rgba(10, 10, 10, 0.7)';\n\nconst BOTTOM_RULE_WEIGHT_PX = STRIP_RULE_PX;\n\nconst OVERFLOW_INSET_PX = 16;\n\nconst CLIP_SLACK_PX = 9999;\nconst FOCUS_RING_REACH_PX = 4;\n\n/** A sheet, not inline styles: these rules have to reach children this component does not render. */\nconst SCROLLER_CSS = `\n.${ROOT_CLASS} {\n clip-path: inset(-${CLIP_SLACK_PX}px -${CLIP_SLACK_PX}px 0);\n}\n.${ROOT_CLASS}:has(:focus-visible) {\n clip-path: inset(-${CLIP_SLACK_PX}px -${CLIP_SLACK_PX}px -${FOCUS_RING_REACH_PX}px);\n}\n.${SCROLLER_CLASS} {\n display: flex;\n flex-wrap: nowrap;\n overflow-x: auto;\n overflow-y: hidden;\n scroll-behavior: smooth;\n scrollbar-width: none;\n -ms-overflow-style: none;\n padding-block: ${OVERFLOW_INSET_PX}px;\n margin-block: -${OVERFLOW_INSET_PX}px;\n /* overflow-x clips both axes, so the end cards would lose a flare; the negative margin trades\n the padding back out and scroll-padding keeps the rest position at zero. */\n padding-inline: ${CARD_FLARE_PX}px;\n margin-inline: -${CARD_FLARE_PX}px;\n scroll-padding-inline: ${CARD_FLARE_PX}px;\n}\n/* A card focused at a faded edge has to stay readable, so the fade lifts entirely. */\n.${ROOT_CLASS}:has(:focus-visible) .${FADE_CLASS} {\n display: none;\n}\n.${SCROLLER_CLASS}::-webkit-scrollbar {\n display: none;\n}\n/* No bottom edge: the strip's rule is the baseline for the add tile as much as for a card. */\n.${ADD_TILE_CLASS} > * {\n background: ${TROUGH_BACKGROUND};\n border-radius: ${CARD_RADIUS};\n}\n.${ADD_TILE_CLASS}:hover:not(:has(button:disabled)) > * {\n background: ${ADD_HOVER_BACKGROUND};\n}\n.${ADD_TILE_CLASS} button:active:not(:disabled) {\n opacity: 0.8;\n}\n.${ADD_TILE_CLASS}:has(button:active) > * {\n background: ${TROUGH_BACKGROUND};\n}\n.${ADD_TILE_CLASS}[data-emphasis='secondary'] button {\n border-color: ${ADD_PILL_BORDER};\n box-shadow: ${LIP_SHADOW};\n}\n.${ADD_TILE_CLASS}[data-emphasis='secondary']:hover:not(:has(button:disabled)) button {\n border-color: ${ADD_PILL_BORDER_ACTIVE};\n box-shadow: ${ADD_PILL_SHADOW_DEEP};\n}\n.${ADD_TILE_CLASS}[data-emphasis='secondary'] button:active:not(:disabled) {\n border-color: ${ADD_PILL_BORDER_ACTIVE};\n box-shadow: ${ADD_PILL_SHADOW_DEEP};\n}\n.${ADD_TILE_CLASS}[data-emphasis='primary'] button {\n border-color: transparent;\n box-shadow: ${ADD_PILL_PRIMARY_SHADOW};\n}\n.${SCROLLER_CLASS} > * {\n flex: 0 0 auto;\n min-width: 0;\n}\n.${SCROLLER_CLASS} > * + * {\n border-left: 1px solid ${DIVIDER_COLOR};\n}\n.${SCROLLER_CLASS}[data-snap='true'] {\n scroll-snap-type: x proximity;\n}\n.${SCROLLER_CLASS}[data-snap='true'] > * {\n scroll-snap-align: start;\n}\n@media (prefers-reduced-motion: reduce) {\n .${SCROLLER_CLASS} {\n scroll-behavior: auto;\n }\n .${SCROLLER_CLASS}[data-snap='true'] {\n scroll-snap-type: none;\n }\n .${SCROLLER_CLASS}[data-snap='true'] > * {\n scroll-snap-align: none;\n }\n}`;\n\nconst STYLE_ID = 'malachite-card-strip-style';\n// Its own id: an older malachite copy on the page may already own STYLE_ID without these rules.\nconst ADD_SLOT_STYLE_ID = 'malachite-card-strip-add-slot';\n// In the track the trough is already the tile's shoulder, and it is no card to rule off.\nconst ADD_SLOT_CSS = `\n.${SCROLLER_CLASS} > [${ADD_SLOT_ATTR}] {\n display: flex;\n border-left: none;\n}\n[${ADD_SLOT_ATTR}] > .${ADD_TILE_CLASS} {\n height: 100% !important;\n padding: 0 !important;\n}\n`;\n\nconst READER_INPUT_EVENTS = ['wheel', 'touchstart', 'pointerdown', 'keydown'] as const;\n\ntype Crowding = 'roomy' | 'cramped' | 'unpinned';\n\n/** `window.matchMedia` is absent under SSR and in older test environments. */\nconst prefersReducedMotion = (): boolean => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return true;\n }\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n};\n\nconst radioValue = (radio: HTMLElement): string | null =>\n radio.dataset.value ?? radio.getAttribute('value');\n\nconst isRadioDisabled = (radio: HTMLElement): boolean =>\n radio.hasAttribute('disabled') || radio.getAttribute('aria-disabled') === 'true';\n\nconst cardFor = (radio: HTMLElement): HTMLElement | null => {\n const card = radio.parentElement;\n return card && card.querySelectorAll(RADIO_SELECTOR).length === 1 ? card : null;\n};\n\nconst actionsFor = (radio: HTMLElement): HTMLElement | null =>\n cardFor(radio)?.querySelector<HTMLElement>(ACTIONS_SELECTOR) ?? null;\n\n/** A card is wider than its radio, so `closest` misses clicks on the count row. */\nconst radioForClick = (target: Element, root: Element): HTMLElement | null => {\n const direct = target.closest<HTMLElement>(RADIO_SELECTOR);\n if (direct) {\n return direct;\n }\n if (target.closest(ACTIONS_SELECTOR) || target.closest(BESIDE_RADIO_SELECTOR)) {\n return null;\n }\n let node: Element | null = target;\n while (node && root.contains(node)) {\n const radios: NodeListOf<HTMLElement> = node.querySelectorAll(RADIO_SELECTOR);\n if (radios.length === 1 && radios[0].parentElement === node) {\n return radios[0];\n }\n node = node.parentElement;\n }\n return null;\n};\n\n// The card's own controls beside its radio, which only the card holding the tab stop keeps.\nconst cardControls = (radio: HTMLElement): HTMLElement[] => {\n const actions = actionsFor(radio);\n const besideRadio = cardFor(radio)?.querySelectorAll<HTMLElement>(BESIDE_RADIO_SELECTOR);\n return [\n ...(actions ? Array.from(actions.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)) : []),\n ...(besideRadio ? Array.from(besideRadio) : []),\n ];\n};\n\nconst suppressTabStop = (element: HTMLElement): void => {\n if (!element.hasAttribute(PREV_TABINDEX_ATTR)) {\n element.setAttribute(PREV_TABINDEX_ATTR, element.getAttribute('tabindex') ?? '');\n }\n element.setAttribute('tabindex', '-1');\n};\n\nconst restoreTabStop = (element: HTMLElement): void => {\n if (!element.hasAttribute(PREV_TABINDEX_ATTR)) {\n return;\n }\n const previous = element.getAttribute(PREV_TABINDEX_ATTR);\n element.removeAttribute(PREV_TABINDEX_ATTR);\n if (previous) {\n element.setAttribute('tabindex', previous);\n } else {\n element.removeAttribute('tabindex');\n }\n};\n\nconst TONE_RULE: Record<CardStripTone, string> = {\n profile: 'var(--malachite-selectable-card-selected-border, #047b43)',\n library: 'var(--malachite-selectable-card-library-selected-border, #bbbbbb)',\n editing: 'var(--malachite-selectable-card-pending-foreground, #96580a)',\n};\nconst RULE_ROW_STYLE: React.CSSProperties = {\n backgroundRepeat: 'no-repeat',\n backgroundPosition: 'left bottom',\n backgroundSize: `100% ${BOTTOM_RULE_WEIGHT_PX}px`,\n};\n\n/** The rule runs under the flare's tip rather than butting against it, so antialiasing\n * cannot open the junction into a gap. */\nconst RULE_GAP_OVERLAP_PX = 0.5;\n\nconst FLARE_TIP_INSET_PX = CARD_FLARE_PX - CARD_FLARE_TIP_PX;\n\nconst flareTips = (card: HTMLElement): { left: number; right: number } | null => {\n const flare = card.querySelector(`:scope > .${FLARE_CLASS}`);\n if (!flare) {\n return null;\n }\n const { left, right } = flare.getBoundingClientRect();\n return { left: left + FLARE_TIP_INSET_PX, right: right - FLARE_TIP_INSET_PX };\n};\n\n/** The rule is cut where the selected card stands, never covered: the strip's clip lands on the\n * rule's own edge, so a cover takes the same partial alpha and the rule bleeds through it. */\nconst paintRule = (\n root: HTMLElement | null,\n scroller: HTMLElement | null,\n card: HTMLElement | null | undefined,\n tone: CardStripTone,\n): void => {\n if (!root) {\n return;\n }\n const colour = TONE_RULE[tone];\n const rootRect = root.getBoundingClientRect();\n const rootLeft = rootRect.left;\n const clip = card && scroller?.contains(card) ? scroller.getBoundingClientRect() : rootRect;\n const cardRect = card?.getBoundingClientRect();\n const tips = card ? flareTips(card) : null;\n const tipLeft = tips?.left ?? (cardRect ? cardRect.left - CARD_FLARE_TIP_PX : 0);\n const tipRight = tips?.right ?? (cardRect ? cardRect.right + CARD_FLARE_TIP_PX : 0);\n const from = cardRect ? Math.max(tipLeft, clip.left) + RULE_GAP_OVERLAP_PX - rootLeft : 0;\n const to = cardRect ? Math.min(tipRight, clip.right) - RULE_GAP_OVERLAP_PX - rootLeft : 0;\n\n root.style.backgroundImage =\n to > from\n ? `linear-gradient(to right, ${colour} 0 ${from}px, transparent ${from}px ${to}px, ${colour} ${to}px 100%)`\n : `linear-gradient(to right, ${colour} 0 100%)`;\n};\n\nconst BLEED_STYLE: React.CSSProperties = {\n marginInline: `calc(${CONTENT_INSET} * -1)`,\n paddingInline: CONTENT_INSET,\n};\n\n/** The rule's row is a margin, never padding: a background paints under its own padding, which\n * would hide the very line the troughs stand on. */\nconst TROUGH_SURFACE: React.CSSProperties = {\n background: TROUGH_BACKGROUND,\n borderRadius: TROUGH_RADIUS,\n paddingTop: `${TROUGH_SHOULDER_PX}px`,\n paddingInline: `${TROUGH_SHOULDER_PX}px`,\n marginBottom: `${BOTTOM_RULE_WEIGHT_PX}px`,\n};\nconst TROUGH_STYLE: React.CSSProperties = {\n ...TROUGH_SURFACE,\n marginInline: `${GUTTER_REST_PX}px`,\n};\nconst ADD_TILE_SHAPE_STYLE: React.CSSProperties = TROUGH_SURFACE;\n\nconst PINNED_SLOT_STYLE: React.CSSProperties = TROUGH_SURFACE;\n\nconst EMPTY_ADD_SLOT_STYLE: React.CSSProperties = {\n marginInlineStart: `${GUTTER_REST_PX}px`,\n};\n\nconst ADD_PILL_FILL: Record<CardStripAddTileEmphasis, React.CSSProperties> = {\n primary: {\n backgroundColor: ADD_PILL_PRIMARY_BACKGROUND,\n color: ADD_PILL_PRIMARY_FOREGROUND,\n },\n secondary: { backgroundColor: ADD_PILL_BACKGROUND, color: ADD_PILL_FOREGROUND },\n};\n\nconst AddTile: React.FC<CardStripAddTileProps> = ({\n label,\n icon,\n onClick,\n disabled = false,\n emphasis = 'secondary',\n className = '',\n}) => (\n <div\n className={ADD_TILE_CLASS}\n data-emphasis={emphasis}\n style={{\n ...ADD_TILE_SHAPE_STYLE,\n width: `${SLOT_WIDTH_PX}px`,\n height: `calc(100% - ${BOTTOM_RULE_WEIGHT_PX}px)`,\n }}\n >\n <div className=\"flex h-full items-center justify-center px-4\">\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n style={ADD_PILL_FILL[emphasis]}\n className={`${FOCUS_RING} inline-flex items-center justify-center gap-1.5 h-[var(--malachite-control-height-medium,32px)] w-[108px] px-2.5 rounded-xl border border-solid text-[length:var(--malachite-font-size-small,13px)] font-medium whitespace-nowrap ${\n disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'\n } ${className}`}\n >\n <span className=\"inline-flex h-[13px] w-[13px] shrink-0 items-center justify-center\">\n {icon ?? <PlusIcon size={13} />}\n </span>\n {label}\n </button>\n </div>\n </div>\n);\n\nconst isAddTile = (node: React.ReactNode): node is React.ReactElement<CardStripAddTileProps> =>\n React.isValidElement(node) && node.type === AddTile;\n\nconst hidesAddTile = (child: React.ReactNode): boolean =>\n React.isValidElement<{ children?: React.ReactNode }>(child) &&\n child.type === React.Fragment &&\n React.Children.toArray(child.props.children).some(isAddTile);\n\ninterface ArrowButtonProps {\n side: 'left' | 'right';\n onClick: () => void;\n}\n\nconst ArrowButton: React.FC<ArrowButtonProps> = ({ side, onClick }) => (\n <button\n type=\"button\"\n tabIndex={-1}\n aria-hidden=\"true\"\n aria-label={side === 'left' ? 'Scroll left' : 'Scroll right'}\n onClick={onClick}\n className=\"absolute top-1/2 -translate-y-1/2 z-10 inline-flex items-center justify-center w-5 h-5 rounded-lg cursor-pointer text-[var(--malachite-text-secondary)]\"\n style={{\n [side]: `-${ARROW_OFFSET_PX}px`,\n background: ARROW_BACKGROUND,\n border: `1px solid ${ARROW_BORDER}`,\n boxShadow: LIP_SHADOW,\n }}\n >\n {side === 'left' ? (\n <ArrowLeftIcon size={ARROW_GLYPH_PX} />\n ) : (\n <ForwardArrowIcon size={ARROW_GLYPH_PX} />\n )}\n </button>\n);\n\n/** A horizontally scrolling radiogroup of cards — the \"pick one of these\" strip. */\nconst CardStrip: React.FC<CardStripProps> & { AddTile: React.FC<CardStripAddTileProps> } = ({\n value,\n tone,\n onChange,\n children,\n leading,\n arrows = true,\n fadeEdges = true,\n snap = true,\n bleed = false,\n scrollSelectedIntoView = true,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n className = '',\n style,\n}) => {\n const rootRef = useRef<HTMLDivElement>(null);\n const pinnedRef = useRef<HTMLDivElement>(null);\n const scrollerRef = useRef<HTMLDivElement>(null);\n const [edges, setEdges] = useState({ canScrollLeft: false, canScrollRight: false });\n const [crowding, setCrowding] = useState<Crowding>('roomy');\n // Cached: in the track the slot can't be measured, and its width there flips the decision.\n const leadingWidth = useRef({ pinned: 0, inTrack: 0 });\n const addHostRef = useRef<HTMLDivElement>(null);\n const leadingHostRef = useRef<HTMLDivElement>(null);\n const movedFocus = useRef<{ slot: 'add' | 'leading'; index: number } | null>(null);\n const [reader] = useState(createReaderPlace);\n\n useAdoptedStyleSheet(rootRef, SCROLLER_CSS, STYLE_ID);\n useAdoptedStyleSheet(rootRef, ADD_SLOT_CSS, ADD_SLOT_STYLE_ID);\n\n const childList = React.Children.toArray(children);\n const cards = childList.filter((child) => !isAddTile(child));\n const wrappedAddTile = cards.some(hidesAddTile);\n const hasCards = cards.length > 0;\n\n const addTiles = childList\n .filter(isAddTile)\n .map((tile) =>\n tile.props.emphasis === undefined\n ? React.cloneElement(tile, { emphasis: hasCards ? 'secondary' : 'primary' })\n : tile,\n );\n\n const childCount = childList.length;\n const addTileCount = addTiles.length;\n const hasLeading = Boolean(leading);\n const leadingInTrack = crowding === 'unpinned' && hasCards && hasLeading;\n const addInTrack = crowding !== 'roomy' && hasCards;\n\n // With no room for a card beside the pinned slots, the add tile and then `leading` scroll too.\n // biome-ignore lint/correctness/useExhaustiveDependencies: a new pinned slot needs observing\n useLayoutEffect(() => {\n const root = rootRef.current;\n if (!root || !hasCards || (addTileCount === 0 && !hasLeading)) {\n setCrowding('roomy');\n return;\n }\n const pinnedSlot = pinnedRef.current;\n if (!hasLeading) {\n leadingWidth.current.pinned = 0;\n } else if (pinnedSlot) {\n leadingWidth.current.pinned = pinnedSlot.offsetWidth;\n }\n const { paddingLeft, paddingRight } = getComputedStyle(root);\n const inset = (parseFloat(paddingLeft) || 0) + (parseFloat(paddingRight) || 0);\n let content = root.clientWidth - inset;\n const decide = (): void => {\n const { pinned } = leadingWidth.current;\n if (content <= 0) {\n setCrowding('roomy');\n } else if (hasLeading && content < pinned + TROUGH_FLOOR_PX) {\n setCrowding('unpinned');\n } else {\n const needs = pinned + addTileCount * SLOT_WIDTH_PX + TROUGH_FLOOR_PX;\n setCrowding(content < needs ? 'cramped' : 'roomy');\n }\n };\n decide();\n // Sizes from the entries: reading layout here would force the layout the observer spares.\n const observer =\n typeof ResizeObserver !== 'undefined'\n ? new ResizeObserver((entries) => {\n for (const entry of entries ?? []) {\n if (entry.target === root) {\n content = entry.contentRect.width;\n } else {\n leadingWidth.current.pinned =\n entry.borderBoxSize?.[0]?.inlineSize ??\n entry.contentRect.width + 2 * TROUGH_SHOULDER_PX;\n }\n }\n decide();\n })\n : null;\n observer?.observe(root);\n if (pinnedSlot) {\n observer?.observe(pinnedSlot);\n }\n return () => observer?.disconnect();\n }, [hasCards, addTileCount, hasLeading, leadingInTrack]);\n\n const trackMovingFocus = useCallback((event: React.FocusEvent) => {\n const focused = event.type === 'focus' ? event.target : event.relatedTarget;\n const slots = [\n { slot: 'add', host: addHostRef.current },\n { slot: 'leading', host: pinnedRef.current },\n { slot: 'leading', host: leadingHostRef.current },\n ] as const;\n const holder =\n focused instanceof HTMLElement\n ? slots.find(({ host }) => host?.contains(focused))\n : undefined;\n movedFocus.current = holder?.host\n ? {\n slot: holder.slot,\n index: Array.from(holder.host.querySelectorAll(FOCUSABLE_SELECTOR)).indexOf(\n focused as HTMLElement,\n ),\n }\n : null;\n }, []);\n\n // A moved slot remounts, so focus has to follow it there. Removing a focused element fires\n // no blur, so the record survives the move.\n // biome-ignore lint/correctness/useExhaustiveDependencies: a slot moving is the signal\n useLayoutEffect(() => {\n const moved = movedFocus.current;\n if (!moved) {\n return;\n }\n const host =\n moved.slot === 'add'\n ? addHostRef.current\n : leadingInTrack\n ? leadingHostRef.current\n : pinnedRef.current;\n const active = host ? (host.getRootNode() as Document | ShadowRoot).activeElement : null;\n if (!host || (active && host.contains(active))) {\n return;\n }\n const target =\n host.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)[moved.index] ??\n host.querySelector<HTMLElement>(moved.slot === 'add' ? 'button' : RADIO_SELECTOR);\n target?.focus({ preventScroll: true });\n }, [addInTrack, leadingInTrack]);\n\n const getRadios = useCallback((): HTMLElement[] => {\n const root = rootRef.current;\n if (!root) {\n return [];\n }\n return Array.from(root.querySelectorAll<HTMLElement>(RADIO_SELECTOR));\n }, []);\n\n // Read off the DOM rather than the children's keys: unkeyed cards reorder under positional\n // keys that never change. Attribute reads only, so this forces no layout.\n const radioOrder = useRef<string | null>(null);\n const [reorders, setReorders] = useState(0);\n useLayoutEffect(() => {\n const next = JSON.stringify(getRadios().map(radioValue));\n if (radioOrder.current !== null && radioOrder.current !== next) {\n setReorders((count) => count + 1);\n }\n radioOrder.current = next;\n });\n\n const getNavigableRadios = useCallback(\n (): HTMLElement[] => getRadios().filter((radio) => radioValue(radio) !== null),\n [getRadios],\n );\n\n const warned = useRef(new Set<string>());\n const warnOnce = useCallback((key: string, message: string): void => {\n if (warned.current.has(key)) {\n return;\n }\n warned.current.add(key);\n console.warn(message);\n }, []);\n\n const [derivedTone, setDerivedTone] = useState<CardStripTone>('profile');\n\n useLayoutEffect(() => {\n const selectedCard = getRadios()\n .find((radio) => radioValue(radio) === value)\n ?.parentElement?.getAttribute('data-variant');\n if (\n selectedCard === 'profile' ||\n selectedCard === 'library' ||\n selectedCard === 'editing'\n ) {\n setDerivedTone(selectedCard);\n }\n }, [getRadios, value]);\n\n useLayoutEffect(() => {\n const radios = getRadios();\n const navigable = getNavigableRadios();\n const selected = navigable.find((radio) => radioValue(radio) === value);\n const tabStop = selected ?? navigable.find((radio) => !isRadioDisabled(radio)) ?? null;\n\n radios.forEach((radio) => {\n const holdsTabStop = radio === tabStop;\n const nextTabIndex = holdsTabStop ? 0 : -1;\n if (radio.tabIndex !== nextTabIndex) {\n radio.tabIndex = nextTabIndex;\n }\n for (const control of cardControls(radio)) {\n if (holdsTabStop) {\n restoreTabStop(control);\n } else {\n suppressTabStop(control);\n }\n }\n });\n\n if (process.env.NODE_ENV === 'production') {\n return;\n }\n\n if (navigable.length !== radios.length) {\n warnOnce(\n 'missing-value',\n 'CardStrip: a child with role=\"radio\" has no `data-value`. It cannot be selected or reached by arrow keys, so it is excluded from the radio set. Give every card a `data-value`.',\n );\n }\n\n const values = new Set<string>();\n for (const radio of navigable) {\n const current = radioValue(radio);\n if (current === null) {\n continue;\n }\n if (values.has(current)) {\n warnOnce(\n `duplicate:${current}`,\n `CardStrip: two cards share the data-value \"${current}\". Both will read as checked and only the first is selectable — data-value must be unique.`,\n );\n }\n values.add(current);\n }\n\n const selectionSettled = value !== '';\n\n if (radios.length > 0 && selectionSettled && !values.has(value)) {\n warnOnce(\n `unmatched:${value}`,\n `CardStrip: value \"${value}\" matches no card's data-value, so the group reports no selection.`,\n );\n }\n\n const checked = radios.filter(\n (radio) => radio.getAttribute('aria-checked') === 'true',\n ).length;\n if (radios.length > 0 && selectionSettled && checked !== 1) {\n warnOnce(\n `checked:${checked}`,\n `CardStrip: ${checked} cards are aria-checked, but a radiogroup has exactly one. Drive each card's checked state from the strip's \\`value\\`.`,\n );\n }\n\n if (wrappedAddTile) {\n warnOnce(\n 'wrapped-add-tile',\n 'CardStrip: a CardStrip.AddTile inside a fragment is treated as a card and scrolls with them. The add tile must be a direct child of CardStrip.',\n );\n }\n });\n\n const selectedCard = useCallback((): HTMLElement | null => {\n const selected = getRadios().find((radio) => radioValue(radio) === value);\n return selected ? ((selected.closest('[role=\"radio\"]') ?? selected) as HTMLElement) : null;\n }, [getRadios, value]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: cards arriving is the signal\n useLayoutEffect(() => {\n if (!scrollSelectedIntoView) {\n return;\n }\n const scroller = scrollerRef.current;\n // jsdom implements neither, and a strip that cannot scroll has nothing to bring into view.\n if (!scroller || typeof scroller.scrollTo !== 'function') {\n return;\n }\n\n // Shift from the reader's last position, not the current one: re-snapping may already\n // have moved the scroll.\n const slotsMoved = reader.leadingMoved(leadingInTrack);\n // A card picked in the same render as the move is chased however far the reader scrolled.\n const picked = reader.newSelection(value);\n if (slotsMoved) {\n const host = leadingHostRef.current;\n const next = host?.nextElementSibling;\n if (leadingInTrack && host && next) {\n leadingWidth.current.inTrack =\n next.getBoundingClientRect().left - host.getBoundingClientRect().left;\n }\n const { inTrack } = leadingWidth.current;\n const shift = leadingInTrack ? inTrack : -inTrack;\n const kept = reader.keptFor(shift);\n if (shift !== 0 && kept !== scroller.scrollLeft) {\n scroller.scrollTo({ left: kept, behavior: 'instant' });\n }\n if (!picked && !reader.selectionShowing()) {\n return;\n }\n }\n\n const card = selectedCard();\n if (!card) {\n return;\n }\n\n // The first placement is where the strip opens — a deep link, not a move — so it is\n // instant. Every later one is the selection travelling, and travel is legible only if\n // you can see it happen.\n const opening = reader.firstPlacement();\n\n const target = revealTargetFor(scroller, card, snap);\n if (target !== null) {\n const smooth = !opening && !slotsMoved && !prefersReducedMotion();\n scroller.scrollTo({ left: target, behavior: smooth ? 'smooth' : 'instant' });\n reader.revealing(Math.min(target, scroller.scrollWidth - scroller.clientWidth), smooth);\n }\n }, [\n reader,\n value,\n selectedCard,\n scrollSelectedIntoView,\n snap,\n childCount,\n reorders,\n leadingInTrack,\n ]);\n\n const ruleTone = tone ?? derivedTone;\n const callerPaintsBackground =\n style?.background !== undefined || style?.backgroundImage !== undefined;\n\n const measure = useCallback(() => {\n const scroller = scrollerRef.current;\n if (!callerPaintsBackground) {\n paintRule(\n rootRef.current,\n scroller,\n getRadios().find((radio) => radioValue(radio) === value)?.parentElement,\n ruleTone,\n );\n }\n if (!scroller) {\n return;\n }\n const { scrollWidth, clientWidth, scrollLeft } = scroller;\n const overflows = scrollWidth > clientWidth + EDGE_EPSILON_PX;\n const next = {\n canScrollLeft: overflows && scrollLeft > EDGE_EPSILON_PX,\n canScrollRight: overflows && scrollLeft + clientWidth < scrollWidth - EDGE_EPSILON_PX,\n };\n setEdges((previous) =>\n previous.canScrollLeft === next.canScrollLeft &&\n previous.canScrollRight === next.canScrollRight\n ? previous\n : next,\n );\n }, [callerPaintsBackground, getRadios, ruleTone, value]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: adding, removing or reordering cards moves the selected card, so re-measure on the order\n useLayoutEffect(() => {\n measure();\n\n const scroller = scrollerRef.current;\n if (!scroller) {\n return;\n }\n\n const selectedInView = (): boolean => {\n const card = selectedCard();\n return (\n card !== null &&\n (isCardInView(scroller, card) || Boolean(pinnedRef.current?.contains(card)))\n );\n };\n const settle = (): void => reader.settle(scroller.scrollLeft, selectedInView());\n if (!reader.travelling()) {\n settle();\n }\n\n const onScroll = (): void => {\n measure();\n reader.scrolled(scroller.scrollLeft, selectedInView);\n };\n const onReaderInput = (): void => reader.tookOver();\n const listening = new AbortController();\n const listenerOptions = { passive: true, signal: listening.signal };\n scroller.addEventListener('scroll', onScroll, listenerOptions);\n for (const type of READER_INPUT_EVENTS) {\n scroller.addEventListener(type, onReaderInput, listenerOptions);\n }\n\n // Bring back a selected card the resize cut off, unless the reader scrolled it away.\n // Next frame rather than in the callback, whose layout reads would force a layout.\n let frame = 0;\n const reveal = (): void => {\n const card = selectedCard();\n const canReveal =\n scrollSelectedIntoView &&\n reader.selectionShowing() &&\n typeof scroller.scrollTo === 'function';\n const target = canReveal && card ? revealTargetFor(scroller, card, snap) : null;\n if (target !== null) {\n scroller.scrollTo({ left: target, behavior: 'instant' });\n }\n settle();\n };\n const onResize = (): void => {\n measure();\n if (typeof requestAnimationFrame !== 'function') {\n reveal();\n return;\n }\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(reveal);\n };\n const observer =\n typeof ResizeObserver !== 'undefined' ? new ResizeObserver(onResize) : null;\n observer?.observe(scroller);\n\n return () => {\n listening.abort();\n observer?.disconnect();\n if (typeof cancelAnimationFrame === 'function') {\n cancelAnimationFrame(frame);\n }\n };\n }, [\n measure,\n reader,\n selectedCard,\n scrollSelectedIntoView,\n snap,\n childCount,\n reorders,\n leadingInTrack,\n ]);\n\n const scrollByStep = useCallback(\n (direction: -1 | 1) => {\n const scroller = scrollerRef.current;\n if (!scroller || typeof scroller.scrollBy !== 'function') {\n return;\n }\n scroller.scrollBy({\n left: pageTargetFor(scroller, direction, snap) - scroller.scrollLeft,\n behavior: prefersReducedMotion() ? 'auto' : 'smooth',\n });\n },\n [snap],\n );\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n if (!NAV_KEYS.has(event.key)) {\n return;\n }\n\n const target = event.target;\n if (!(target instanceof Element) || !target.closest(RADIO_SELECTOR)) {\n return;\n }\n\n const enabled = getNavigableRadios().filter((radio) => !isRadioDisabled(radio));\n if (enabled.length === 0) {\n return;\n }\n\n event.preventDefault();\n\n const currentPosition = enabled.findIndex((radio) => radioValue(radio) === value);\n let nextPosition: number;\n if (event.key === 'Home') {\n nextPosition = 0;\n } else if (event.key === 'End') {\n nextPosition = enabled.length - 1;\n } else {\n const delta = event.key === 'ArrowLeft' || event.key === 'ArrowUp' ? -1 : 1;\n const from = currentPosition === -1 ? 0 : currentPosition;\n nextPosition = (from + delta + enabled.length) % enabled.length;\n }\n\n const next = enabled[nextPosition];\n const nextValue = radioValue(next);\n if (nextValue !== null && nextValue !== value) {\n onChange(nextValue);\n }\n next.focus();\n },\n [getNavigableRadios, onChange, value],\n );\n\n const handleClick = useCallback(\n (event: React.MouseEvent) => {\n const target = event.target;\n if (!(target instanceof Element) || target.closest(`.${ADD_TILE_CLASS}`)) {\n return;\n }\n const root = rootRef.current;\n if (!root) {\n return;\n }\n const radio = radioForClick(target, root);\n if (!radio || isRadioDisabled(radio)) {\n return;\n }\n const clicked = radioValue(radio);\n if (clicked !== null && clicked !== value) {\n onChange(clicked);\n }\n },\n [onChange, value],\n );\n\n const showLeftArrow = arrows && edges.canScrollLeft;\n const showRightArrow = arrows && edges.canScrollRight;\n\n const fadeLeft = fadeEdges && edges.canScrollLeft;\n const fadeRight = fadeEdges && edges.canScrollRight;\n\n return (\n <div\n ref={rootRef}\n onKeyDown={handleKeyDown}\n onFocus={trackMovingFocus}\n onBlur={trackMovingFocus}\n onClick={handleClick}\n className={`${ROOT_CLASS} flex items-stretch ${className}`}\n style={{\n ...RULE_ROW_STYLE,\n ...(bleed ? BLEED_STYLE : null),\n ...style,\n }}\n >\n <div\n role=\"radiogroup\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n className={hasCards ? 'flex items-stretch min-w-0' : 'flex items-stretch'}\n >\n {leading && !leadingInTrack ? (\n <CardStripSlotContext.Provider value={PINNED_SLOT}>\n <div ref={pinnedRef} className=\"shrink-0\" style={PINNED_SLOT_STYLE}>\n {leading}\n </div>\n </CardStripSlotContext.Provider>\n ) : null}\n {hasCards ? (\n <div\n {...{ [TROUGH_ATTR]: '' }}\n className=\"relative min-w-0 shrink basis-auto\"\n style={TROUGH_STYLE}\n >\n <div\n ref={scrollerRef}\n tabIndex={-1}\n {...{ [SCROLLER_ATTR]: '' }}\n data-snap={snap ? 'true' : 'false'}\n className={SCROLLER_CLASS}\n >\n <CardStripSlotContext.Provider value={SCROLLED_SLOT}>\n {leadingInTrack ? (\n <div ref={leadingHostRef} className=\"flex\">\n {leading}\n </div>\n ) : null}\n {cards}\n </CardStripSlotContext.Provider>\n {addInTrack ? (\n <div ref={addHostRef} {...{ [ADD_SLOT_ATTR]: '' }}>\n {addTiles}\n </div>\n ) : null}\n </div>\n {showLeftArrow ? (\n <ArrowButton side=\"left\" onClick={() => scrollByStep(-1)} />\n ) : null}\n {showRightArrow ? (\n <ArrowButton side=\"right\" onClick={() => scrollByStep(1)} />\n ) : null}\n {fadeLeft || fadeRight ? (\n <div\n aria-hidden\n data-card-strip-fade=\"\"\n className={`${FADE_CLASS} absolute pointer-events-none`}\n style={{\n inset: `-${TROUGH_SHOULDER_PX}px -${FADE_OVERHANG_PX}px 0`,\n background: `linear-gradient(to right, ${fadeLeft ? FADE_COLOR : 'transparent'} 0, transparent ${FADE_RAMP_PX}px, transparent calc(100% - ${FADE_RAMP_PX}px), ${fadeRight ? FADE_COLOR : 'transparent'} 100%)`,\n }}\n />\n ) : null}\n </div>\n ) : null}\n </div>\n {addTiles.length > 0 && !addInTrack ? (\n <div\n ref={addHostRef}\n className=\"shrink-0\"\n style={!hasCards && leading ? EMPTY_ADD_SLOT_STYLE : undefined}\n >\n {addTiles}\n </div>\n ) : null}\n </div>\n );\n};\n\nCardStrip.AddTile = AddTile;\n\nexport default CardStrip;\nexport { CardStrip };\n"],"names":["NAV_KEYS","RADIO_SELECTOR","SCROLLER_ATTR","TROUGH_ATTR","ROOT_CLASS","SCROLLER_CLASS","FADE_CLASS","ADD_TILE_CLASS","ADD_SLOT_ATTR","ACTIONS_SELECTOR","BESIDE_RADIO_SELECTOR","FOCUSABLE_SELECTOR","PREV_TABINDEX_ATTR","FADE_WIDTH_PX","ARROW_GLYPH_PX","GUTTER_REST_PX","LIP_SHADOW","ARROW_BACKGROUND","ARROW_BORDER","TROUGH_BACKGROUND","CARD_CORNER","CARD_RADIUS","TROUGH_CORNER","TROUGH_RADIUS","TROUGH_SHOULDER_PX","ARROW_OFFSET_PX","FADE_OVERHANG_PX","FADE_RAMP_PX","SLOT_WIDTH_PX","CARD_WIDTH_PX","TROUGH_FLOOR_PX","DIVIDER_COLOR","FADE_COLOR","ADD_HOVER_BACKGROUND","ADD_PILL_BORDER","ADD_PILL_BORDER_ACTIVE","ADD_PILL_SHADOW_DEEP","ADD_PILL_BACKGROUND","ADD_PILL_FOREGROUND","ADD_PILL_PRIMARY_BACKGROUND","ADD_PILL_PRIMARY_FOREGROUND","ADD_PILL_PRIMARY_SHADOW","BOTTOM_RULE_WEIGHT_PX","STRIP_RULE_PX","OVERFLOW_INSET_PX","CLIP_SLACK_PX","FOCUS_RING_REACH_PX","SCROLLER_CSS","CARD_FLARE_PX","STYLE_ID","ADD_SLOT_STYLE_ID","ADD_SLOT_CSS","READER_INPUT_EVENTS","prefersReducedMotion","radioValue","radio","isRadioDisabled","cardFor","card","actionsFor","_a","radioForClick","target","root","direct","node","radios","cardControls","actions","besideRadio","suppressTabStop","element","restoreTabStop","previous","TONE_RULE","RULE_ROW_STYLE","RULE_GAP_OVERLAP_PX","FLARE_TIP_INSET_PX","CARD_FLARE_TIP_PX","flareTips","flare","FLARE_CLASS","left","right","paintRule","scroller","tone","colour","rootRect","rootLeft","clip","cardRect","tips","tipLeft","tipRight","from","to","BLEED_STYLE","CONTENT_INSET","TROUGH_SURFACE","TROUGH_STYLE","ADD_TILE_SHAPE_STYLE","PINNED_SLOT_STYLE","EMPTY_ADD_SLOT_STYLE","ADD_PILL_FILL","AddTile","label","icon","onClick","disabled","emphasis","className","jsx","jsxs","FOCUS_RING","PlusIcon","isAddTile","React","hidesAddTile","child","ArrowButton","side","ArrowLeftIcon","ForwardArrowIcon","CardStrip","value","onChange","children","leading","arrows","fadeEdges","snap","bleed","scrollSelectedIntoView","ariaLabel","ariaLabelledBy","style","rootRef","useRef","pinnedRef","scrollerRef","edges","setEdges","useState","crowding","setCrowding","leadingWidth","addHostRef","leadingHostRef","movedFocus","reader","createReaderPlace","useAdoptedStyleSheet","childList","cards","wrappedAddTile","hasCards","addTiles","tile","childCount","addTileCount","hasLeading","leadingInTrack","addInTrack","useLayoutEffect","pinnedSlot","paddingLeft","paddingRight","inset","content","decide","pinned","needs","observer","entries","entry","_b","trackMovingFocus","useCallback","event","focused","slots","holder","host","moved","active","getRadios","radioOrder","reorders","setReorders","next","count","getNavigableRadios","warned","warnOnce","key","message","derivedTone","setDerivedTone","selectedCard","navigable","tabStop","holdsTabStop","nextTabIndex","control","values","current","selectionSettled","checked","selected","slotsMoved","picked","inTrack","shift","kept","opening","revealTargetFor","smooth","ruleTone","callerPaintsBackground","measure","scrollWidth","clientWidth","scrollLeft","overflows","EDGE_EPSILON_PX","selectedInView","isCardInView","settle","onScroll","onReaderInput","listening","listenerOptions","type","frame","reveal","onResize","scrollByStep","direction","pageTargetFor","handleKeyDown","enabled","currentPosition","nextPosition","delta","nextValue","handleClick","clicked","showLeftArrow","showRightArrow","fadeLeft","fadeRight","CardStripSlotContext","PINNED_SLOT","SCROLLED_SLOT"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { useState as w, useId as g } from "react";
3
3
  import { Alert as H } from "./Alert.js";
4
4
  import { Button as N } from "./Button.js";
5
5
  import Z from "./CustomIcons/CheckCircleFilled.js";
6
- import J from "./CustomIcons/CheckIcon.js";
6
+ import J from "./CustomIcons/CheckmarkIcon.js";
7
7
  import K from "./CustomIcons/CopyIcon.js";
8
8
  import Q from "./CustomIcons/ErrorCircleFilled.js";
9
9
  import U from "./CustomIcons/InfoCircleFilled.js";
@@ -86,7 +86,7 @@ const v = 12, C = (e) => e.replace(/\s+/g, " "), te = (e, i) => {
86
86
  warning: /* @__PURE__ */ r(X, { size: v }),
87
87
  destructive: /* @__PURE__ */ r(Q, { size: v }),
88
88
  success: /* @__PURE__ */ r(Z, { size: v })
89
- }, Fe = ({
89
+ }, ke = ({
90
90
  open: e,
91
91
  onClose: i,
92
92
  onConfirm: o,
@@ -110,9 +110,9 @@ const v = 12, C = (e) => e.replace(/\s+/g, " "), te = (e, i) => {
110
110
  "aria-labelledby": _,
111
111
  children: B
112
112
  }) => {
113
- const [z, A] = w(""), T = g(), h = re[l], [F, D] = w({ open: e, confirmPhrase: t });
114
- (F.open !== e || F.confirmPhrase !== t) && (D({ open: e, confirmPhrase: t }), e && A(""));
115
- const { parts: $, text: k } = le(
113
+ const [z, A] = w(""), T = g(), h = re[l], [k, D] = w({ open: e, confirmPhrase: t });
114
+ (k.open !== e || k.confirmPhrase !== t) && (D({ open: e, confirmPhrase: t }), e && A(""));
115
+ const { parts: $, text: F } = le(
116
116
  I,
117
117
  t
118
118
  ), {
@@ -129,7 +129,7 @@ const v = 12, C = (e) => e.replace(/\s+/g, " "), te = (e, i) => {
129
129
  onClick: o,
130
130
  disabled: W,
131
131
  loading: a,
132
- tooltip: !a && !L ? k : void 0,
132
+ tooltip: !a && !L ? F : void 0,
133
133
  children: b
134
134
  }
135
135
  )
@@ -185,7 +185,7 @@ const v = 12, C = (e) => e.replace(/\s+/g, " "), te = (e, i) => {
185
185
  {
186
186
  phrase: t,
187
187
  parts: $,
188
- plainLabel: k,
188
+ plainLabel: F,
189
189
  placeholder: y,
190
190
  value: z,
191
191
  onChange: A,
@@ -203,7 +203,7 @@ const v = 12, C = (e) => e.replace(/\s+/g, " "), te = (e, i) => {
203
203
  );
204
204
  };
205
205
  export {
206
- Fe as ConfirmationDialog,
207
- Fe as default
206
+ ke as ConfirmationDialog,
207
+ ke as default
208
208
  };
209
209
  //# sourceMappingURL=ConfirmationDialog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ConfirmationDialog.js","sources":["../../src/components/ConfirmationDialog.tsx"],"sourcesContent":["import type React from 'react';\nimport { useId, useState } from 'react';\nimport Alert, { type AlertType } from './Alert';\nimport Button from './Button';\nimport CheckCircleFilled from './CustomIcons/CheckCircleFilled';\nimport CheckIcon from './CustomIcons/CheckIcon';\nimport CopyIcon from './CustomIcons/CopyIcon';\nimport ErrorCircleFilled from './CustomIcons/ErrorCircleFilled';\nimport InfoCircleFilled from './CustomIcons/InfoCircleFilled';\nimport WarningCircleFilled from './CustomIcons/WarningCircleFilled';\nimport FieldMessage from './FieldMessage';\nimport { Input } from './Input';\nimport Modal, { type ModalSize } from './Modal';\nimport Typography from './Typography';\nimport { type CopyOutcome, useClipboardCopy } from './useClipboardCopy';\nimport {\n CONFIRMATION_DIALOG_VARIANTS,\n type ConfirmationDialogVariant,\n} from './confirmationDialogVariants';\n\n/** Kept beside the component: an icon is a node, not a value the variant table can hold. */\nconst BADGE_GLYPH_SIZE = 12;\n\nconst collapseSpaces = (value: string) => value.replace(/\\s+/g, ' ');\n\nconst nearMissAgainst = (typedPhrase: string, confirmPhrase?: string): string | undefined => {\n const typed = typedPhrase.trim();\n if (!confirmPhrase || typed === '' || typed === confirmPhrase) {\n return undefined;\n }\n if (collapseSpaces(typed).toLowerCase() !== collapseSpaces(confirmPhrase).toLowerCase()) {\n return undefined;\n }\n return collapseSpaces(typed) === collapseSpaces(confirmPhrase)\n ? 'The spacing has to match exactly.'\n : 'This is case-sensitive.';\n};\n\ntype PhraseLabel = string | { prefix: string; suffix?: string };\n\nconst COPY_FAILED_MESSAGE = 'Could not copy. Enter the phrase manually.';\n\nconst resolvePhraseLabel = (\n label: PhraseLabel | undefined,\n phrase?: string,\n): { parts?: { prefix: string; suffix?: string }; text: string } => {\n const parts = typeof label === 'object' && label !== null ? label : undefined;\n if (parts) {\n return { parts, text: [parts.prefix, phrase, parts.suffix].filter(Boolean).join(' ') };\n }\n return { text: typeof label === 'string' ? label : `Type ${phrase} to confirm` };\n};\n\nconst copyStatusFor = (outcome: CopyOutcome, phrase: string): string =>\n outcome === 'copied' ? `${phrase} copied to clipboard` : '';\n\nconst usePhraseCopy = (\n phrase: string | undefined,\n open: boolean,\n disabled: boolean,\n onCopied?: (phrase: string) => void,\n): { outcome: CopyOutcome; canCopy: boolean; copy: () => void } => {\n const { outcome, canCopy, copy } = useClipboardCopy(`${open}|${phrase}`);\n\n const run = async () => {\n if (phrase && (await copy(phrase))) {\n queueMicrotask(() => onCopied?.(phrase));\n }\n };\n\n return { outcome, canCopy: canCopy && !disabled, copy: () => void run() };\n};\n\nconst LabelText: React.FC<{ children: React.ReactNode }> = ({ children }) => (\n <Typography.Text size=\"xsmall\" color=\"primary\" fontWeight=\"semi-bold\">\n {children}\n </Typography.Text>\n);\n\ninterface PhraseFieldProps {\n phrase: string;\n parts?: { prefix: string; suffix?: string };\n plainLabel: string;\n placeholder?: string;\n value: string;\n onChange: (value: string) => void;\n disabled: boolean;\n copyLabel: string;\n copyOutcome: CopyOutcome;\n canCopy: boolean;\n onCopy: () => void;\n nearMissMessage?: string;\n}\n\nconst PhraseField: React.FC<PhraseFieldProps> = ({\n phrase,\n parts,\n plainLabel,\n placeholder,\n value,\n onChange,\n disabled,\n copyLabel,\n copyOutcome,\n canCopy,\n onCopy,\n nearMissMessage,\n}) => {\n const beforeId = useId();\n const afterId = useId();\n const messageId = useId();\n const message =\n [nearMissMessage, copyOutcome === 'failed' ? COPY_FAILED_MESSAGE : undefined]\n .filter(Boolean)\n .join(' ') || undefined;\n const labelledBy = parts\n ? [beforeId, parts.suffix ? afterId : undefined].filter(Boolean).join(' ')\n : undefined;\n\n return (\n <div className=\"flex flex-col gap-[6px]\">\n {parts ? (\n <div className=\"flex flex-wrap items-center gap-[6px] px-2\">\n <span id={beforeId}>\n <LabelText>{parts.prefix}</LabelText>{' '}\n <Typography.CodeText variant=\"inline\" size=\"small\">\n {phrase}\n </Typography.CodeText>\n </span>\n <Button\n variant=\"ghost\"\n size=\"small\"\n iconOnly\n aria-label={`${copyLabel} ${phrase}`}\n icon={\n copyOutcome === 'copied' ? (\n <CheckIcon size={13} />\n ) : (\n <CopyIcon size={13} />\n )\n }\n onClick={onCopy}\n disabled={!canCopy}\n />\n {parts.suffix ? (\n <span id={afterId}>\n <LabelText>{parts.suffix}</LabelText>\n </span>\n ) : null}\n </div>\n ) : null}\n <Input\n name=\"confirmation-phrase\"\n size=\"large\"\n value={value}\n onChange={onChange}\n label={parts ? undefined : plainLabel}\n aria-labelledby={labelledBy}\n placeholder={placeholder}\n requiredIndicator=\"none\"\n error={!!nearMissMessage}\n aria-describedby={message ? messageId : undefined}\n disabled={disabled}\n />\n <FieldMessage id={messageId} message={message} />\n {parts ? (\n <span role=\"status\" className=\"sr-only\">\n {copyStatusFor(copyOutcome, phrase)}\n </span>\n ) : null}\n </div>\n );\n};\n\nconst VARIANT_ICON: Record<ConfirmationDialogVariant, React.ReactNode> = {\n default: <InfoCircleFilled size={BADGE_GLYPH_SIZE} />,\n warning: <WarningCircleFilled size={BADGE_GLYPH_SIZE} />,\n destructive: <ErrorCircleFilled size={BADGE_GLYPH_SIZE} />,\n success: <CheckCircleFilled size={BADGE_GLYPH_SIZE} />,\n};\n\nexport interface ConfirmationDialogProps {\n open: boolean;\n /** Dismiss handler — cancel, close button, ESC or scrim. `loading` suppresses every\n * route except ESC, which always reaches this so a keyboard user is never stranded. */\n onClose: () => void;\n /** Runs when the confirm action is taken. Closing is left to the consumer. */\n onConfirm: () => void;\n title: React.ReactNode;\n titleAdornment?: React.ReactNode;\n badgeIcon?: React.ReactNode;\n /** The sentence saying what is about to happen. */\n description?: React.ReactNode;\n variant?: ConfirmationDialogVariant;\n /** Coloured callout below the body. Defaults to the variant's own type. */\n callout?: { type?: AlertType; message: string };\n /**\n * Requires this exact phrase to be typed before the confirm action enables — for a\n * consequence severe enough that a single click should not be able to cause it.\n */\n confirmPhrase?: string;\n /** Label above the type-to-confirm field. `{ prefix, suffix }` makes the phrase copyable. */\n confirmPhraseLabel?: PhraseLabel;\n /** Names the copy control, e.g. `Copy profile name`. Default: `Copy`. */\n confirmPhraseCopyLabel?: string;\n /** Runs after the phrase reaches the clipboard, with what was copied. */\n onPhraseCopied?: (phrase: string) => void;\n confirmPhrasePlaceholder?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n /**\n * Disables the actions, spins the confirm button and suppresses the pointer dismissal\n * routes — but never ESC, which always reaches `onClose`.\n */\n loading?: boolean;\n tip?: { label?: string; message: string };\n size?: ModalSize;\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 /** Extra body content between the description and the callout, e.g. an entity chip. */\n children?: React.ReactNode;\n}\n\nconst ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({\n open,\n onClose,\n onConfirm,\n title,\n titleAdornment,\n badgeIcon,\n description,\n variant = 'default',\n callout,\n confirmPhrase,\n confirmPhraseLabel,\n confirmPhraseCopyLabel = 'Copy',\n onPhraseCopied,\n confirmPhrasePlaceholder,\n confirmLabel = 'Confirm',\n cancelLabel = 'Cancel',\n loading = false,\n tip,\n size = 'medium',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n children,\n}) => {\n const [typedPhrase, setTypedPhrase] = useState('');\n const descriptionId = useId();\n const base = CONFIRMATION_DIALOG_VARIANTS[variant];\n\n // On open, not close: the dialog outlives `open` by its exit transition, so resetting on\n // close empties the field in front of the user. Adjusted during render rather than from an\n // effect, or a reopen paints the previous answer once with confirm already enabled.\n const [answeredFor, setAnsweredFor] = useState({ open, confirmPhrase });\n if (answeredFor.open !== open || answeredFor.confirmPhrase !== confirmPhrase) {\n setAnsweredFor({ open, confirmPhrase });\n if (open) {\n setTypedPhrase('');\n }\n }\n\n const { parts: phraseParts, text: phraseLabel } = resolvePhraseLabel(\n confirmPhraseLabel,\n confirmPhrase,\n );\n const {\n outcome: copyOutcome,\n canCopy: canCopyPhrase,\n copy: handleCopyPhrase,\n } = usePhraseCopy(confirmPhrase, open, loading, onPhraseCopied);\n // Trimmed because copying an entity name commonly picks up a trailing space; the gate is\n // still exact, and deliberately case-sensitive.\n const phraseSatisfied = !confirmPhrase || typedPhrase.trim() === confirmPhrase;\n const nearMissMessage = nearMissAgainst(typedPhrase, confirmPhrase);\n const confirmDisabled = loading || !phraseSatisfied;\n\n const footer = (\n <div className=\"flex w-full flex-wrap items-center justify-end gap-[8px]\">\n <Button variant=\"outline\" size=\"small\" onClick={onClose} disabled={loading}>\n {cancelLabel}\n </Button>\n <Button\n variant={base.confirmVariant}\n size=\"small\"\n onClick={onConfirm}\n disabled={confirmDisabled}\n loading={loading}\n // A disabled control emits no hover, so the tooltip explains the gate from the\n // wrapper Button renders for exactly this case.\n tooltip={!loading && !phraseSatisfied ? phraseLabel : undefined}\n >\n {confirmLabel}\n </Button>\n </div>\n );\n\n return (\n <Modal\n open={open}\n onClose={onClose}\n title={title}\n size={size}\n footer={footer}\n busy={loading}\n // A consequential choice is an alertdialog: it asks assistive tech to announce the\n // body, which is where the consequence is written.\n role={variant === 'destructive' ? 'alertdialog' : 'dialog'}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={description ? descriptionId : undefined}\n // A choice this deliberate should not be undone by a stray click, and a scrim\n // click must not discard a phrase the customer has already typed.\n closeOnOverlayClick={variant !== 'destructive' && !confirmPhrase}\n headerDivider\n titleAdornment={titleAdornment}\n bodyPadding={false}\n tray={{ tone: base.tray, caption: tip }}\n icon={\n <span\n aria-hidden=\"true\"\n className=\"confirmation-badge flex size-[18px] shrink-0 items-center justify-center rounded-[7px] text-[var(--malachite-card-background)]\"\n style={{ backgroundColor: base.badge }}\n >\n {badgeIcon ?? VARIANT_ICON[variant]}\n </span>\n }\n >\n {callout ? (\n <div className=\"border-0 border-b border-solid border-[var(--malachite-card-subtle-divider,var(--malachite-card-border))] p-[10px]\">\n <Alert\n type={callout.type ?? base.calloutType}\n message={callout.message}\n hasMargin={false}\n />\n </div>\n ) : null}\n <div className=\"flex flex-col gap-[12px] p-[28px]\">\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 {children}\n {confirmPhrase ? (\n <PhraseField\n phrase={confirmPhrase}\n parts={phraseParts}\n plainLabel={phraseLabel}\n placeholder={confirmPhrasePlaceholder}\n value={typedPhrase}\n onChange={setTypedPhrase}\n disabled={loading}\n copyLabel={confirmPhraseCopyLabel}\n copyOutcome={copyOutcome}\n canCopy={canCopyPhrase}\n onCopy={handleCopyPhrase}\n nearMissMessage={nearMissMessage}\n />\n ) : null}\n </div>\n </Modal>\n );\n};\n\nexport default ConfirmationDialog;\nexport { ConfirmationDialog };\nexport type { ConfirmationDialogVariant };\n"],"names":["BADGE_GLYPH_SIZE","collapseSpaces","value","nearMissAgainst","typedPhrase","confirmPhrase","typed","COPY_FAILED_MESSAGE","resolvePhraseLabel","label","phrase","parts","copyStatusFor","outcome","usePhraseCopy","open","disabled","onCopied","canCopy","copy","useClipboardCopy","run","LabelText","children","jsx","Typography","PhraseField","plainLabel","placeholder","onChange","copyLabel","copyOutcome","onCopy","nearMissMessage","beforeId","useId","afterId","messageId","message","labelledBy","jsxs","Button","CheckIcon","CopyIcon","Input","FieldMessage","VARIANT_ICON","InfoCircleFilled","WarningCircleFilled","ErrorCircleFilled","CheckCircleFilled","ConfirmationDialog","onClose","onConfirm","title","titleAdornment","badgeIcon","description","variant","callout","confirmPhraseLabel","confirmPhraseCopyLabel","onPhraseCopied","confirmPhrasePlaceholder","confirmLabel","cancelLabel","loading","tip","size","ariaLabel","ariaLabelledBy","setTypedPhrase","useState","descriptionId","base","CONFIRMATION_DIALOG_VARIANTS","answeredFor","setAnsweredFor","phraseParts","phraseLabel","canCopyPhrase","handleCopyPhrase","phraseSatisfied","confirmDisabled","footer","Modal","Alert"],"mappings":"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"}
1
+ {"version":3,"file":"ConfirmationDialog.js","sources":["../../src/components/ConfirmationDialog.tsx"],"sourcesContent":["import type React from 'react';\nimport { useId, useState } from 'react';\nimport Alert, { type AlertType } from './Alert';\nimport Button from './Button';\nimport CheckCircleFilled from './CustomIcons/CheckCircleFilled';\nimport CheckmarkIcon from './CustomIcons/CheckmarkIcon';\nimport CopyIcon from './CustomIcons/CopyIcon';\nimport ErrorCircleFilled from './CustomIcons/ErrorCircleFilled';\nimport InfoCircleFilled from './CustomIcons/InfoCircleFilled';\nimport WarningCircleFilled from './CustomIcons/WarningCircleFilled';\nimport FieldMessage from './FieldMessage';\nimport { Input } from './Input';\nimport Modal, { type ModalSize } from './Modal';\nimport Typography from './Typography';\nimport { type CopyOutcome, useClipboardCopy } from './useClipboardCopy';\nimport {\n CONFIRMATION_DIALOG_VARIANTS,\n type ConfirmationDialogVariant,\n} from './confirmationDialogVariants';\n\n/** Kept beside the component: an icon is a node, not a value the variant table can hold. */\nconst BADGE_GLYPH_SIZE = 12;\n\nconst collapseSpaces = (value: string) => value.replace(/\\s+/g, ' ');\n\nconst nearMissAgainst = (typedPhrase: string, confirmPhrase?: string): string | undefined => {\n const typed = typedPhrase.trim();\n if (!confirmPhrase || typed === '' || typed === confirmPhrase) {\n return undefined;\n }\n if (collapseSpaces(typed).toLowerCase() !== collapseSpaces(confirmPhrase).toLowerCase()) {\n return undefined;\n }\n return collapseSpaces(typed) === collapseSpaces(confirmPhrase)\n ? 'The spacing has to match exactly.'\n : 'This is case-sensitive.';\n};\n\ntype PhraseLabel = string | { prefix: string; suffix?: string };\n\nconst COPY_FAILED_MESSAGE = 'Could not copy. Enter the phrase manually.';\n\nconst resolvePhraseLabel = (\n label: PhraseLabel | undefined,\n phrase?: string,\n): { parts?: { prefix: string; suffix?: string }; text: string } => {\n const parts = typeof label === 'object' && label !== null ? label : undefined;\n if (parts) {\n return { parts, text: [parts.prefix, phrase, parts.suffix].filter(Boolean).join(' ') };\n }\n return { text: typeof label === 'string' ? label : `Type ${phrase} to confirm` };\n};\n\nconst copyStatusFor = (outcome: CopyOutcome, phrase: string): string =>\n outcome === 'copied' ? `${phrase} copied to clipboard` : '';\n\nconst usePhraseCopy = (\n phrase: string | undefined,\n open: boolean,\n disabled: boolean,\n onCopied?: (phrase: string) => void,\n): { outcome: CopyOutcome; canCopy: boolean; copy: () => void } => {\n const { outcome, canCopy, copy } = useClipboardCopy(`${open}|${phrase}`);\n\n const run = async () => {\n if (phrase && (await copy(phrase))) {\n queueMicrotask(() => onCopied?.(phrase));\n }\n };\n\n return { outcome, canCopy: canCopy && !disabled, copy: () => void run() };\n};\n\nconst LabelText: React.FC<{ children: React.ReactNode }> = ({ children }) => (\n <Typography.Text size=\"xsmall\" color=\"primary\" fontWeight=\"semi-bold\">\n {children}\n </Typography.Text>\n);\n\ninterface PhraseFieldProps {\n phrase: string;\n parts?: { prefix: string; suffix?: string };\n plainLabel: string;\n placeholder?: string;\n value: string;\n onChange: (value: string) => void;\n disabled: boolean;\n copyLabel: string;\n copyOutcome: CopyOutcome;\n canCopy: boolean;\n onCopy: () => void;\n nearMissMessage?: string;\n}\n\nconst PhraseField: React.FC<PhraseFieldProps> = ({\n phrase,\n parts,\n plainLabel,\n placeholder,\n value,\n onChange,\n disabled,\n copyLabel,\n copyOutcome,\n canCopy,\n onCopy,\n nearMissMessage,\n}) => {\n const beforeId = useId();\n const afterId = useId();\n const messageId = useId();\n const message =\n [nearMissMessage, copyOutcome === 'failed' ? COPY_FAILED_MESSAGE : undefined]\n .filter(Boolean)\n .join(' ') || undefined;\n const labelledBy = parts\n ? [beforeId, parts.suffix ? afterId : undefined].filter(Boolean).join(' ')\n : undefined;\n\n return (\n <div className=\"flex flex-col gap-[6px]\">\n {parts ? (\n <div className=\"flex flex-wrap items-center gap-[6px] px-2\">\n <span id={beforeId}>\n <LabelText>{parts.prefix}</LabelText>{' '}\n <Typography.CodeText variant=\"inline\" size=\"small\">\n {phrase}\n </Typography.CodeText>\n </span>\n <Button\n variant=\"ghost\"\n size=\"small\"\n iconOnly\n aria-label={`${copyLabel} ${phrase}`}\n icon={\n copyOutcome === 'copied' ? (\n <CheckmarkIcon size={13} />\n ) : (\n <CopyIcon size={13} />\n )\n }\n onClick={onCopy}\n disabled={!canCopy}\n />\n {parts.suffix ? (\n <span id={afterId}>\n <LabelText>{parts.suffix}</LabelText>\n </span>\n ) : null}\n </div>\n ) : null}\n <Input\n name=\"confirmation-phrase\"\n size=\"large\"\n value={value}\n onChange={onChange}\n label={parts ? undefined : plainLabel}\n aria-labelledby={labelledBy}\n placeholder={placeholder}\n requiredIndicator=\"none\"\n error={!!nearMissMessage}\n aria-describedby={message ? messageId : undefined}\n disabled={disabled}\n />\n <FieldMessage id={messageId} message={message} />\n {parts ? (\n <span role=\"status\" className=\"sr-only\">\n {copyStatusFor(copyOutcome, phrase)}\n </span>\n ) : null}\n </div>\n );\n};\n\nconst VARIANT_ICON: Record<ConfirmationDialogVariant, React.ReactNode> = {\n default: <InfoCircleFilled size={BADGE_GLYPH_SIZE} />,\n warning: <WarningCircleFilled size={BADGE_GLYPH_SIZE} />,\n destructive: <ErrorCircleFilled size={BADGE_GLYPH_SIZE} />,\n success: <CheckCircleFilled size={BADGE_GLYPH_SIZE} />,\n};\n\nexport interface ConfirmationDialogProps {\n open: boolean;\n /** Dismiss handler — cancel, close button, ESC or scrim. `loading` suppresses every\n * route except ESC, which always reaches this so a keyboard user is never stranded. */\n onClose: () => void;\n /** Runs when the confirm action is taken. Closing is left to the consumer. */\n onConfirm: () => void;\n title: React.ReactNode;\n titleAdornment?: React.ReactNode;\n badgeIcon?: React.ReactNode;\n /** The sentence saying what is about to happen. */\n description?: React.ReactNode;\n variant?: ConfirmationDialogVariant;\n /** Coloured callout below the body. Defaults to the variant's own type. */\n callout?: { type?: AlertType; message: string };\n /**\n * Requires this exact phrase to be typed before the confirm action enables — for a\n * consequence severe enough that a single click should not be able to cause it.\n */\n confirmPhrase?: string;\n /** Label above the type-to-confirm field. `{ prefix, suffix }` makes the phrase copyable. */\n confirmPhraseLabel?: PhraseLabel;\n /** Names the copy control, e.g. `Copy profile name`. Default: `Copy`. */\n confirmPhraseCopyLabel?: string;\n /** Runs after the phrase reaches the clipboard, with what was copied. */\n onPhraseCopied?: (phrase: string) => void;\n confirmPhrasePlaceholder?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n /**\n * Disables the actions, spins the confirm button and suppresses the pointer dismissal\n * routes — but never ESC, which always reaches `onClose`.\n */\n loading?: boolean;\n tip?: { label?: string; message: string };\n size?: ModalSize;\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 /** Extra body content between the description and the callout, e.g. an entity chip. */\n children?: React.ReactNode;\n}\n\nconst ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({\n open,\n onClose,\n onConfirm,\n title,\n titleAdornment,\n badgeIcon,\n description,\n variant = 'default',\n callout,\n confirmPhrase,\n confirmPhraseLabel,\n confirmPhraseCopyLabel = 'Copy',\n onPhraseCopied,\n confirmPhrasePlaceholder,\n confirmLabel = 'Confirm',\n cancelLabel = 'Cancel',\n loading = false,\n tip,\n size = 'medium',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n children,\n}) => {\n const [typedPhrase, setTypedPhrase] = useState('');\n const descriptionId = useId();\n const base = CONFIRMATION_DIALOG_VARIANTS[variant];\n\n // On open, not close: the dialog outlives `open` by its exit transition, so resetting on\n // close empties the field in front of the user. Adjusted during render rather than from an\n // effect, or a reopen paints the previous answer once with confirm already enabled.\n const [answeredFor, setAnsweredFor] = useState({ open, confirmPhrase });\n if (answeredFor.open !== open || answeredFor.confirmPhrase !== confirmPhrase) {\n setAnsweredFor({ open, confirmPhrase });\n if (open) {\n setTypedPhrase('');\n }\n }\n\n const { parts: phraseParts, text: phraseLabel } = resolvePhraseLabel(\n confirmPhraseLabel,\n confirmPhrase,\n );\n const {\n outcome: copyOutcome,\n canCopy: canCopyPhrase,\n copy: handleCopyPhrase,\n } = usePhraseCopy(confirmPhrase, open, loading, onPhraseCopied);\n // Trimmed because copying an entity name commonly picks up a trailing space; the gate is\n // still exact, and deliberately case-sensitive.\n const phraseSatisfied = !confirmPhrase || typedPhrase.trim() === confirmPhrase;\n const nearMissMessage = nearMissAgainst(typedPhrase, confirmPhrase);\n const confirmDisabled = loading || !phraseSatisfied;\n\n const footer = (\n <div className=\"flex w-full flex-wrap items-center justify-end gap-[8px]\">\n <Button variant=\"outline\" size=\"small\" onClick={onClose} disabled={loading}>\n {cancelLabel}\n </Button>\n <Button\n variant={base.confirmVariant}\n size=\"small\"\n onClick={onConfirm}\n disabled={confirmDisabled}\n loading={loading}\n // A disabled control emits no hover, so the tooltip explains the gate from the\n // wrapper Button renders for exactly this case.\n tooltip={!loading && !phraseSatisfied ? phraseLabel : undefined}\n >\n {confirmLabel}\n </Button>\n </div>\n );\n\n return (\n <Modal\n open={open}\n onClose={onClose}\n title={title}\n size={size}\n footer={footer}\n busy={loading}\n // A consequential choice is an alertdialog: it asks assistive tech to announce the\n // body, which is where the consequence is written.\n role={variant === 'destructive' ? 'alertdialog' : 'dialog'}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={description ? descriptionId : undefined}\n // A choice this deliberate should not be undone by a stray click, and a scrim\n // click must not discard a phrase the customer has already typed.\n closeOnOverlayClick={variant !== 'destructive' && !confirmPhrase}\n headerDivider\n titleAdornment={titleAdornment}\n bodyPadding={false}\n tray={{ tone: base.tray, caption: tip }}\n icon={\n <span\n aria-hidden=\"true\"\n className=\"confirmation-badge flex size-[18px] shrink-0 items-center justify-center rounded-[7px] text-[var(--malachite-card-background)]\"\n style={{ backgroundColor: base.badge }}\n >\n {badgeIcon ?? VARIANT_ICON[variant]}\n </span>\n }\n >\n {callout ? (\n <div className=\"border-0 border-b border-solid border-[var(--malachite-card-subtle-divider,var(--malachite-card-border))] p-[10px]\">\n <Alert\n type={callout.type ?? base.calloutType}\n message={callout.message}\n hasMargin={false}\n />\n </div>\n ) : null}\n <div className=\"flex flex-col gap-[12px] p-[28px]\">\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 {children}\n {confirmPhrase ? (\n <PhraseField\n phrase={confirmPhrase}\n parts={phraseParts}\n plainLabel={phraseLabel}\n placeholder={confirmPhrasePlaceholder}\n value={typedPhrase}\n onChange={setTypedPhrase}\n disabled={loading}\n copyLabel={confirmPhraseCopyLabel}\n copyOutcome={copyOutcome}\n canCopy={canCopyPhrase}\n onCopy={handleCopyPhrase}\n nearMissMessage={nearMissMessage}\n />\n ) : null}\n </div>\n </Modal>\n );\n};\n\nexport default ConfirmationDialog;\nexport { ConfirmationDialog };\nexport type { ConfirmationDialogVariant };\n"],"names":["BADGE_GLYPH_SIZE","collapseSpaces","value","nearMissAgainst","typedPhrase","confirmPhrase","typed","COPY_FAILED_MESSAGE","resolvePhraseLabel","label","phrase","parts","copyStatusFor","outcome","usePhraseCopy","open","disabled","onCopied","canCopy","copy","useClipboardCopy","run","LabelText","children","jsx","Typography","PhraseField","plainLabel","placeholder","onChange","copyLabel","copyOutcome","onCopy","nearMissMessage","beforeId","useId","afterId","messageId","message","labelledBy","jsxs","Button","CheckmarkIcon","CopyIcon","Input","FieldMessage","VARIANT_ICON","InfoCircleFilled","WarningCircleFilled","ErrorCircleFilled","CheckCircleFilled","ConfirmationDialog","onClose","onConfirm","title","titleAdornment","badgeIcon","description","variant","callout","confirmPhraseLabel","confirmPhraseCopyLabel","onPhraseCopied","confirmPhrasePlaceholder","confirmLabel","cancelLabel","loading","tip","size","ariaLabel","ariaLabelledBy","setTypedPhrase","useState","descriptionId","base","CONFIRMATION_DIALOG_VARIANTS","answeredFor","setAnsweredFor","phraseParts","phraseLabel","canCopyPhrase","handleCopyPhrase","phraseSatisfied","confirmDisabled","footer","Modal","Alert"],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { EDGE_EPSILON_PX as u } from "./cardStripGeometry.js";
2
+ const d = () => {
3
+ let n = 0, r = !1, t = null, o = !1, a = !1, s = null;
4
+ const c = (e, l) => {
5
+ t = null, n = e, r = l;
6
+ };
7
+ return {
8
+ revealing: (e, l) => {
9
+ n = e, r = !0, t = l ? e : null;
10
+ },
11
+ settle: c,
12
+ scrolled: (e, l) => {
13
+ (t === null || Math.abs(e - t) <= u) && c(e, l());
14
+ },
15
+ // Not `scrollend`: browsers fire it when they abort a slide on a layout change.
16
+ tookOver: () => {
17
+ t = null;
18
+ },
19
+ travelling: () => t !== null,
20
+ selectionShowing: () => r,
21
+ leadingMoved: (e) => {
22
+ const l = a !== e;
23
+ return a = e, l;
24
+ },
25
+ keptFor: (e) => Math.max(0, n + e),
26
+ firstPlacement: () => {
27
+ const e = !o;
28
+ return o = !0, e;
29
+ },
30
+ newSelection: (e) => {
31
+ const l = s !== e;
32
+ return s = e, l;
33
+ }
34
+ };
35
+ };
36
+ export {
37
+ d as createReaderPlace
38
+ };
39
+ //# sourceMappingURL=cardStripReader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cardStripReader.js","sources":["../../src/components/cardStripReader.ts"],"sourcesContent":["import { EDGE_EPSILON_PX } from './cardStripGeometry';\n\nexport interface ReaderPlace {\n revealing: (lands: number, smooth: boolean) => void;\n settle: (scrollLeft: number, selectionShowing: boolean) => void;\n scrolled: (scrollLeft: number, showing: () => boolean) => void;\n tookOver: () => void;\n travelling: () => boolean;\n selectionShowing: () => boolean;\n leadingMoved: (leadingInTrack: boolean) => boolean;\n keptFor: (shift: number) => number;\n firstPlacement: () => boolean;\n newSelection: (value: string) => boolean;\n}\n\nexport const createReaderPlace = (): ReaderPlace => {\n // A slide in flight stands for the reader's place by its destination until it lands.\n let scrollLeft = 0;\n let showing = false;\n let heading: number | null = null;\n let placed = false;\n let leadingInTrack = false;\n let placedValue: string | null = null;\n\n const settle = (nextScrollLeft: number, nextShowing: boolean): void => {\n heading = null;\n scrollLeft = nextScrollLeft;\n showing = nextShowing;\n };\n\n return {\n revealing: (lands, smooth) => {\n scrollLeft = lands;\n showing = true;\n heading = smooth ? lands : null;\n },\n settle,\n scrolled: (nextScrollLeft, isShowing) => {\n if (heading === null || Math.abs(nextScrollLeft - heading) <= EDGE_EPSILON_PX) {\n settle(nextScrollLeft, isShowing());\n }\n },\n // Not `scrollend`: browsers fire it when they abort a slide on a layout change.\n tookOver: () => {\n heading = null;\n },\n travelling: () => heading !== null,\n selectionShowing: () => showing,\n leadingMoved: (nextLeadingInTrack) => {\n const moved = leadingInTrack !== nextLeadingInTrack;\n leadingInTrack = nextLeadingInTrack;\n return moved;\n },\n keptFor: (shift) => Math.max(0, scrollLeft + shift),\n firstPlacement: () => {\n const first = !placed;\n placed = true;\n return first;\n },\n newSelection: (value) => {\n const changed = placedValue !== value;\n placedValue = value;\n return changed;\n },\n };\n};\n"],"names":["createReaderPlace","scrollLeft","showing","heading","placed","leadingInTrack","placedValue","settle","nextScrollLeft","nextShowing","lands","smooth","isShowing","EDGE_EPSILON_PX","nextLeadingInTrack","moved","shift","first","value","changed"],"mappings":";AAeO,MAAMA,IAAoB,MAAmB;AAEhD,MAAIC,IAAa,GACbC,IAAU,IACVC,IAAyB,MACzBC,IAAS,IACTC,IAAiB,IACjBC,IAA6B;AAEjC,QAAMC,IAAS,CAACC,GAAwBC,MAA+B;AACnE,IAAAN,IAAU,MACVF,IAAaO,GACbN,IAAUO;AAAA,EACd;AAEA,SAAO;AAAA,IACH,WAAW,CAACC,GAAOC,MAAW;AAC1B,MAAAV,IAAaS,GACbR,IAAU,IACVC,IAAUQ,IAASD,IAAQ;AAAA,IAC/B;AAAA,IACA,QAAAH;AAAA,IACA,UAAU,CAACC,GAAgBI,MAAc;AACrC,OAAIT,MAAY,QAAQ,KAAK,IAAIK,IAAiBL,CAAO,KAAKU,MAC1DN,EAAOC,GAAgBI,GAAW;AAAA,IAE1C;AAAA;AAAA,IAEA,UAAU,MAAM;AACZ,MAAAT,IAAU;AAAA,IACd;AAAA,IACA,YAAY,MAAMA,MAAY;AAAA,IAC9B,kBAAkB,MAAMD;AAAA,IACxB,cAAc,CAACY,MAAuB;AAClC,YAAMC,IAAQV,MAAmBS;AACjC,aAAAT,IAAiBS,GACVC;AAAA,IACX;AAAA,IACA,SAAS,CAACC,MAAU,KAAK,IAAI,GAAGf,IAAae,CAAK;AAAA,IAClD,gBAAgB,MAAM;AAClB,YAAMC,IAAQ,CAACb;AACf,aAAAA,IAAS,IACFa;AAAA,IACX;AAAA,IACA,cAAc,CAACC,MAAU;AACrB,YAAMC,IAAUb,MAAgBY;AAChC,aAAAZ,IAAcY,GACPC;AAAA,IACX;AAAA,EAAA;AAER;"}
package/dist/index.d.ts CHANGED
@@ -446,7 +446,8 @@ export declare interface CardStripProps {
446
446
  /** Cards. Any element with `role="radio"` and a `data-value` joins the group; an
447
447
  * add tile must be a direct child or it scrolls with the cards. */
448
448
  children: default_2.ReactNode;
449
- /** Pinned, non-scrolling slot before the cards (the Library card in the profile picker). */
449
+ /** Pinned, non-scrolling slot before the cards (the Library card in the profile picker).
450
+ * Scrolls with the cards when the strip has no room for one beside it. */
450
451
  leading?: default_2.ReactNode;
451
452
  /** Circular scroll affordances, shown only on an edge that can actually scroll. */
452
453
  arrows?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackone/malachite",
3
- "version": "2.51.0",
3
+ "version": "2.51.2",
4
4
  "description": "StackOne's themeable React component library — Shadow DOM isolated, scoped Tailwind, theme-token driven.",
5
5
  "license": "MIT",
6
6
  "author": "StackOne",