@stackone/malachite 2.69.0 → 2.71.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 { getTextColor } from './Typography/textStyles';\n\nexport type AlertType = 'info' | 'success' | 'error' | 'warning' | 'assistant';\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}\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 statusIcon = (type: AlertType, icon: React.ReactNode) =>\n icon ? (\n <span\n className=\"alert-status-icon inline-flex shrink-0 [&_svg]:size-full!\"\n style={{ ...ICON_STYLE, color: ICON_COLOR[type] }}\n >\n {icon}\n </span>\n ) : (\n AlertIcon[type]\n );\n\nconst SimpleTextRenderer: React.FC<{\n text: string;\n type: AlertType;\n renderLink?: RenderLink;\n}> = ({ text, type, renderLink }) => {\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}) => {\n const isExpandable = !!children;\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 isExpandable && expanded ? 'flex-col' : 'gap-2 items-center min-h-[2rem]',\n !hasMargin ? 'alert-no-margin' : '',\n 'text-left',\n 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)}\n <div className=\"flex-1 flex items-center\">\n <div className=\"[&>div]:mb-0\" role={liveRole}>\n <SimpleTextRenderer text={message} type={type} renderLink={renderLink} />\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 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","statusIcon","type","icon","SimpleTextRenderer","text","renderLink","MarkdownBlock","Alert","message","hasMargin","className","children","isExpanded","onToggle","action","live","isExpandable","liveRole","internalExpanded","setInternalExpanded","useState","expanded","handleToggle","newExpanded","alertClasses","headerClasses","expandableContentClasses","jsxs","Button","getTextColor","CaretUpIcon","CaretDownIcon","Fragment"],"mappings":";;;;;;;;;;;;AA8CA,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,IAAa,CAACC,GAAiBC,MACjCA,IACI,gBAAAR;AAAA,EAAC;AAAA,EAAA;AAAA,IACG,WAAU;AAAA,IACV,OAAO,EAAE,GAAGH,GAAY,OAAOC,EAAWS,CAAI,EAAA;AAAA,IAE7C,UAAAC;AAAA,EAAA;AACL,IAEAT,EAAUQ,CAAI,GAGhBE,IAID,CAAC,EAAE,MAAAC,GAAM,MAAAH,GAAM,YAAAI,QAEZ,gBAAAX;AAAA,EAACY;AAAA,EAAA;AAAA,IACG,MAAAF;AAAA,IACA,OAAOf,EAAkBY,CAAI;AAAA,IAC7B,MAAK;AAAA,IACL,YAAU;AAAA,IACV,YAAAI;AAAA,EAAA;AAAA,GAKNE,KAA8B,CAAC;AAAA,EACjC,MAAAN,IAAO;AAAA,EACP,SAAAO;AAAA,EACA,WAAAC,IAAY;AAAA,EACZ,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,EACA,UAAAC;AAAA,EACA,QAAAC;AAAA,EACA,MAAAZ;AAAA,EACA,MAAAa,IAAO;AAAA,EACP,YAAAV;AACJ,MAAM;AACF,QAAMW,IAAe,CAAC,CAACL,GAEjBM,IAAWF,IADFd,MAAS,WAAWA,MAAS,YACV,UAAU,WAAY,QAClD,CAACiB,GAAkBC,CAAmB,IAAIC,EAAS,EAAK,GAGxDC,IAAWT,MAAe,SAAYA,IAAaM,GAEnDI,IAAe,MAAM;AACvB,UAAMC,IAAc,CAACF;AACrB,IAAIT,MAAe,UAEfO,EAAoBI,CAAW,GAGnCV,KAAA,QAAAA,EAAWU;AAAA,EACf,GAEMC,IAAe;AAAA,IACjB;AAAA,IACA,SAASvB,CAAI;AAAA,IACb;AAAA,IACA;AAAA;AAAA,IACAe,KAAgBK,IAAW,aAAa;AAAA,IACvCZ,IAAgC,KAApB;AAAA,IACb;AAAA,IACAO,IAAe,oBAAoB;AAAA;AAAA,IACnCN;AAAA,EAAA,EAEC,OAAO,OAAO,EACd,KAAK,GAAG,GAEPe,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,IAgBD,gBAAAW,EAAC,OAAA,EAAI,WAAW,GAAGH,CAAY,qBAE3B,UAAA;AAAA,IAAA,gBAAAG,EAAC,OAAA,EAAI,WAAWF,GACZ,UAAA;AAAA,MAAA,gBAAAE,EAAC,OAAA,EAAI,WAAU,kCACV,UAAA;AAAA,QAAA3B,EAAWC,GAAMC,CAAI;AAAA,QACtB,gBAAAR,EAAC,SAAI,WAAU,4BACX,4BAAC,OAAA,EAAI,WAAU,gBAAe,MAAMuB,GAChC,UAAA,gBAAAvB;AAAA,UAACS;AAAA,UAAA;AAAA,YACG,MAAMK;AAAA,YACN,MAAAP;AAAA,YACA,YAAAI;AAAA,UAAA;AAAA,QAAA,GAER,EAAA,CACJ;AAAA,MAAA,GACJ;AAAA,MAGA,gBAAAsB,EAAC,OAAA,EAAI,WAAU,2BACV,UAAA;AAAA,QAAAb,KAAU,gBAAApB,EAAC,OAAA,EAAI,WAAU,kCAAkC,UAAAoB,GAAO;AAAA,QAEnE,gBAAApB;AAAA,UAACkC;AAAA,UAAA;AAAA,YACG,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,SAASN;AAAA,YACT,cAAYD,IAAW,aAAa;AAAA,YACpC,OAAO;AAAA,cACH,OAAOQ,EAAaxC,EAAkBY,CAAI,CAAC;AAAA,YAAA;AAAA,YAG9C,UAAAoB,IACG,gBAAA3B;AAAA,cAACoC;AAAA,cAAA;AAAA,gBACG,OAAO,EAAE,UAAU,QAAQ,WAAW,OAAA;AAAA,cAAO;AAAA,YAAA,IAGjD,gBAAApC;AAAA,cAACqC;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,IAGCV,KACG,gBAAA3B,EAAAsC,GAAA,EACI,UAAA,gBAAAtC,EAAC,OAAA,EAAI,WAAWgC,GACZ,UAAA,gBAAAhC,EAAC,OAAA,EAAI,WAAU,6KACV,UAAAiB,EAAA,CACL,EAAA,CACJ,EAAA,CACJ;AAAA,EAAA,GAER,IAlEI,gBAAAgB,EAAC,OAAA,EAAI,WAAWH,GACX,UAAA;AAAA,IAAAxB,EAAWC,GAAMC,CAAI;AAAA,sBACrB,OAAA,EAAI,WAAU,4BACX,UAAA,gBAAAR,EAAC,SAAI,WAAU,gBAAe,MAAMuB,GAChC,4BAACd,GAAA,EAAmB,MAAMK,GAAS,MAAAP,GAAY,YAAAI,EAAA,CAAwB,GAC3E,GACJ;AAAA,IACCS,KAAU,gBAAApB,EAAC,OAAA,EAAI,WAAU,2CAA2C,UAAAoB,EAAA,CAAO;AAAA,EAAA,GAChF;AA4DZ;"}
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":"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"}
@@ -1,27 +1,27 @@
1
1
  import { jsx as r, jsxs as i, Fragment as d } from "react/jsx-runtime";
