@karakuri-ui/react 0.3.10 → 0.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ui/accordion.d.mts +1 -1
- package/dist/components/ui/accordion.d.ts +1 -1
- package/dist/components/ui/alert.d.mts +2 -0
- package/dist/components/ui/alert.d.ts +2 -0
- package/dist/components/ui/alert.js +2 -1
- package/dist/components/ui/alert.js.map +1 -1
- package/dist/components/ui/alert.mjs +2 -1
- package/dist/components/ui/alert.mjs.map +1 -1
- package/dist/components/ui/badge.d.mts +1 -1
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/badge.js +54 -43
- package/dist/components/ui/badge.js.map +1 -1
- package/dist/components/ui/badge.mjs +55 -44
- package/dist/components/ui/badge.mjs.map +1 -1
- package/dist/components/ui/button.js +31 -20
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/button.mjs +31 -20
- package/dist/components/ui/button.mjs.map +1 -1
- package/dist/components/ui/chart.d.mts +1 -1
- package/dist/components/ui/chart.d.ts +1 -1
- package/dist/components/ui/drawer.d.mts +2 -0
- package/dist/components/ui/drawer.d.ts +2 -0
- package/dist/components/ui/drawer.js +2 -2
- package/dist/components/ui/drawer.js.map +1 -1
- package/dist/components/ui/drawer.mjs +2 -2
- package/dist/components/ui/drawer.mjs.map +1 -1
- package/dist/components/ui/dropdown.js +35 -18
- package/dist/components/ui/dropdown.js.map +1 -1
- package/dist/components/ui/dropdown.mjs +35 -18
- package/dist/components/ui/dropdown.mjs.map +1 -1
- package/dist/components/ui/icon-button.js +24 -13
- package/dist/components/ui/icon-button.js.map +1 -1
- package/dist/components/ui/icon-button.mjs +24 -13
- package/dist/components/ui/icon-button.mjs.map +1 -1
- package/dist/components/ui/modal.d.mts +2 -0
- package/dist/components/ui/modal.d.ts +2 -0
- package/dist/components/ui/modal.js +2 -2
- package/dist/components/ui/modal.js.map +1 -1
- package/dist/components/ui/modal.mjs +2 -2
- package/dist/components/ui/modal.mjs.map +1 -1
- package/dist/components/ui/navigation-menu.js +25 -11
- package/dist/components/ui/navigation-menu.js.map +1 -1
- package/dist/components/ui/navigation-menu.mjs +25 -11
- package/dist/components/ui/navigation-menu.mjs.map +1 -1
- package/dist/components/ui/popover.d.mts +2 -0
- package/dist/components/ui/popover.d.ts +2 -0
- package/dist/components/ui/popover.js +2 -2
- package/dist/components/ui/popover.js.map +1 -1
- package/dist/components/ui/popover.mjs +2 -2
- package/dist/components/ui/popover.mjs.map +1 -1
- package/dist/components/ui/segmented.js +3 -1
- package/dist/components/ui/segmented.js.map +1 -1
- package/dist/components/ui/segmented.mjs +3 -1
- package/dist/components/ui/segmented.mjs.map +1 -1
- package/dist/components/ui/select.js +16 -1
- package/dist/components/ui/select.js.map +1 -1
- package/dist/components/ui/select.mjs +16 -1
- package/dist/components/ui/select.mjs.map +1 -1
- package/dist/components/ui/toast.d.mts +3 -1
- package/dist/components/ui/toast.d.ts +3 -1
- package/dist/components/ui/toast.js +4 -1
- package/dist/components/ui/toast.js.map +1 -1
- package/dist/components/ui/toast.mjs +4 -1
- package/dist/components/ui/toast.mjs.map +1 -1
- package/package.json +5 -5
|
@@ -25,7 +25,7 @@ interface AccordionItemProps extends React.ComponentPropsWithoutRef<typeof Accor
|
|
|
25
25
|
}
|
|
26
26
|
declare const AccordionItem: React.ForwardRefExoticComponent<AccordionItemProps & React.RefAttributes<HTMLDivElement>>;
|
|
27
27
|
declare const accordionTriggerVariants: (props?: ({
|
|
28
|
-
size?: "
|
|
28
|
+
size?: "default" | "sm" | "lg" | null | undefined;
|
|
29
29
|
iconPosition?: "left" | "right" | null | undefined;
|
|
30
30
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
31
31
|
interface AccordionTriggerProps extends React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger> {
|
|
@@ -25,7 +25,7 @@ interface AccordionItemProps extends React.ComponentPropsWithoutRef<typeof Accor
|
|
|
25
25
|
}
|
|
26
26
|
declare const AccordionItem: React.ForwardRefExoticComponent<AccordionItemProps & React.RefAttributes<HTMLDivElement>>;
|
|
27
27
|
declare const accordionTriggerVariants: (props?: ({
|
|
28
|
-
size?: "
|
|
28
|
+
size?: "default" | "sm" | "lg" | null | undefined;
|
|
29
29
|
iconPosition?: "left" | "right" | null | undefined;
|
|
30
30
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
31
31
|
interface AccordionTriggerProps extends React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger> {
|
|
@@ -22,6 +22,8 @@ interface AlertProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<
|
|
|
22
22
|
onClose?: () => void;
|
|
23
23
|
/** Custom close icon */
|
|
24
24
|
closeIcon?: React.ReactNode;
|
|
25
|
+
/** Accessible label for the close button (screen readers) — localize per page */
|
|
26
|
+
closeLabel?: string;
|
|
25
27
|
/** Custom status icon (overrides default) */
|
|
26
28
|
icon?: React.ReactNode;
|
|
27
29
|
/** Hide the status icon entirely */
|
|
@@ -22,6 +22,8 @@ interface AlertProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<
|
|
|
22
22
|
onClose?: () => void;
|
|
23
23
|
/** Custom close icon */
|
|
24
24
|
closeIcon?: React.ReactNode;
|
|
25
|
+
/** Accessible label for the close button (screen readers) — localize per page */
|
|
26
|
+
closeLabel?: string;
|
|
25
27
|
/** Custom status icon (overrides default) */
|
|
26
28
|
icon?: React.ReactNode;
|
|
27
29
|
/** Hide the status icon entirely */
|
|
@@ -150,6 +150,7 @@ const Alert = React.forwardRef(
|
|
|
150
150
|
closable = false,
|
|
151
151
|
onClose,
|
|
152
152
|
closeIcon,
|
|
153
|
+
closeLabel = "Close",
|
|
153
154
|
icon,
|
|
154
155
|
hideIcon = false,
|
|
155
156
|
children,
|
|
@@ -182,7 +183,7 @@ const Alert = React.forwardRef(
|
|
|
182
183
|
size === "sm" ? "group-has-[[data-alert-description]]:top-2 group-has-[[data-alert-description]]:right-2" : "group-has-[[data-alert-description]]:top-3 group-has-[[data-alert-description]]:right-3"
|
|
183
184
|
),
|
|
184
185
|
onClick: onClose,
|
|
185
|
-
"aria-label":
|
|
186
|
+
"aria-label": closeLabel,
|
|
186
187
|
children: closeIcon || /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DefaultCloseIcon, { className: "icon-sm" })
|
|
187
188
|
}
|
|
188
189
|
)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/alert.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '../../lib/utils'\n\n// ─── Built-in status icons (no external dependency) ──────────────\n// All icons: Lucide-compatible 24×24 viewBox, strokeWidth=2, rounded caps/joins\n// Info = circle + \"i\" | Success = circle + check | Warning = triangle + \"!\" | Error = circle + small ×\nconst InfoIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n </svg>\n)\n\nconst SuccessIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m9 12 2 2 4-4\" />\n </svg>\n)\n\nconst WarningIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n </svg>\n)\n\nconst ErrorIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 8v5\" />\n <path d=\"M12 16h.01\" />\n </svg>\n)\n\nconst DefaultCloseIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2} aria-hidden=\"true\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n)\n\n// ─── Status icon mapping ─────────────────────────────────────────\nconst STATUS_ICONS: Record<AlertColor, React.FC<{ className?: string }>> = {\n info: InfoIcon,\n success: SuccessIcon,\n warning: WarningIcon,\n error: ErrorIcon,\n}\n\n// ─── Color × Variant class mapping ──────────────────────────────\n// Uses semantic tokens (*.tint, text-text-*) — same pattern as Badge\nconst colorMap = {\n info: {\n default: 'bg-info-tint text-text-info border-info/20',\n outline: 'bg-transparent text-text-info border-info',\n filled: 'bg-info text-info-foreground border-transparent',\n },\n success: {\n default: 'bg-success-tint text-text-success border-success/20',\n outline: 'bg-transparent text-text-success border-success',\n filled: 'bg-success text-success-foreground border-transparent',\n },\n warning: {\n default: 'bg-warning-tint text-text-warning border-warning/20',\n outline: 'bg-transparent text-text-warning border-warning',\n filled: 'bg-warning text-warning-foreground border-transparent',\n },\n error: {\n default: 'bg-error-tint text-text-error border-error/20',\n outline: 'bg-transparent text-text-error border-error',\n filled: 'bg-error text-error-foreground border-transparent',\n },\n} as const\n\n// ─── Alert variants ──────────────────────────────────────────────\nconst alertVariants = cva(\n 'group relative flex items-center border w-full [&:has([data-alert-description])]:items-start',\n {\n variants: {\n size: {\n sm: 'gap-2 p-3 text-sm',\n default: 'gap-2.5 p-4 text-md',\n lg: 'gap-3 p-5 text-md',\n },\n radius: {\n none: 'rounded-none',\n sm: 'rounded-sm',\n base: 'rounded',\n md: 'rounded-md',\n lg: 'rounded-lg',\n xl: 'rounded-xl',\n '2xl': 'rounded-2xl',\n '3xl': 'rounded-3xl',\n full: 'rounded-full',\n },\n },\n defaultVariants: {\n size: 'default',\n radius: 'lg',\n },\n }\n)\n\n// ─── Size-dependent class maps ──────────────────────────────────\nconst ICON_SIZE_MAP = {\n sm: 'icon-sm', // 16px\n default: 'w-[18px] h-[18px]', // 18px — TOKEN-EXCEPTION: no 18px icon token\n lg: 'icon-md', // 20px\n} as const\n\nconst TITLE_SIZE_MAP = {\n sm: 'text-sm font-semibold leading-4 tracking-tight',\n default: 'font-semibold leading-[18px] tracking-tight',\n lg: 'text-base font-semibold leading-5 tracking-tight',\n} as const\n\nconst DESC_SIZE_MAP = {\n sm: 'text-xs mt-0.5',\n default: 'text-sm mt-1',\n lg: 'text-md mt-1.5',\n} as const\n\n// ─── Types ───────────────────────────────────────────────────────\nexport type AlertVariant = 'default' | 'outline' | 'filled'\nexport type AlertColor = 'info' | 'success' | 'warning' | 'error'\nexport type AlertSize = 'sm' | 'default' | 'lg'\n\n// ─── Context ─────────────────────────────────────────────────────\ninterface AlertContextValue {\n variant: AlertVariant\n color: AlertColor\n size: AlertSize\n}\n\nconst AlertContext = React.createContext<AlertContextValue>({\n variant: 'default',\n color: 'info',\n size: 'default',\n})\n\nfunction useAlertContext() {\n return React.useContext(AlertContext)\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// Alert\n// ═══════════════════════════════════════════════════════════════════\n\n// ─── Alert ───────────────────────────────────────────────────────\nexport interface AlertProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof alertVariants> {\n /** Visual style */\n variant?: AlertVariant\n /** Semantic color (determines icon, colors, and ARIA role) */\n color?: AlertColor\n /** Size */\n size?: AlertSize\n /** Closable — shows close button */\n closable?: boolean\n /** Close callback */\n onClose?: () => void\n /** Custom close icon */\n closeIcon?: React.ReactNode\n /** Custom status icon (overrides default) */\n icon?: React.ReactNode\n /** Hide the status icon entirely */\n hideIcon?: boolean\n}\n\nconst Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n ({\n className,\n variant = 'default',\n color = 'info',\n size = 'default',\n radius,\n closable = false,\n onClose,\n closeIcon,\n icon,\n hideIcon = false,\n children,\n ...props\n }, ref) => {\n // error → role=\"alert\" (assertive), others → role=\"status\" (polite)\n const role = color === 'error' ? 'alert' : 'status'\n const colorClasses = colorMap[color][variant]\n const StatusIcon = STATUS_ICONS[color]\n\n return (\n <AlertContext.Provider value={{ variant, color, size }}>\n <div\n ref={ref}\n role={role}\n className={cn(\n alertVariants({ size, radius }),\n colorClasses,\n className,\n )}\n {...props}\n >\n {/* Icon */}\n {!hideIcon && (\n <span className=\"shrink-0\">\n {icon || <StatusIcon className={ICON_SIZE_MAP[size]} />}\n </span>\n )}\n\n {/* Content area */}\n <div className=\"flex-1 min-w-0\">\n {children}\n </div>\n\n {/* Close button */}\n {closable && (\n <button\n type=\"button\"\n className={cn(\n 'shrink-0 rounded-md p-1 opacity-70 hover:opacity-100 transition-opacity focus-visible:focus-ring',\n 'group-has-[[data-alert-description]]:absolute',\n size === 'sm'\n ? 'group-has-[[data-alert-description]]:top-2 group-has-[[data-alert-description]]:right-2'\n : 'group-has-[[data-alert-description]]:top-3 group-has-[[data-alert-description]]:right-3',\n )}\n onClick={onClose}\n aria-label=\"Close\"\n >\n {closeIcon || <DefaultCloseIcon className=\"icon-sm\" />}\n </button>\n )}\n </div>\n </AlertContext.Provider>\n )\n }\n)\nAlert.displayName = 'Alert'\n\n// ─── AlertTitle ──────────────────────────────────────────────────\nexport interface AlertTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {}\n\nconst AlertTitle = React.forwardRef<HTMLHeadingElement, AlertTitleProps>(\n ({ className, ...props }, ref) => {\n const { size } = useAlertContext()\n return (\n <h5\n ref={ref}\n className={cn(TITLE_SIZE_MAP[size], className)}\n {...props}\n />\n )\n }\n)\nAlertTitle.displayName = 'AlertTitle'\n\n// ─── AlertDescription ────────────────────────────────────────────\nexport interface AlertDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {}\n\nconst AlertDescription = React.forwardRef<HTMLParagraphElement, AlertDescriptionProps>(\n ({ className, ...props }, ref) => {\n const { variant, size } = useAlertContext()\n return (\n <p\n ref={ref}\n data-alert-description=\"\"\n className={cn(\n DESC_SIZE_MAP[size],\n variant === 'filled' ? 'opacity-90' : 'opacity-80',\n className,\n )}\n {...props}\n />\n )\n }\n)\nAlertDescription.displayName = 'AlertDescription'\n\n// ─── Exports ─────────────────────────────────────────────────────\nexport {\n Alert,\n AlertTitle,\n AlertDescription,\n alertVariants,\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUE;AARF,YAAuB;AACvB,sCAAuC;AACvC,mBAAmB;AAKnB,MAAM,WAAW,CAAC,EAAE,UAAU,MAC5B,6CAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GAAG,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QACxJ;AAAA,8CAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK;AAAA,EAC/B,4CAAC,UAAK,GAAE,aAAY;AAAA,EACpB,4CAAC,UAAK,GAAE,aAAY;AAAA,GACtB;AAGF,MAAM,cAAc,CAAC,EAAE,UAAU,MAC/B,6CAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GAAG,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QACxJ;AAAA,8CAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK;AAAA,EAC/B,4CAAC,UAAK,GAAE,iBAAgB;AAAA,GAC1B;AAGF,MAAM,cAAc,CAAC,EAAE,UAAU,MAC/B,6CAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GAAG,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QACxJ;AAAA,8CAAC,UAAK,GAAE,4EAA2E;AAAA,EACnF,4CAAC,UAAK,GAAE,WAAU;AAAA,EAClB,4CAAC,UAAK,GAAE,cAAa;AAAA,GACvB;AAGF,MAAM,YAAY,CAAC,EAAE,UAAU,MAC7B,6CAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GAAG,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QACxJ;AAAA,8CAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK;AAAA,EAC/B,4CAAC,UAAK,GAAE,WAAU;AAAA,EAClB,4CAAC,UAAK,GAAE,cAAa;AAAA,GACvB;AAGF,MAAM,mBAAmB,CAAC,EAAE,UAAU,MACpC,4CAAC,SAAI,WAAsB,MAAK,QAAO,SAAQ,aAAY,QAAO,gBAAe,aAAa,GAAG,eAAY,QAC3G,sDAAC,UAAK,eAAc,SAAQ,gBAAe,SAAQ,GAAE,wBAAuB,GAC9E;AAIF,MAAM,eAAqE;AAAA,EACzE,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAO;AACT;AAIA,MAAM,WAAW;AAAA,EACf,MAAM;AAAA,IACJ,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,SAAS;AAAA,IACP,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,SAAS;AAAA,IACP,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,OAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AACF;AAGA,MAAM,oBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,IAAI;AAAA,MACN;AAAA,MACA,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,OAAO;AAAA,QACP,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,QAAQ;AAAA,IACV;AAAA,EACF;AACF;AAGA,MAAM,gBAAgB;AAAA,EACpB,IAAI;AAAA;AAAA,EACJ,SAAS;AAAA;AAAA,EACT,IAAI;AAAA;AACN;AAEA,MAAM,iBAAiB;AAAA,EACrB,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,IAAI;AACN;AAEA,MAAM,gBAAgB;AAAA,EACpB,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,IAAI;AACN;AAcA,MAAM,eAAe,MAAM,cAAiC;AAAA,EAC1D,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AACR,CAAC;AAED,SAAS,kBAAkB;AACzB,SAAO,MAAM,WAAW,YAAY;AACtC;AA4BA,MAAM,QAAQ,MAAM;AAAA,EAClB,CAAC;AAAA,IACC;AAAA,IACA,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,GAAG;AAAA,EACL,GAAG,QAAQ;AAET,UAAM,OAAO,UAAU,UAAU,UAAU;AAC3C,UAAM,eAAe,SAAS,KAAK,EAAE,OAAO;AAC5C,UAAM,aAAa,aAAa,KAAK;AAErC,WACE,4CAAC,aAAa,UAAb,EAAsB,OAAO,EAAE,SAAS,OAAO,KAAK,GACnD;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,eAAW;AAAA,UACT,cAAc,EAAE,MAAM,OAAO,CAAC;AAAA,UAC9B;AAAA,UACA;AAAA,QACF;AAAA,QACC,GAAG;AAAA,QAGH;AAAA,WAAC,YACA,4CAAC,UAAK,WAAU,YACb,kBAAQ,4CAAC,cAAW,WAAW,cAAc,IAAI,GAAG,GACvD;AAAA,UAIF,4CAAC,SAAI,WAAU,kBACZ,UACH;AAAA,UAGC,YACC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,eAAW;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA,SAAS,OACL,4FACA;AAAA,cACN;AAAA,cACA,SAAS;AAAA,cACT,cAAW;AAAA,cAEV,uBAAa,4CAAC,oBAAiB,WAAU,WAAU;AAAA;AAAA,UACtD;AAAA;AAAA;AAAA,IAEJ,GACF;AAAA,EAEJ;AACF;AACA,MAAM,cAAc;AAKpB,MAAM,aAAa,MAAM;AAAA,EACvB,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAAQ;AAChC,UAAM,EAAE,KAAK,IAAI,gBAAgB;AACjC,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,eAAW,iBAAG,eAAe,IAAI,GAAG,SAAS;AAAA,QAC5C,GAAG;AAAA;AAAA,IACN;AAAA,EAEJ;AACF;AACA,WAAW,cAAc;AAKzB,MAAM,mBAAmB,MAAM;AAAA,EAC7B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAAQ;AAChC,UAAM,EAAE,SAAS,KAAK,IAAI,gBAAgB;AAC1C,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,0BAAuB;AAAA,QACvB,eAAW;AAAA,UACT,cAAc,IAAI;AAAA,UAClB,YAAY,WAAW,eAAe;AAAA,UACtC;AAAA,QACF;AAAA,QACC,GAAG;AAAA;AAAA,IACN;AAAA,EAEJ;AACF;AACA,iBAAiB,cAAc;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/alert.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '../../lib/utils'\n\n// ─── Built-in status icons (no external dependency) ──────────────\n// All icons: Lucide-compatible 24×24 viewBox, strokeWidth=2, rounded caps/joins\n// Info = circle + \"i\" | Success = circle + check | Warning = triangle + \"!\" | Error = circle + small ×\nconst InfoIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n </svg>\n)\n\nconst SuccessIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m9 12 2 2 4-4\" />\n </svg>\n)\n\nconst WarningIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n </svg>\n)\n\nconst ErrorIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 8v5\" />\n <path d=\"M12 16h.01\" />\n </svg>\n)\n\nconst DefaultCloseIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2} aria-hidden=\"true\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n)\n\n// ─── Status icon mapping ─────────────────────────────────────────\nconst STATUS_ICONS: Record<AlertColor, React.FC<{ className?: string }>> = {\n info: InfoIcon,\n success: SuccessIcon,\n warning: WarningIcon,\n error: ErrorIcon,\n}\n\n// ─── Color × Variant class mapping ──────────────────────────────\n// Uses semantic tokens (*.tint, text-text-*) — same pattern as Badge\nconst colorMap = {\n info: {\n default: 'bg-info-tint text-text-info border-info/20',\n outline: 'bg-transparent text-text-info border-info',\n filled: 'bg-info text-info-foreground border-transparent',\n },\n success: {\n default: 'bg-success-tint text-text-success border-success/20',\n outline: 'bg-transparent text-text-success border-success',\n filled: 'bg-success text-success-foreground border-transparent',\n },\n warning: {\n default: 'bg-warning-tint text-text-warning border-warning/20',\n outline: 'bg-transparent text-text-warning border-warning',\n filled: 'bg-warning text-warning-foreground border-transparent',\n },\n error: {\n default: 'bg-error-tint text-text-error border-error/20',\n outline: 'bg-transparent text-text-error border-error',\n filled: 'bg-error text-error-foreground border-transparent',\n },\n} as const\n\n// ─── Alert variants ──────────────────────────────────────────────\nconst alertVariants = cva(\n 'group relative flex items-center border w-full [&:has([data-alert-description])]:items-start',\n {\n variants: {\n size: {\n sm: 'gap-2 p-3 text-sm',\n default: 'gap-2.5 p-4 text-md',\n lg: 'gap-3 p-5 text-md',\n },\n radius: {\n none: 'rounded-none',\n sm: 'rounded-sm',\n base: 'rounded',\n md: 'rounded-md',\n lg: 'rounded-lg',\n xl: 'rounded-xl',\n '2xl': 'rounded-2xl',\n '3xl': 'rounded-3xl',\n full: 'rounded-full',\n },\n },\n defaultVariants: {\n size: 'default',\n radius: 'lg',\n },\n }\n)\n\n// ─── Size-dependent class maps ──────────────────────────────────\nconst ICON_SIZE_MAP = {\n sm: 'icon-sm', // 16px\n default: 'w-[18px] h-[18px]', // 18px — TOKEN-EXCEPTION: no 18px icon token\n lg: 'icon-md', // 20px\n} as const\n\nconst TITLE_SIZE_MAP = {\n sm: 'text-sm font-semibold leading-4 tracking-tight',\n default: 'font-semibold leading-[18px] tracking-tight',\n lg: 'text-base font-semibold leading-5 tracking-tight',\n} as const\n\nconst DESC_SIZE_MAP = {\n sm: 'text-xs mt-0.5',\n default: 'text-sm mt-1',\n lg: 'text-md mt-1.5',\n} as const\n\n// ─── Types ───────────────────────────────────────────────────────\nexport type AlertVariant = 'default' | 'outline' | 'filled'\nexport type AlertColor = 'info' | 'success' | 'warning' | 'error'\nexport type AlertSize = 'sm' | 'default' | 'lg'\n\n// ─── Context ─────────────────────────────────────────────────────\ninterface AlertContextValue {\n variant: AlertVariant\n color: AlertColor\n size: AlertSize\n}\n\nconst AlertContext = React.createContext<AlertContextValue>({\n variant: 'default',\n color: 'info',\n size: 'default',\n})\n\nfunction useAlertContext() {\n return React.useContext(AlertContext)\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// Alert\n// ═══════════════════════════════════════════════════════════════════\n\n// ─── Alert ───────────────────────────────────────────────────────\nexport interface AlertProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof alertVariants> {\n /** Visual style */\n variant?: AlertVariant\n /** Semantic color (determines icon, colors, and ARIA role) */\n color?: AlertColor\n /** Size */\n size?: AlertSize\n /** Closable — shows close button */\n closable?: boolean\n /** Close callback */\n onClose?: () => void\n /** Custom close icon */\n closeIcon?: React.ReactNode\n /** Accessible label for the close button (screen readers) — localize per page */\n closeLabel?: string\n /** Custom status icon (overrides default) */\n icon?: React.ReactNode\n /** Hide the status icon entirely */\n hideIcon?: boolean\n}\n\nconst Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n ({\n className,\n variant = 'default',\n color = 'info',\n size = 'default',\n radius,\n closable = false,\n onClose,\n closeIcon,\n closeLabel = 'Close',\n icon,\n hideIcon = false,\n children,\n ...props\n }, ref) => {\n // error → role=\"alert\" (assertive), others → role=\"status\" (polite)\n const role = color === 'error' ? 'alert' : 'status'\n const colorClasses = colorMap[color][variant]\n const StatusIcon = STATUS_ICONS[color]\n\n return (\n <AlertContext.Provider value={{ variant, color, size }}>\n <div\n ref={ref}\n role={role}\n className={cn(\n alertVariants({ size, radius }),\n colorClasses,\n className,\n )}\n {...props}\n >\n {/* Icon */}\n {!hideIcon && (\n <span className=\"shrink-0\">\n {icon || <StatusIcon className={ICON_SIZE_MAP[size]} />}\n </span>\n )}\n\n {/* Content area */}\n <div className=\"flex-1 min-w-0\">\n {children}\n </div>\n\n {/* Close button */}\n {closable && (\n <button\n type=\"button\"\n className={cn(\n 'shrink-0 rounded-md p-1 opacity-70 hover:opacity-100 transition-opacity focus-visible:focus-ring',\n 'group-has-[[data-alert-description]]:absolute',\n size === 'sm'\n ? 'group-has-[[data-alert-description]]:top-2 group-has-[[data-alert-description]]:right-2'\n : 'group-has-[[data-alert-description]]:top-3 group-has-[[data-alert-description]]:right-3',\n )}\n onClick={onClose}\n aria-label={closeLabel}\n >\n {closeIcon || <DefaultCloseIcon className=\"icon-sm\" />}\n </button>\n )}\n </div>\n </AlertContext.Provider>\n )\n }\n)\nAlert.displayName = 'Alert'\n\n// ─── AlertTitle ──────────────────────────────────────────────────\nexport interface AlertTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {}\n\nconst AlertTitle = React.forwardRef<HTMLHeadingElement, AlertTitleProps>(\n ({ className, ...props }, ref) => {\n const { size } = useAlertContext()\n return (\n <h5\n ref={ref}\n className={cn(TITLE_SIZE_MAP[size], className)}\n {...props}\n />\n )\n }\n)\nAlertTitle.displayName = 'AlertTitle'\n\n// ─── AlertDescription ────────────────────────────────────────────\nexport interface AlertDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {}\n\nconst AlertDescription = React.forwardRef<HTMLParagraphElement, AlertDescriptionProps>(\n ({ className, ...props }, ref) => {\n const { variant, size } = useAlertContext()\n return (\n <p\n ref={ref}\n data-alert-description=\"\"\n className={cn(\n DESC_SIZE_MAP[size],\n variant === 'filled' ? 'opacity-90' : 'opacity-80',\n className,\n )}\n {...props}\n />\n )\n }\n)\nAlertDescription.displayName = 'AlertDescription'\n\n// ─── Exports ─────────────────────────────────────────────────────\nexport {\n Alert,\n AlertTitle,\n AlertDescription,\n alertVariants,\n}\n"],"mappings":"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","names":[]}
|
|
@@ -114,6 +114,7 @@ const Alert = React.forwardRef(
|
|
|
114
114
|
closable = false,
|
|
115
115
|
onClose,
|
|
116
116
|
closeIcon,
|
|
117
|
+
closeLabel = "Close",
|
|
117
118
|
icon,
|
|
118
119
|
hideIcon = false,
|
|
119
120
|
children,
|
|
@@ -146,7 +147,7 @@ const Alert = React.forwardRef(
|
|
|
146
147
|
size === "sm" ? "group-has-[[data-alert-description]]:top-2 group-has-[[data-alert-description]]:right-2" : "group-has-[[data-alert-description]]:top-3 group-has-[[data-alert-description]]:right-3"
|
|
147
148
|
),
|
|
148
149
|
onClick: onClose,
|
|
149
|
-
"aria-label":
|
|
150
|
+
"aria-label": closeLabel,
|
|
150
151
|
children: closeIcon || /* @__PURE__ */ jsx(DefaultCloseIcon, { className: "icon-sm" })
|
|
151
152
|
}
|
|
152
153
|
)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/alert.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '../../lib/utils'\n\n// ─── Built-in status icons (no external dependency) ──────────────\n// All icons: Lucide-compatible 24×24 viewBox, strokeWidth=2, rounded caps/joins\n// Info = circle + \"i\" | Success = circle + check | Warning = triangle + \"!\" | Error = circle + small ×\nconst InfoIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n </svg>\n)\n\nconst SuccessIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m9 12 2 2 4-4\" />\n </svg>\n)\n\nconst WarningIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n </svg>\n)\n\nconst ErrorIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 8v5\" />\n <path d=\"M12 16h.01\" />\n </svg>\n)\n\nconst DefaultCloseIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2} aria-hidden=\"true\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n)\n\n// ─── Status icon mapping ─────────────────────────────────────────\nconst STATUS_ICONS: Record<AlertColor, React.FC<{ className?: string }>> = {\n info: InfoIcon,\n success: SuccessIcon,\n warning: WarningIcon,\n error: ErrorIcon,\n}\n\n// ─── Color × Variant class mapping ──────────────────────────────\n// Uses semantic tokens (*.tint, text-text-*) — same pattern as Badge\nconst colorMap = {\n info: {\n default: 'bg-info-tint text-text-info border-info/20',\n outline: 'bg-transparent text-text-info border-info',\n filled: 'bg-info text-info-foreground border-transparent',\n },\n success: {\n default: 'bg-success-tint text-text-success border-success/20',\n outline: 'bg-transparent text-text-success border-success',\n filled: 'bg-success text-success-foreground border-transparent',\n },\n warning: {\n default: 'bg-warning-tint text-text-warning border-warning/20',\n outline: 'bg-transparent text-text-warning border-warning',\n filled: 'bg-warning text-warning-foreground border-transparent',\n },\n error: {\n default: 'bg-error-tint text-text-error border-error/20',\n outline: 'bg-transparent text-text-error border-error',\n filled: 'bg-error text-error-foreground border-transparent',\n },\n} as const\n\n// ─── Alert variants ──────────────────────────────────────────────\nconst alertVariants = cva(\n 'group relative flex items-center border w-full [&:has([data-alert-description])]:items-start',\n {\n variants: {\n size: {\n sm: 'gap-2 p-3 text-sm',\n default: 'gap-2.5 p-4 text-md',\n lg: 'gap-3 p-5 text-md',\n },\n radius: {\n none: 'rounded-none',\n sm: 'rounded-sm',\n base: 'rounded',\n md: 'rounded-md',\n lg: 'rounded-lg',\n xl: 'rounded-xl',\n '2xl': 'rounded-2xl',\n '3xl': 'rounded-3xl',\n full: 'rounded-full',\n },\n },\n defaultVariants: {\n size: 'default',\n radius: 'lg',\n },\n }\n)\n\n// ─── Size-dependent class maps ──────────────────────────────────\nconst ICON_SIZE_MAP = {\n sm: 'icon-sm', // 16px\n default: 'w-[18px] h-[18px]', // 18px — TOKEN-EXCEPTION: no 18px icon token\n lg: 'icon-md', // 20px\n} as const\n\nconst TITLE_SIZE_MAP = {\n sm: 'text-sm font-semibold leading-4 tracking-tight',\n default: 'font-semibold leading-[18px] tracking-tight',\n lg: 'text-base font-semibold leading-5 tracking-tight',\n} as const\n\nconst DESC_SIZE_MAP = {\n sm: 'text-xs mt-0.5',\n default: 'text-sm mt-1',\n lg: 'text-md mt-1.5',\n} as const\n\n// ─── Types ───────────────────────────────────────────────────────\nexport type AlertVariant = 'default' | 'outline' | 'filled'\nexport type AlertColor = 'info' | 'success' | 'warning' | 'error'\nexport type AlertSize = 'sm' | 'default' | 'lg'\n\n// ─── Context ─────────────────────────────────────────────────────\ninterface AlertContextValue {\n variant: AlertVariant\n color: AlertColor\n size: AlertSize\n}\n\nconst AlertContext = React.createContext<AlertContextValue>({\n variant: 'default',\n color: 'info',\n size: 'default',\n})\n\nfunction useAlertContext() {\n return React.useContext(AlertContext)\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// Alert\n// ═══════════════════════════════════════════════════════════════════\n\n// ─── Alert ───────────────────────────────────────────────────────\nexport interface AlertProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof alertVariants> {\n /** Visual style */\n variant?: AlertVariant\n /** Semantic color (determines icon, colors, and ARIA role) */\n color?: AlertColor\n /** Size */\n size?: AlertSize\n /** Closable — shows close button */\n closable?: boolean\n /** Close callback */\n onClose?: () => void\n /** Custom close icon */\n closeIcon?: React.ReactNode\n /** Custom status icon (overrides default) */\n icon?: React.ReactNode\n /** Hide the status icon entirely */\n hideIcon?: boolean\n}\n\nconst Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n ({\n className,\n variant = 'default',\n color = 'info',\n size = 'default',\n radius,\n closable = false,\n onClose,\n closeIcon,\n icon,\n hideIcon = false,\n children,\n ...props\n }, ref) => {\n // error → role=\"alert\" (assertive), others → role=\"status\" (polite)\n const role = color === 'error' ? 'alert' : 'status'\n const colorClasses = colorMap[color][variant]\n const StatusIcon = STATUS_ICONS[color]\n\n return (\n <AlertContext.Provider value={{ variant, color, size }}>\n <div\n ref={ref}\n role={role}\n className={cn(\n alertVariants({ size, radius }),\n colorClasses,\n className,\n )}\n {...props}\n >\n {/* Icon */}\n {!hideIcon && (\n <span className=\"shrink-0\">\n {icon || <StatusIcon className={ICON_SIZE_MAP[size]} />}\n </span>\n )}\n\n {/* Content area */}\n <div className=\"flex-1 min-w-0\">\n {children}\n </div>\n\n {/* Close button */}\n {closable && (\n <button\n type=\"button\"\n className={cn(\n 'shrink-0 rounded-md p-1 opacity-70 hover:opacity-100 transition-opacity focus-visible:focus-ring',\n 'group-has-[[data-alert-description]]:absolute',\n size === 'sm'\n ? 'group-has-[[data-alert-description]]:top-2 group-has-[[data-alert-description]]:right-2'\n : 'group-has-[[data-alert-description]]:top-3 group-has-[[data-alert-description]]:right-3',\n )}\n onClick={onClose}\n aria-label=\"Close\"\n >\n {closeIcon || <DefaultCloseIcon className=\"icon-sm\" />}\n </button>\n )}\n </div>\n </AlertContext.Provider>\n )\n }\n)\nAlert.displayName = 'Alert'\n\n// ─── AlertTitle ──────────────────────────────────────────────────\nexport interface AlertTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {}\n\nconst AlertTitle = React.forwardRef<HTMLHeadingElement, AlertTitleProps>(\n ({ className, ...props }, ref) => {\n const { size } = useAlertContext()\n return (\n <h5\n ref={ref}\n className={cn(TITLE_SIZE_MAP[size], className)}\n {...props}\n />\n )\n }\n)\nAlertTitle.displayName = 'AlertTitle'\n\n// ─── AlertDescription ────────────────────────────────────────────\nexport interface AlertDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {}\n\nconst AlertDescription = React.forwardRef<HTMLParagraphElement, AlertDescriptionProps>(\n ({ className, ...props }, ref) => {\n const { variant, size } = useAlertContext()\n return (\n <p\n ref={ref}\n data-alert-description=\"\"\n className={cn(\n DESC_SIZE_MAP[size],\n variant === 'filled' ? 'opacity-90' : 'opacity-80',\n className,\n )}\n {...props}\n />\n )\n }\n)\nAlertDescription.displayName = 'AlertDescription'\n\n// ─── Exports ─────────────────────────────────────────────────────\nexport {\n Alert,\n AlertTitle,\n AlertDescription,\n alertVariants,\n}\n"],"mappings":";AAUE,SACE,KADF;AARF,YAAY,WAAW;AACvB,SAAS,WAA8B;AACvC,SAAS,UAAU;AAKnB,MAAM,WAAW,CAAC,EAAE,UAAU,MAC5B,qBAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GAAG,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QACxJ;AAAA,sBAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK;AAAA,EAC/B,oBAAC,UAAK,GAAE,aAAY;AAAA,EACpB,oBAAC,UAAK,GAAE,aAAY;AAAA,GACtB;AAGF,MAAM,cAAc,CAAC,EAAE,UAAU,MAC/B,qBAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GAAG,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QACxJ;AAAA,sBAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK;AAAA,EAC/B,oBAAC,UAAK,GAAE,iBAAgB;AAAA,GAC1B;AAGF,MAAM,cAAc,CAAC,EAAE,UAAU,MAC/B,qBAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GAAG,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QACxJ;AAAA,sBAAC,UAAK,GAAE,4EAA2E;AAAA,EACnF,oBAAC,UAAK,GAAE,WAAU;AAAA,EAClB,oBAAC,UAAK,GAAE,cAAa;AAAA,GACvB;AAGF,MAAM,YAAY,CAAC,EAAE,UAAU,MAC7B,qBAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GAAG,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QACxJ;AAAA,sBAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK;AAAA,EAC/B,oBAAC,UAAK,GAAE,WAAU;AAAA,EAClB,oBAAC,UAAK,GAAE,cAAa;AAAA,GACvB;AAGF,MAAM,mBAAmB,CAAC,EAAE,UAAU,MACpC,oBAAC,SAAI,WAAsB,MAAK,QAAO,SAAQ,aAAY,QAAO,gBAAe,aAAa,GAAG,eAAY,QAC3G,8BAAC,UAAK,eAAc,SAAQ,gBAAe,SAAQ,GAAE,wBAAuB,GAC9E;AAIF,MAAM,eAAqE;AAAA,EACzE,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAO;AACT;AAIA,MAAM,WAAW;AAAA,EACf,MAAM;AAAA,IACJ,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,SAAS;AAAA,IACP,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,SAAS;AAAA,IACP,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,OAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AACF;AAGA,MAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,IAAI;AAAA,MACN;AAAA,MACA,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,OAAO;AAAA,QACP,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,QAAQ;AAAA,IACV;AAAA,EACF;AACF;AAGA,MAAM,gBAAgB;AAAA,EACpB,IAAI;AAAA;AAAA,EACJ,SAAS;AAAA;AAAA,EACT,IAAI;AAAA;AACN;AAEA,MAAM,iBAAiB;AAAA,EACrB,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,IAAI;AACN;AAEA,MAAM,gBAAgB;AAAA,EACpB,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,IAAI;AACN;AAcA,MAAM,eAAe,MAAM,cAAiC;AAAA,EAC1D,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AACR,CAAC;AAED,SAAS,kBAAkB;AACzB,SAAO,MAAM,WAAW,YAAY;AACtC;AA4BA,MAAM,QAAQ,MAAM;AAAA,EAClB,CAAC;AAAA,IACC;AAAA,IACA,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,GAAG;AAAA,EACL,GAAG,QAAQ;AAET,UAAM,OAAO,UAAU,UAAU,UAAU;AAC3C,UAAM,eAAe,SAAS,KAAK,EAAE,OAAO;AAC5C,UAAM,aAAa,aAAa,KAAK;AAErC,WACE,oBAAC,aAAa,UAAb,EAAsB,OAAO,EAAE,SAAS,OAAO,KAAK,GACnD;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,WAAW;AAAA,UACT,cAAc,EAAE,MAAM,OAAO,CAAC;AAAA,UAC9B;AAAA,UACA;AAAA,QACF;AAAA,QACC,GAAG;AAAA,QAGH;AAAA,WAAC,YACA,oBAAC,UAAK,WAAU,YACb,kBAAQ,oBAAC,cAAW,WAAW,cAAc,IAAI,GAAG,GACvD;AAAA,UAIF,oBAAC,SAAI,WAAU,kBACZ,UACH;AAAA,UAGC,YACC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAW;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA,SAAS,OACL,4FACA;AAAA,cACN;AAAA,cACA,SAAS;AAAA,cACT,cAAW;AAAA,cAEV,uBAAa,oBAAC,oBAAiB,WAAU,WAAU;AAAA;AAAA,UACtD;AAAA;AAAA;AAAA,IAEJ,GACF;AAAA,EAEJ;AACF;AACA,MAAM,cAAc;AAKpB,MAAM,aAAa,MAAM;AAAA,EACvB,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAAQ;AAChC,UAAM,EAAE,KAAK,IAAI,gBAAgB;AACjC,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,WAAW,GAAG,eAAe,IAAI,GAAG,SAAS;AAAA,QAC5C,GAAG;AAAA;AAAA,IACN;AAAA,EAEJ;AACF;AACA,WAAW,cAAc;AAKzB,MAAM,mBAAmB,MAAM;AAAA,EAC7B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAAQ;AAChC,UAAM,EAAE,SAAS,KAAK,IAAI,gBAAgB;AAC1C,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,0BAAuB;AAAA,QACvB,WAAW;AAAA,UACT,cAAc,IAAI;AAAA,UAClB,YAAY,WAAW,eAAe;AAAA,UACtC;AAAA,QACF;AAAA,QACC,GAAG;AAAA;AAAA,IACN;AAAA,EAEJ;AACF;AACA,iBAAiB,cAAc;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/alert.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '../../lib/utils'\n\n// ─── Built-in status icons (no external dependency) ──────────────\n// All icons: Lucide-compatible 24×24 viewBox, strokeWidth=2, rounded caps/joins\n// Info = circle + \"i\" | Success = circle + check | Warning = triangle + \"!\" | Error = circle + small ×\nconst InfoIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n </svg>\n)\n\nconst SuccessIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m9 12 2 2 4-4\" />\n </svg>\n)\n\nconst WarningIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n </svg>\n)\n\nconst ErrorIcon = ({ className }: { className?: string }) => (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 8v5\" />\n <path d=\"M12 16h.01\" />\n </svg>\n)\n\nconst DefaultCloseIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2} aria-hidden=\"true\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n)\n\n// ─── Status icon mapping ─────────────────────────────────────────\nconst STATUS_ICONS: Record<AlertColor, React.FC<{ className?: string }>> = {\n info: InfoIcon,\n success: SuccessIcon,\n warning: WarningIcon,\n error: ErrorIcon,\n}\n\n// ─── Color × Variant class mapping ──────────────────────────────\n// Uses semantic tokens (*.tint, text-text-*) — same pattern as Badge\nconst colorMap = {\n info: {\n default: 'bg-info-tint text-text-info border-info/20',\n outline: 'bg-transparent text-text-info border-info',\n filled: 'bg-info text-info-foreground border-transparent',\n },\n success: {\n default: 'bg-success-tint text-text-success border-success/20',\n outline: 'bg-transparent text-text-success border-success',\n filled: 'bg-success text-success-foreground border-transparent',\n },\n warning: {\n default: 'bg-warning-tint text-text-warning border-warning/20',\n outline: 'bg-transparent text-text-warning border-warning',\n filled: 'bg-warning text-warning-foreground border-transparent',\n },\n error: {\n default: 'bg-error-tint text-text-error border-error/20',\n outline: 'bg-transparent text-text-error border-error',\n filled: 'bg-error text-error-foreground border-transparent',\n },\n} as const\n\n// ─── Alert variants ──────────────────────────────────────────────\nconst alertVariants = cva(\n 'group relative flex items-center border w-full [&:has([data-alert-description])]:items-start',\n {\n variants: {\n size: {\n sm: 'gap-2 p-3 text-sm',\n default: 'gap-2.5 p-4 text-md',\n lg: 'gap-3 p-5 text-md',\n },\n radius: {\n none: 'rounded-none',\n sm: 'rounded-sm',\n base: 'rounded',\n md: 'rounded-md',\n lg: 'rounded-lg',\n xl: 'rounded-xl',\n '2xl': 'rounded-2xl',\n '3xl': 'rounded-3xl',\n full: 'rounded-full',\n },\n },\n defaultVariants: {\n size: 'default',\n radius: 'lg',\n },\n }\n)\n\n// ─── Size-dependent class maps ──────────────────────────────────\nconst ICON_SIZE_MAP = {\n sm: 'icon-sm', // 16px\n default: 'w-[18px] h-[18px]', // 18px — TOKEN-EXCEPTION: no 18px icon token\n lg: 'icon-md', // 20px\n} as const\n\nconst TITLE_SIZE_MAP = {\n sm: 'text-sm font-semibold leading-4 tracking-tight',\n default: 'font-semibold leading-[18px] tracking-tight',\n lg: 'text-base font-semibold leading-5 tracking-tight',\n} as const\n\nconst DESC_SIZE_MAP = {\n sm: 'text-xs mt-0.5',\n default: 'text-sm mt-1',\n lg: 'text-md mt-1.5',\n} as const\n\n// ─── Types ───────────────────────────────────────────────────────\nexport type AlertVariant = 'default' | 'outline' | 'filled'\nexport type AlertColor = 'info' | 'success' | 'warning' | 'error'\nexport type AlertSize = 'sm' | 'default' | 'lg'\n\n// ─── Context ─────────────────────────────────────────────────────\ninterface AlertContextValue {\n variant: AlertVariant\n color: AlertColor\n size: AlertSize\n}\n\nconst AlertContext = React.createContext<AlertContextValue>({\n variant: 'default',\n color: 'info',\n size: 'default',\n})\n\nfunction useAlertContext() {\n return React.useContext(AlertContext)\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// Alert\n// ═══════════════════════════════════════════════════════════════════\n\n// ─── Alert ───────────────────────────────────────────────────────\nexport interface AlertProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof alertVariants> {\n /** Visual style */\n variant?: AlertVariant\n /** Semantic color (determines icon, colors, and ARIA role) */\n color?: AlertColor\n /** Size */\n size?: AlertSize\n /** Closable — shows close button */\n closable?: boolean\n /** Close callback */\n onClose?: () => void\n /** Custom close icon */\n closeIcon?: React.ReactNode\n /** Accessible label for the close button (screen readers) — localize per page */\n closeLabel?: string\n /** Custom status icon (overrides default) */\n icon?: React.ReactNode\n /** Hide the status icon entirely */\n hideIcon?: boolean\n}\n\nconst Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n ({\n className,\n variant = 'default',\n color = 'info',\n size = 'default',\n radius,\n closable = false,\n onClose,\n closeIcon,\n closeLabel = 'Close',\n icon,\n hideIcon = false,\n children,\n ...props\n }, ref) => {\n // error → role=\"alert\" (assertive), others → role=\"status\" (polite)\n const role = color === 'error' ? 'alert' : 'status'\n const colorClasses = colorMap[color][variant]\n const StatusIcon = STATUS_ICONS[color]\n\n return (\n <AlertContext.Provider value={{ variant, color, size }}>\n <div\n ref={ref}\n role={role}\n className={cn(\n alertVariants({ size, radius }),\n colorClasses,\n className,\n )}\n {...props}\n >\n {/* Icon */}\n {!hideIcon && (\n <span className=\"shrink-0\">\n {icon || <StatusIcon className={ICON_SIZE_MAP[size]} />}\n </span>\n )}\n\n {/* Content area */}\n <div className=\"flex-1 min-w-0\">\n {children}\n </div>\n\n {/* Close button */}\n {closable && (\n <button\n type=\"button\"\n className={cn(\n 'shrink-0 rounded-md p-1 opacity-70 hover:opacity-100 transition-opacity focus-visible:focus-ring',\n 'group-has-[[data-alert-description]]:absolute',\n size === 'sm'\n ? 'group-has-[[data-alert-description]]:top-2 group-has-[[data-alert-description]]:right-2'\n : 'group-has-[[data-alert-description]]:top-3 group-has-[[data-alert-description]]:right-3',\n )}\n onClick={onClose}\n aria-label={closeLabel}\n >\n {closeIcon || <DefaultCloseIcon className=\"icon-sm\" />}\n </button>\n )}\n </div>\n </AlertContext.Provider>\n )\n }\n)\nAlert.displayName = 'Alert'\n\n// ─── AlertTitle ──────────────────────────────────────────────────\nexport interface AlertTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {}\n\nconst AlertTitle = React.forwardRef<HTMLHeadingElement, AlertTitleProps>(\n ({ className, ...props }, ref) => {\n const { size } = useAlertContext()\n return (\n <h5\n ref={ref}\n className={cn(TITLE_SIZE_MAP[size], className)}\n {...props}\n />\n )\n }\n)\nAlertTitle.displayName = 'AlertTitle'\n\n// ─── AlertDescription ────────────────────────────────────────────\nexport interface AlertDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {}\n\nconst AlertDescription = React.forwardRef<HTMLParagraphElement, AlertDescriptionProps>(\n ({ className, ...props }, ref) => {\n const { variant, size } = useAlertContext()\n return (\n <p\n ref={ref}\n data-alert-description=\"\"\n className={cn(\n DESC_SIZE_MAP[size],\n variant === 'filled' ? 'opacity-90' : 'opacity-80',\n className,\n )}\n {...props}\n />\n )\n }\n)\nAlertDescription.displayName = 'AlertDescription'\n\n// ─── Exports ─────────────────────────────────────────────────────\nexport {\n Alert,\n AlertTitle,\n AlertDescription,\n alertVariants,\n}\n"],"mappings":"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","names":[]}
|
|
@@ -19,7 +19,7 @@ interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement>, VariantProps
|
|
|
19
19
|
icon?: React.ReactNode;
|
|
20
20
|
/** Show a status dot before text */
|
|
21
21
|
dot?: boolean;
|
|
22
|
-
/** Show a remove button */
|
|
22
|
+
/** Show a remove button (not rendered with asChild — a button inside a link is invalid HTML) */
|
|
23
23
|
removable?: boolean;
|
|
24
24
|
/** Callback when remove button is clicked */
|
|
25
25
|
onRemove?: () => void;
|
|
@@ -19,7 +19,7 @@ interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement>, VariantProps
|
|
|
19
19
|
icon?: React.ReactNode;
|
|
20
20
|
/** Show a status dot before text */
|
|
21
21
|
dot?: boolean;
|
|
22
|
-
/** Show a remove button */
|
|
22
|
+
/** Show a remove button (not rendered with asChild — a button inside a link is invalid HTML) */
|
|
23
23
|
removable?: boolean;
|
|
24
24
|
/** Callback when remove button is clicked */
|
|
25
25
|
onRemove?: () => void;
|
|
@@ -122,6 +122,16 @@ const dotColorMap = {
|
|
|
122
122
|
error: "bg-error",
|
|
123
123
|
info: "bg-info"
|
|
124
124
|
};
|
|
125
|
+
const REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy");
|
|
126
|
+
function renderInsideChild(children, render) {
|
|
127
|
+
let child = children;
|
|
128
|
+
const resolve = React.use;
|
|
129
|
+
if (typeof child === "object" && child !== null && child.$$typeof === REACT_LAZY_TYPE && typeof resolve === "function") {
|
|
130
|
+
child = resolve(child._payload);
|
|
131
|
+
}
|
|
132
|
+
if (!React.isValidElement(child)) return render(children);
|
|
133
|
+
return React.cloneElement(child, void 0, render(child.props.children));
|
|
134
|
+
}
|
|
125
135
|
const Badge = React.forwardRef(
|
|
126
136
|
({
|
|
127
137
|
className,
|
|
@@ -142,7 +152,49 @@ const Badge = React.forwardRef(
|
|
|
142
152
|
const colorClasses = colorMap[color][variant];
|
|
143
153
|
const outlineClasses = variant === "outline" ? "border bg-transparent" : "";
|
|
144
154
|
const dotColor = variant === "solid" ? "bg-current" : dotColorMap[color];
|
|
145
|
-
|
|
155
|
+
const renderContent = (content) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
156
|
+
dot && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
157
|
+
"span",
|
|
158
|
+
{
|
|
159
|
+
className: (0, import_utils.cn)("shrink-0 rounded-full", badgeDotSizes[resolvedSize], dotColor),
|
|
160
|
+
"aria-hidden": "true"
|
|
161
|
+
}
|
|
162
|
+
),
|
|
163
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
164
|
+
"span",
|
|
165
|
+
{
|
|
166
|
+
className: (0, import_utils.cn)("shrink-0 [&>svg]:w-full [&>svg]:h-full", badgeIconSizes[resolvedSize]),
|
|
167
|
+
"aria-hidden": "true",
|
|
168
|
+
children: icon
|
|
169
|
+
}
|
|
170
|
+
),
|
|
171
|
+
content,
|
|
172
|
+
removable && !asChild && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
173
|
+
"button",
|
|
174
|
+
{
|
|
175
|
+
type: "button",
|
|
176
|
+
className: "shrink-0 -mr-0.5 ml-0.5 rounded-full p-0.5 opacity-70 hover:opacity-100 transition-opacity focus-visible:focus-ring",
|
|
177
|
+
onClick: (e) => {
|
|
178
|
+
e.stopPropagation();
|
|
179
|
+
onRemove?.();
|
|
180
|
+
},
|
|
181
|
+
"aria-label": "Remove",
|
|
182
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
183
|
+
"svg",
|
|
184
|
+
{
|
|
185
|
+
className: "w-3 h-3",
|
|
186
|
+
fill: "none",
|
|
187
|
+
viewBox: "0 0 24 24",
|
|
188
|
+
stroke: "currentColor",
|
|
189
|
+
strokeWidth: 2,
|
|
190
|
+
"aria-hidden": "true",
|
|
191
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" })
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
] });
|
|
197
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
146
198
|
Comp,
|
|
147
199
|
{
|
|
148
200
|
ref,
|
|
@@ -153,48 +205,7 @@ const Badge = React.forwardRef(
|
|
|
153
205
|
className
|
|
154
206
|
),
|
|
155
207
|
...props,
|
|
156
|
-
children:
|
|
157
|
-
dot && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
158
|
-
"span",
|
|
159
|
-
{
|
|
160
|
-
className: (0, import_utils.cn)("shrink-0 rounded-full", badgeDotSizes[resolvedSize], dotColor),
|
|
161
|
-
"aria-hidden": "true"
|
|
162
|
-
}
|
|
163
|
-
),
|
|
164
|
-
icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
165
|
-
"span",
|
|
166
|
-
{
|
|
167
|
-
className: (0, import_utils.cn)("shrink-0 [&>svg]:w-full [&>svg]:h-full", badgeIconSizes[resolvedSize]),
|
|
168
|
-
"aria-hidden": "true",
|
|
169
|
-
children: icon
|
|
170
|
-
}
|
|
171
|
-
),
|
|
172
|
-
children,
|
|
173
|
-
removable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
174
|
-
"button",
|
|
175
|
-
{
|
|
176
|
-
type: "button",
|
|
177
|
-
className: "shrink-0 -mr-0.5 ml-0.5 rounded-full p-0.5 opacity-70 hover:opacity-100 transition-opacity focus-visible:focus-ring",
|
|
178
|
-
onClick: (e) => {
|
|
179
|
-
e.stopPropagation();
|
|
180
|
-
onRemove?.();
|
|
181
|
-
},
|
|
182
|
-
"aria-label": "Remove",
|
|
183
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
184
|
-
"svg",
|
|
185
|
-
{
|
|
186
|
-
className: "w-3 h-3",
|
|
187
|
-
fill: "none",
|
|
188
|
-
viewBox: "0 0 24 24",
|
|
189
|
-
stroke: "currentColor",
|
|
190
|
-
strokeWidth: 2,
|
|
191
|
-
"aria-hidden": "true",
|
|
192
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" })
|
|
193
|
-
}
|
|
194
|
-
)
|
|
195
|
-
}
|
|
196
|
-
)
|
|
197
|
-
]
|
|
208
|
+
children: asChild ? renderInsideChild(children, renderContent) : renderContent(children)
|
|
198
209
|
}
|
|
199
210
|
);
|
|
200
211
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/badge.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '../../lib/utils'\n\n// Badge color maps for variant × color combinations\n// Uses semantic tokens (*.tint, text-text-*) for v4 dark mode compatibility\nconst colorMap = {\n default: {\n solid: 'bg-foreground text-background',\n subtle: 'bg-background-muted text-foreground',\n outline: 'border-border text-foreground',\n },\n primary: {\n solid: 'bg-primary text-primary-foreground',\n subtle: 'bg-primary-tint text-text-primary',\n outline: 'border-primary text-text-primary',\n },\n success: {\n solid: 'bg-success text-success-foreground',\n subtle: 'bg-success-tint text-text-success',\n outline: 'border-success text-text-success',\n },\n warning: {\n solid: 'bg-warning text-warning-foreground',\n subtle: 'bg-warning-tint text-text-warning',\n outline: 'border-warning text-text-warning',\n },\n error: {\n solid: 'bg-error text-error-foreground',\n subtle: 'bg-error-tint text-text-error',\n outline: 'border-error text-text-error',\n },\n info: {\n solid: 'bg-info text-info-foreground',\n subtle: 'bg-info-tint text-text-info',\n outline: 'border-info text-text-info',\n },\n} as const\n\nconst badgeVariants = cva(\n 'inline-flex items-center justify-center whitespace-nowrap font-semibold transition-colors select-none',\n {\n variants: {\n size: {\n sm: 'h-5 min-w-5 px-1.5 text-2xs gap-1', // 20px height, 6px paddingX, 11px font\n default: 'h-6 min-w-6 px-2 text-xs gap-1', // 24px height, 8px paddingX, 12px font\n lg: 'h-7 min-w-7 px-2.5 text-sm gap-1.5', // 28px height, 10px paddingX, 13px font\n },\n radius: {\n sm: 'rounded-sm', // 2px\n base: 'rounded', // 4px\n md: 'rounded-md', // 6px\n lg: 'rounded-lg', // 8px\n full: 'rounded-full', // 9999px\n },\n },\n defaultVariants: {\n size: 'default',\n radius: 'full',\n },\n }\n)\n\n// Icon sizes per badge size\nconst badgeIconSizes = {\n sm: 'icon-2xs', // 12px\n default: 'icon-2xs', // 12px\n lg: 'icon-xs', // 14px\n} as const\n\n// Dot sizes per badge size\nconst badgeDotSizes = {\n sm: 'w-1 h-1',\n default: 'w-1.5 h-1.5',\n lg: 'w-1.5 h-1.5',\n} as const\n\n// Dot colors per color (matches the solid background)\nconst dotColorMap = {\n default: 'bg-foreground',\n primary: 'bg-primary',\n success: 'bg-success',\n warning: 'bg-warning',\n error: 'bg-error',\n info: 'bg-info',\n} as const\n\nexport type BadgeVariant = 'solid' | 'subtle' | 'outline'\nexport type BadgeColor = 'default' | 'primary' | 'success' | 'warning' | 'error' | 'info'\nexport type BadgeSize = 'sm' | 'default' | 'lg'\nexport type BadgeRadius = 'sm' | 'base' | 'md' | 'lg' | 'full'\n\nexport interface BadgeProps\n extends React.HTMLAttributes<HTMLSpanElement>,\n VariantProps<typeof badgeVariants> {\n /** Visual style */\n variant?: BadgeVariant\n /** Semantic color */\n color?: BadgeColor\n /** Leading icon slot */\n icon?: React.ReactNode\n /** Show a status dot before text */\n dot?: boolean\n /** Show a remove button */\n removable?: boolean\n /** Callback when remove button is clicked */\n onRemove?: () => void\n /** Render as child element (Slot pattern) */\n asChild?: boolean\n}\n\nconst Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(\n ({\n className,\n variant = 'subtle',\n color = 'default',\n size,\n radius,\n icon,\n dot = false,\n removable = false,\n onRemove,\n asChild = false,\n children,\n ...props\n }, ref) => {\n const Comp = asChild ? Slot : 'span'\n const resolvedSize = size || 'default'\n\n // Color classes from variant × color map\n const colorClasses = colorMap[color][variant]\n // Outline variant needs border\n const outlineClasses = variant === 'outline' ? 'border bg-transparent' : ''\n\n // Dot color: in solid variant use currentColor (white/foreground), otherwise use semantic color\n const dotColor = variant === 'solid' ? 'bg-current' : dotColorMap[color]\n\n
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/badge.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '../../lib/utils'\n\n// Badge color maps for variant × color combinations\n// Uses semantic tokens (*.tint, text-text-*) for v4 dark mode compatibility\nconst colorMap = {\n default: {\n solid: 'bg-foreground text-background',\n subtle: 'bg-background-muted text-foreground',\n outline: 'border-border text-foreground',\n },\n primary: {\n solid: 'bg-primary text-primary-foreground',\n subtle: 'bg-primary-tint text-text-primary',\n outline: 'border-primary text-text-primary',\n },\n success: {\n solid: 'bg-success text-success-foreground',\n subtle: 'bg-success-tint text-text-success',\n outline: 'border-success text-text-success',\n },\n warning: {\n solid: 'bg-warning text-warning-foreground',\n subtle: 'bg-warning-tint text-text-warning',\n outline: 'border-warning text-text-warning',\n },\n error: {\n solid: 'bg-error text-error-foreground',\n subtle: 'bg-error-tint text-text-error',\n outline: 'border-error text-text-error',\n },\n info: {\n solid: 'bg-info text-info-foreground',\n subtle: 'bg-info-tint text-text-info',\n outline: 'border-info text-text-info',\n },\n} as const\n\nconst badgeVariants = cva(\n 'inline-flex items-center justify-center whitespace-nowrap font-semibold transition-colors select-none',\n {\n variants: {\n size: {\n sm: 'h-5 min-w-5 px-1.5 text-2xs gap-1', // 20px height, 6px paddingX, 11px font\n default: 'h-6 min-w-6 px-2 text-xs gap-1', // 24px height, 8px paddingX, 12px font\n lg: 'h-7 min-w-7 px-2.5 text-sm gap-1.5', // 28px height, 10px paddingX, 13px font\n },\n radius: {\n sm: 'rounded-sm', // 2px\n base: 'rounded', // 4px\n md: 'rounded-md', // 6px\n lg: 'rounded-lg', // 8px\n full: 'rounded-full', // 9999px\n },\n },\n defaultVariants: {\n size: 'default',\n radius: 'full',\n },\n }\n)\n\n// Icon sizes per badge size\nconst badgeIconSizes = {\n sm: 'icon-2xs', // 12px\n default: 'icon-2xs', // 12px\n lg: 'icon-xs', // 14px\n} as const\n\n// Dot sizes per badge size\nconst badgeDotSizes = {\n sm: 'w-1 h-1',\n default: 'w-1.5 h-1.5',\n lg: 'w-1.5 h-1.5',\n} as const\n\n// Dot colors per color (matches the solid background)\nconst dotColorMap = {\n default: 'bg-foreground',\n primary: 'bg-primary',\n success: 'bg-success',\n warning: 'bg-warning',\n error: 'bg-error',\n info: 'bg-info',\n} as const\n\nexport type BadgeVariant = 'solid' | 'subtle' | 'outline'\nexport type BadgeColor = 'default' | 'primary' | 'success' | 'warning' | 'error' | 'info'\nexport type BadgeSize = 'sm' | 'default' | 'lg'\nexport type BadgeRadius = 'sm' | 'base' | 'md' | 'lg' | 'full'\n\nconst REACT_LAZY_TYPE = Symbol.for('react.lazy')\n\n// asChild: Slot merges props only into a single child element, so the dot and\n// icon must go inside that child, not next to it. Lazy children (from Server\n// Components) are resolved the same way Radix Slot does.\nfunction renderInsideChild(\n children: React.ReactNode,\n render: (content: React.ReactNode) => React.ReactNode\n): React.ReactNode {\n let child: unknown = children\n const resolve = (React as { use?: (usable: unknown) => unknown }).use\n if (\n typeof child === 'object' && child !== null &&\n (child as { $$typeof?: symbol }).$$typeof === REACT_LAZY_TYPE &&\n typeof resolve === 'function'\n ) {\n child = resolve((child as { _payload: unknown })._payload)\n }\n if (!React.isValidElement<{ children?: React.ReactNode }>(child)) return render(children)\n return React.cloneElement(child, undefined, render(child.props.children))\n}\n\nexport interface BadgeProps\n extends React.HTMLAttributes<HTMLSpanElement>,\n VariantProps<typeof badgeVariants> {\n /** Visual style */\n variant?: BadgeVariant\n /** Semantic color */\n color?: BadgeColor\n /** Leading icon slot */\n icon?: React.ReactNode\n /** Show a status dot before text */\n dot?: boolean\n /** Show a remove button (not rendered with asChild — a button inside a link is invalid HTML) */\n removable?: boolean\n /** Callback when remove button is clicked */\n onRemove?: () => void\n /** Render as child element (Slot pattern) */\n asChild?: boolean\n}\n\nconst Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(\n ({\n className,\n variant = 'subtle',\n color = 'default',\n size,\n radius,\n icon,\n dot = false,\n removable = false,\n onRemove,\n asChild = false,\n children,\n ...props\n }, ref) => {\n const Comp = asChild ? Slot : 'span'\n const resolvedSize = size || 'default'\n\n // Color classes from variant × color map\n const colorClasses = colorMap[color][variant]\n // Outline variant needs border\n const outlineClasses = variant === 'outline' ? 'border bg-transparent' : ''\n\n // Dot color: in solid variant use currentColor (white/foreground), otherwise use semantic color\n const dotColor = variant === 'solid' ? 'bg-current' : dotColorMap[color]\n\n const renderContent = (content: React.ReactNode) => (\n <>\n {dot && (\n <span\n className={cn('shrink-0 rounded-full', badgeDotSizes[resolvedSize], dotColor)}\n aria-hidden=\"true\"\n />\n )}\n {icon && (\n <span\n className={cn('shrink-0 [&>svg]:w-full [&>svg]:h-full', badgeIconSizes[resolvedSize])}\n aria-hidden=\"true\"\n >\n {icon}\n </span>\n )}\n {content}\n {/* A button inside the asChild element (usually a link) would be invalid HTML */}\n {removable && !asChild && (\n <button\n type=\"button\"\n className=\"shrink-0 -mr-0.5 ml-0.5 rounded-full p-0.5 opacity-70 hover:opacity-100 transition-opacity focus-visible:focus-ring\"\n onClick={(e) => {\n e.stopPropagation()\n onRemove?.()\n }}\n aria-label=\"Remove\"\n >\n <svg\n className=\"w-3 h-3\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={2}\n aria-hidden=\"true\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n </>\n )\n\n return (\n <Comp\n ref={ref}\n className={cn(\n badgeVariants({ size, radius }),\n colorClasses,\n outlineClasses,\n className\n )}\n {...props}\n >\n {asChild ? renderInsideChild(children, renderContent) : renderContent(children)}\n </Comp>\n )\n }\n)\nBadge.displayName = 'Badge'\n\nexport { Badge, badgeVariants }\n"],"mappings":"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","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Slot } from "@radix-ui/react-slot";
|
|
5
5
|
import { cva } from "class-variance-authority";
|
|
@@ -88,6 +88,16 @@ const dotColorMap = {
|
|
|
88
88
|
error: "bg-error",
|
|
89
89
|
info: "bg-info"
|
|
90
90
|
};
|
|
91
|
+
const REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy");
|
|
92
|
+
function renderInsideChild(children, render) {
|
|
93
|
+
let child = children;
|
|
94
|
+
const resolve = React.use;
|
|
95
|
+
if (typeof child === "object" && child !== null && child.$$typeof === REACT_LAZY_TYPE && typeof resolve === "function") {
|
|
96
|
+
child = resolve(child._payload);
|
|
97
|
+
}
|
|
98
|
+
if (!React.isValidElement(child)) return render(children);
|
|
99
|
+
return React.cloneElement(child, void 0, render(child.props.children));
|
|
100
|
+
}
|
|
91
101
|
const Badge = React.forwardRef(
|
|
92
102
|
({
|
|
93
103
|
className,
|
|
@@ -108,7 +118,49 @@ const Badge = React.forwardRef(
|
|
|
108
118
|
const colorClasses = colorMap[color][variant];
|
|
109
119
|
const outlineClasses = variant === "outline" ? "border bg-transparent" : "";
|
|
110
120
|
const dotColor = variant === "solid" ? "bg-current" : dotColorMap[color];
|
|
111
|
-
|
|
121
|
+
const renderContent = (content) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
122
|
+
dot && /* @__PURE__ */ jsx(
|
|
123
|
+
"span",
|
|
124
|
+
{
|
|
125
|
+
className: cn("shrink-0 rounded-full", badgeDotSizes[resolvedSize], dotColor),
|
|
126
|
+
"aria-hidden": "true"
|
|
127
|
+
}
|
|
128
|
+
),
|
|
129
|
+
icon && /* @__PURE__ */ jsx(
|
|
130
|
+
"span",
|
|
131
|
+
{
|
|
132
|
+
className: cn("shrink-0 [&>svg]:w-full [&>svg]:h-full", badgeIconSizes[resolvedSize]),
|
|
133
|
+
"aria-hidden": "true",
|
|
134
|
+
children: icon
|
|
135
|
+
}
|
|
136
|
+
),
|
|
137
|
+
content,
|
|
138
|
+
removable && !asChild && /* @__PURE__ */ jsx(
|
|
139
|
+
"button",
|
|
140
|
+
{
|
|
141
|
+
type: "button",
|
|
142
|
+
className: "shrink-0 -mr-0.5 ml-0.5 rounded-full p-0.5 opacity-70 hover:opacity-100 transition-opacity focus-visible:focus-ring",
|
|
143
|
+
onClick: (e) => {
|
|
144
|
+
e.stopPropagation();
|
|
145
|
+
onRemove?.();
|
|
146
|
+
},
|
|
147
|
+
"aria-label": "Remove",
|
|
148
|
+
children: /* @__PURE__ */ jsx(
|
|
149
|
+
"svg",
|
|
150
|
+
{
|
|
151
|
+
className: "w-3 h-3",
|
|
152
|
+
fill: "none",
|
|
153
|
+
viewBox: "0 0 24 24",
|
|
154
|
+
stroke: "currentColor",
|
|
155
|
+
strokeWidth: 2,
|
|
156
|
+
"aria-hidden": "true",
|
|
157
|
+
children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" })
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
)
|
|
162
|
+
] });
|
|
163
|
+
return /* @__PURE__ */ jsx(
|
|
112
164
|
Comp,
|
|
113
165
|
{
|
|
114
166
|
ref,
|
|
@@ -119,48 +171,7 @@ const Badge = React.forwardRef(
|
|
|
119
171
|
className
|
|
120
172
|
),
|
|
121
173
|
...props,
|
|
122
|
-
children:
|
|
123
|
-
dot && /* @__PURE__ */ jsx(
|
|
124
|
-
"span",
|
|
125
|
-
{
|
|
126
|
-
className: cn("shrink-0 rounded-full", badgeDotSizes[resolvedSize], dotColor),
|
|
127
|
-
"aria-hidden": "true"
|
|
128
|
-
}
|
|
129
|
-
),
|
|
130
|
-
icon && /* @__PURE__ */ jsx(
|
|
131
|
-
"span",
|
|
132
|
-
{
|
|
133
|
-
className: cn("shrink-0 [&>svg]:w-full [&>svg]:h-full", badgeIconSizes[resolvedSize]),
|
|
134
|
-
"aria-hidden": "true",
|
|
135
|
-
children: icon
|
|
136
|
-
}
|
|
137
|
-
),
|
|
138
|
-
children,
|
|
139
|
-
removable && /* @__PURE__ */ jsx(
|
|
140
|
-
"button",
|
|
141
|
-
{
|
|
142
|
-
type: "button",
|
|
143
|
-
className: "shrink-0 -mr-0.5 ml-0.5 rounded-full p-0.5 opacity-70 hover:opacity-100 transition-opacity focus-visible:focus-ring",
|
|
144
|
-
onClick: (e) => {
|
|
145
|
-
e.stopPropagation();
|
|
146
|
-
onRemove?.();
|
|
147
|
-
},
|
|
148
|
-
"aria-label": "Remove",
|
|
149
|
-
children: /* @__PURE__ */ jsx(
|
|
150
|
-
"svg",
|
|
151
|
-
{
|
|
152
|
-
className: "w-3 h-3",
|
|
153
|
-
fill: "none",
|
|
154
|
-
viewBox: "0 0 24 24",
|
|
155
|
-
stroke: "currentColor",
|
|
156
|
-
strokeWidth: 2,
|
|
157
|
-
"aria-hidden": "true",
|
|
158
|
-
children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" })
|
|
159
|
-
}
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
)
|
|
163
|
-
]
|
|
174
|
+
children: asChild ? renderInsideChild(children, renderContent) : renderContent(children)
|
|
164
175
|
}
|
|
165
176
|
);
|
|
166
177
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/badge.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '../../lib/utils'\n\n// Badge color maps for variant × color combinations\n// Uses semantic tokens (*.tint, text-text-*) for v4 dark mode compatibility\nconst colorMap = {\n default: {\n solid: 'bg-foreground text-background',\n subtle: 'bg-background-muted text-foreground',\n outline: 'border-border text-foreground',\n },\n primary: {\n solid: 'bg-primary text-primary-foreground',\n subtle: 'bg-primary-tint text-text-primary',\n outline: 'border-primary text-text-primary',\n },\n success: {\n solid: 'bg-success text-success-foreground',\n subtle: 'bg-success-tint text-text-success',\n outline: 'border-success text-text-success',\n },\n warning: {\n solid: 'bg-warning text-warning-foreground',\n subtle: 'bg-warning-tint text-text-warning',\n outline: 'border-warning text-text-warning',\n },\n error: {\n solid: 'bg-error text-error-foreground',\n subtle: 'bg-error-tint text-text-error',\n outline: 'border-error text-text-error',\n },\n info: {\n solid: 'bg-info text-info-foreground',\n subtle: 'bg-info-tint text-text-info',\n outline: 'border-info text-text-info',\n },\n} as const\n\nconst badgeVariants = cva(\n 'inline-flex items-center justify-center whitespace-nowrap font-semibold transition-colors select-none',\n {\n variants: {\n size: {\n sm: 'h-5 min-w-5 px-1.5 text-2xs gap-1', // 20px height, 6px paddingX, 11px font\n default: 'h-6 min-w-6 px-2 text-xs gap-1', // 24px height, 8px paddingX, 12px font\n lg: 'h-7 min-w-7 px-2.5 text-sm gap-1.5', // 28px height, 10px paddingX, 13px font\n },\n radius: {\n sm: 'rounded-sm', // 2px\n base: 'rounded', // 4px\n md: 'rounded-md', // 6px\n lg: 'rounded-lg', // 8px\n full: 'rounded-full', // 9999px\n },\n },\n defaultVariants: {\n size: 'default',\n radius: 'full',\n },\n }\n)\n\n// Icon sizes per badge size\nconst badgeIconSizes = {\n sm: 'icon-2xs', // 12px\n default: 'icon-2xs', // 12px\n lg: 'icon-xs', // 14px\n} as const\n\n// Dot sizes per badge size\nconst badgeDotSizes = {\n sm: 'w-1 h-1',\n default: 'w-1.5 h-1.5',\n lg: 'w-1.5 h-1.5',\n} as const\n\n// Dot colors per color (matches the solid background)\nconst dotColorMap = {\n default: 'bg-foreground',\n primary: 'bg-primary',\n success: 'bg-success',\n warning: 'bg-warning',\n error: 'bg-error',\n info: 'bg-info',\n} as const\n\nexport type BadgeVariant = 'solid' | 'subtle' | 'outline'\nexport type BadgeColor = 'default' | 'primary' | 'success' | 'warning' | 'error' | 'info'\nexport type BadgeSize = 'sm' | 'default' | 'lg'\nexport type BadgeRadius = 'sm' | 'base' | 'md' | 'lg' | 'full'\n\nexport interface BadgeProps\n extends React.HTMLAttributes<HTMLSpanElement>,\n VariantProps<typeof badgeVariants> {\n /** Visual style */\n variant?: BadgeVariant\n /** Semantic color */\n color?: BadgeColor\n /** Leading icon slot */\n icon?: React.ReactNode\n /** Show a status dot before text */\n dot?: boolean\n /** Show a remove button */\n removable?: boolean\n /** Callback when remove button is clicked */\n onRemove?: () => void\n /** Render as child element (Slot pattern) */\n asChild?: boolean\n}\n\nconst Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(\n ({\n className,\n variant = 'subtle',\n color = 'default',\n size,\n radius,\n icon,\n dot = false,\n removable = false,\n onRemove,\n asChild = false,\n children,\n ...props\n }, ref) => {\n const Comp = asChild ? Slot : 'span'\n const resolvedSize = size || 'default'\n\n // Color classes from variant × color map\n const colorClasses = colorMap[color][variant]\n // Outline variant needs border\n const outlineClasses = variant === 'outline' ? 'border bg-transparent' : ''\n\n // Dot color: in solid variant use currentColor (white/foreground), otherwise use semantic color\n const dotColor = variant === 'solid' ? 'bg-current' : dotColorMap[color]\n\n
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/badge.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '../../lib/utils'\n\n// Badge color maps for variant × color combinations\n// Uses semantic tokens (*.tint, text-text-*) for v4 dark mode compatibility\nconst colorMap = {\n default: {\n solid: 'bg-foreground text-background',\n subtle: 'bg-background-muted text-foreground',\n outline: 'border-border text-foreground',\n },\n primary: {\n solid: 'bg-primary text-primary-foreground',\n subtle: 'bg-primary-tint text-text-primary',\n outline: 'border-primary text-text-primary',\n },\n success: {\n solid: 'bg-success text-success-foreground',\n subtle: 'bg-success-tint text-text-success',\n outline: 'border-success text-text-success',\n },\n warning: {\n solid: 'bg-warning text-warning-foreground',\n subtle: 'bg-warning-tint text-text-warning',\n outline: 'border-warning text-text-warning',\n },\n error: {\n solid: 'bg-error text-error-foreground',\n subtle: 'bg-error-tint text-text-error',\n outline: 'border-error text-text-error',\n },\n info: {\n solid: 'bg-info text-info-foreground',\n subtle: 'bg-info-tint text-text-info',\n outline: 'border-info text-text-info',\n },\n} as const\n\nconst badgeVariants = cva(\n 'inline-flex items-center justify-center whitespace-nowrap font-semibold transition-colors select-none',\n {\n variants: {\n size: {\n sm: 'h-5 min-w-5 px-1.5 text-2xs gap-1', // 20px height, 6px paddingX, 11px font\n default: 'h-6 min-w-6 px-2 text-xs gap-1', // 24px height, 8px paddingX, 12px font\n lg: 'h-7 min-w-7 px-2.5 text-sm gap-1.5', // 28px height, 10px paddingX, 13px font\n },\n radius: {\n sm: 'rounded-sm', // 2px\n base: 'rounded', // 4px\n md: 'rounded-md', // 6px\n lg: 'rounded-lg', // 8px\n full: 'rounded-full', // 9999px\n },\n },\n defaultVariants: {\n size: 'default',\n radius: 'full',\n },\n }\n)\n\n// Icon sizes per badge size\nconst badgeIconSizes = {\n sm: 'icon-2xs', // 12px\n default: 'icon-2xs', // 12px\n lg: 'icon-xs', // 14px\n} as const\n\n// Dot sizes per badge size\nconst badgeDotSizes = {\n sm: 'w-1 h-1',\n default: 'w-1.5 h-1.5',\n lg: 'w-1.5 h-1.5',\n} as const\n\n// Dot colors per color (matches the solid background)\nconst dotColorMap = {\n default: 'bg-foreground',\n primary: 'bg-primary',\n success: 'bg-success',\n warning: 'bg-warning',\n error: 'bg-error',\n info: 'bg-info',\n} as const\n\nexport type BadgeVariant = 'solid' | 'subtle' | 'outline'\nexport type BadgeColor = 'default' | 'primary' | 'success' | 'warning' | 'error' | 'info'\nexport type BadgeSize = 'sm' | 'default' | 'lg'\nexport type BadgeRadius = 'sm' | 'base' | 'md' | 'lg' | 'full'\n\nconst REACT_LAZY_TYPE = Symbol.for('react.lazy')\n\n// asChild: Slot merges props only into a single child element, so the dot and\n// icon must go inside that child, not next to it. Lazy children (from Server\n// Components) are resolved the same way Radix Slot does.\nfunction renderInsideChild(\n children: React.ReactNode,\n render: (content: React.ReactNode) => React.ReactNode\n): React.ReactNode {\n let child: unknown = children\n const resolve = (React as { use?: (usable: unknown) => unknown }).use\n if (\n typeof child === 'object' && child !== null &&\n (child as { $$typeof?: symbol }).$$typeof === REACT_LAZY_TYPE &&\n typeof resolve === 'function'\n ) {\n child = resolve((child as { _payload: unknown })._payload)\n }\n if (!React.isValidElement<{ children?: React.ReactNode }>(child)) return render(children)\n return React.cloneElement(child, undefined, render(child.props.children))\n}\n\nexport interface BadgeProps\n extends React.HTMLAttributes<HTMLSpanElement>,\n VariantProps<typeof badgeVariants> {\n /** Visual style */\n variant?: BadgeVariant\n /** Semantic color */\n color?: BadgeColor\n /** Leading icon slot */\n icon?: React.ReactNode\n /** Show a status dot before text */\n dot?: boolean\n /** Show a remove button (not rendered with asChild — a button inside a link is invalid HTML) */\n removable?: boolean\n /** Callback when remove button is clicked */\n onRemove?: () => void\n /** Render as child element (Slot pattern) */\n asChild?: boolean\n}\n\nconst Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(\n ({\n className,\n variant = 'subtle',\n color = 'default',\n size,\n radius,\n icon,\n dot = false,\n removable = false,\n onRemove,\n asChild = false,\n children,\n ...props\n }, ref) => {\n const Comp = asChild ? Slot : 'span'\n const resolvedSize = size || 'default'\n\n // Color classes from variant × color map\n const colorClasses = colorMap[color][variant]\n // Outline variant needs border\n const outlineClasses = variant === 'outline' ? 'border bg-transparent' : ''\n\n // Dot color: in solid variant use currentColor (white/foreground), otherwise use semantic color\n const dotColor = variant === 'solid' ? 'bg-current' : dotColorMap[color]\n\n const renderContent = (content: React.ReactNode) => (\n <>\n {dot && (\n <span\n className={cn('shrink-0 rounded-full', badgeDotSizes[resolvedSize], dotColor)}\n aria-hidden=\"true\"\n />\n )}\n {icon && (\n <span\n className={cn('shrink-0 [&>svg]:w-full [&>svg]:h-full', badgeIconSizes[resolvedSize])}\n aria-hidden=\"true\"\n >\n {icon}\n </span>\n )}\n {content}\n {/* A button inside the asChild element (usually a link) would be invalid HTML */}\n {removable && !asChild && (\n <button\n type=\"button\"\n className=\"shrink-0 -mr-0.5 ml-0.5 rounded-full p-0.5 opacity-70 hover:opacity-100 transition-opacity focus-visible:focus-ring\"\n onClick={(e) => {\n e.stopPropagation()\n onRemove?.()\n }}\n aria-label=\"Remove\"\n >\n <svg\n className=\"w-3 h-3\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={2}\n aria-hidden=\"true\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n </>\n )\n\n return (\n <Comp\n ref={ref}\n className={cn(\n badgeVariants({ size, radius }),\n colorClasses,\n outlineClasses,\n className\n )}\n {...props}\n >\n {asChild ? renderInsideChild(children, renderContent) : renderContent(children)}\n </Comp>\n )\n }\n)\nBadge.displayName = 'Badge'\n\nexport { Badge, badgeVariants }\n"],"mappings":"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","names":[]}
|