@stackone/malachite 2.71.0 → 2.73.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"Alert.js","sources":["../../src/components/Alert.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { Button, ButtonVariant } from './Button';\nimport CaretDownIcon from './CustomIcons/CaretDownIcon';\nimport CaretUpIcon from './CustomIcons/CaretUpIcon';\nimport CheckCircleFilled from './CustomIcons/CheckCircleFilled';\nimport ErrorCircleFilled from './CustomIcons/ErrorCircleFilled';\nimport InfoCircleFilled from './CustomIcons/InfoCircleFilled';\nimport SparkleIcon from './CustomIcons/SparkleIcon';\nimport WarningCircleFilled from './CustomIcons/WarningCircleFilled';\nimport type { RenderLink } from './PageLayout/types';\nimport MarkdownBlock from './Typography/MarkdownBlock';\nimport type { TextColor } from './Typography/Text';\nimport { getFontSize, getTextColor } from './Typography/textStyles';\n\nexport type AlertType = 'info' | 'success' | 'error' | 'warning' | 'assistant';\n\nexport type AlertLayout = 'inline' | 'banner';\n\ninterface AlertButtonConfig {\n label: string;\n onClick: () => void;\n variant?: ButtonVariant;\n icon?: React.ReactNode;\n}\n\ninterface AlertProps {\n type?: AlertType;\n message: string;\n hasMargin?: boolean;\n className?: string;\n children?: React.ReactNode;\n isExpanded?: boolean;\n onToggle?: (expanded: boolean) => void;\n /** A control at the end of the alert — a link or button that resolves what it reports. */\n action?: React.ReactNode;\n /** Replaces the type's status icon: an svg painting with `currentColor`, such as a CustomIcon,\n * is drawn in the type's colour at the same size. */\n icon?: React.ReactNode;\n /** Makes its message a live region: `role=\"alert\"` for error and warning, `status` otherwise.\n * Screen readers reliably announce an `alert` as it mounts; a `status` is read when its\n * `message` changes, so keep a live info or success alert mounted. Off by default. */\n live?: boolean;\n /** Routes root-relative links in `message` through the app's router; see `MarkdownBlock`. */\n renderLink?: RenderLink;\n /** `banner`: a 48px page-wide band. Applies only without `children`. */\n layout?: AlertLayout;\n /** A bold sentence the message runs on from. Plain text, not markdown. */\n lead?: string;\n}\n\n// Distinct from the imported `getTextColor`, which maps a TextColor to a CSS variable.\n// This maps an AlertType to a TextColor token; both are used in this file.\nconst getAlertTextColor = (alertType: AlertType): TextColor => {\n switch (alertType) {\n case 'info':\n return 'primary';\n case 'success':\n return 'success';\n case 'error':\n return 'error';\n case 'warning':\n return 'warning';\n // The violet is on `.alert-assistant` itself; inheriting it keeps one definition\n // rather than a TextColor token that only this alert would ever name.\n case 'assistant':\n return 'inherit';\n default:\n return 'primary';\n }\n};\n\nconst ICON_STYLE = {\n width: '13px',\n height: '13px',\n flexShrink: 0,\n};\n\nconst ICON_COLOR: Record<AlertType, string> = {\n info: 'var(--malachite-alert-info-foreground)',\n success: 'var(--malachite-alert-success-foreground)',\n error: 'var(--malachite-alert-error-foreground)',\n warning: 'var(--malachite-alert-warning-foreground)',\n assistant: 'var(--malachite-alert-assistant-foreground)',\n};\n\nconst AlertIcon = {\n info: (\n <InfoCircleFilled\n style={{ ...ICON_STYLE, color: ICON_COLOR.info }}\n />\n ),\n success: (\n <CheckCircleFilled\n style={{ ...ICON_STYLE, color: ICON_COLOR.success }}\n />\n ),\n error: (\n <ErrorCircleFilled\n style={{ ...ICON_STYLE, color: ICON_COLOR.error }}\n />\n ),\n warning: (\n <WarningCircleFilled\n style={{ ...ICON_STYLE, color: ICON_COLOR.warning }}\n />\n ),\n // The same sparkle the control that produced the message carries, so the two read as\n // one thread rather than two unrelated affordances.\n assistant: (\n <SparkleIcon\n style={{ ...ICON_STYLE, color: ICON_COLOR.assistant }}\n />\n ),\n};\n\nconst BANNER_ICON_STYLE = { ...ICON_STYLE, width: '16px', height: '16px' };\n\nconst statusIcon = (type: AlertType, icon: React.ReactNode, banner = false) => {\n if (!icon && !banner) return AlertIcon[type];\n const style = banner ? BANNER_ICON_STYLE : ICON_STYLE;\n return (\n <span\n className=\"alert-status-icon inline-flex shrink-0 [&_svg]:size-full!\"\n style={{ ...style, color: ICON_COLOR[type] }}\n >\n {icon || AlertIcon[type]}\n </span>\n );\n};\n\nconst SimpleTextRenderer: React.FC<{\n text: string;\n type: AlertType;\n lead?: string;\n renderLink?: RenderLink;\n}> = ({ text, type, lead, renderLink }) => {\n if (lead) {\n return (\n <div className=\"alert-lead\">\n <strong\n style={{\n color: getTextColor(getAlertTextColor(type)),\n fontSize: getFontSize('small'),\n fontWeight: 600,\n }}\n >\n {lead}\n </strong>{' '}\n <SimpleTextRenderer text={text} type={type} renderLink={renderLink} />\n </div>\n );\n }\n return (\n <MarkdownBlock\n text={text}\n color={getAlertTextColor(type)}\n size=\"small\"\n omitMargin\n renderLink={renderLink}\n />\n );\n};\n\nconst Alert: React.FC<AlertProps> = ({\n type = 'info',\n message,\n hasMargin = true,\n className,\n children,\n isExpanded,\n onToggle,\n action,\n icon,\n live = false,\n renderLink,\n layout = 'inline',\n lead,\n}) => {\n const isExpandable = !!children;\n const banner = layout === 'banner' && !isExpandable;\n const urgent = type === 'error' || type === 'warning';\n const liveRole = live ? (urgent ? 'alert' : 'status') : undefined;\n const [internalExpanded, setInternalExpanded] = useState(false);\n\n // Use controlled state if isExpanded is provided, otherwise use internal state\n const expanded = isExpanded !== undefined ? isExpanded : internalExpanded;\n\n const handleToggle = () => {\n const newExpanded = !expanded;\n if (isExpanded === undefined) {\n // Uncontrolled mode\n setInternalExpanded(newExpanded);\n }\n // Call onToggle if provided (for both controlled and uncontrolled)\n onToggle?.(newExpanded);\n };\n\n const alertClasses = [\n 'alert-base',\n `alert-${type}`,\n 'flex',\n 'w-full', // Ensure full width\n banner\n ? 'alert-banner'\n : isExpandable && expanded\n ? 'flex-col'\n : 'gap-2 items-center min-h-[2rem]',\n !hasMargin ? 'alert-no-margin' : '',\n 'text-left',\n banner ? '' : isExpandable ? 'rounded-xl !p-0' : '!px-2.5 !py-1.5', // Slightly more vertical padding for centering\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const headerClasses = [\n 'flex items-center justify-between w-full',\n isExpandable ? 'gap-2 px-2.5 py-1' : 'gap-2',\n isExpandable && expanded ? 'rounded-t-xl' : isExpandable ? 'rounded-xl' : '',\n ]\n .filter(Boolean)\n .join(' ');\n\n const expandableContentClasses = ['w-full', 'overflow-hidden'].filter(Boolean).join(' ');\n\n // Non-expandable alert (original behavior)\n if (!isExpandable) {\n return (\n <div className={alertClasses}>\n {statusIcon(type, icon, banner)}\n <div className=\"flex-1 flex items-center\">\n <div className=\"[&>div]:mb-0\" role={liveRole}>\n <SimpleTextRenderer\n text={message}\n type={type}\n lead={lead}\n renderLink={renderLink}\n />\n </div>\n </div>\n {action && <div className=\"alert-action shrink-0 flex items-center\">{action}</div>}\n </div>\n );\n }\n\n // Expandable alert\n return (\n <div className={`${alertClasses} alert-expandable`}>\n {/* Header section with title and expand/collapse button */}\n <div className={headerClasses}>\n <div className=\"flex-1 flex gap-2 items-center\">\n {statusIcon(type, icon)}\n <div className=\"flex-1 flex items-center\">\n <div className=\"[&>div]:mb-0\" role={liveRole}>\n <SimpleTextRenderer\n text={message}\n lead={lead}\n type={type}\n renderLink={renderLink}\n />\n </div>\n </div>\n </div>\n\n {/* Right side buttons container */}\n <div className=\"flex items-center gap-2\">\n {action && <div className=\"alert-action flex items-center\">{action}</div>}\n {/* Expand/collapse button */}\n <Button\n variant=\"no-style\"\n size=\"xsmall\"\n onClick={handleToggle}\n aria-label={expanded ? 'Collapse' : 'Expand'}\n style={{\n color: getTextColor(getAlertTextColor(type)),\n }}\n >\n {expanded ? (\n <CaretUpIcon\n style={{ minWidth: '13px', minHeight: '13px' }}\n />\n ) : (\n <CaretDownIcon\n style={{ minWidth: '13px', minHeight: '13px' }}\n />\n )}\n </Button>\n </div>\n </div>\n\n {/* Expandable content */}\n {expanded && (\n <>\n <div className={expandableContentClasses}>\n <div className=\"bg-white rounded-[var(--malachite-radius-10,10px)] shadow-[0px_1px_0px_0px_rgba(0,0,0,0.15)] border border-[#e8e8e8] border-solid mx-2 mb-2 max-h-[553px] overflow-y-auto\">\n {children}\n </div>\n </div>\n </>\n )}\n </div>\n );\n};\n\nexport default Alert;\nexport { Alert };\nexport type { AlertButtonConfig };\n"],"names":["getAlertTextColor","alertType","ICON_STYLE","ICON_COLOR","AlertIcon","jsx","InfoCircleFilled","CheckCircleFilled","ErrorCircleFilled","WarningCircleFilled","SparkleIcon","BANNER_ICON_STYLE","statusIcon","type","icon","banner","SimpleTextRenderer","text","lead","renderLink","jsxs","getTextColor","getFontSize","MarkdownBlock","Alert","message","hasMargin","className","children","isExpanded","onToggle","action","live","layout","isExpandable","liveRole","internalExpanded","setInternalExpanded","useState","expanded","handleToggle","newExpanded","alertClasses","headerClasses","expandableContentClasses","Button","CaretUpIcon","CaretDownIcon","Fragment"],"mappings":";;;;;;;;;;;;AAoDA,MAAMA,IAAoB,CAACC,MAAoC;AAC3D,UAAQA,GAAA;AAAA,IACJ,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA;AAAA;AAAA,IAGX,KAAK;AACD,aAAO;AAAA,IACX;AACI,aAAO;AAAA,EAAA;AAEnB,GAEMC,IAAa;AAAA,EACf,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,YAAY;AAChB,GAEMC,IAAwC;AAAA,EAC1C,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,WAAW;AACf,GAEMC,IAAY;AAAA,EACd,MACI,gBAAAC;AAAA,IAACC;AAAA,IAAA;AAAA,MACG,OAAO,EAAE,GAAGJ,GAAY,OAAOC,EAAW,KAAA;AAAA,IAAK;AAAA,EAAA;AAAA,EAGvD,SACI,gBAAAE;AAAA,IAACE;AAAA,IAAA;AAAA,MACG,OAAO,EAAE,GAAGL,GAAY,OAAOC,EAAW,QAAA;AAAA,IAAQ;AAAA,EAAA;AAAA,EAG1D,OACI,gBAAAE;AAAA,IAACG;AAAA,IAAA;AAAA,MACG,OAAO,EAAE,GAAGN,GAAY,OAAOC,EAAW,MAAA;AAAA,IAAM;AAAA,EAAA;AAAA,EAGxD,SACI,gBAAAE;AAAA,IAACI;AAAA,IAAA;AAAA,MACG,OAAO,EAAE,GAAGP,GAAY,OAAOC,EAAW,QAAA;AAAA,IAAQ;AAAA,EAAA;AAAA;AAAA;AAAA,EAK1D,WACI,gBAAAE;AAAA,IAACK;AAAA,IAAA;AAAA,MACG,OAAO,EAAE,GAAGR,GAAY,OAAOC,EAAW,UAAA;AAAA,IAAU;AAAA,EAAA;AAGhE,GAEMQ,IAAoB,EAAE,GAAGT,GAAY,OAAO,QAAQ,QAAQ,OAAA,GAE5DU,IAAa,CAACC,GAAiBC,GAAuBC,IAAS,OAC7D,CAACD,KAAQ,CAACC,IAAeX,EAAUS,CAAI,IAGvC,gBAAAR;AAAA,EAAC;AAAA,EAAA;AAAA,IACG,WAAU;AAAA,IACV,OAAO,EAAE,GAJHU,IAASJ,IAAoBT,GAIhB,OAAOC,EAAWU,CAAI,EAAA;AAAA,IAExC,UAAAC,KAAQV,EAAUS,CAAI;AAAA,EAAA;AAAA,GAK7BG,IAKD,CAAC,EAAE,MAAAC,GAAM,MAAAJ,GAAM,MAAAK,GAAM,YAAAC,QAClBD,IAEI,gBAAAE,EAAC,OAAA,EAAI,WAAU,cACX,UAAA;AAAA,EAAA,gBAAAf;AAAA,IAAC;AAAA,IAAA;AAAA,MACG,OAAO;AAAA,QACH,OAAOgB,EAAarB,EAAkBa,CAAI,CAAC;AAAA,QAC3C,UAAUS,EAAY,OAAO;AAAA,QAC7B,YAAY;AAAA,MAAA;AAAA,MAGf,UAAAJ;AAAA,IAAA;AAAA,EAAA;AAAA,EACK;AAAA,EACV,gBAAAb,EAACW,GAAA,EAAmB,MAAAC,GAAY,MAAAJ,GAAY,YAAAM,EAAA,CAAwB;AAAA,GACxE,IAIJ,gBAAAd;AAAA,EAACkB;AAAA,EAAA;AAAA,IACG,MAAAN;AAAA,IACA,OAAOjB,EAAkBa,CAAI;AAAA,IAC7B,MAAK;AAAA,IACL,YAAU;AAAA,IACV,YAAAM;AAAA,EAAA;AAAA,GAKNK,KAA8B,CAAC;AAAA,EACjC,MAAAX,IAAO;AAAA,EACP,SAAAY;AAAA,EACA,WAAAC,IAAY;AAAA,EACZ,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,EACA,UAAAC;AAAA,EACA,QAAAC;AAAA,EACA,MAAAjB;AAAA,EACA,MAAAkB,IAAO;AAAA,EACP,YAAAb;AAAA,EACA,QAAAc,IAAS;AAAA,EACT,MAAAf;AACJ,MAAM;AACF,QAAMgB,IAAe,CAAC,CAACN,GACjBb,IAASkB,MAAW,YAAY,CAACC,GAEjCC,IAAWH,IADFnB,MAAS,WAAWA,MAAS,YACV,UAAU,WAAY,QAClD,CAACuB,GAAkBC,CAAmB,IAAIC,EAAS,EAAK,GAGxDC,IAAWV,MAAe,SAAYA,IAAaO,GAEnDI,IAAe,MAAM;AACvB,UAAMC,IAAc,CAACF;AACrB,IAAIV,MAAe,UAEfQ,EAAoBI,CAAW,GAGnCX,KAAA,QAAAA,EAAWW;AAAA,EACf,GAEMC,IAAe;AAAA,IACjB;AAAA,IACA,SAAS7B,CAAI;AAAA,IACb;AAAA,IACA;AAAA;AAAA,IACAE,IACM,iBACAmB,KAAgBK,IACd,aACA;AAAA,IACPb,IAAgC,KAApB;AAAA,IACb;AAAA,IACAX,IAAS,KAAKmB,IAAe,oBAAoB;AAAA;AAAA,IACjDP;AAAA,EAAA,EAEC,OAAO,OAAO,EACd,KAAK,GAAG,GAEPgB,IAAgB;AAAA,IAClB;AAAA,IACAT,IAAe,sBAAsB;AAAA,IACrCA,KAAgBK,IAAW,iBAAiBL,IAAe,eAAe;AAAA,EAAA,EAEzE,OAAO,OAAO,EACd,KAAK,GAAG,GAEPU,IAA2B,CAAC,UAAU,iBAAiB,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAGvF,SAAKV,IAqBD,gBAAAd,EAAC,OAAA,EAAI,WAAW,GAAGsB,CAAY,qBAE3B,UAAA;AAAA,IAAA,gBAAAtB,EAAC,OAAA,EAAI,WAAWuB,GACZ,UAAA;AAAA,MAAA,gBAAAvB,EAAC,OAAA,EAAI,WAAU,kCACV,UAAA;AAAA,QAAAR,EAAWC,GAAMC,CAAI;AAAA,QACtB,gBAAAT,EAAC,SAAI,WAAU,4BACX,4BAAC,OAAA,EAAI,WAAU,gBAAe,MAAM8B,GAChC,UAAA,gBAAA9B;AAAA,UAACW;AAAA,UAAA;AAAA,YACG,MAAMS;AAAA,YACN,MAAAP;AAAA,YACA,MAAAL;AAAA,YACA,YAAAM;AAAA,UAAA;AAAA,QAAA,GAER,EAAA,CACJ;AAAA,MAAA,GACJ;AAAA,MAGA,gBAAAC,EAAC,OAAA,EAAI,WAAU,2BACV,UAAA;AAAA,QAAAW,KAAU,gBAAA1B,EAAC,OAAA,EAAI,WAAU,kCAAkC,UAAA0B,GAAO;AAAA,QAEnE,gBAAA1B;AAAA,UAACwC;AAAA,UAAA;AAAA,YACG,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,SAASL;AAAA,YACT,cAAYD,IAAW,aAAa;AAAA,YACpC,OAAO;AAAA,cACH,OAAOlB,EAAarB,EAAkBa,CAAI,CAAC;AAAA,YAAA;AAAA,YAG9C,UAAA0B,IACG,gBAAAlC;AAAA,cAACyC;AAAA,cAAA;AAAA,gBACG,OAAO,EAAE,UAAU,QAAQ,WAAW,OAAA;AAAA,cAAO;AAAA,YAAA,IAGjD,gBAAAzC;AAAA,cAAC0C;AAAA,cAAA;AAAA,gBACG,OAAO,EAAE,UAAU,QAAQ,WAAW,OAAA;AAAA,cAAO;AAAA,YAAA;AAAA,UACjD;AAAA,QAAA;AAAA,MAER,EAAA,CACJ;AAAA,IAAA,GACJ;AAAA,IAGCR,KACG,gBAAAlC,EAAA2C,GAAA,EACI,UAAA,gBAAA3C,EAAC,OAAA,EAAI,WAAWuC,GACZ,UAAA,gBAAAvC,EAAC,OAAA,EAAI,WAAU,6KACV,UAAAuB,EAAA,CACL,EAAA,CACJ,EAAA,CACJ;AAAA,EAAA,GAER,IAxEI,gBAAAR,EAAC,OAAA,EAAI,WAAWsB,GACX,UAAA;AAAA,IAAA9B,EAAWC,GAAMC,GAAMC,CAAM;AAAA,IAC9B,gBAAAV,EAAC,SAAI,WAAU,4BACX,4BAAC,OAAA,EAAI,WAAU,gBAAe,MAAM8B,GAChC,UAAA,gBAAA9B;AAAA,MAACW;AAAA,MAAA;AAAA,QACG,MAAMS;AAAA,QACN,MAAAZ;AAAA,QACA,MAAAK;AAAA,QACA,YAAAC;AAAA,MAAA;AAAA,IAAA,GAER,EAAA,CACJ;AAAA,IACCY,KAAU,gBAAA1B,EAAC,OAAA,EAAI,WAAU,2CAA2C,UAAA0B,EAAA,CAAO;AAAA,EAAA,GAChF;AA6DZ;"}
1
+ {"version":3,"file":"Alert.js","sources":["../../src/components/Alert.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { Button, ButtonVariant } from './Button';\nimport CaretDownIcon from './CustomIcons/CaretDownIcon';\nimport CaretUpIcon from './CustomIcons/CaretUpIcon';\nimport CheckCircleFilled from './CustomIcons/CheckCircleFilled';\nimport ErrorCircleFilled from './CustomIcons/ErrorCircleFilled';\nimport InfoCircleFilled from './CustomIcons/InfoCircleFilled';\nimport SparkleIcon from './CustomIcons/SparkleIcon';\nimport WarningCircleFilled from './CustomIcons/WarningCircleFilled';\nimport type { RenderLink } from './PageLayout/types';\nimport MarkdownBlock from './Typography/MarkdownBlock';\nimport type { TextColor } from './Typography/Text';\nimport { getFontSize, getTextColor } from './Typography/textStyles';\n\nexport type AlertType = 'info' | 'success' | 'error' | 'warning' | 'assistant';\n\nexport type AlertLayout = 'inline' | 'banner';\n\ninterface AlertButtonConfig {\n label: string;\n onClick: () => void;\n variant?: ButtonVariant;\n icon?: React.ReactNode;\n}\n\ninterface AlertProps {\n type?: AlertType;\n message: string;\n hasMargin?: boolean;\n className?: string;\n children?: React.ReactNode;\n isExpanded?: boolean;\n onToggle?: (expanded: boolean) => void;\n /** A control at the end of the alert — a link or button that resolves what it reports. */\n action?: React.ReactNode;\n /** Replaces the type's status icon: an svg painting with `currentColor`, such as a CustomIcon,\n * is drawn in the type's colour at the same size. */\n icon?: React.ReactNode;\n /** Makes its message a live region: `role=\"alert\"` for error and warning, `status` otherwise.\n * Screen readers reliably announce an `alert` as it mounts; a `status` is read when its\n * `message` changes, so keep a live info or success alert mounted. Off by default. */\n live?: boolean;\n /** Routes root-relative links in `message` through the app's router; see `MarkdownBlock`. */\n renderLink?: RenderLink;\n /** `banner`: a 48px page-wide band. Applies only without `children`. */\n layout?: AlertLayout;\n /** `center` gathers a banner's icon and text in the middle of the band, for a wide page. */\n align?: 'start' | 'center';\n /** A bold sentence the message runs on from. Plain text, not markdown. */\n lead?: string;\n}\n\n// Distinct from the imported `getTextColor`, which maps a TextColor to a CSS variable.\n// This maps an AlertType to a TextColor token; both are used in this file.\nconst getAlertTextColor = (alertType: AlertType): TextColor => {\n switch (alertType) {\n case 'info':\n return 'primary';\n case 'success':\n return 'success';\n case 'error':\n return 'error';\n case 'warning':\n return 'warning';\n // The violet is on `.alert-assistant` itself; inheriting it keeps one definition\n // rather than a TextColor token that only this alert would ever name.\n case 'assistant':\n return 'inherit';\n default:\n return 'primary';\n }\n};\n\nconst ICON_STYLE = {\n width: '13px',\n height: '13px',\n flexShrink: 0,\n};\n\nconst ICON_COLOR: Record<AlertType, string> = {\n info: 'var(--malachite-alert-info-foreground)',\n success: 'var(--malachite-alert-success-foreground)',\n error: 'var(--malachite-alert-error-foreground)',\n warning: 'var(--malachite-alert-warning-foreground)',\n assistant: 'var(--malachite-alert-assistant-foreground)',\n};\n\nconst AlertIcon = {\n info: <InfoCircleFilled style={{ ...ICON_STYLE, color: ICON_COLOR.info }} />,\n success: <CheckCircleFilled style={{ ...ICON_STYLE, color: ICON_COLOR.success }} />,\n error: <ErrorCircleFilled style={{ ...ICON_STYLE, color: ICON_COLOR.error }} />,\n warning: <WarningCircleFilled style={{ ...ICON_STYLE, color: ICON_COLOR.warning }} />,\n // The same sparkle the control that produced the message carries, so the two read as\n // one thread rather than two unrelated affordances.\n assistant: <SparkleIcon style={{ ...ICON_STYLE, color: ICON_COLOR.assistant }} />,\n};\n\nconst BANNER_ICON_STYLE = { ...ICON_STYLE, width: '16px', height: '16px' };\n\nconst statusIcon = (type: AlertType, icon: React.ReactNode, banner = false) => {\n if (!icon && !banner) return AlertIcon[type];\n const style = banner ? BANNER_ICON_STYLE : ICON_STYLE;\n return (\n <span\n className=\"alert-status-icon inline-flex shrink-0 [&_svg]:size-full!\"\n style={{ ...style, color: ICON_COLOR[type] }}\n >\n {icon || AlertIcon[type]}\n </span>\n );\n};\n\nconst SimpleTextRenderer: React.FC<{\n text: string;\n type: AlertType;\n lead?: string;\n renderLink?: RenderLink;\n}> = ({ text, type, lead, renderLink }) => {\n if (lead) {\n return (\n <div className=\"alert-lead\">\n <strong\n style={{\n color: getTextColor(getAlertTextColor(type)),\n fontSize: getFontSize('small'),\n fontWeight: 600,\n }}\n >\n {lead}\n </strong>{' '}\n <SimpleTextRenderer text={text} type={type} renderLink={renderLink} />\n </div>\n );\n }\n return (\n <MarkdownBlock\n text={text}\n color={getAlertTextColor(type)}\n size=\"small\"\n omitMargin\n renderLink={renderLink}\n />\n );\n};\n\nconst Alert: React.FC<AlertProps> = ({\n type = 'info',\n message,\n hasMargin = true,\n className,\n children,\n isExpanded,\n onToggle,\n action,\n icon,\n live = false,\n renderLink,\n layout = 'inline',\n lead,\n align = 'start',\n}) => {\n const isExpandable = !!children;\n const banner = layout === 'banner' && !isExpandable;\n const centred = banner && align === 'center';\n const urgent = type === 'error' || type === 'warning';\n const liveRole = live ? (urgent ? 'alert' : 'status') : undefined;\n const [internalExpanded, setInternalExpanded] = useState(false);\n\n // Use controlled state if isExpanded is provided, otherwise use internal state\n const expanded = isExpanded !== undefined ? isExpanded : internalExpanded;\n\n const handleToggle = () => {\n const newExpanded = !expanded;\n if (isExpanded === undefined) {\n // Uncontrolled mode\n setInternalExpanded(newExpanded);\n }\n // Call onToggle if provided (for both controlled and uncontrolled)\n onToggle?.(newExpanded);\n };\n\n const alertClasses = [\n 'alert-base',\n `alert-${type}`,\n 'flex',\n 'w-full', // Ensure full width\n banner\n ? 'alert-banner'\n : isExpandable && expanded\n ? 'flex-col'\n : 'gap-2 items-center min-h-[2rem]',\n !hasMargin ? 'alert-no-margin' : '',\n centred ? 'text-center' : 'text-left',\n banner ? '' : isExpandable ? 'rounded-xl !p-0' : '!px-2.5 !py-1.5', // Slightly more vertical padding for centering\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const headerClasses = [\n 'flex items-center justify-between w-full',\n isExpandable ? 'gap-2 px-2.5 py-1' : 'gap-2',\n isExpandable && expanded ? 'rounded-t-xl' : isExpandable ? 'rounded-xl' : '',\n ]\n .filter(Boolean)\n .join(' ');\n\n const expandableContentClasses = ['w-full', 'overflow-hidden'].filter(Boolean).join(' ');\n\n // Non-expandable alert (original behavior)\n if (!isExpandable) {\n const content = (\n <>\n {statusIcon(type, icon, banner)}\n <div className={centred ? 'flex items-center' : 'flex-1 flex items-center'}>\n <div className=\"[&>div]:mb-0\" role={liveRole}>\n <SimpleTextRenderer\n text={message}\n type={type}\n lead={lead}\n renderLink={renderLink}\n />\n </div>\n </div>\n </>\n );\n return (\n <div className={alertClasses}>\n {centred ? (\n // Its own group, so an `action` stays at the end while this sits mid-band.\n <div className=\"alert-centred flex-1 flex items-center justify-center gap-[10px]\">\n {content}\n </div>\n ) : (\n content\n )}\n {action && <div className=\"alert-action shrink-0 flex items-center\">{action}</div>}\n </div>\n );\n }\n\n // Expandable alert\n return (\n <div className={`${alertClasses} alert-expandable`}>\n {/* Header section with title and expand/collapse button */}\n <div className={headerClasses}>\n <div className=\"flex-1 flex gap-2 items-center\">\n {statusIcon(type, icon)}\n <div className=\"flex-1 flex items-center\">\n <div className=\"[&>div]:mb-0\" role={liveRole}>\n <SimpleTextRenderer\n text={message}\n lead={lead}\n type={type}\n renderLink={renderLink}\n />\n </div>\n </div>\n </div>\n\n {/* Right side buttons container */}\n <div className=\"flex items-center gap-2\">\n {action && <div className=\"alert-action flex items-center\">{action}</div>}\n {/* Expand/collapse button */}\n <Button\n variant=\"no-style\"\n size=\"xsmall\"\n onClick={handleToggle}\n aria-label={expanded ? 'Collapse' : 'Expand'}\n style={{\n color: getTextColor(getAlertTextColor(type)),\n }}\n >\n {expanded ? (\n <CaretUpIcon style={{ minWidth: '13px', minHeight: '13px' }} />\n ) : (\n <CaretDownIcon style={{ minWidth: '13px', minHeight: '13px' }} />\n )}\n </Button>\n </div>\n </div>\n\n {/* Expandable content */}\n {expanded && (\n <>\n <div className={expandableContentClasses}>\n <div className=\"bg-white rounded-[var(--malachite-radius-10,10px)] shadow-[0px_1px_0px_0px_rgba(0,0,0,0.15)] border border-[#e8e8e8] border-solid mx-2 mb-2 max-h-[553px] overflow-y-auto\">\n {children}\n </div>\n </div>\n </>\n )}\n </div>\n );\n};\n\nexport default Alert;\nexport { Alert };\nexport type { AlertButtonConfig };\n"],"names":["getAlertTextColor","alertType","ICON_STYLE","ICON_COLOR","AlertIcon","jsx","InfoCircleFilled","CheckCircleFilled","ErrorCircleFilled","WarningCircleFilled","SparkleIcon","BANNER_ICON_STYLE","statusIcon","type","icon","banner","SimpleTextRenderer","text","lead","renderLink","jsxs","getTextColor","getFontSize","MarkdownBlock","Alert","message","hasMargin","className","children","isExpanded","onToggle","action","live","layout","align","isExpandable","centred","liveRole","internalExpanded","setInternalExpanded","useState","expanded","handleToggle","newExpanded","alertClasses","headerClasses","expandableContentClasses","content","Fragment","Button","CaretUpIcon","CaretDownIcon"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as r, jsxs as i, Fragment as d } from "react/jsx-runtime";
2
- import { forwardRef as A } from "react";
3
- import E from "./Tooltip.js";
4
- import { getIconOnlySizeClasses as H, getIconSizeClass as M } from "./buttonSizing.js";
2
+ import { forwardRef as E } from "react";
3
+ import H from "./Tooltip.js";
4
+ import { getIconOnlySizeClasses as M, getIconSizeClass as q } from "./buttonSizing.js";
5
5
  import { resolveRel as D } from "./linkRel.js";
6
6
  const O = (t) => {
7
7
  switch (t) {
@@ -57,8 +57,9 @@ const O = (t) => {
57
57
  success: "focus-visible:[outline:2px_solid_var(--malachite-text-success)]",
58
58
  warning: "focus-visible:[outline:2px_solid_var(--malachite-text-warning)]",
59
59
  error: "focus-visible:[outline:2px_solid_var(--malachite-text-error)]",
60
- assistant: "focus-visible:[outline:2px_solid_var(--malachite-assistant-foreground)]"
61
- }, q = (t, a) => {
60
+ assistant: "focus-visible:[outline:2px_solid_var(--malachite-assistant-foreground)]",
61
+ accent: "focus-visible:[outline:2px_solid_var(--malachite-accent-foreground)]"
62
+ }, J = (t, a) => {
62
63
  const e = `font-medium transition-[color,background-color,border-color,opacity,box-shadow] duration-200 ease-in-out outline-none focus-visible:outline-offset-2 border border-solid ${a} cursor-pointer`;
63
64
  switch (t) {
64
65
  case "filled":
@@ -79,12 +80,14 @@ const O = (t) => {
79
80
  return `${e} ${s.error} btn-glass-error`;
80
81
  case "ghost":
81
82
  return `${e} ${s.neutral} btn-ghost`;
83
+ case "quiet":
84
+ return `${e} ${s.accent} btn-quiet`;
82
85
  case "no-style":
83
86
  return `${e} ${s.neutral} btn-no-style border-none`;
84
87
  default:
85
88
  return `${e} ${s.neutral} btn-filled shadow-default`;
86
89
  }
87
- }, J = () => /* @__PURE__ */ i(
90
+ }, K = () => /* @__PURE__ */ i(
88
91
  "svg",
89
92
  {
90
93
  className: "animate-spin -ml-1 mr-2 h-4 w-4",
@@ -113,7 +116,7 @@ const O = (t) => {
113
116
  )
114
117
  ]
115
118
  }
116
- ), K = A(
119
+ ), Q = E(
117
120
  ({
118
121
  variant: t,
119
122
  size: a = "large",
@@ -125,28 +128,30 @@ const O = (t) => {
125
128
  leading: v,
126
129
  contentAlign: k = "center",
127
130
  flexGrow: m = !1,
128
- fill: j = !1,
131
+ width: j = "auto",
132
+ fill: B = !1,
129
133
  tooltip: x,
130
- tooltipPlacement: B = "top",
134
+ tooltipPlacement: L = "top",
131
135
  iconOnly: p = !1,
132
- flush: L = !1,
136
+ flush: z = !1,
133
137
  children: n,
134
- className: z = "",
138
+ className: F = "",
135
139
  href: w,
136
140
  target: $,
137
- rel: F,
138
- ...C
139
- }, _) => {
140
- const U = e ? G(e) : W(a, p), l = I || g, N = k === "start", y = [
141
+ rel: U,
142
+ ..._
143
+ }, C) => {
144
+ const A = e ? G(e) : W(a, p), l = I || g, N = k === "start", y = [
141
145
  N ? "flex items-center justify-start text-left" : "flex items-center justify-center",
142
- p ? `p-0 ${H(a)}` : O(a),
143
- q(t, U),
146
+ p ? `p-0 ${M(a)}` : O(a),
147
+ J(t, A),
144
148
  m ? "w-full" : "",
145
- j ? "max-w-full" : "",
149
+ j === "wide" ? "min-w-[120px]" : "",
150
+ B ? "max-w-full" : "",
146
151
  l ? "cursor-not-allowed" : "",
147
152
  x && l ? "pointer-events-none" : "",
148
- L && !p ? t === "no-style" ? V : T : "",
149
- z
153
+ z && !p ? t === "no-style" ? V : T : "",
154
+ F
150
155
  ].filter(Boolean).join(" "), o = N ? /* @__PURE__ */ r("span", { className: "flex min-w-0 flex-1 items-center", children: n }) : n, u = v ? /* @__PURE__ */ r(
151
156
  "span",
152
157
  {
@@ -157,10 +162,10 @@ const O = (t) => {
157
162
  if (g)
158
163
  return /* @__PURE__ */ i(d, { children: [
159
164
  u,
160
- /* @__PURE__ */ r(J, {}),
165
+ /* @__PURE__ */ r(K, {}),
161
166
  o
162
167
  ] });
163
- const f = `inline-flex items-center shrink-0 ${M(a)}`;
168
+ const f = `inline-flex items-center shrink-0 ${q(a)}`;
164
169
  return c && b === "start" ? /* @__PURE__ */ i(d, { children: [
165
170
  u,
166
171
  /* @__PURE__ */ r(
@@ -188,30 +193,30 @@ const O = (t) => {
188
193
  }, h = w && !l ? /* @__PURE__ */ r(
189
194
  "a",
190
195
  {
191
- ref: _,
196
+ ref: C,
192
197
  href: w,
193
198
  target: $,
194
- rel: D(F, $),
199
+ rel: D(U, $),
195
200
  className: y,
196
- ...C,
201
+ ..._,
197
202
  children: S()
198
203
  }
199
204
  ) : /* @__PURE__ */ r(
200
205
  "button",
201
206
  {
202
- ref: _,
207
+ ref: C,
203
208
  disabled: l,
204
209
  className: y,
205
- ...C,
210
+ ..._,
206
211
  children: S()
207
212
  }
208
213
  );
209
- return x ? /* @__PURE__ */ r(E, { text: x, placement: B, fill: m, children: l ? /* @__PURE__ */ r("span", { className: `inline-flex cursor-not-allowed${m ? " w-full" : ""}`, children: h }) : h }) : h;
214
+ return x ? /* @__PURE__ */ r(H, { text: x, placement: L, fill: m, children: l ? /* @__PURE__ */ r("span", { className: `inline-flex cursor-not-allowed${m ? " w-full" : ""}`, children: h }) : h }) : h;
210
215
  }
211
216
  );
212
- K.displayName = "Button";
217
+ Q.displayName = "Button";
213
218
  export {
214
- K as Button,
215
- K as default
219
+ Q as Button,
220
+ Q as default
216
221
  };
217
222
  //# sourceMappingURL=Button.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Button.js","sources":["../../src/components/Button.tsx"],"sourcesContent":["import React, { forwardRef } from 'react';\nimport Tooltip from './Tooltip';\nimport { getIconOnlySizeClasses, getIconSizeClass } from './buttonSizing';\nimport { resolveRel } from './linkRel';\n\n// Button variant types matching the old component\nexport type ButtonVariant =\n | 'filled'\n | 'outline'\n /**\n * `outline`'s geometry on a ground of its own. For a button sitting on a tinted or\n * patterned surface, where `outline`'s transparent background lets that surface through\n * and the control stops reading as a button.\n */\n | 'surface'\n /**\n * The violet the assistant is named in elsewhere — for a control that hands work to it, or\n * acts on what it produced. Provenance, not a severity: it says who is being asked, not\n * that anything is wrong.\n */\n | 'assistant'\n | 'destructive'\n | 'success'\n | 'warning'\n | 'error'\n | 'ghost'\n | 'no-style';\n\nexport type ButtonSize = 'xsmall' | 'small' | 'medium' | 'large';\n\nexport type ButtonRadius = 'md' | 'lg' | 'xl';\n\ninterface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant: ButtonVariant;\n size?: ButtonSize;\n radius?: ButtonRadius;\n loading?: boolean;\n disabled?: boolean;\n icon?: React.ReactNode;\n iconPosition?: 'start' | 'end';\n /** Before the label at its own size, never squeezed (an avatar); pairs with an end `icon`. */\n leading?: React.ReactNode;\n /** `start` aligns the label to the leading edge and pins an end `icon` to the far edge. */\n contentAlign?: 'center' | 'start';\n /** Fills the container's width. */\n flexGrow?: boolean;\n fill?: boolean;\n tooltip?: string;\n tooltipPlacement?: 'top' | 'bottom' | 'left' | 'right';\n iconOnly?: boolean;\n /** Pulls a borderless button out by its own padding, so its label lines up with the text around it. */\n flush?: boolean;\n children?: React.ReactNode;\n onClick?: React.MouseEventHandler<HTMLButtonElement & HTMLAnchorElement>;\n /** Renders the button as a real anchor, so a link action is never an <a> nested in a <button>. */\n href?: string;\n target?: React.HTMLAttributeAnchorTarget;\n rel?: string;\n}\n\n// Geometry from the Figma component library, which is variant-independent.\n// `min-h` not `h`: Tailwind emits `h-*` after `min-h-*`, so a hard height would beat a\n// consumer's own `h-*`. Paired with `whitespace-nowrap` (as Figma has it) so a long label\n// cannot wrap into a line the height would clip.\nconst getSizeClasses = (size: ButtonSize) => {\n switch (size) {\n // Figma has no text X-Small button, only the icon-only square — nothing to align to.\n case 'xsmall':\n return '[--btn-px:calc(var(--spacing)*2)] px-[var(--btn-px)] py-1 text-xs';\n case 'small':\n return 'min-h-[var(--malachite-control-height-small,28px)] [--btn-px:10px] px-[var(--btn-px)] text-[length:var(--malachite-font-size-small,13px)] leading-[16px] whitespace-nowrap';\n case 'medium':\n return 'min-h-[var(--malachite-control-height-medium,32px)] [--btn-px:10px] px-[var(--btn-px)] text-[length:var(--malachite-font-size-small,13px)] leading-[16px] whitespace-nowrap';\n case 'large':\n return 'min-h-[var(--malachite-control-height-large,42px)] [--btn-px:12px] px-[var(--btn-px)] text-[length:var(--malachite-font-size-15,15px)] leading-[18px] whitespace-nowrap';\n default:\n return 'min-h-[var(--malachite-control-height-large,42px)] [--btn-px:12px] px-[var(--btn-px)] text-[length:var(--malachite-font-size-15,15px)] leading-[18px] whitespace-nowrap';\n }\n};\n\n// The 1px is the transparent border, which `no-style` drops.\nconst FLUSH_MARGIN = '-mx-[calc(var(--btn-px)+1px)]';\nconst FLUSH_MARGIN_BORDERLESS = '-mx-[var(--btn-px)]';\n\n// `rounded-*` reads the malachite radius tokens via `@theme`, so these follow the theme.\n// An explicit `radius` prop still wins, via getRadiusClass.\nconst getDefaultRadiusClass = (size: ButtonSize, iconOnly: boolean): string => {\n switch (size) {\n // Only the square rounds at 8px — text buttons at this size keep their 6px corner.\n case 'xsmall':\n return iconOnly ? 'rounded-lg' : 'rounded-md';\n case 'small':\n return 'rounded-[var(--malachite-radius-10,10px)]';\n case 'medium':\n return 'rounded-xl';\n case 'large':\n return 'rounded-[var(--malachite-radius-xxl,14px)]';\n default:\n return 'rounded-[var(--malachite-radius-xxl,14px)]';\n }\n};\n\n// Figma pairs a 6px icon gap with the 13px tiers and 8px with the 15px tier.\nconst getIconGapClass = (size: ButtonSize, position: 'start' | 'end'): string => {\n const start = position === 'start';\n switch (size) {\n case 'small':\n case 'medium':\n return start ? 'mr-[6px]' : 'ml-[6px]';\n default:\n return start ? 'mr-2' : 'ml-2';\n }\n};\n\nconst getRadiusClass = (radius: ButtonRadius): string => {\n switch (radius) {\n case 'md':\n return 'rounded-md';\n case 'lg':\n return 'rounded-lg';\n case 'xl':\n return 'rounded-xl';\n default:\n return 'rounded-xl';\n }\n};\n\n// Focus ring: a `focus-visible` outline (keyboard only), coloured to match the variant.\n// Uses `outline` rather than Tailwind's `ring-*` because ring composes `--tw-ring-*`\n// custom properties via box-shadow that rely on document-level @property registration\n// and silently don't paint inside a shadow root (same class of issue as transform\n// utilities). The classes are full literals so Tailwind's JIT can generate them.\nconst FOCUS_OUTLINE = {\n neutral: 'focus-visible:[outline:2px_solid_var(--malachite-text-primary)]',\n destructive: 'focus-visible:[outline:2px_solid_var(--malachite-text-destructive)]',\n success: 'focus-visible:[outline:2px_solid_var(--malachite-text-success)]',\n warning: 'focus-visible:[outline:2px_solid_var(--malachite-text-warning)]',\n error: 'focus-visible:[outline:2px_solid_var(--malachite-text-error)]',\n assistant: 'focus-visible:[outline:2px_solid_var(--malachite-assistant-foreground)]',\n};\n\nconst getVariantClasses = (variant: ButtonVariant, radiusClass: string): string => {\n const baseClasses = `font-medium transition-[color,background-color,border-color,opacity,box-shadow] duration-200 ease-in-out outline-none focus-visible:outline-offset-2 border border-solid ${radiusClass} cursor-pointer`;\n\n switch (variant) {\n case 'filled':\n return `${baseClasses} ${FOCUS_OUTLINE.neutral} btn-filled shadow-default`;\n\n case 'outline':\n return `${baseClasses} ${FOCUS_OUTLINE.neutral} btn-outline shadow-default`;\n\n case 'surface':\n return `${baseClasses} ${FOCUS_OUTLINE.neutral} btn-surface shadow-default`;\n\n case 'assistant':\n return `${baseClasses} ${FOCUS_OUTLINE.assistant} btn-assistant shadow-default`;\n\n case 'destructive':\n return `${baseClasses} ${FOCUS_OUTLINE.destructive} btn-destructive`;\n\n case 'success':\n return `${baseClasses} ${FOCUS_OUTLINE.success} btn-glass-success`;\n\n case 'warning':\n return `${baseClasses} ${FOCUS_OUTLINE.warning} btn-glass-warning`;\n\n case 'error':\n return `${baseClasses} ${FOCUS_OUTLINE.error} btn-glass-error`;\n\n case 'ghost':\n return `${baseClasses} ${FOCUS_OUTLINE.neutral} btn-ghost`;\n\n case 'no-style':\n return `${baseClasses} ${FOCUS_OUTLINE.neutral} btn-no-style border-none`;\n\n default:\n return `${baseClasses} ${FOCUS_OUTLINE.neutral} btn-filled shadow-default`;\n }\n};\n\nconst LoadingSpinner = () => (\n <svg\n className=\"animate-spin -ml-1 mr-2 h-4 w-4\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n);\n\nexport const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonProps>(\n (\n {\n variant,\n size = 'large',\n radius,\n loading = false,\n disabled = false,\n icon,\n iconPosition = 'start',\n leading,\n contentAlign = 'center',\n flexGrow = false,\n fill = false,\n tooltip,\n tooltipPlacement = 'top',\n iconOnly = false,\n flush = false,\n children,\n className = '',\n href,\n target,\n rel,\n ...props\n },\n ref,\n ) => {\n const radiusClass = radius ? getRadiusClass(radius) : getDefaultRadiusClass(size, iconOnly);\n const isInactive = disabled || loading;\n const alignStart = contentAlign === 'start';\n const baseClasses = [\n alignStart\n ? 'flex items-center justify-start text-left'\n : 'flex items-center justify-center',\n iconOnly ? `p-0 ${getIconOnlySizeClasses(size)}` : getSizeClasses(size),\n getVariantClasses(variant, radiusClass),\n flexGrow ? 'w-full' : '',\n fill ? 'max-w-full' : '',\n isInactive ? 'cursor-not-allowed' : '',\n tooltip && isInactive ? 'pointer-events-none' : '',\n flush && !iconOnly\n ? variant === 'no-style'\n ? FLUSH_MARGIN_BORDERLESS\n : FLUSH_MARGIN\n : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const label = alignStart ? (\n <span className=\"flex min-w-0 flex-1 items-center\">{children}</span>\n ) : (\n children\n );\n\n const leadingSlot = leading ? (\n <span\n className={\n children\n ? 'btn-leading inline-flex shrink-0 items-center me-2'\n : 'btn-leading inline-flex shrink-0 items-center'\n }\n >\n {leading}\n </span>\n ) : null;\n\n const renderContent = () => {\n if (loading) {\n return (\n <>\n {leadingSlot}\n <LoadingSpinner />\n {label}\n </>\n );\n }\n\n const iconWrapperClasses = `inline-flex items-center shrink-0 ${getIconSizeClass(size)}`;\n\n if (icon && iconPosition === 'start') {\n return (\n <>\n {leadingSlot}\n <span\n className={`${iconWrapperClasses}${children ? ` ${getIconGapClass(size, 'start')}` : ''}`}\n >\n {icon}\n </span>\n {label}\n </>\n );\n }\n\n if (icon && iconPosition === 'end') {\n return (\n <>\n {leadingSlot}\n {label}\n <span\n className={`${iconWrapperClasses}${children ? ` ${getIconGapClass(size, 'end')}` : ''}`}\n >\n {icon}\n </span>\n </>\n );\n }\n\n return (\n <>\n {leadingSlot}\n {label}\n </>\n );\n };\n\n // An anchor cannot be disabled, so an inactive link still renders as a <button>.\n const button =\n href && !isInactive ? (\n <a\n ref={ref as React.Ref<HTMLAnchorElement>}\n href={href}\n target={target}\n rel={resolveRel(rel, target)}\n className={baseClasses}\n {...(props as React.AnchorHTMLAttributes<HTMLAnchorElement>)}\n >\n {renderContent()}\n </a>\n ) : (\n <button\n ref={ref as React.Ref<HTMLButtonElement>}\n disabled={isInactive}\n className={baseClasses}\n {...props}\n >\n {renderContent()}\n </button>\n );\n\n if (tooltip) {\n const trigger = isInactive ? (\n <span className={`inline-flex cursor-not-allowed${flexGrow ? ' w-full' : ''}`}>\n {button}\n </span>\n ) : (\n button\n );\n return (\n <Tooltip text={tooltip} placement={tooltipPlacement} fill={flexGrow}>\n {trigger}\n </Tooltip>\n );\n }\n\n return button;\n },\n);\n\nButton.displayName = 'Button';\n\nexport default Button;\n"],"names":["getSizeClasses","size","FLUSH_MARGIN","FLUSH_MARGIN_BORDERLESS","getDefaultRadiusClass","iconOnly","getIconGapClass","position","start","getRadiusClass","radius","FOCUS_OUTLINE","getVariantClasses","variant","radiusClass","baseClasses","LoadingSpinner","jsxs","jsx","Button","forwardRef","loading","disabled","icon","iconPosition","leading","contentAlign","flexGrow","fill","tooltip","tooltipPlacement","flush","children","className","href","target","rel","props","ref","isInactive","alignStart","getIconOnlySizeClasses","label","leadingSlot","renderContent","Fragment","iconWrapperClasses","getIconSizeClass","button","resolveRel","Tooltip"],"mappings":"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"}
1
+ {"version":3,"file":"Button.js","sources":["../../src/components/Button.tsx"],"sourcesContent":["import React, { forwardRef } from 'react';\nimport Tooltip from './Tooltip';\nimport { getIconOnlySizeClasses, getIconSizeClass } from './buttonSizing';\nimport { resolveRel } from './linkRel';\n\n// Button variant types matching the old component\nexport type ButtonVariant =\n | 'filled'\n | 'outline'\n /**\n * `outline`'s geometry on a ground of its own. For a button sitting on a tinted or\n * patterned surface, where `outline`'s transparent background lets that surface through\n * and the control stops reading as a button.\n */\n | 'surface'\n /**\n * The violet the assistant is named in elsewhere — for a control that hands work to it, or\n * acts on what it produced. Provenance, not a severity: it says who is being asked, not\n * that anything is wrong.\n */\n | 'assistant'\n | 'destructive'\n | 'success'\n | 'warning'\n | 'error'\n | 'ghost'\n /** Text only, in the accent colour, for a secondary action beside a primary one. */\n | 'quiet'\n | 'no-style';\n\nexport type ButtonSize = 'xsmall' | 'small' | 'medium' | 'large';\n\nexport type ButtonRadius = 'md' | 'lg' | 'xl';\n\ninterface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant: ButtonVariant;\n size?: ButtonSize;\n radius?: ButtonRadius;\n loading?: boolean;\n disabled?: boolean;\n icon?: React.ReactNode;\n iconPosition?: 'start' | 'end';\n /** Before the label at its own size, never squeezed (an avatar); pairs with an end `icon`. */\n leading?: React.ReactNode;\n /** `start` aligns the label to the leading edge and pins an end `icon` to the far edge. */\n contentAlign?: 'center' | 'start';\n /** Fills the container's width. */\n flexGrow?: boolean;\n /** `wide` holds a 120px floor for a primary action. */\n width?: 'auto' | 'wide';\n fill?: boolean;\n tooltip?: string;\n tooltipPlacement?: 'top' | 'bottom' | 'left' | 'right';\n iconOnly?: boolean;\n /** Pulls a borderless button out by its own padding, so its label lines up with the text around it. */\n flush?: boolean;\n children?: React.ReactNode;\n onClick?: React.MouseEventHandler<HTMLButtonElement & HTMLAnchorElement>;\n /** Renders the button as a real anchor, so a link action is never an <a> nested in a <button>. */\n href?: string;\n target?: React.HTMLAttributeAnchorTarget;\n rel?: string;\n}\n\n// Geometry from the Figma component library, which is variant-independent.\n// `min-h` not `h`: Tailwind emits `h-*` after `min-h-*`, so a hard height would beat a\n// consumer's own `h-*`. Paired with `whitespace-nowrap` (as Figma has it) so a long label\n// cannot wrap into a line the height would clip.\nconst getSizeClasses = (size: ButtonSize) => {\n switch (size) {\n // Figma has no text X-Small button, only the icon-only square — nothing to align to.\n case 'xsmall':\n return '[--btn-px:calc(var(--spacing)*2)] px-[var(--btn-px)] py-1 text-xs';\n case 'small':\n return 'min-h-[var(--malachite-control-height-small,28px)] [--btn-px:10px] px-[var(--btn-px)] text-[length:var(--malachite-font-size-small,13px)] leading-[16px] whitespace-nowrap';\n case 'medium':\n return 'min-h-[var(--malachite-control-height-medium,32px)] [--btn-px:10px] px-[var(--btn-px)] text-[length:var(--malachite-font-size-small,13px)] leading-[16px] whitespace-nowrap';\n case 'large':\n return 'min-h-[var(--malachite-control-height-large,42px)] [--btn-px:12px] px-[var(--btn-px)] text-[length:var(--malachite-font-size-15,15px)] leading-[18px] whitespace-nowrap';\n default:\n return 'min-h-[var(--malachite-control-height-large,42px)] [--btn-px:12px] px-[var(--btn-px)] text-[length:var(--malachite-font-size-15,15px)] leading-[18px] whitespace-nowrap';\n }\n};\n\n// The 1px is the transparent border, which `no-style` drops.\nconst FLUSH_MARGIN = '-mx-[calc(var(--btn-px)+1px)]';\nconst FLUSH_MARGIN_BORDERLESS = '-mx-[var(--btn-px)]';\n\n// `rounded-*` reads the malachite radius tokens via `@theme`, so these follow the theme.\n// An explicit `radius` prop still wins, via getRadiusClass.\nconst getDefaultRadiusClass = (size: ButtonSize, iconOnly: boolean): string => {\n switch (size) {\n // Only the square rounds at 8px — text buttons at this size keep their 6px corner.\n case 'xsmall':\n return iconOnly ? 'rounded-lg' : 'rounded-md';\n case 'small':\n return 'rounded-[var(--malachite-radius-10,10px)]';\n case 'medium':\n return 'rounded-xl';\n case 'large':\n return 'rounded-[var(--malachite-radius-xxl,14px)]';\n default:\n return 'rounded-[var(--malachite-radius-xxl,14px)]';\n }\n};\n\n// Figma pairs a 6px icon gap with the 13px tiers and 8px with the 15px tier.\nconst getIconGapClass = (size: ButtonSize, position: 'start' | 'end'): string => {\n const start = position === 'start';\n switch (size) {\n case 'small':\n case 'medium':\n return start ? 'mr-[6px]' : 'ml-[6px]';\n default:\n return start ? 'mr-2' : 'ml-2';\n }\n};\n\nconst getRadiusClass = (radius: ButtonRadius): string => {\n switch (radius) {\n case 'md':\n return 'rounded-md';\n case 'lg':\n return 'rounded-lg';\n case 'xl':\n return 'rounded-xl';\n default:\n return 'rounded-xl';\n }\n};\n\n// Focus ring: a `focus-visible` outline (keyboard only), coloured to match the variant.\n// Uses `outline` rather than Tailwind's `ring-*` because ring composes `--tw-ring-*`\n// custom properties via box-shadow that rely on document-level @property registration\n// and silently don't paint inside a shadow root (same class of issue as transform\n// utilities). The classes are full literals so Tailwind's JIT can generate them.\nconst FOCUS_OUTLINE = {\n neutral: 'focus-visible:[outline:2px_solid_var(--malachite-text-primary)]',\n destructive: 'focus-visible:[outline:2px_solid_var(--malachite-text-destructive)]',\n success: 'focus-visible:[outline:2px_solid_var(--malachite-text-success)]',\n warning: 'focus-visible:[outline:2px_solid_var(--malachite-text-warning)]',\n error: 'focus-visible:[outline:2px_solid_var(--malachite-text-error)]',\n assistant: 'focus-visible:[outline:2px_solid_var(--malachite-assistant-foreground)]',\n accent: 'focus-visible:[outline:2px_solid_var(--malachite-accent-foreground)]',\n};\n\nconst getVariantClasses = (variant: ButtonVariant, radiusClass: string): string => {\n const baseClasses = `font-medium transition-[color,background-color,border-color,opacity,box-shadow] duration-200 ease-in-out outline-none focus-visible:outline-offset-2 border border-solid ${radiusClass} cursor-pointer`;\n\n switch (variant) {\n case 'filled':\n return `${baseClasses} ${FOCUS_OUTLINE.neutral} btn-filled shadow-default`;\n\n case 'outline':\n return `${baseClasses} ${FOCUS_OUTLINE.neutral} btn-outline shadow-default`;\n\n case 'surface':\n return `${baseClasses} ${FOCUS_OUTLINE.neutral} btn-surface shadow-default`;\n\n case 'assistant':\n return `${baseClasses} ${FOCUS_OUTLINE.assistant} btn-assistant shadow-default`;\n\n case 'destructive':\n return `${baseClasses} ${FOCUS_OUTLINE.destructive} btn-destructive`;\n\n case 'success':\n return `${baseClasses} ${FOCUS_OUTLINE.success} btn-glass-success`;\n\n case 'warning':\n return `${baseClasses} ${FOCUS_OUTLINE.warning} btn-glass-warning`;\n\n case 'error':\n return `${baseClasses} ${FOCUS_OUTLINE.error} btn-glass-error`;\n\n case 'ghost':\n return `${baseClasses} ${FOCUS_OUTLINE.neutral} btn-ghost`;\n\n case 'quiet':\n return `${baseClasses} ${FOCUS_OUTLINE.accent} btn-quiet`;\n\n case 'no-style':\n return `${baseClasses} ${FOCUS_OUTLINE.neutral} btn-no-style border-none`;\n\n default:\n return `${baseClasses} ${FOCUS_OUTLINE.neutral} btn-filled shadow-default`;\n }\n};\n\nconst LoadingSpinner = () => (\n <svg\n className=\"animate-spin -ml-1 mr-2 h-4 w-4\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n);\n\nexport const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonProps>(\n (\n {\n variant,\n size = 'large',\n radius,\n loading = false,\n disabled = false,\n icon,\n iconPosition = 'start',\n leading,\n contentAlign = 'center',\n flexGrow = false,\n width = 'auto',\n fill = false,\n tooltip,\n tooltipPlacement = 'top',\n iconOnly = false,\n flush = false,\n children,\n className = '',\n href,\n target,\n rel,\n ...props\n },\n ref,\n ) => {\n const radiusClass = radius ? getRadiusClass(radius) : getDefaultRadiusClass(size, iconOnly);\n const isInactive = disabled || loading;\n const alignStart = contentAlign === 'start';\n const baseClasses = [\n alignStart\n ? 'flex items-center justify-start text-left'\n : 'flex items-center justify-center',\n iconOnly ? `p-0 ${getIconOnlySizeClasses(size)}` : getSizeClasses(size),\n getVariantClasses(variant, radiusClass),\n flexGrow ? 'w-full' : '',\n width === 'wide' ? 'min-w-[120px]' : '',\n fill ? 'max-w-full' : '',\n isInactive ? 'cursor-not-allowed' : '',\n tooltip && isInactive ? 'pointer-events-none' : '',\n flush && !iconOnly\n ? variant === 'no-style'\n ? FLUSH_MARGIN_BORDERLESS\n : FLUSH_MARGIN\n : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const label = alignStart ? (\n <span className=\"flex min-w-0 flex-1 items-center\">{children}</span>\n ) : (\n children\n );\n\n const leadingSlot = leading ? (\n <span\n className={\n children\n ? 'btn-leading inline-flex shrink-0 items-center me-2'\n : 'btn-leading inline-flex shrink-0 items-center'\n }\n >\n {leading}\n </span>\n ) : null;\n\n const renderContent = () => {\n if (loading) {\n return (\n <>\n {leadingSlot}\n <LoadingSpinner />\n {label}\n </>\n );\n }\n\n const iconWrapperClasses = `inline-flex items-center shrink-0 ${getIconSizeClass(size)}`;\n\n if (icon && iconPosition === 'start') {\n return (\n <>\n {leadingSlot}\n <span\n className={`${iconWrapperClasses}${children ? ` ${getIconGapClass(size, 'start')}` : ''}`}\n >\n {icon}\n </span>\n {label}\n </>\n );\n }\n\n if (icon && iconPosition === 'end') {\n return (\n <>\n {leadingSlot}\n {label}\n <span\n className={`${iconWrapperClasses}${children ? ` ${getIconGapClass(size, 'end')}` : ''}`}\n >\n {icon}\n </span>\n </>\n );\n }\n\n return (\n <>\n {leadingSlot}\n {label}\n </>\n );\n };\n\n // An anchor cannot be disabled, so an inactive link still renders as a <button>.\n const button =\n href && !isInactive ? (\n <a\n ref={ref as React.Ref<HTMLAnchorElement>}\n href={href}\n target={target}\n rel={resolveRel(rel, target)}\n className={baseClasses}\n {...(props as React.AnchorHTMLAttributes<HTMLAnchorElement>)}\n >\n {renderContent()}\n </a>\n ) : (\n <button\n ref={ref as React.Ref<HTMLButtonElement>}\n disabled={isInactive}\n className={baseClasses}\n {...props}\n >\n {renderContent()}\n </button>\n );\n\n if (tooltip) {\n const trigger = isInactive ? (\n <span className={`inline-flex cursor-not-allowed${flexGrow ? ' w-full' : ''}`}>\n {button}\n </span>\n ) : (\n button\n );\n return (\n <Tooltip text={tooltip} placement={tooltipPlacement} fill={flexGrow}>\n {trigger}\n </Tooltip>\n );\n }\n\n return button;\n },\n);\n\nButton.displayName = 'Button';\n\nexport default Button;\n"],"names":["getSizeClasses","size","FLUSH_MARGIN","FLUSH_MARGIN_BORDERLESS","getDefaultRadiusClass","iconOnly","getIconGapClass","position","start","getRadiusClass","radius","FOCUS_OUTLINE","getVariantClasses","variant","radiusClass","baseClasses","LoadingSpinner","jsxs","jsx","Button","forwardRef","loading","disabled","icon","iconPosition","leading","contentAlign","flexGrow","width","fill","tooltip","tooltipPlacement","flush","children","className","href","target","rel","props","ref","isInactive","alignStart","getIconOnlySizeClasses","label","leadingSlot","renderContent","Fragment","iconWrapperClasses","getIconSizeClass","button","resolveRel","Tooltip"],"mappings":"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"}