2
- import { forwardRef as O } from "react";
3
- import T from "./Tooltip.js";
4
- import { getIconOnlySizeClasses as U, getIconSizeClass as V } from "./buttonSizing.js";
5
- import { resolveRel as W } from "./linkRel.js";
6
- const A = (s) => {
7
- switch (s) {
2
+ import { forwardRef as A } from "react";
3
+ import E from "./Tooltip.js";
4
+ import { getIconOnlySizeClasses as H, getIconSizeClass as M } from "./buttonSizing.js";
5
+ import { resolveRel as D } from "./linkRel.js";
6
+ const O = (t) => {
7
+ switch (t) {
8
8
  // Figma has no text X-Small button, only the icon-only square — nothing to align to.
9
9
  case "xsmall":
10
- return "px-2 py-1 text-xs";
10
+ return "[--btn-px:calc(var(--spacing)*2)] px-[var(--btn-px)] py-1 text-xs";
11
11
  case "small":
12
- return "min-h-[var(--malachite-control-height-small,28px)] px-[10px] text-[length:var(--malachite-font-size-small,13px)] leading-[16px] whitespace-nowrap";
12
+ 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";
13
13
  case "medium":
14
- return "min-h-[var(--malachite-control-height-medium,32px)] px-[10px] text-[length:var(--malachite-font-size-small,13px)] leading-[16px] whitespace-nowrap";
14
+ 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";
15
15
  case "large":
16
- return "min-h-[var(--malachite-control-height-large,42px)] px-[12px] text-[length:var(--malachite-font-size-15,15px)] leading-[18px] whitespace-nowrap";
16
+ 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";
17
17
  default:
18
- return "min-h-[var(--malachite-control-height-large,42px)] px-[12px] text-[length:var(--malachite-font-size-15,15px)] leading-[18px] whitespace-nowrap";
18
+ 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";
19
19
  }
20
- }, D = (s, t) => {
21
- switch (s) {
20
+ }, T = "-mx-[calc(var(--btn-px)+1px)]", V = "-mx-[var(--btn-px)]", W = (t, a) => {
21
+ switch (t) {
22
22
  // Only the square rounds at 8px — text buttons at this size keep their 6px corner.
23
23
  case "xsmall":
24
- return t ? "rounded-lg" : "rounded-md";
24
+ return a ? "rounded-lg" : "rounded-md";
25
25
  case "small":
26
26
  return "rounded-[var(--malachite-radius-10,10px)]";
27
27
  case "medium":
@@ -31,17 +31,17 @@ const A = (s) => {
31
31
  default:
32
32
  return "rounded-[var(--malachite-radius-xxl,14px)]";
33
33
  }
34
- }, k = (s, t) => {
35
- const e = t === "start";
36
- switch (s) {
34
+ }, R = (t, a) => {
35
+ const e = a === "start";
36
+ switch (t) {
37
37
  case "small":
38
38
  case "medium":
39
39
  return e ? "mr-[6px]" : "ml-[6px]";
40
40
  default:
41
41
  return e ? "mr-2" : "ml-2";
42
42
  }
43
- }, E = (s) => {
44
- switch (s) {
43
+ }, G = (t) => {
44
+ switch (t) {
45
45
  case "md":
46
46
  return "rounded-md";
47
47
  case "lg":
@@ -51,40 +51,40 @@ const A = (s) => {
51
51
  default:
52
52
  return "rounded-xl";
53
53
  }
54
- }, a = {
54
+ }, s = {
55
55
  neutral: "focus-visible:[outline:2px_solid_var(--malachite-text-primary)]",
56
56
  destructive: "focus-visible:[outline:2px_solid_var(--malachite-text-destructive)]",
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
60
  assistant: "focus-visible:[outline:2px_solid_var(--malachite-assistant-foreground)]"
61
- }, H = (s, t) => {
62
- 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 ${t} cursor-pointer`;
63
- switch (s) {
61
+ }, q = (t, a) => {
62
+ 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
+ switch (t) {
64
64
  case "filled":
65
- return `${e} ${a.neutral} btn-filled shadow-default`;
65
+ return `${e} ${s.neutral} btn-filled shadow-default`;
66
66
  case "outline":
67
- return `${e} ${a.neutral} btn-outline shadow-default`;
67
+ return `${e} ${s.neutral} btn-outline shadow-default`;
68
68
  case "surface":
69
- return `${e} ${a.neutral} btn-surface shadow-default`;
69
+ return `${e} ${s.neutral} btn-surface shadow-default`;
70
70
  case "assistant":
71
- return `${e} ${a.assistant} btn-assistant shadow-default`;
71
+ return `${e} ${s.assistant} btn-assistant shadow-default`;
72
72
  case "destructive":
73
- return `${e} ${a.destructive} btn-destructive`;
73
+ return `${e} ${s.destructive} btn-destructive`;
74
74
  case "success":
75
- return `${e} ${a.success} btn-glass-success`;
75
+ return `${e} ${s.success} btn-glass-success`;
76
76
  case "warning":
77
- return `${e} ${a.warning} btn-glass-warning`;
77
+ return `${e} ${s.warning} btn-glass-warning`;
78
78
  case "error":
79
- return `${e} ${a.error} btn-glass-error`;
79
+ return `${e} ${s.error} btn-glass-error`;
80
80
  case "ghost":
81
- return `${e} ${a.neutral} btn-ghost`;
81
+ return `${e} ${s.neutral} btn-ghost`;
82
82
  case "no-style":
83
- return `${e} ${a.neutral} btn-no-style border-none`;
83
+ return `${e} ${s.neutral} btn-no-style border-none`;
84
84
  default:
85
- return `${e} ${a.neutral} btn-filled shadow-default`;
85
+ return `${e} ${s.neutral} btn-filled shadow-default`;
86
86
  }
87
- }, M = () => /* @__PURE__ */ i(
87
+ }, J = () => /* @__PURE__ */ i(
88
88
  "svg",
89
89
  {
90
90
  className: "animate-spin -ml-1 mr-2 h-4 w-4",
@@ -113,83 +113,85 @@ const A = (s) => {
113
113
  )
114
114
  ]
115
115
  }
116
- ), q = O(
116
+ ), K = A(
117
117
  ({
118
- variant: s,
119
- size: t = "large",
118
+ variant: t,
119
+ size: a = "large",
120
120
  radius: e,
121
- loading: f = !1,
122
- disabled: j = !1,
123
- icon: o,
124
- iconPosition: g = "start",
121
+ loading: g = !1,
122
+ disabled: I = !1,
123
+ icon: c,
124
+ iconPosition: b = "start",
125
125
  leading: v,
126
- contentAlign: I = "center",
126
+ contentAlign: k = "center",
127
127
  flexGrow: m = !1,
128
- fill: z = !1,
128
+ fill: j = !1,
129
129
  tooltip: x,
130
130
  tooltipPlacement: B = "top",
131
- iconOnly: w = !1,
131
+ iconOnly: p = !1,
132
+ flush: L = !1,
132
133
  children: n,
133
- className: R = "",
134
- href: b,
134
+ className: z = "",
135
+ href: w,
135
136
  target: $,
136
137
  rel: F,
137
138
  ...C
138
139
  }, _) => {
139
- const L = e ? E(e) : D(t, w), l = j || f, N = I === "start", y = [
140
+ const U = e ? G(e) : W(a, p), l = I || g, N = k === "start", y = [
140
141
  N ? "flex items-center justify-start text-left" : "flex items-center justify-center",
141
- w ? `p-0 ${U(t)}` : A(t),
142
- H(s, L),
142
+ p ? `p-0 ${H(a)}` : O(a),
143
+ q(t, U),
143
144
  m ? "w-full" : "",
144
- z ? "max-w-full" : "",
145
+ j ? "max-w-full" : "",
145
146
  l ? "cursor-not-allowed" : "",
146
147
  x && l ? "pointer-events-none" : "",
147
- R
148
- ].filter(Boolean).join(" "), c = N ? /* @__PURE__ */ r("span", { className: "flex min-w-0 flex-1 items-center", children: n }) : n, u = v ? /* @__PURE__ */ r(
148
+ L && !p ? t === "no-style" ? V : T : "",
149
+ z
150
+ ].filter(Boolean).join(" "), o = N ? /* @__PURE__ */ r("span", { className: "flex min-w-0 flex-1 items-center", children: n }) : n, u = v ? /* @__PURE__ */ r(
149
151
  "span",
150
152
  {
151
153
  className: n ? "btn-leading inline-flex shrink-0 items-center me-2" : "btn-leading inline-flex shrink-0 items-center",
152
154
  children: v
153
155
  }
154
156
  ) : null, S = () => {
155
- if (f)
157
+ if (g)
156
158
  return /* @__PURE__ */ i(d, { children: [
157
159
  u,
158
- /* @__PURE__ */ r(M, {}),
159
- c
160
+ /* @__PURE__ */ r(J, {}),
161
+ o
160
162
  ] });
161
- const h = `inline-flex items-center shrink-0 ${V(t)}`;
162
- return o && g === "start" ? /* @__PURE__ */ i(d, { children: [
163
+ const f = `inline-flex items-center shrink-0 ${M(a)}`;
164
+ return c && b === "start" ? /* @__PURE__ */ i(d, { children: [
163
165
  u,
164
166
  /* @__PURE__ */ r(
165
167
  "span",
166
168
  {
167
- className: `${h}${n ? ` ${k(t, "start")}` : ""}`,
168
- children: o
169
+ className: `${f}${n ? ` ${R(a, "start")}` : ""}`,
170
+ children: c
169
171
  }
170
172
  ),
171
- c
172
- ] }) : o && g === "end" ? /* @__PURE__ */ i(d, { children: [
173
+ o
174
+ ] }) : c && b === "end" ? /* @__PURE__ */ i(d, { children: [
173
175
  u,
174
- c,
176
+ o,
175
177
  /* @__PURE__ */ r(
176
178
  "span",
177
179
  {
178
- className: `${h}${n ? ` ${k(t, "end")}` : ""}`,
179
- children: o
180
+ className: `${f}${n ? ` ${R(a, "end")}` : ""}`,
181
+ children: c
180
182
  }
181
183
  )
182
184
  ] }) : /* @__PURE__ */ i(d, { children: [
183
185
  u,
184
- c
186
+ o
185
187
  ] });
186
- }, p = b && !l ? /* @__PURE__ */ r(
188
+ }, h = w && !l ? /* @__PURE__ */ r(
187
189
  "a",
188
190
  {
189
191
  ref: _,
190
- href: b,
192
+ href: w,
191
193
  target: $,
192
- rel: W(F, $),
194
+ rel: D(F, $),
193
195
  className: y,
194
196
  ...C,
195
197
  children: S()
@@ -204,12 +206,12 @@ const A = (s) => {
204
206
  children: S()
205
207
  }
206
208
  );
207
- return x ? /* @__PURE__ */ r(T, { text: x, placement: B, fill: m, children: l ? /* @__PURE__ */ r("span", { className: `inline-flex cursor-not-allowed${m ? " w-full" : ""}`, children: p }) : p }) : p;
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;
208
210
  }
209
211
  );
210
- q.displayName = "Button";
212
+ K.displayName = "Button";
211
213
  export {
212
- q as Button,
213
- q as default
214
+ K as Button,
215
+ K as default
214
216
  };
215
217
  //# 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 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 'px-2 py-1 text-xs';\n case 'small':\n return 'min-h-[var(--malachite-control-height-small,28px)] px-[10px] 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)] px-[10px] 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)] px-[12px] text-[length:var(--malachite-font-size-15,15px)] leading-[18px] whitespace-nowrap';\n default:\n return 'min-h-[var(--malachite-control-height-large,42px)] px-[12px] text-[length:var(--malachite-font-size-15,15px)] leading-[18px] whitespace-nowrap';\n }\n};\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 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 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","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","children","className","href","target","rel","props","ref","isInactive","alignStart","getIconOnlySizeClasses","label","leadingSlot","renderContent","Fragment","iconWrapperClasses","getIconSizeClass","button","resolveRel","Tooltip"],"mappings":";;;;;AA8DA,MAAMA,IAAiB,CAACC,MAAqB;AACzC,UAAQA,GAAA;AAAA;AAAA,IAEJ,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX;AACI,aAAO;AAAA,EAAA;AAEnB,GAIMC,IAAwB,CAACD,GAAkBE,MAA8B;AAC3E,UAAQF,GAAA;AAAA;AAAA,IAEJ,KAAK;AACD,aAAOE,IAAW,eAAe;AAAA,IACrC,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX;AACI,aAAO;AAAA,EAAA;AAEnB,GAGMC,IAAkB,CAACH,GAAkBI,MAAsC;AAC7E,QAAMC,IAAQD,MAAa;AAC3B,UAAQJ,GAAA;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AACD,aAAOK,IAAQ,aAAa;AAAA,IAChC;AACI,aAAOA,IAAQ,SAAS;AAAA,EAAA;AAEpC,GAEMC,IAAiB,CAACC,MAAiC;AACrD,UAAQA,GAAA;AAAA,IACJ,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX;AACI,aAAO;AAAA,EAAA;AAEnB,GAOMC,IAAgB;AAAA,EAClB,SAAS;AAAA,EACT,aAAa;AAAA,EACb,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAO;AAAA,EACP,WAAW;AACf,GAEMC,IAAoB,CAACC,GAAwBC,MAAgC;AAC/E,QAAMC,IAAc,4KAA4KD,CAAW;AAE3M,UAAQD,GAAA;AAAA,IACJ,KAAK;AACD,aAAO,GAAGE,CAAW,IAAIJ,EAAc,OAAO;AAAA,IAElD,KAAK;AACD,aAAO,GAAGI,CAAW,IAAIJ,EAAc,OAAO;AAAA,IAElD,KAAK;AACD,aAAO,GAAGI,CAAW,IAAIJ,EAAc,OAAO;AAAA,IAElD,KAAK;AACD,aAAO,GAAGI,CAAW,IAAIJ,EAAc,SAAS;AAAA,IAEpD,KAAK;AACD,aAAO,GAAGI,CAAW,IAAIJ,EAAc,WAAW;AAAA,IAEtD,KAAK;AACD,aAAO,GAAGI,CAAW,IAAIJ,EAAc,OAAO;AAAA,IAElD,KAAK;AACD,aAAO,GAAGI,CAAW,IAAIJ,EAAc,OAAO;AAAA,IAElD,KAAK;AACD,aAAO,GAAGI,CAAW,IAAIJ,EAAc,KAAK;AAAA,IAEhD,KAAK;AACD,aAAO,GAAGI,CAAW,IAAIJ,EAAc,OAAO;AAAA,IAElD,KAAK;AACD,aAAO,GAAGI,CAAW,IAAIJ,EAAc,OAAO;AAAA,IAElD;AACI,aAAO,GAAGI,CAAW,IAAIJ,EAAc,OAAO;AAAA,EAAA;AAE1D,GAEMK,IAAiB,MACnB,gBAAAC;AAAA,EAAC;AAAA,EAAA;AAAA,IACG,WAAU;AAAA,IACV,OAAM;AAAA,IACN,MAAK;AAAA,IACL,SAAQ;AAAA,IAER,UAAA;AAAA,MAAA,gBAAAC;AAAA,QAAC;AAAA,QAAA;AAAA,UACG,WAAU;AAAA,UACV,IAAG;AAAA,UACH,IAAG;AAAA,UACH,GAAE;AAAA,UACF,QAAO;AAAA,UACP,aAAY;AAAA,QAAA;AAAA,MAAA;AAAA,MAEhB,gBAAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACG,WAAU;AAAA,UACV,MAAK;AAAA,UACL,GAAE;AAAA,QAAA;AAAA,MAAA;AAAA,IACN;AAAA,EAAA;AACJ,GAGSC,IAASC;AAAA,EAClB,CACI;AAAA,IACI,SAAAP;AAAA,IACA,MAAAV,IAAO;AAAA,IACP,QAAAO;AAAA,IACA,SAAAW,IAAU;AAAA,IACV,UAAAC,IAAW;AAAA,IACX,MAAAC;AAAA,IACA,cAAAC,IAAe;AAAA,IACf,SAAAC;AAAA,IACA,cAAAC,IAAe;AAAA,IACf,UAAAC,IAAW;AAAA,IACX,MAAAC,IAAO;AAAA,IACP,SAAAC;AAAA,IACA,kBAAAC,IAAmB;AAAA,IACnB,UAAAzB,IAAW;AAAA,IACX,UAAA0B;AAAA,IACA,WAAAC,IAAY;AAAA,IACZ,MAAAC;AAAA,IACA,QAAAC;AAAA,IACA,KAAAC;AAAA,IACA,GAAGC;AAAA,EAAA,GAEPC,MACC;AACD,UAAMvB,IAAcJ,IAASD,EAAeC,CAAM,IAAIN,EAAsBD,GAAME,CAAQ,GACpFiC,IAAahB,KAAYD,GACzBkB,IAAab,MAAiB,SAC9BX,IAAc;AAAA,MAChBwB,IACM,8CACA;AAAA,MACNlC,IAAW,OAAOmC,EAAuBrC,CAAI,CAAC,KAAKD,EAAeC,CAAI;AAAA,MACtES,EAAkBC,GAASC,CAAW;AAAA,MACtCa,IAAW,WAAW;AAAA,MACtBC,IAAO,eAAe;AAAA,MACtBU,IAAa,uBAAuB;AAAA,MACpCT,KAAWS,IAAa,wBAAwB;AAAA,MAChDN;AAAA,IAAA,EAEC,OAAO,OAAO,EACd,KAAK,GAAG,GAEPS,IAAQF,IACV,gBAAArB,EAAC,UAAK,WAAU,oCAAoC,UAAAa,GAAS,IAE7DA,GAGEW,IAAcjB,IAChB,gBAAAP;AAAA,MAAC;AAAA,MAAA;AAAA,QACG,WACIa,IACM,uDACA;AAAA,QAGT,UAAAN;AAAA,MAAA;AAAA,IAAA,IAEL,MAEEkB,IAAgB,MAAM;AACxB,UAAItB;AACA,eACI,gBAAAJ,EAAA2B,GAAA,EACK,UAAA;AAAA,UAAAF;AAAA,4BACA1B,GAAA,EAAe;AAAA,UACfyB;AAAA,QAAA,GACL;AAIR,YAAMI,IAAqB,qCAAqCC,EAAiB3C,CAAI,CAAC;AAEtF,aAAIoB,KAAQC,MAAiB,UAErB,gBAAAP,EAAA2B,GAAA,EACK,UAAA;AAAA,QAAAF;AAAA,QACD,gBAAAxB;AAAA,UAAC;AAAA,UAAA;AAAA,YACG,WAAW,GAAG2B,CAAkB,GAAGd,IAAW,IAAIzB,EAAgBH,GAAM,OAAO,CAAC,KAAK,EAAE;AAAA,YAEtF,UAAAoB;AAAA,UAAA;AAAA,QAAA;AAAA,QAEJkB;AAAA,MAAA,GACL,IAIJlB,KAAQC,MAAiB,QAErB,gBAAAP,EAAA2B,GAAA,EACK,UAAA;AAAA,QAAAF;AAAA,QACAD;AAAA,QACD,gBAAAvB;AAAA,UAAC;AAAA,UAAA;AAAA,YACG,WAAW,GAAG2B,CAAkB,GAAGd,IAAW,IAAIzB,EAAgBH,GAAM,KAAK,CAAC,KAAK,EAAE;AAAA,YAEpF,UAAAoB;AAAA,UAAA;AAAA,QAAA;AAAA,MACL,GACJ,IAKJ,gBAAAN,EAAA2B,GAAA,EACK,UAAA;AAAA,QAAAF;AAAA,QACAD;AAAA,MAAA,GACL;AAAA,IAER,GAGMM,IACFd,KAAQ,CAACK,IACL,gBAAApB;AAAA,MAAC;AAAA,MAAA;AAAA,QACG,KAAAmB;AAAA,QACA,MAAAJ;AAAA,QACA,QAAAC;AAAA,QACA,KAAKc,EAAWb,GAAKD,CAAM;AAAA,QAC3B,WAAWnB;AAAA,QACV,GAAIqB;AAAA,QAEJ,UAAAO,EAAA;AAAA,MAAc;AAAA,IAAA,IAGnB,gBAAAzB;AAAA,MAAC;AAAA,MAAA;AAAA,QACG,KAAAmB;AAAA,QACA,UAAUC;AAAA,QACV,WAAWvB;AAAA,QACV,GAAGqB;AAAA,QAEH,UAAAO,EAAA;AAAA,MAAc;AAAA,IAAA;AAI3B,WAAId,IASI,gBAAAX,EAAC+B,KAAQ,MAAMpB,GAAS,WAAWC,GAAkB,MAAMH,GACtD,UATOW,IACZ,gBAAApB,EAAC,QAAA,EAAK,WAAW,iCAAiCS,IAAW,YAAY,EAAE,IACtE,UAAAoB,EAAA,CACL,IAEAA,EAIK,CACL,IAIDA;AAAA,EACX;AACJ;AAEA5B,EAAO,cAAc;"}
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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"}