@music-vine/cadence 2.7.0 → 3.0.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.
- package/README.md +5 -44
- package/dist/components/accordion.js +2 -2
- package/dist/components/accordion.js.map +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/badge.js.map +1 -1
- package/dist/components/button.js +3 -3
- package/dist/components/button.js.map +1 -1
- package/dist/components/carousel-dots.js +1 -1
- package/dist/components/carousel-dots.js.map +1 -1
- package/dist/components/carousel.js +2 -2
- package/dist/components/carousel.js.map +1 -1
- package/dist/components/checkbox.js +1 -1
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/context-menu.js +6 -6
- package/dist/components/context-menu.js.map +1 -1
- package/dist/components/dialog.js +1 -1
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/input.js +2 -2
- package/dist/components/input.js.map +1 -1
- package/dist/components/popover.js +1 -1
- package/dist/components/popover.js.map +1 -1
- package/dist/components/radio-group.js +1 -1
- package/dist/components/radio-group.js.map +1 -1
- package/dist/components/scroll-drum.js +1 -1
- package/dist/components/scroll-drum.js.map +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/select.js.map +1 -1
- package/dist/components/slider.js +1 -1
- package/dist/components/slider.js.map +1 -1
- package/dist/components/stacking-card.js +1 -1
- package/dist/components/stacking-card.js.map +1 -1
- package/dist/components/tabs.js +2 -2
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/toast.js +1 -1
- package/dist/components/toast.js.map +1 -1
- package/dist/components/typography/list.js +1 -1
- package/dist/components/typography/list.js.map +2 -2
- package/dist/lib/utils.d.ts +1 -1
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +7 -17
- package/dist/lib/utils.js.map +2 -2
- package/dist/styles/index.css +16 -3
- package/package.json +3 -9
- package/{tailwind.config.v4.css → tailwind.config.css} +35 -14
- package/dist/styles/index.v4.css +0 -49
- package/tailwind.config.ts +0 -313
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/components/stacking-card.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * @module StackingCard\n *\n * Selectable card component with collapsible content. Supports single selection groups.\n * Features animated expand/collapse and accessible keyboard navigation.\n *\n * @example\n * // Single selectable card\n * <StackingCard selectable selected={isSelected} onSelectedChange={setSelected}>\n * <StackingCardHeader>\n * <StackingCardTitle>Premium Plan</StackingCardTitle>\n * </StackingCardHeader>\n * <StackingCardContent collapsible>\n * <StackingCardList>\n * <StackingCardListItem>Unlimited downloads</StackingCardListItem>\n * <StackingCardListItem>Priority support</StackingCardListItem>\n * </StackingCardList>\n * </StackingCardContent>\n * </StackingCard>\n *\n * @example\n * // Radio group of cards\n * <StackingCardGroup value={plan} onValueChange={setPlan}>\n * <StackingCard value=\"basic\" selectable>\n * <StackingCardHeader>\n * <StackingCardTitle>Basic</StackingCardTitle>\n * </StackingCardHeader>\n * </StackingCard>\n * <StackingCard value=\"pro\" selectable>\n * <StackingCardHeader>\n * <StackingCardTitle>Pro</StackingCardTitle>\n * </StackingCardHeader>\n * </StackingCard>\n * </StackingCardGroup>\n */\nimport type { LucideProps } from \"lucide-react\";\nimport { Check, ChevronDown } from \"lucide-react\";\nimport type { Variants } from \"motion/react\";\nimport { motion } from \"motion/react\";\nimport * as React from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"./button\";\nimport { DummyCheckbox } from \"./checkbox\";\nimport { Separator } from \"./separator\";\n\nimport type { ListItemProps, ListProps } from \"./typography/list\";\nimport { List, ListItem } from \"./typography/list\";\nimport { Text } from \"./typography/text\";\n\n// Motion transition configuration\nconst motionTransition = {\n duration: 0.4,\n type: \"spring\" as const,\n bounce: 0,\n};\n\nconst motionVariantsChevron: Variants = {\n collapsed: {\n rotate: 0,\n transition: motionTransition,\n },\n expanded: {\n rotate: 180,\n transition: motionTransition,\n },\n};\n\n// Hook to handle auto height animation for collapsible content\nconst useAutoHeight = () => {\n const [height, setHeight] = React.useState<number | \"auto\">(\"auto\");\n const ref = React.useRef<HTMLDivElement>(null);\n\n React.useEffect(() => {\n if (!ref.current) {\n return;\n }\n\n const resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0];\n\n if (entry) {\n setHeight(entry.contentRect.height);\n }\n });\n\n resizeObserver.observe(ref.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, []);\n\n const variants: Variants = React.useMemo(\n () => ({\n collapsed: {\n height: 0,\n opacity: 0,\n transition: motionTransition,\n },\n expanded: {\n height: height === \"auto\" ? \"auto\" : height,\n opacity: 1,\n transition: motionTransition,\n },\n }),\n [height]\n );\n\n return { ref, variants };\n};\n\ninterface StackingCardGroupContextValue {\n expandedValue: string | null;\n onExpandedChange?: (value: string | null) => void;\n onValueChange?: (value: string) => void;\n registerCardRef?: (value: string, element: HTMLElement | null) => void;\n selectedValue: string;\n}\n\nconst StackingCardGroupContext =\n React.createContext<StackingCardGroupContextValue | null>(null);\n\nconst useStackingCardGroup = () => {\n const context = React.useContext(StackingCardGroupContext);\n\n return context;\n};\n\ninterface StackingCardContextValue {\n contentId?: string;\n isCollapsed?: boolean;\n isSelected?: boolean;\n onSelectedChange?: (selected: boolean) => void;\n onToggleCollapse?: () => void;\n selectable?: boolean;\n setHasCollapsibleContent?: (hasCollapsible: boolean) => void;\n}\n\nconst StackingCardContext = React.createContext<StackingCardContextValue>({});\n\nconst useStackingCard = () => {\n const context = React.useContext(StackingCardContext);\n\n return context;\n};\n\ninterface StackingCardProps extends React.HTMLAttributes<HTMLDivElement> {\n \"aria-describedby\"?: string;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n initiallyCollapsed?: boolean;\n onSelectedChange?: (selected: boolean) => void;\n selectable?: boolean;\n selected?: boolean;\n value?: string;\n variant?: \"default\" | \"selected\";\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Complex component with many state interactions\nfunction StackingCard({\n className,\n children,\n selectable = false,\n selected = false,\n onSelectedChange,\n variant,\n onClick,\n value,\n onKeyDown,\n initiallyCollapsed = true,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n \"aria-describedby\": ariaDescribedby,\n ref,\n ...props\n}: StackingCardProps & { ref?: React.Ref<HTMLDivElement> }) {\n const [isCollapsed, setIsCollapsed] = React.useState(true);\n const [hasCollapsibleContent, setHasCollapsibleContent] =\n React.useState(false);\n\n const [wasSelected, setWasSelected] = React.useState(false);\n\n const ignoreInitiallyCollapsedAfterRenderRef = React.useRef(false);\n const timeoutRef = React.useRef<NodeJS.Timeout | null>(null);\n\n // Generate unique ID for this card instance\n const cardId = React.useId();\n const contentId = `${cardId}-content`;\n\n const group = useStackingCardGroup();\n\n // Determine if we're in a group\n const isInGroup = group !== null;\n\n // Determine selection state\n const isSelected = isInGroup\n ? group.selectedValue === value\n : variant === \"selected\" || selected;\n\n // Auto-expand logic: if card becomes selected and has collapsible content, expand it\n // This is calculated during render, not in useEffect\n const justBecameSelected = isSelected && !wasSelected;\n\n if (justBecameSelected && hasCollapsibleContent) {\n if (isInGroup) {\n // In group mode, expand this card\n group?.onExpandedChange?.(value ?? null);\n } else {\n // In standalone mode, expand this card\n setIsCollapsed(false);\n }\n }\n\n // Update wasSelected for next render (this is safe during render)\n if (isSelected !== wasSelected) {\n setWasSelected(isSelected);\n }\n\n // Determine collapse state\n let shouldBeCollapsed = isCollapsed;\n\n if (initiallyCollapsed && !ignoreInitiallyCollapsedAfterRenderRef.current) {\n shouldBeCollapsed = true;\n } else if (isInGroup) {\n shouldBeCollapsed = group.expandedValue !== value;\n }\n\n const ignoreInitiallyCollapsedRef = React.useCallback(() => {\n if (initiallyCollapsed && !ignoreInitiallyCollapsedAfterRenderRef.current) {\n ignoreInitiallyCollapsedAfterRenderRef.current = true;\n }\n }, [initiallyCollapsed]);\n\n const handleSelect = React.useCallback(() => {\n if (selectable) {\n if (isInGroup && value) {\n group?.onValueChange?.(value);\n } else if (!isInGroup && onSelectedChange) {\n onSelectedChange(!selected);\n }\n }\n }, [selectable, isInGroup, value, onSelectedChange, group, selected]);\n\n const handleToggleCollapse = React.useCallback(() => {\n if (isInGroup) {\n // In group mode, toggle between this card and null (all collapsed)\n group?.onExpandedChange?.(\n group.expandedValue === value ? null : (value ?? null)\n );\n } else {\n setIsCollapsed(!isCollapsed);\n }\n }, [isInGroup, group, value, isCollapsed]);\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLDivElement>) => {\n // Immediately mark that we're no longer in the initial state\n ignoreInitiallyCollapsedRef();\n\n if (selectable && (e.key === \"Enter\" || e.key === \" \")) {\n e.preventDefault();\n handleSelect();\n }\n\n onKeyDown?.(e);\n },\n [selectable, handleSelect, onKeyDown, ignoreInitiallyCollapsedRef]\n );\n\n const handleClick = React.useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n e.stopPropagation();\n\n // Immediately mark that we're no longer in the initial state\n ignoreInitiallyCollapsedRef();\n\n handleSelect();\n\n if (isSelected) {\n handleToggleCollapse();\n }\n\n onClick?.(e);\n },\n [\n handleSelect,\n onClick,\n isSelected,\n handleToggleCollapse,\n ignoreInitiallyCollapsedRef,\n ]\n );\n\n const contextValue: StackingCardContextValue = React.useMemo(\n () => ({\n isSelected,\n isCollapsed: shouldBeCollapsed,\n selectable,\n onSelectedChange,\n onToggleCollapse: handleToggleCollapse,\n setHasCollapsibleContent,\n contentId,\n }),\n [\n isSelected,\n shouldBeCollapsed,\n selectable,\n onSelectedChange,\n handleToggleCollapse,\n contentId,\n ]\n );\n\n // Generate accessible label if not provided\n const accessibleLabel = ariaLabel ?? (isInGroup ? `Option ${value}` : \"Card\");\n\n // Compute role separately to avoid nested ternary\n const computeRole = (): \"radio\" | \"button\" | undefined => {\n if (isInGroup) {\n return \"radio\";\n }\n if (selectable) {\n return \"button\";\n }\n return;\n };\n const cardRole = computeRole();\n\n // Combined ref callback that handles both forwarded ref and group registration\n const combinedRef = React.useCallback(\n (element: HTMLDivElement | null) => {\n // Handle forwarded ref\n if (typeof ref === \"function\") {\n ref(element);\n } else if (ref) {\n ref.current = element;\n }\n\n // Register with group if needed\n if (isInGroup && value && group?.registerCardRef) {\n group.registerCardRef(value, element);\n }\n },\n [ref, isInGroup, value, group]\n );\n\n React.useEffect(() => {\n if (initiallyCollapsed && !ignoreInitiallyCollapsedAfterRenderRef.current) {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n timeoutRef.current = setTimeout(() => {\n ignoreInitiallyCollapsedAfterRenderRef.current = true;\n }, 1500); // delay to allow for rapid re-renders to settle\n }\n\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, [initiallyCollapsed]);\n\n return (\n <StackingCardContext.Provider value={contextValue}>\n {/* biome-ignore lint/a11y/noStaticElementInteractions: Role is dynamically assigned based on context */}\n {/* biome-ignore lint/a11y/noNoninteractiveElementInteractions: Interactive behavior with proper role */}\n {/* biome-ignore lint/a11y/useAriaPropsSupportedByRole: aria-checked is valid for radio role */}\n <div\n aria-checked={isInGroup ? isSelected : undefined}\n aria-describedby={ariaDescribedby}\n aria-expanded={hasCollapsibleContent ? !shouldBeCollapsed : undefined}\n aria-label={accessibleLabel}\n aria-labelledby={ariaLabelledby}\n aria-pressed={!isInGroup && selectable ? isSelected : undefined}\n className={cn(\n \"relative flex w-full min-w-64 max-w-96 flex-col overflow-hidden border border-solid\",\n \"rounded-2xl shadow-sm transition-colors\",\n selectable && \"cursor-pointer\",\n isSelected\n ? \"border-gray-950 dark:border-white\"\n : \"border-gray-150 dark:border-gray-800\",\n \"focus-visible:ring-[var(--focus-ring)] focus-visible:ring-offset-2\",\n className\n )}\n key={value}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n ref={combinedRef}\n role={cardRole}\n style={props.style}\n tabIndex={selectable ? 0 : undefined}\n {...props}\n >\n {children}\n </div>\n </StackingCardContext.Provider>\n );\n}\n\ntype StackingCardHeaderProps = React.HTMLAttributes<HTMLDivElement> & {\n ref?: React.Ref<HTMLDivElement>;\n};\n\nfunction StackingCardHeader({\n className,\n children,\n ref,\n ...props\n}: StackingCardHeaderProps) {\n const { selectable, isSelected } = useStackingCard();\n\n return (\n <div\n className={cn(\n \"flex items-center justify-between gap-2 px-6 pt-4 pb-1\",\n isSelected && \"bg-gray-100 dark:bg-gray-800\",\n className\n )}\n ref={ref}\n {...props}\n >\n <div className=\"flex flex-1 items-center gap-2\">\n {selectable && (\n <DummyCheckbox checked={isSelected} tabIndex={-1} variant=\"rounded\" />\n )}\n {children}\n </div>\n </div>\n );\n}\n\ntype StackingCardTitleProps = React.HTMLAttributes<HTMLDivElement> & {\n ref?: React.Ref<HTMLDivElement>;\n};\n\nfunction StackingCardTitle({\n className,\n ref,\n ...props\n}: StackingCardTitleProps) {\n return (\n <Text\n className={cn(\n \"font-sans font-semibold text-gray-950 text-sm leading-6 dark:text-white\",\n className\n )}\n ref={ref}\n {...props}\n />\n );\n}\n\ntype StackingCardDescriptionProps = React.HTMLAttributes<HTMLDivElement> & {\n ref?: React.Ref<HTMLDivElement>;\n};\n\nfunction StackingCardDescription({\n className,\n ref,\n ...props\n}: StackingCardDescriptionProps) {\n return (\n <Text\n className={cn(\"text-gray-700 text-sm dark:text-gray-200\", className)}\n ref={ref}\n {...props}\n />\n );\n}\n\nconst MotionChevron = ({ isCollapsed }: { isCollapsed: boolean }) => (\n <motion.div\n animate={isCollapsed ? \"collapsed\" : \"expanded\"}\n className=\"flex items-center justify-center\"\n initial=\"collapsed\"\n variants={motionVariantsChevron}\n >\n <ChevronDown className=\"h-3 w-3\" />\n </motion.div>\n);\n\ninterface StackingCardContentProps\n extends React.HTMLAttributes<HTMLDivElement> {\n collapsible?: boolean;\n description?: React.ReactNode;\n minimal?: boolean;\n ref?: React.Ref<HTMLDivElement>;\n}\n\nfunction StackingCardContent({\n className,\n children,\n collapsible = false,\n minimal = false,\n description,\n ref,\n ...props\n}: StackingCardContentProps) {\n const {\n isCollapsed,\n isSelected,\n onToggleCollapse,\n setHasCollapsibleContent,\n contentId,\n } = useStackingCard();\n\n const { ref: autoHeightRef, variants: autoHeightVariants } = useAutoHeight();\n\n // Register this component as having collapsible content\n React.useEffect(() => {\n if (collapsible) {\n setHasCollapsibleContent?.(true);\n }\n\n return () => {\n setHasCollapsibleContent?.(false);\n };\n }, [collapsible, setHasCollapsibleContent]);\n\n if (!collapsible) {\n return (\n <div\n className={cn(\n \"flex flex-col gap-1.5 px-6 pt-0 pb-4\",\n isSelected && \"bg-gray-100 dark:bg-gray-800\",\n className\n )}\n ref={ref}\n {...props}\n >\n {children}\n </div>\n );\n }\n\n return (\n <>\n <div\n className={cn(\n \"flex items-center justify-between gap-2 px-6 pb-4\",\n isSelected && \"bg-gray-100 dark:bg-gray-800\"\n )}\n >\n {description ? (\n <div className=\"flex-1\">{description}</div>\n ) : (\n <div aria-hidden className=\"flex-1\" />\n )}\n <Button\n aria-controls={contentId}\n aria-expanded={!isCollapsed}\n aria-label={isCollapsed ? \"Show details\" : \"Hide details\"}\n borderRadius=\"full\"\n className={cn(isSelected && \"bg-white dark:bg-gray-900\", \"h-3\")}\n onClick={(e) => {\n e.stopPropagation();\n onToggleCollapse?.();\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.stopPropagation();\n }\n }}\n size=\"xs\"\n variant=\"light\"\n >\n <span className={cn({ \"sr-only\": minimal })}>Details</span>\n <MotionChevron isCollapsed={isCollapsed ?? false} />\n </Button>\n </div>\n <motion.div\n animate={isCollapsed ? \"collapsed\" : \"expanded\"}\n aria-hidden={isCollapsed}\n className=\"overflow-hidden\"\n id={contentId}\n initial=\"collapsed\"\n variants={autoHeightVariants}\n >\n <div className=\"flex flex-col\" ref={autoHeightRef}>\n <Separator\n className={cn(\n \"mt-0 mb-0\",\n isSelected\n ? \"bg-gray-950 dark:bg-white\"\n : \"border-gray-150 dark:border-gray-800\"\n )}\n />\n <div\n className={cn(\"flex flex-col gap-1.5\", className)}\n ref={ref}\n {...props}\n >\n {children}\n </div>\n </div>\n </motion.div>\n </>\n );\n}\n\nfunction StackingCardList({\n className,\n children,\n ref,\n ...props\n}: ListProps & { ref?: React.Ref<HTMLUListElement | HTMLOListElement> }) {\n return (\n <List\n className={cn(\"list-none px-6 pt-3 pb-5\", className)}\n ref={ref}\n {...props}\n >\n {children}\n </List>\n );\n}\n\nconst StackingCardCheck = React.memo<LucideProps>(({ className, ...props }) => {\n const { selectable, isSelected } = useStackingCard();\n\n return (\n <Check\n className={cn(\n \"-mt-0.5 size-[14px]\",\n {\n \"text-current\": !selectable,\n \"text-brand-primary\": isSelected && selectable,\n \"text-gray-950 dark:text-white\": !isSelected && selectable,\n },\n className\n )}\n {...props}\n />\n );\n});\n\nStackingCardCheck.displayName = \"StackingCardCheck\";\n\nfunction StackingCardListItem({\n className,\n children,\n ref,\n ...props\n}: ListItemProps & { ref?: React.Ref<HTMLLIElement> }) {\n return (\n <ListItem\n className={cn(\n \"flex font-sans text-gray-700 text-sm first:mt-0 dark:text-gray-200\",\n className\n )}\n customBullet={<StackingCardCheck />}\n ref={ref}\n {...props}\n >\n {children}\n </ListItem>\n );\n}\n\ninterface StackingCardGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n ref?: React.Ref<HTMLDivElement>;\n value?: string;\n}\n\nfunction StackingCardGroup({\n className,\n children,\n value,\n onValueChange,\n defaultValue,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n ref,\n ...props\n}: StackingCardGroupProps) {\n const [internalSelectedValue, setInternalSelectedValue] = React.useState(\n defaultValue ?? \"\"\n );\n\n const [internalExpandedValue, setInternalExpandedValue] = React.useState<\n string | null\n >(defaultValue ?? null);\n\n // For controlled mode, we need to manage expansion state separately\n const [controlledExpandedValue, setControlledExpandedValue] = React.useState<\n string | null\n >(value ?? null);\n\n // Calculate effective values during render (controlled vs uncontrolled pattern)\n const effectiveSelectedValue =\n value === undefined ? internalSelectedValue : value;\n\n const effectiveExpandedValue =\n value === undefined ? internalExpandedValue : controlledExpandedValue;\n\n // Auto-expand selected card in controlled mode\n React.useEffect(() => {\n if (value !== undefined) {\n setControlledExpandedValue(value);\n }\n }, [value]);\n\n const handleValueChange = React.useCallback(\n (newValue: string) => {\n // Only update internal state if uncontrolled\n if (value === undefined) {\n setInternalSelectedValue(newValue);\n setInternalExpandedValue(newValue);\n } else {\n // In controlled mode, auto-expand the newly selected card\n setControlledExpandedValue(newValue);\n }\n\n onValueChange?.(newValue);\n },\n [value, onValueChange]\n );\n\n const handleExpandedChange = React.useCallback(\n (newExpandedValue: string | null) => {\n if (value === undefined) {\n // Uncontrolled mode\n setInternalExpandedValue(newExpandedValue);\n } else {\n // Controlled mode\n setControlledExpandedValue(newExpandedValue);\n }\n },\n [value]\n );\n\n // Extract card values from children for arrow key navigation\n const cardValues = React.useMemo(() => {\n const values: string[] = [];\n\n React.Children.forEach(children, (child) => {\n if (React.isValidElement(child)) {\n const childValue = (child.props as { value?: unknown }).value;\n\n if (typeof childValue === \"string\") {\n values.push(childValue);\n }\n }\n });\n\n return values;\n }, [children]);\n\n // Store card refs for navigation\n const cardRefsRef = React.useRef<Map<string, HTMLElement>>(new Map());\n\n const registerCardRef = React.useCallback(\n (cardValue: string, element: HTMLElement | null) => {\n if (element) {\n cardRefsRef.current.set(cardValue, element);\n } else {\n cardRefsRef.current.delete(cardValue);\n }\n },\n []\n );\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (cardValues.length === 0) {\n return;\n }\n\n const currentIndex = cardValues.indexOf(effectiveSelectedValue);\n let nextIndex = currentIndex;\n\n switch (e.key) {\n case \"ArrowDown\":\n case \"ArrowRight\":\n e.preventDefault();\n nextIndex =\n currentIndex < cardValues.length - 1 ? currentIndex + 1 : 0;\n break;\n case \"ArrowUp\":\n case \"ArrowLeft\":\n e.preventDefault();\n nextIndex =\n currentIndex > 0 ? currentIndex - 1 : cardValues.length - 1;\n break;\n case \"Home\":\n e.preventDefault();\n nextIndex = 0;\n break;\n case \"End\":\n e.preventDefault();\n nextIndex = cardValues.length - 1;\n break;\n default:\n return;\n }\n\n const nextValue = cardValues[nextIndex];\n\n if (nextValue && nextValue !== effectiveSelectedValue) {\n handleValueChange(nextValue);\n\n // Focus the newly selected card using registered ref\n setTimeout(() => {\n const targetCard = cardRefsRef.current.get(nextValue);\n\n targetCard?.focus();\n }, 0);\n }\n },\n [cardValues, effectiveSelectedValue, handleValueChange]\n );\n\n const contextValue: StackingCardGroupContextValue = React.useMemo(\n () => ({\n selectedValue: effectiveSelectedValue,\n expandedValue: effectiveExpandedValue,\n onValueChange: handleValueChange,\n onExpandedChange: handleExpandedChange,\n registerCardRef,\n }),\n [\n effectiveSelectedValue,\n effectiveExpandedValue,\n handleValueChange,\n handleExpandedChange,\n registerCardRef,\n ]\n );\n\n // Generate accessible label if not provided\n const accessibleLabel = ariaLabel ?? \"Select an option\";\n\n return (\n <StackingCardGroupContext.Provider value={contextValue}>\n <div\n aria-label={accessibleLabel}\n aria-labelledby={ariaLabelledby}\n className={cn(\"flex flex-col gap-4\", className)}\n onKeyDown={handleKeyDown}\n ref={ref}\n role=\"radiogroup\"\n tabIndex={-1}\n {...props}\n >\n {children}\n </div>\n </StackingCardGroupContext.Provider>\n );\n}\n\nexport {\n StackingCard,\n StackingCardCheck,\n StackingCardContent,\n StackingCardDescription,\n StackingCardGroup,\n StackingCardHeader,\n StackingCardList,\n StackingCardListItem,\n StackingCardTitle,\n};\n"],
|
|
4
|
+
"sourcesContent": ["/**\n * @module StackingCard\n *\n * Selectable card component with collapsible content. Supports single selection groups.\n * Features animated expand/collapse and accessible keyboard navigation.\n *\n * @example\n * // Single selectable card\n * <StackingCard selectable selected={isSelected} onSelectedChange={setSelected}>\n * <StackingCardHeader>\n * <StackingCardTitle>Premium Plan</StackingCardTitle>\n * </StackingCardHeader>\n * <StackingCardContent collapsible>\n * <StackingCardList>\n * <StackingCardListItem>Unlimited downloads</StackingCardListItem>\n * <StackingCardListItem>Priority support</StackingCardListItem>\n * </StackingCardList>\n * </StackingCardContent>\n * </StackingCard>\n *\n * @example\n * // Radio group of cards\n * <StackingCardGroup value={plan} onValueChange={setPlan}>\n * <StackingCard value=\"basic\" selectable>\n * <StackingCardHeader>\n * <StackingCardTitle>Basic</StackingCardTitle>\n * </StackingCardHeader>\n * </StackingCard>\n * <StackingCard value=\"pro\" selectable>\n * <StackingCardHeader>\n * <StackingCardTitle>Pro</StackingCardTitle>\n * </StackingCardHeader>\n * </StackingCard>\n * </StackingCardGroup>\n */\nimport type { LucideProps } from \"lucide-react\";\nimport { Check, ChevronDown } from \"lucide-react\";\nimport type { Variants } from \"motion/react\";\nimport { motion } from \"motion/react\";\nimport * as React from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"./button\";\nimport { DummyCheckbox } from \"./checkbox\";\nimport { Separator } from \"./separator\";\n\nimport type { ListItemProps, ListProps } from \"./typography/list\";\nimport { List, ListItem } from \"./typography/list\";\nimport { Text } from \"./typography/text\";\n\n// Motion transition configuration\nconst motionTransition = {\n duration: 0.4,\n type: \"spring\" as const,\n bounce: 0,\n};\n\nconst motionVariantsChevron: Variants = {\n collapsed: {\n rotate: 0,\n transition: motionTransition,\n },\n expanded: {\n rotate: 180,\n transition: motionTransition,\n },\n};\n\n// Hook to handle auto height animation for collapsible content\nconst useAutoHeight = () => {\n const [height, setHeight] = React.useState<number | \"auto\">(\"auto\");\n const ref = React.useRef<HTMLDivElement>(null);\n\n React.useEffect(() => {\n if (!ref.current) {\n return;\n }\n\n const resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0];\n\n if (entry) {\n setHeight(entry.contentRect.height);\n }\n });\n\n resizeObserver.observe(ref.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, []);\n\n const variants: Variants = React.useMemo(\n () => ({\n collapsed: {\n height: 0,\n opacity: 0,\n transition: motionTransition,\n },\n expanded: {\n height: height === \"auto\" ? \"auto\" : height,\n opacity: 1,\n transition: motionTransition,\n },\n }),\n [height]\n );\n\n return { ref, variants };\n};\n\ninterface StackingCardGroupContextValue {\n expandedValue: string | null;\n onExpandedChange?: (value: string | null) => void;\n onValueChange?: (value: string) => void;\n registerCardRef?: (value: string, element: HTMLElement | null) => void;\n selectedValue: string;\n}\n\nconst StackingCardGroupContext =\n React.createContext<StackingCardGroupContextValue | null>(null);\n\nconst useStackingCardGroup = () => {\n const context = React.useContext(StackingCardGroupContext);\n\n return context;\n};\n\ninterface StackingCardContextValue {\n contentId?: string;\n isCollapsed?: boolean;\n isSelected?: boolean;\n onSelectedChange?: (selected: boolean) => void;\n onToggleCollapse?: () => void;\n selectable?: boolean;\n setHasCollapsibleContent?: (hasCollapsible: boolean) => void;\n}\n\nconst StackingCardContext = React.createContext<StackingCardContextValue>({});\n\nconst useStackingCard = () => {\n const context = React.useContext(StackingCardContext);\n\n return context;\n};\n\ninterface StackingCardProps extends React.HTMLAttributes<HTMLDivElement> {\n \"aria-describedby\"?: string;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n initiallyCollapsed?: boolean;\n onSelectedChange?: (selected: boolean) => void;\n selectable?: boolean;\n selected?: boolean;\n value?: string;\n variant?: \"default\" | \"selected\";\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Complex component with many state interactions\nfunction StackingCard({\n className,\n children,\n selectable = false,\n selected = false,\n onSelectedChange,\n variant,\n onClick,\n value,\n onKeyDown,\n initiallyCollapsed = true,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n \"aria-describedby\": ariaDescribedby,\n ref,\n ...props\n}: StackingCardProps & { ref?: React.Ref<HTMLDivElement> }) {\n const [isCollapsed, setIsCollapsed] = React.useState(true);\n const [hasCollapsibleContent, setHasCollapsibleContent] =\n React.useState(false);\n\n const [wasSelected, setWasSelected] = React.useState(false);\n\n const ignoreInitiallyCollapsedAfterRenderRef = React.useRef(false);\n const timeoutRef = React.useRef<NodeJS.Timeout | null>(null);\n\n // Generate unique ID for this card instance\n const cardId = React.useId();\n const contentId = `${cardId}-content`;\n\n const group = useStackingCardGroup();\n\n // Determine if we're in a group\n const isInGroup = group !== null;\n\n // Determine selection state\n const isSelected = isInGroup\n ? group.selectedValue === value\n : variant === \"selected\" || selected;\n\n // Auto-expand logic: if card becomes selected and has collapsible content, expand it\n // This is calculated during render, not in useEffect\n const justBecameSelected = isSelected && !wasSelected;\n\n if (justBecameSelected && hasCollapsibleContent) {\n if (isInGroup) {\n // In group mode, expand this card\n group?.onExpandedChange?.(value ?? null);\n } else {\n // In standalone mode, expand this card\n setIsCollapsed(false);\n }\n }\n\n // Update wasSelected for next render (this is safe during render)\n if (isSelected !== wasSelected) {\n setWasSelected(isSelected);\n }\n\n // Determine collapse state\n let shouldBeCollapsed = isCollapsed;\n\n if (initiallyCollapsed && !ignoreInitiallyCollapsedAfterRenderRef.current) {\n shouldBeCollapsed = true;\n } else if (isInGroup) {\n shouldBeCollapsed = group.expandedValue !== value;\n }\n\n const ignoreInitiallyCollapsedRef = React.useCallback(() => {\n if (initiallyCollapsed && !ignoreInitiallyCollapsedAfterRenderRef.current) {\n ignoreInitiallyCollapsedAfterRenderRef.current = true;\n }\n }, [initiallyCollapsed]);\n\n const handleSelect = React.useCallback(() => {\n if (selectable) {\n if (isInGroup && value) {\n group?.onValueChange?.(value);\n } else if (!isInGroup && onSelectedChange) {\n onSelectedChange(!selected);\n }\n }\n }, [selectable, isInGroup, value, onSelectedChange, group, selected]);\n\n const handleToggleCollapse = React.useCallback(() => {\n if (isInGroup) {\n // In group mode, toggle between this card and null (all collapsed)\n group?.onExpandedChange?.(\n group.expandedValue === value ? null : (value ?? null)\n );\n } else {\n setIsCollapsed(!isCollapsed);\n }\n }, [isInGroup, group, value, isCollapsed]);\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLDivElement>) => {\n // Immediately mark that we're no longer in the initial state\n ignoreInitiallyCollapsedRef();\n\n if (selectable && (e.key === \"Enter\" || e.key === \" \")) {\n e.preventDefault();\n handleSelect();\n }\n\n onKeyDown?.(e);\n },\n [selectable, handleSelect, onKeyDown, ignoreInitiallyCollapsedRef]\n );\n\n const handleClick = React.useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n e.stopPropagation();\n\n // Immediately mark that we're no longer in the initial state\n ignoreInitiallyCollapsedRef();\n\n handleSelect();\n\n if (isSelected) {\n handleToggleCollapse();\n }\n\n onClick?.(e);\n },\n [\n handleSelect,\n onClick,\n isSelected,\n handleToggleCollapse,\n ignoreInitiallyCollapsedRef,\n ]\n );\n\n const contextValue: StackingCardContextValue = React.useMemo(\n () => ({\n isSelected,\n isCollapsed: shouldBeCollapsed,\n selectable,\n onSelectedChange,\n onToggleCollapse: handleToggleCollapse,\n setHasCollapsibleContent,\n contentId,\n }),\n [\n isSelected,\n shouldBeCollapsed,\n selectable,\n onSelectedChange,\n handleToggleCollapse,\n contentId,\n ]\n );\n\n // Generate accessible label if not provided\n const accessibleLabel = ariaLabel ?? (isInGroup ? `Option ${value}` : \"Card\");\n\n // Compute role separately to avoid nested ternary\n const computeRole = (): \"radio\" | \"button\" | undefined => {\n if (isInGroup) {\n return \"radio\";\n }\n if (selectable) {\n return \"button\";\n }\n return;\n };\n const cardRole = computeRole();\n\n // Combined ref callback that handles both forwarded ref and group registration\n const combinedRef = React.useCallback(\n (element: HTMLDivElement | null) => {\n // Handle forwarded ref\n if (typeof ref === \"function\") {\n ref(element);\n } else if (ref) {\n ref.current = element;\n }\n\n // Register with group if needed\n if (isInGroup && value && group?.registerCardRef) {\n group.registerCardRef(value, element);\n }\n },\n [ref, isInGroup, value, group]\n );\n\n React.useEffect(() => {\n if (initiallyCollapsed && !ignoreInitiallyCollapsedAfterRenderRef.current) {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n timeoutRef.current = setTimeout(() => {\n ignoreInitiallyCollapsedAfterRenderRef.current = true;\n }, 1500); // delay to allow for rapid re-renders to settle\n }\n\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, [initiallyCollapsed]);\n\n return (\n <StackingCardContext.Provider value={contextValue}>\n {/* biome-ignore lint/a11y/noStaticElementInteractions: Role is dynamically assigned based on context */}\n {/* biome-ignore lint/a11y/noNoninteractiveElementInteractions: Interactive behavior with proper role */}\n {/* biome-ignore lint/a11y/useAriaPropsSupportedByRole: aria-checked is valid for radio role */}\n <div\n aria-checked={isInGroup ? isSelected : undefined}\n aria-describedby={ariaDescribedby}\n aria-expanded={hasCollapsibleContent ? !shouldBeCollapsed : undefined}\n aria-label={accessibleLabel}\n aria-labelledby={ariaLabelledby}\n aria-pressed={!isInGroup && selectable ? isSelected : undefined}\n className={cn(\n \"relative flex w-full min-w-64 max-w-96 flex-col overflow-hidden border border-solid\",\n \"rounded-2xl shadow-xs transition-colors\",\n selectable && \"cursor-pointer\",\n isSelected\n ? \"border-gray-950 dark:border-white\"\n : \"border-gray-150 dark:border-gray-800\",\n \"focus-visible:ring-[var(--focus-ring)] focus-visible:ring-offset-2\",\n className\n )}\n key={value}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n ref={combinedRef}\n role={cardRole}\n style={props.style}\n tabIndex={selectable ? 0 : undefined}\n {...props}\n >\n {children}\n </div>\n </StackingCardContext.Provider>\n );\n}\n\ntype StackingCardHeaderProps = React.HTMLAttributes<HTMLDivElement> & {\n ref?: React.Ref<HTMLDivElement>;\n};\n\nfunction StackingCardHeader({\n className,\n children,\n ref,\n ...props\n}: StackingCardHeaderProps) {\n const { selectable, isSelected } = useStackingCard();\n\n return (\n <div\n className={cn(\n \"flex items-center justify-between gap-2 px-6 pt-4 pb-1\",\n isSelected && \"bg-gray-100 dark:bg-gray-800\",\n className\n )}\n ref={ref}\n {...props}\n >\n <div className=\"flex flex-1 items-center gap-2\">\n {selectable && (\n <DummyCheckbox checked={isSelected} tabIndex={-1} variant=\"rounded\" />\n )}\n {children}\n </div>\n </div>\n );\n}\n\ntype StackingCardTitleProps = React.HTMLAttributes<HTMLDivElement> & {\n ref?: React.Ref<HTMLDivElement>;\n};\n\nfunction StackingCardTitle({\n className,\n ref,\n ...props\n}: StackingCardTitleProps) {\n return (\n <Text\n className={cn(\n \"font-sans font-semibold text-gray-950 text-sm leading-6 dark:text-white\",\n className\n )}\n ref={ref}\n {...props}\n />\n );\n}\n\ntype StackingCardDescriptionProps = React.HTMLAttributes<HTMLDivElement> & {\n ref?: React.Ref<HTMLDivElement>;\n};\n\nfunction StackingCardDescription({\n className,\n ref,\n ...props\n}: StackingCardDescriptionProps) {\n return (\n <Text\n className={cn(\"text-gray-700 text-sm dark:text-gray-200\", className)}\n ref={ref}\n {...props}\n />\n );\n}\n\nconst MotionChevron = ({ isCollapsed }: { isCollapsed: boolean }) => (\n <motion.div\n animate={isCollapsed ? \"collapsed\" : \"expanded\"}\n className=\"flex items-center justify-center\"\n initial=\"collapsed\"\n variants={motionVariantsChevron}\n >\n <ChevronDown className=\"h-3 w-3\" />\n </motion.div>\n);\n\ninterface StackingCardContentProps\n extends React.HTMLAttributes<HTMLDivElement> {\n collapsible?: boolean;\n description?: React.ReactNode;\n minimal?: boolean;\n ref?: React.Ref<HTMLDivElement>;\n}\n\nfunction StackingCardContent({\n className,\n children,\n collapsible = false,\n minimal = false,\n description,\n ref,\n ...props\n}: StackingCardContentProps) {\n const {\n isCollapsed,\n isSelected,\n onToggleCollapse,\n setHasCollapsibleContent,\n contentId,\n } = useStackingCard();\n\n const { ref: autoHeightRef, variants: autoHeightVariants } = useAutoHeight();\n\n // Register this component as having collapsible content\n React.useEffect(() => {\n if (collapsible) {\n setHasCollapsibleContent?.(true);\n }\n\n return () => {\n setHasCollapsibleContent?.(false);\n };\n }, [collapsible, setHasCollapsibleContent]);\n\n if (!collapsible) {\n return (\n <div\n className={cn(\n \"flex flex-col gap-1.5 px-6 pt-0 pb-4\",\n isSelected && \"bg-gray-100 dark:bg-gray-800\",\n className\n )}\n ref={ref}\n {...props}\n >\n {children}\n </div>\n );\n }\n\n return (\n <>\n <div\n className={cn(\n \"flex items-center justify-between gap-2 px-6 pb-4\",\n isSelected && \"bg-gray-100 dark:bg-gray-800\"\n )}\n >\n {description ? (\n <div className=\"flex-1\">{description}</div>\n ) : (\n <div aria-hidden className=\"flex-1\" />\n )}\n <Button\n aria-controls={contentId}\n aria-expanded={!isCollapsed}\n aria-label={isCollapsed ? \"Show details\" : \"Hide details\"}\n borderRadius=\"full\"\n className={cn(isSelected && \"bg-white dark:bg-gray-900\", \"h-3\")}\n onClick={(e) => {\n e.stopPropagation();\n onToggleCollapse?.();\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.stopPropagation();\n }\n }}\n size=\"xs\"\n variant=\"light\"\n >\n <span className={cn({ \"sr-only\": minimal })}>Details</span>\n <MotionChevron isCollapsed={isCollapsed ?? false} />\n </Button>\n </div>\n <motion.div\n animate={isCollapsed ? \"collapsed\" : \"expanded\"}\n aria-hidden={isCollapsed}\n className=\"overflow-hidden\"\n id={contentId}\n initial=\"collapsed\"\n variants={autoHeightVariants}\n >\n <div className=\"flex flex-col\" ref={autoHeightRef}>\n <Separator\n className={cn(\n \"mt-0 mb-0\",\n isSelected\n ? \"bg-gray-950 dark:bg-white\"\n : \"border-gray-150 dark:border-gray-800\"\n )}\n />\n <div\n className={cn(\"flex flex-col gap-1.5\", className)}\n ref={ref}\n {...props}\n >\n {children}\n </div>\n </div>\n </motion.div>\n </>\n );\n}\n\nfunction StackingCardList({\n className,\n children,\n ref,\n ...props\n}: ListProps & { ref?: React.Ref<HTMLUListElement | HTMLOListElement> }) {\n return (\n <List\n className={cn(\"list-none px-6 pt-3 pb-5\", className)}\n ref={ref}\n {...props}\n >\n {children}\n </List>\n );\n}\n\nconst StackingCardCheck = React.memo<LucideProps>(({ className, ...props }) => {\n const { selectable, isSelected } = useStackingCard();\n\n return (\n <Check\n className={cn(\n \"-mt-0.5 size-[14px]\",\n {\n \"text-current\": !selectable,\n \"text-brand-primary\": isSelected && selectable,\n \"text-gray-950 dark:text-white\": !isSelected && selectable,\n },\n className\n )}\n {...props}\n />\n );\n});\n\nStackingCardCheck.displayName = \"StackingCardCheck\";\n\nfunction StackingCardListItem({\n className,\n children,\n ref,\n ...props\n}: ListItemProps & { ref?: React.Ref<HTMLLIElement> }) {\n return (\n <ListItem\n className={cn(\n \"flex font-sans text-gray-700 text-sm first:mt-0 dark:text-gray-200\",\n className\n )}\n customBullet={<StackingCardCheck />}\n ref={ref}\n {...props}\n >\n {children}\n </ListItem>\n );\n}\n\ninterface StackingCardGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n ref?: React.Ref<HTMLDivElement>;\n value?: string;\n}\n\nfunction StackingCardGroup({\n className,\n children,\n value,\n onValueChange,\n defaultValue,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n ref,\n ...props\n}: StackingCardGroupProps) {\n const [internalSelectedValue, setInternalSelectedValue] = React.useState(\n defaultValue ?? \"\"\n );\n\n const [internalExpandedValue, setInternalExpandedValue] = React.useState<\n string | null\n >(defaultValue ?? null);\n\n // For controlled mode, we need to manage expansion state separately\n const [controlledExpandedValue, setControlledExpandedValue] = React.useState<\n string | null\n >(value ?? null);\n\n // Calculate effective values during render (controlled vs uncontrolled pattern)\n const effectiveSelectedValue =\n value === undefined ? internalSelectedValue : value;\n\n const effectiveExpandedValue =\n value === undefined ? internalExpandedValue : controlledExpandedValue;\n\n // Auto-expand selected card in controlled mode\n React.useEffect(() => {\n if (value !== undefined) {\n setControlledExpandedValue(value);\n }\n }, [value]);\n\n const handleValueChange = React.useCallback(\n (newValue: string) => {\n // Only update internal state if uncontrolled\n if (value === undefined) {\n setInternalSelectedValue(newValue);\n setInternalExpandedValue(newValue);\n } else {\n // In controlled mode, auto-expand the newly selected card\n setControlledExpandedValue(newValue);\n }\n\n onValueChange?.(newValue);\n },\n [value, onValueChange]\n );\n\n const handleExpandedChange = React.useCallback(\n (newExpandedValue: string | null) => {\n if (value === undefined) {\n // Uncontrolled mode\n setInternalExpandedValue(newExpandedValue);\n } else {\n // Controlled mode\n setControlledExpandedValue(newExpandedValue);\n }\n },\n [value]\n );\n\n // Extract card values from children for arrow key navigation\n const cardValues = React.useMemo(() => {\n const values: string[] = [];\n\n React.Children.forEach(children, (child) => {\n if (React.isValidElement(child)) {\n const childValue = (child.props as { value?: unknown }).value;\n\n if (typeof childValue === \"string\") {\n values.push(childValue);\n }\n }\n });\n\n return values;\n }, [children]);\n\n // Store card refs for navigation\n const cardRefsRef = React.useRef<Map<string, HTMLElement>>(new Map());\n\n const registerCardRef = React.useCallback(\n (cardValue: string, element: HTMLElement | null) => {\n if (element) {\n cardRefsRef.current.set(cardValue, element);\n } else {\n cardRefsRef.current.delete(cardValue);\n }\n },\n []\n );\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (cardValues.length === 0) {\n return;\n }\n\n const currentIndex = cardValues.indexOf(effectiveSelectedValue);\n let nextIndex = currentIndex;\n\n switch (e.key) {\n case \"ArrowDown\":\n case \"ArrowRight\":\n e.preventDefault();\n nextIndex =\n currentIndex < cardValues.length - 1 ? currentIndex + 1 : 0;\n break;\n case \"ArrowUp\":\n case \"ArrowLeft\":\n e.preventDefault();\n nextIndex =\n currentIndex > 0 ? currentIndex - 1 : cardValues.length - 1;\n break;\n case \"Home\":\n e.preventDefault();\n nextIndex = 0;\n break;\n case \"End\":\n e.preventDefault();\n nextIndex = cardValues.length - 1;\n break;\n default:\n return;\n }\n\n const nextValue = cardValues[nextIndex];\n\n if (nextValue && nextValue !== effectiveSelectedValue) {\n handleValueChange(nextValue);\n\n // Focus the newly selected card using registered ref\n setTimeout(() => {\n const targetCard = cardRefsRef.current.get(nextValue);\n\n targetCard?.focus();\n }, 0);\n }\n },\n [cardValues, effectiveSelectedValue, handleValueChange]\n );\n\n const contextValue: StackingCardGroupContextValue = React.useMemo(\n () => ({\n selectedValue: effectiveSelectedValue,\n expandedValue: effectiveExpandedValue,\n onValueChange: handleValueChange,\n onExpandedChange: handleExpandedChange,\n registerCardRef,\n }),\n [\n effectiveSelectedValue,\n effectiveExpandedValue,\n handleValueChange,\n handleExpandedChange,\n registerCardRef,\n ]\n );\n\n // Generate accessible label if not provided\n const accessibleLabel = ariaLabel ?? \"Select an option\";\n\n return (\n <StackingCardGroupContext.Provider value={contextValue}>\n <div\n aria-label={accessibleLabel}\n aria-labelledby={ariaLabelledby}\n className={cn(\"flex flex-col gap-4\", className)}\n onKeyDown={handleKeyDown}\n ref={ref}\n role=\"radiogroup\"\n tabIndex={-1}\n {...props}\n >\n {children}\n </div>\n </StackingCardGroupContext.Provider>\n );\n}\n\nexport {\n StackingCard,\n StackingCardCheck,\n StackingCardContent,\n StackingCardDescription,\n StackingCardGroup,\n StackingCardHeader,\n StackingCardList,\n StackingCardListItem,\n StackingCardTitle,\n};\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/components/tabs.js
CHANGED
|
@@ -25,7 +25,7 @@ const TabsTrigger = ({
|
|
|
25
25
|
TabsPrimitive.Trigger,
|
|
26
26
|
{
|
|
27
27
|
className: cn(
|
|
28
|
-
"inline-flex items-center justify-center whitespace-nowrap rounded-
|
|
28
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-xs px-3 py-1.5 font-semibold text-sm ring-offset-gray-100 transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)] focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-white data-[state=active]:text-gray-950 data-[state=active]:shadow-xs dark:ring-offset-gray-900 dark:data-[state=active]:bg-gray-800 dark:data-[state=active]:text-gray-50 dark:focus-visible:ring-[var(--focus-ring)]",
|
|
29
29
|
className
|
|
30
30
|
),
|
|
31
31
|
ref,
|
|
@@ -40,7 +40,7 @@ const TabsContent = ({
|
|
|
40
40
|
TabsPrimitive.Content,
|
|
41
41
|
{
|
|
42
42
|
className: cn(
|
|
43
|
-
"mt-2 ring-offset-gray-100 focus-visible:outline-
|
|
43
|
+
"mt-2 ring-offset-gray-100 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)] focus-visible:ring-offset-2 dark:ring-offset-gray-900 dark:focus-visible:ring-[var(--focus-ring)]",
|
|
44
44
|
className
|
|
45
45
|
),
|
|
46
46
|
ref,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/components/tabs.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * @module Tabs\n *\n * Tabbed interface for switching between content panels. Built on Radix UI Tabs primitive.\n *\n * @see {@link https://ui.shadcn.com/docs/components/tabs Shadcn Tabs}\n * @see {@link https://www.radix-ui.com/primitives/docs/components/tabs Radix Tabs}\n *\n * @example\n * // Basic tabs\n * <Tabs defaultValue=\"tab1\">\n * <TabsList>\n * <TabsTrigger value=\"tab1\">Tab 1</TabsTrigger>\n * <TabsTrigger value=\"tab2\">Tab 2</TabsTrigger>\n * </TabsList>\n * <TabsContent value=\"tab1\">Content 1</TabsContent>\n * <TabsContent value=\"tab2\">Content 2</TabsContent>\n * </Tabs>\n *\n * @example\n * // Controlled tabs\n * const [activeTab, setActiveTab] = useState(\"settings\");\n *\n * <Tabs value={activeTab} onValueChange={setActiveTab}>\n * ...\n * </Tabs>\n */\nimport { Tabs as TabsPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport type { Ref } from \"react\";\n\nimport { cn } from \"../lib/utils\";\n\n/** Root component that manages tab selection state. */\nconst Tabs = TabsPrimitive.Root;\n\n/** Container for tab trigger buttons. */\nconst TabsList = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {\n ref?: Ref<React.ElementRef<typeof TabsPrimitive.List>>;\n}) => (\n <TabsPrimitive.List\n className={cn(\n \"inline-flex h-10 items-center justify-center rounded-md bg-gray-100 p-1 text-gray-900 dark:bg-gray-900 dark:text-gray-100\",\n className\n )}\n ref={ref}\n {...props}\n />\n);\n\n/** Button that activates its associated tab content. */\nconst TabsTrigger = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> & {\n ref?: Ref<React.ElementRef<typeof TabsPrimitive.Trigger>>;\n}) => (\n <TabsPrimitive.Trigger\n className={cn(\n \"inline-flex items-center justify-center whitespace-nowrap rounded-
|
|
4
|
+
"sourcesContent": ["/**\n * @module Tabs\n *\n * Tabbed interface for switching between content panels. Built on Radix UI Tabs primitive.\n *\n * @see {@link https://ui.shadcn.com/docs/components/tabs Shadcn Tabs}\n * @see {@link https://www.radix-ui.com/primitives/docs/components/tabs Radix Tabs}\n *\n * @example\n * // Basic tabs\n * <Tabs defaultValue=\"tab1\">\n * <TabsList>\n * <TabsTrigger value=\"tab1\">Tab 1</TabsTrigger>\n * <TabsTrigger value=\"tab2\">Tab 2</TabsTrigger>\n * </TabsList>\n * <TabsContent value=\"tab1\">Content 1</TabsContent>\n * <TabsContent value=\"tab2\">Content 2</TabsContent>\n * </Tabs>\n *\n * @example\n * // Controlled tabs\n * const [activeTab, setActiveTab] = useState(\"settings\");\n *\n * <Tabs value={activeTab} onValueChange={setActiveTab}>\n * ...\n * </Tabs>\n */\nimport { Tabs as TabsPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport type { Ref } from \"react\";\n\nimport { cn } from \"../lib/utils\";\n\n/** Root component that manages tab selection state. */\nconst Tabs = TabsPrimitive.Root;\n\n/** Container for tab trigger buttons. */\nconst TabsList = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {\n ref?: Ref<React.ElementRef<typeof TabsPrimitive.List>>;\n}) => (\n <TabsPrimitive.List\n className={cn(\n \"inline-flex h-10 items-center justify-center rounded-md bg-gray-100 p-1 text-gray-900 dark:bg-gray-900 dark:text-gray-100\",\n className\n )}\n ref={ref}\n {...props}\n />\n);\n\n/** Button that activates its associated tab content. */\nconst TabsTrigger = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> & {\n ref?: Ref<React.ElementRef<typeof TabsPrimitive.Trigger>>;\n}) => (\n <TabsPrimitive.Trigger\n className={cn(\n \"inline-flex items-center justify-center whitespace-nowrap rounded-xs px-3 py-1.5 font-semibold text-sm ring-offset-gray-100 transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)] focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-white data-[state=active]:text-gray-950 data-[state=active]:shadow-xs dark:ring-offset-gray-900 dark:data-[state=active]:bg-gray-800 dark:data-[state=active]:text-gray-50 dark:focus-visible:ring-[var(--focus-ring)]\",\n className\n )}\n ref={ref}\n {...props}\n />\n);\n\n/** Content panel shown when its associated tab is active. */\nconst TabsContent = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content> & {\n ref?: Ref<React.ElementRef<typeof TabsPrimitive.Content>>;\n}) => (\n <TabsPrimitive.Content\n className={cn(\n \"mt-2 ring-offset-gray-100 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)] focus-visible:ring-offset-2 dark:ring-offset-gray-900 dark:focus-visible:ring-[var(--focus-ring)]\",\n className\n )}\n ref={ref}\n {...props}\n />\n);\n\nexport { Tabs, TabsContent, TabsList, TabsTrigger };\n"],
|
|
5
5
|
"mappings": "AA4CE;AAjBF,SAAS,QAAQ,qBAAqB;AAItC,SAAS,UAAU;AAGnB,MAAM,OAAO,cAAc;AAG3B,MAAM,WAAW,CAAC;AAAA,EAChB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAGE;AAAA,EAAC,cAAc;AAAA,EAAd;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACC,GAAG;AAAA;AACN;AAIF,MAAM,cAAc,CAAC;AAAA,EACnB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAGE;AAAA,EAAC,cAAc;AAAA,EAAd;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACC,GAAG;AAAA;AACN;AAIF,MAAM,cAAc,CAAC;AAAA,EACnB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAGE;AAAA,EAAC,cAAc;AAAA,EAAd;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACC,GAAG;AAAA;AACN;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/components/toast.js
CHANGED
|
@@ -32,7 +32,7 @@ const GlobalToastDescription = ({ children }) => {
|
|
|
32
32
|
return /* @__PURE__ */ jsx("div", { className: "line-clamp-2 font-normal text-gray-700 text-sm dark:text-gray-200 ", children });
|
|
33
33
|
};
|
|
34
34
|
const globalToastIconVariants = cva(
|
|
35
|
-
"size-7
|
|
35
|
+
"size-7 shrink-0 rounded-full flex items-center justify-center [&_svg]:size-4",
|
|
36
36
|
{
|
|
37
37
|
variants: {
|
|
38
38
|
variant: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/components/toast.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * @module Toast\n *\n * Toast notification system built on Sonner. Provides success, error, info, and warning variants.\n *\n * @see {@link https://ui.shadcn.com/docs/components/sonner Shadcn Sonner}\n * @see {@link https://sonner.emilkowal.ski/ Sonner Documentation}\n *\n * @example\n * // Setup: Add Toaster to your app root\n * <Toaster />\n *\n * @example\n * // Show toast notifications\n * toast(\"Default message\");\n * toast.success(\"Operation completed\");\n * toast.error(\"Something went wrong\");\n * toast.info(\"Information\");\n * toast.warning(\"Warning message\");\n *\n * @example\n * // With action button\n * toast({\n * title: \"Item deleted\",\n * action: {\n * label: \"Undo\",\n * onClick: () => undoDelete(),\n * },\n * });\n *\n * @example\n * // Promise toast\n * const id = toast.promise(promise, {\n loading: { title: \"Saving document...\" },\n success: (data) => ({\n title: `${data.name} saved successfully!`,\n duration: 10_000,\n }),\n\n error: { title: \"Failed to save document\", duration: 10_000 },\n });\n */\nimport { cva } from \"class-variance-authority\";\nimport type React from \"react\";\nimport type { ReactNode } from \"react\";\nimport {\n type ExternalToast,\n Toaster as SonnerToaster,\n toast as sonnerToast,\n} from \"sonner\";\nimport { Button, type ButtonProps, cn } from \"..\";\nimport { Check, CircleX, InfoIcon, Loader, TriangleAlert, X } from \"../icons\";\n\ntype ToasterProps = React.ComponentProps<typeof SonnerToaster>;\ntype ToastVariant = \"error\" | \"info\" | \"loading\" | \"success\" | \"warning\";\n\nconst GLOBAL_TOASTER_ID = \"global\";\n\ninterface GlobalToastAction {\n label: string;\n onClick: () => void;\n}\n\ninterface GlobalToastProps {\n action?: GlobalToastAction;\n description?: string;\n id: string | number;\n title?: string;\n variant: ToastVariant;\n}\n\nexport interface GlobalToastOptions extends ExternalToast {\n action?: GlobalToastAction;\n description?: string;\n title?: string;\n}\n\nconst GlobalToastTitle = ({\n className,\n children,\n}: {\n className?: string;\n children: ReactNode;\n}) => {\n if (!children) {\n return;\n }\n\n return (\n <div\n className={cn(\n \"line-clamp-1 font-semibold text-black text-sm dark:text-white w-[90%]\",\n className\n )}\n >\n {children}\n </div>\n );\n};\n\nconst GlobalToastDescription = ({ children }: { children: ReactNode }) => {\n if (!children) {\n return;\n }\n\n return (\n <div className=\"line-clamp-2 font-normal text-gray-700 text-sm dark:text-gray-200 \">\n {children}\n </div>\n );\n};\n\nconst globalToastIconVariants = cva(\n \"size-7 flex-shrink-0 rounded-full flex items-center justify-center [&_svg]:size-4\",\n {\n variants: {\n variant: {\n error:\n \"bg-red-50 dark:bg-red-950 [&_svg]:text-red-500 [&_svg]:dark:text-red-400\",\n info: \"bg-gray-100 dark:bg-gray-800 [&_svg]:text-gray-700 [&_svg]:dark:text-gray-200\",\n loading:\n \"bg-gray-100 dark:bg-gray-800 [&_svg]:text-gray-700 [&_svg]:dark:text-gray-200\",\n success:\n \"bg-green-50 dark:bg-green-950 [&_svg]:text-green-500 [&_svg]:dark:text-green-400\",\n warning:\n \"bg-yellow-50 dark:bg-yellow-950 [&_svg]:text-yellow-500 [&_svg]:dark:text-yellow-400\",\n },\n },\n defaultVariants: {\n variant: \"info\",\n },\n }\n);\n\nconst GlobalToastIcon = ({ variant }: { variant: ToastVariant }) => {\n if (variant === \"error\") {\n return (\n <span className={cn(globalToastIconVariants({ variant }))}>\n <CircleX />\n </span>\n );\n }\n\n if (variant === \"info\") {\n return (\n <span className={cn(globalToastIconVariants({ variant }))}>\n <InfoIcon />\n </span>\n );\n }\n\n if (variant === \"loading\") {\n return (\n <span className={cn(globalToastIconVariants({ variant }))}>\n <Loader\n className=\"animate-spin\"\n style={{ animationDuration: \"1.5s\" }}\n />\n </span>\n );\n }\n\n if (variant === \"success\") {\n return (\n <span className={cn(globalToastIconVariants({ variant }))}>\n <Check />\n </span>\n );\n }\n\n if (variant === \"warning\") {\n return (\n <span className={cn(globalToastIconVariants({ variant }))}>\n <TriangleAlert className=\"pb-[1px]\" />\n </span>\n );\n }\n};\n\nconst globalToastLeftBorderVariants = cva(\"absolute top-0 left-0 h-full w-1\", {\n variants: {\n variant: {\n error: \"bg-red-500 dark:bg-red-400\",\n info: \"bg-gray-700 dark:bg-gray-200\",\n loading: \"bg-gray-700 dark:bg-gray-200\",\n success: \"bg-green-500 dark:bg-green-400\",\n warning: \"bg-yellow-500 dark:bg-yellow-400\",\n },\n },\n defaultVariants: {\n variant: \"info\",\n },\n});\n\nconst GlobalToastLeftBorder = ({ variant }: { variant: ToastVariant }) => (\n <div className={cn(globalToastLeftBorderVariants({ variant }))} />\n);\n\nconst GlobalToastDismissButton = ({\n toastId,\n}: {\n toastId: number | string;\n}) => (\n <Button\n className=\"absolute top-1 right-1\"\n onClick={() => sonnerToast.dismiss(toastId)}\n size=\"icon\"\n variant=\"subtle\"\n >\n <X className=\"size-5\" />\n </Button>\n);\n\nconst GlobalToastActionButton = ({ onClick, ...props }: ButtonProps) => (\n <Button onClick={onClick} size=\"sm\" variant=\"light\" {...props} />\n);\n\nexport const GlobalToast = (props: GlobalToastProps) => {\n const { id, title, description, variant, action } = props;\n\n return (\n <div\n className={cn(\n \"flex max-h-24 min-h-16 w-full xs:w-80 md:w-[360px] items-center gap-2 rounded-lg border-1 border-gray-150 border-solid bg-white px-4 py-3 text-black shadow-md dark:border-gray-800 dark:bg-black dark:text-white\",\n \"relative overflow-hidden\"\n )}\n key={id}\n >\n <GlobalToastLeftBorder variant={variant} />\n <GlobalToastIcon variant={variant} />\n <div className=\"flex w-full flex-col gap-0.5\">\n <GlobalToastTitle className={action ? \"w-full\" : \"w-[90%]\"}>\n {title}\n </GlobalToastTitle>\n <GlobalToastDescription>{description}</GlobalToastDescription>\n </div>\n\n {action ? (\n <GlobalToastActionButton onClick={action.onClick}>\n {action.label}\n </GlobalToastActionButton>\n ) : (\n <GlobalToastDismissButton toastId={id} />\n )}\n </div>\n );\n};\n\n/**\n * Toast container component. Place once at app root.\n * Positioned bottom-right by default, shows up to 4 toasts.\n */\nconst Toaster: React.FC<ToasterProps> = ({ toastOptions, ...props }) => (\n <SonnerToaster\n id={GLOBAL_TOASTER_ID}\n position=\"bottom-right\"\n visibleToasts={4}\n {...props}\n />\n);\n\nconst createGlobalToast = (\n variant: ToastVariant,\n options?: GlobalToastOptions\n) => {\n const { action, description, title, id: customId, ...rest } = options || {};\n\n const toastId = sonnerToast.custom(\n (id) => (\n <GlobalToast\n action={action}\n description={description}\n id={customId || id}\n title={title}\n variant={variant}\n />\n ),\n {\n ...(customId !== undefined && { id: customId }),\n toasterId: GLOBAL_TOASTER_ID,\n ...rest,\n }\n );\n\n // Return the custom ID if provided, otherwise return Sonner's generated ID\n return customId === undefined ? toastId : customId;\n};\n\n// Helper method to normalize toast arguments\nconst normalizeToastArgs = (\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n): GlobalToastOptions | undefined => {\n if (typeof messageOrOptions === \"string\") {\n return { ...options, title: messageOrOptions };\n }\n\n return messageOrOptions;\n};\n\ninterface PromiseToastOptions<T> {\n error: GlobalToastOptions | ((error: unknown) => GlobalToastOptions);\n loading: GlobalToastOptions;\n success: GlobalToastOptions | ((data: T) => GlobalToastOptions);\n}\n\ninterface ToastFunction {\n custom: typeof sonnerToast.custom;\n dismiss: typeof sonnerToast.dismiss;\n error: {\n (message: string, options?: GlobalToastOptions): string | number;\n (options: GlobalToastOptions): string | number;\n };\n getHistory: typeof sonnerToast.getHistory;\n getToasts: typeof sonnerToast.getToasts;\n info: {\n (message: string, options?: GlobalToastOptions): string | number;\n (options: GlobalToastOptions): string | number;\n };\n loading: {\n (message: string, options?: GlobalToastOptions): string | number;\n (options: GlobalToastOptions): string | number;\n };\n message: {\n (message: string, options?: GlobalToastOptions): string | number;\n (options: GlobalToastOptions): string | number;\n };\n promise: <T>(\n promise: Promise<T> | (() => Promise<T>),\n options: PromiseToastOptions<T>\n ) => Promise<T>;\n success: {\n (message: string, options?: GlobalToastOptions): string | number;\n (options: GlobalToastOptions): string | number;\n };\n warning: {\n (message: string, options?: GlobalToastOptions): string | number;\n (options: GlobalToastOptions): string | number;\n };\n (message: string, options?: GlobalToastOptions): string | number;\n (options: GlobalToastOptions): string | number;\n}\n\nconst toast: ToastFunction = ((\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n) =>\n createGlobalToast(\n \"info\",\n normalizeToastArgs(messageOrOptions, options)\n )) as ToastFunction;\n\ntoast.success = ((\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n) =>\n createGlobalToast(\n \"success\",\n normalizeToastArgs(messageOrOptions, options)\n )) as ToastFunction[\"success\"];\n\ntoast.error = ((\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n) =>\n createGlobalToast(\n \"error\",\n normalizeToastArgs(messageOrOptions, options)\n )) as ToastFunction[\"error\"];\n\ntoast.info = ((\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n) =>\n createGlobalToast(\n \"info\",\n normalizeToastArgs(messageOrOptions, options)\n )) as ToastFunction[\"info\"];\n\ntoast.warning = ((\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n) =>\n createGlobalToast(\n \"warning\",\n normalizeToastArgs(messageOrOptions, options)\n )) as ToastFunction[\"warning\"];\n\ntoast.loading = ((\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n) =>\n createGlobalToast(\n \"loading\",\n normalizeToastArgs(messageOrOptions, options)\n )) as ToastFunction[\"loading\"];\n\ntoast.message = ((\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n) =>\n createGlobalToast(\n \"info\",\n normalizeToastArgs(messageOrOptions, options)\n )) as ToastFunction[\"message\"];\n\ntoast.promise = <T,>(\n promiseOrFn: Promise<T> | (() => Promise<T>),\n states: PromiseToastOptions<T>\n): Promise<T> => {\n const toastId = createGlobalToast(\"loading\", states.loading);\n const promise =\n typeof promiseOrFn === \"function\" ? promiseOrFn() : promiseOrFn;\n\n promise\n .then((data) => {\n const successToastOptions =\n typeof states.success === \"function\"\n ? states.success(data)\n : states.success;\n\n const { title, description, action, ...rest } = successToastOptions || {};\n\n sonnerToast.custom(\n (id) => (\n <GlobalToast\n action={action}\n description={description}\n id={id}\n title={title}\n variant=\"success\"\n />\n ),\n { id: toastId, toasterId: GLOBAL_TOASTER_ID, ...rest }\n );\n })\n .catch((error) => {\n const errorToastOptions =\n typeof states.error === \"function\" ? states.error(error) : states.error;\n\n const { title, description, action, ...rest } = errorToastOptions || {};\n\n sonnerToast.custom(\n (id) => (\n <GlobalToast\n action={action}\n description={description}\n id={id}\n title={title}\n variant=\"error\"\n />\n ),\n { id: toastId, toasterId: GLOBAL_TOASTER_ID, ...rest }\n );\n });\n\n return promise;\n};\n\ntoast.custom = sonnerToast.custom;\ntoast.dismiss = sonnerToast.dismiss;\ntoast.getHistory = sonnerToast.getHistory;\ntoast.getToasts = sonnerToast.getToasts;\n\nexport { Toaster, type ToasterProps, toast };\n"],
|
|
4
|
+
"sourcesContent": ["/**\n * @module Toast\n *\n * Toast notification system built on Sonner. Provides success, error, info, and warning variants.\n *\n * @see {@link https://ui.shadcn.com/docs/components/sonner Shadcn Sonner}\n * @see {@link https://sonner.emilkowal.ski/ Sonner Documentation}\n *\n * @example\n * // Setup: Add Toaster to your app root\n * <Toaster />\n *\n * @example\n * // Show toast notifications\n * toast(\"Default message\");\n * toast.success(\"Operation completed\");\n * toast.error(\"Something went wrong\");\n * toast.info(\"Information\");\n * toast.warning(\"Warning message\");\n *\n * @example\n * // With action button\n * toast({\n * title: \"Item deleted\",\n * action: {\n * label: \"Undo\",\n * onClick: () => undoDelete(),\n * },\n * });\n *\n * @example\n * // Promise toast\n * const id = toast.promise(promise, {\n loading: { title: \"Saving document...\" },\n success: (data) => ({\n title: `${data.name} saved successfully!`,\n duration: 10_000,\n }),\n\n error: { title: \"Failed to save document\", duration: 10_000 },\n });\n */\nimport { cva } from \"class-variance-authority\";\nimport type React from \"react\";\nimport type { ReactNode } from \"react\";\nimport {\n type ExternalToast,\n Toaster as SonnerToaster,\n toast as sonnerToast,\n} from \"sonner\";\nimport { Button, type ButtonProps, cn } from \"..\";\nimport { Check, CircleX, InfoIcon, Loader, TriangleAlert, X } from \"../icons\";\n\ntype ToasterProps = React.ComponentProps<typeof SonnerToaster>;\ntype ToastVariant = \"error\" | \"info\" | \"loading\" | \"success\" | \"warning\";\n\nconst GLOBAL_TOASTER_ID = \"global\";\n\ninterface GlobalToastAction {\n label: string;\n onClick: () => void;\n}\n\ninterface GlobalToastProps {\n action?: GlobalToastAction;\n description?: string;\n id: string | number;\n title?: string;\n variant: ToastVariant;\n}\n\nexport interface GlobalToastOptions extends ExternalToast {\n action?: GlobalToastAction;\n description?: string;\n title?: string;\n}\n\nconst GlobalToastTitle = ({\n className,\n children,\n}: {\n className?: string;\n children: ReactNode;\n}) => {\n if (!children) {\n return;\n }\n\n return (\n <div\n className={cn(\n \"line-clamp-1 font-semibold text-black text-sm dark:text-white w-[90%]\",\n className\n )}\n >\n {children}\n </div>\n );\n};\n\nconst GlobalToastDescription = ({ children }: { children: ReactNode }) => {\n if (!children) {\n return;\n }\n\n return (\n <div className=\"line-clamp-2 font-normal text-gray-700 text-sm dark:text-gray-200 \">\n {children}\n </div>\n );\n};\n\nconst globalToastIconVariants = cva(\n \"size-7 shrink-0 rounded-full flex items-center justify-center [&_svg]:size-4\",\n {\n variants: {\n variant: {\n error:\n \"bg-red-50 dark:bg-red-950 [&_svg]:text-red-500 [&_svg]:dark:text-red-400\",\n info: \"bg-gray-100 dark:bg-gray-800 [&_svg]:text-gray-700 [&_svg]:dark:text-gray-200\",\n loading:\n \"bg-gray-100 dark:bg-gray-800 [&_svg]:text-gray-700 [&_svg]:dark:text-gray-200\",\n success:\n \"bg-green-50 dark:bg-green-950 [&_svg]:text-green-500 [&_svg]:dark:text-green-400\",\n warning:\n \"bg-yellow-50 dark:bg-yellow-950 [&_svg]:text-yellow-500 [&_svg]:dark:text-yellow-400\",\n },\n },\n defaultVariants: {\n variant: \"info\",\n },\n }\n);\n\nconst GlobalToastIcon = ({ variant }: { variant: ToastVariant }) => {\n if (variant === \"error\") {\n return (\n <span className={cn(globalToastIconVariants({ variant }))}>\n <CircleX />\n </span>\n );\n }\n\n if (variant === \"info\") {\n return (\n <span className={cn(globalToastIconVariants({ variant }))}>\n <InfoIcon />\n </span>\n );\n }\n\n if (variant === \"loading\") {\n return (\n <span className={cn(globalToastIconVariants({ variant }))}>\n <Loader\n className=\"animate-spin\"\n style={{ animationDuration: \"1.5s\" }}\n />\n </span>\n );\n }\n\n if (variant === \"success\") {\n return (\n <span className={cn(globalToastIconVariants({ variant }))}>\n <Check />\n </span>\n );\n }\n\n if (variant === \"warning\") {\n return (\n <span className={cn(globalToastIconVariants({ variant }))}>\n <TriangleAlert className=\"pb-[1px]\" />\n </span>\n );\n }\n};\n\nconst globalToastLeftBorderVariants = cva(\"absolute top-0 left-0 h-full w-1\", {\n variants: {\n variant: {\n error: \"bg-red-500 dark:bg-red-400\",\n info: \"bg-gray-700 dark:bg-gray-200\",\n loading: \"bg-gray-700 dark:bg-gray-200\",\n success: \"bg-green-500 dark:bg-green-400\",\n warning: \"bg-yellow-500 dark:bg-yellow-400\",\n },\n },\n defaultVariants: {\n variant: \"info\",\n },\n});\n\nconst GlobalToastLeftBorder = ({ variant }: { variant: ToastVariant }) => (\n <div className={cn(globalToastLeftBorderVariants({ variant }))} />\n);\n\nconst GlobalToastDismissButton = ({\n toastId,\n}: {\n toastId: number | string;\n}) => (\n <Button\n className=\"absolute top-1 right-1\"\n onClick={() => sonnerToast.dismiss(toastId)}\n size=\"icon\"\n variant=\"subtle\"\n >\n <X className=\"size-5\" />\n </Button>\n);\n\nconst GlobalToastActionButton = ({ onClick, ...props }: ButtonProps) => (\n <Button onClick={onClick} size=\"sm\" variant=\"light\" {...props} />\n);\n\nexport const GlobalToast = (props: GlobalToastProps) => {\n const { id, title, description, variant, action } = props;\n\n return (\n <div\n className={cn(\n \"flex max-h-24 min-h-16 w-full xs:w-80 md:w-[360px] items-center gap-2 rounded-lg border-1 border-gray-150 border-solid bg-white px-4 py-3 text-black shadow-md dark:border-gray-800 dark:bg-black dark:text-white\",\n \"relative overflow-hidden\"\n )}\n key={id}\n >\n <GlobalToastLeftBorder variant={variant} />\n <GlobalToastIcon variant={variant} />\n <div className=\"flex w-full flex-col gap-0.5\">\n <GlobalToastTitle className={action ? \"w-full\" : \"w-[90%]\"}>\n {title}\n </GlobalToastTitle>\n <GlobalToastDescription>{description}</GlobalToastDescription>\n </div>\n\n {action ? (\n <GlobalToastActionButton onClick={action.onClick}>\n {action.label}\n </GlobalToastActionButton>\n ) : (\n <GlobalToastDismissButton toastId={id} />\n )}\n </div>\n );\n};\n\n/**\n * Toast container component. Place once at app root.\n * Positioned bottom-right by default, shows up to 4 toasts.\n */\nconst Toaster: React.FC<ToasterProps> = ({ toastOptions, ...props }) => (\n <SonnerToaster\n id={GLOBAL_TOASTER_ID}\n position=\"bottom-right\"\n visibleToasts={4}\n {...props}\n />\n);\n\nconst createGlobalToast = (\n variant: ToastVariant,\n options?: GlobalToastOptions\n) => {\n const { action, description, title, id: customId, ...rest } = options || {};\n\n const toastId = sonnerToast.custom(\n (id) => (\n <GlobalToast\n action={action}\n description={description}\n id={customId || id}\n title={title}\n variant={variant}\n />\n ),\n {\n ...(customId !== undefined && { id: customId }),\n toasterId: GLOBAL_TOASTER_ID,\n ...rest,\n }\n );\n\n // Return the custom ID if provided, otherwise return Sonner's generated ID\n return customId === undefined ? toastId : customId;\n};\n\n// Helper method to normalize toast arguments\nconst normalizeToastArgs = (\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n): GlobalToastOptions | undefined => {\n if (typeof messageOrOptions === \"string\") {\n return { ...options, title: messageOrOptions };\n }\n\n return messageOrOptions;\n};\n\ninterface PromiseToastOptions<T> {\n error: GlobalToastOptions | ((error: unknown) => GlobalToastOptions);\n loading: GlobalToastOptions;\n success: GlobalToastOptions | ((data: T) => GlobalToastOptions);\n}\n\ninterface ToastFunction {\n custom: typeof sonnerToast.custom;\n dismiss: typeof sonnerToast.dismiss;\n error: {\n (message: string, options?: GlobalToastOptions): string | number;\n (options: GlobalToastOptions): string | number;\n };\n getHistory: typeof sonnerToast.getHistory;\n getToasts: typeof sonnerToast.getToasts;\n info: {\n (message: string, options?: GlobalToastOptions): string | number;\n (options: GlobalToastOptions): string | number;\n };\n loading: {\n (message: string, options?: GlobalToastOptions): string | number;\n (options: GlobalToastOptions): string | number;\n };\n message: {\n (message: string, options?: GlobalToastOptions): string | number;\n (options: GlobalToastOptions): string | number;\n };\n promise: <T>(\n promise: Promise<T> | (() => Promise<T>),\n options: PromiseToastOptions<T>\n ) => Promise<T>;\n success: {\n (message: string, options?: GlobalToastOptions): string | number;\n (options: GlobalToastOptions): string | number;\n };\n warning: {\n (message: string, options?: GlobalToastOptions): string | number;\n (options: GlobalToastOptions): string | number;\n };\n (message: string, options?: GlobalToastOptions): string | number;\n (options: GlobalToastOptions): string | number;\n}\n\nconst toast: ToastFunction = ((\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n) =>\n createGlobalToast(\n \"info\",\n normalizeToastArgs(messageOrOptions, options)\n )) as ToastFunction;\n\ntoast.success = ((\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n) =>\n createGlobalToast(\n \"success\",\n normalizeToastArgs(messageOrOptions, options)\n )) as ToastFunction[\"success\"];\n\ntoast.error = ((\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n) =>\n createGlobalToast(\n \"error\",\n normalizeToastArgs(messageOrOptions, options)\n )) as ToastFunction[\"error\"];\n\ntoast.info = ((\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n) =>\n createGlobalToast(\n \"info\",\n normalizeToastArgs(messageOrOptions, options)\n )) as ToastFunction[\"info\"];\n\ntoast.warning = ((\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n) =>\n createGlobalToast(\n \"warning\",\n normalizeToastArgs(messageOrOptions, options)\n )) as ToastFunction[\"warning\"];\n\ntoast.loading = ((\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n) =>\n createGlobalToast(\n \"loading\",\n normalizeToastArgs(messageOrOptions, options)\n )) as ToastFunction[\"loading\"];\n\ntoast.message = ((\n messageOrOptions?: GlobalToastOptions | string,\n options?: GlobalToastOptions\n) =>\n createGlobalToast(\n \"info\",\n normalizeToastArgs(messageOrOptions, options)\n )) as ToastFunction[\"message\"];\n\ntoast.promise = <T,>(\n promiseOrFn: Promise<T> | (() => Promise<T>),\n states: PromiseToastOptions<T>\n): Promise<T> => {\n const toastId = createGlobalToast(\"loading\", states.loading);\n const promise =\n typeof promiseOrFn === \"function\" ? promiseOrFn() : promiseOrFn;\n\n promise\n .then((data) => {\n const successToastOptions =\n typeof states.success === \"function\"\n ? states.success(data)\n : states.success;\n\n const { title, description, action, ...rest } = successToastOptions || {};\n\n sonnerToast.custom(\n (id) => (\n <GlobalToast\n action={action}\n description={description}\n id={id}\n title={title}\n variant=\"success\"\n />\n ),\n { id: toastId, toasterId: GLOBAL_TOASTER_ID, ...rest }\n );\n })\n .catch((error) => {\n const errorToastOptions =\n typeof states.error === \"function\" ? states.error(error) : states.error;\n\n const { title, description, action, ...rest } = errorToastOptions || {};\n\n sonnerToast.custom(\n (id) => (\n <GlobalToast\n action={action}\n description={description}\n id={id}\n title={title}\n variant=\"error\"\n />\n ),\n { id: toastId, toasterId: GLOBAL_TOASTER_ID, ...rest }\n );\n });\n\n return promise;\n};\n\ntoast.custom = sonnerToast.custom;\ntoast.dismiss = sonnerToast.dismiss;\ntoast.getHistory = sonnerToast.getHistory;\ntoast.getToasts = sonnerToast.getToasts;\n\nexport { Toaster, type ToasterProps, toast };\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -53,7 +53,7 @@ const ListItem = ({
|
|
|
53
53
|
}) => {
|
|
54
54
|
const CustomBulletComp = typeof customBullet === "string" ? "span" : SlotPrimitive.Slot;
|
|
55
55
|
return /* @__PURE__ */ jsxs("li", { className, ref, ...props, children: [
|
|
56
|
-
!!customBullet && /* @__PURE__ */ jsx(CustomBulletComp, { className: "mr-2
|
|
56
|
+
!!customBullet && /* @__PURE__ */ jsx(CustomBulletComp, { className: "mr-2 shrink-0", children: customBullet }),
|
|
57
57
|
children
|
|
58
58
|
] });
|
|
59
59
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/typography/list.tsx"],
|
|
4
|
-
"sourcesContent": ["import { cva, type VariantProps } from \"class-variance-authority\";\nimport { Slot as SlotPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport type { Ref } from \"react\";\n\nimport { Tick } from \"../../icons/custom/tick\";\nimport { cn } from \"../../lib/utils\";\n\nconst listVariants = cva(\"text-gray-600 dark:text-gray-100\", {\n variants: {\n size: {\n sm: \"text-sm [&>li]:mt-1\",\n default: \"text-base [&>li]:mt-2\",\n lg: \"text-lg [&>li]:mt-3\",\n },\n variant: {\n unordered: \"list-disc pl-5\",\n ordered: \"list-decimal pl-5\",\n custom: \"list-none pl-0 [&>li>ul]:ml-4\",\n },\n color: {\n default: \"text-gray-600 dark:text-gray-100\",\n modal: \"text-gray-700 dark:text-gray-200\",\n },\n },\n defaultVariants: {\n size: \"default\",\n variant: \"unordered\",\n color: \"default\",\n },\n});\n\nexport interface ListProps\n extends Omit<\n React.HTMLAttributes<HTMLUListElement | HTMLOListElement>,\n \"color\"\n >,\n VariantProps<typeof listVariants> {\n ref?: Ref<HTMLUListElement | HTMLOListElement>;\n}\n\nconst List = ({\n className,\n size,\n variant,\n color,\n ref,\n ...props\n}: ListProps) => {\n const Component = variant === \"ordered\" ? \"ol\" : \"ul\";\n\n return (\n <Component\n className={cn(listVariants({ size, variant, color, className }))}\n ref={ref as React.Ref<HTMLUListElement & HTMLOListElement>}\n {...props}\n />\n );\n};\n\nexport interface ListItemProps extends React.LiHTMLAttributes<HTMLLIElement> {\n customBullet?: React.ReactNode;\n ref?: Ref<HTMLLIElement>;\n}\n\nconst ListItem = ({\n className,\n customBullet,\n children,\n ref,\n ...props\n}: ListItemProps) => {\n const CustomBulletComp =\n typeof customBullet === \"string\" ? \"span\" : SlotPrimitive.Slot;\n\n return (\n <li className={className} ref={ref} {...props}>\n {!!customBullet && (\n <CustomBulletComp className=\"mr-2
|
|
5
|
-
"mappings": "AAoDI,cAwBA,YAxBA;AApDJ,SAAS,WAA8B;AACvC,SAAS,QAAQ,qBAAqB;AAItC,SAAS,YAAY;AACrB,SAAS,UAAU;AAEnB,MAAM,eAAe,IAAI,oCAAoC;AAAA,EAC3D,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,IAAI;AAAA,MACJ,SAAS;AAAA,MACT,IAAI;AAAA,IACN;AAAA,IACA,SAAS;AAAA,MACP,WAAW;AAAA,MACX,SAAS;AAAA,MACT,QAAQ;AAAA,IACV;AAAA,IACA,OAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AACF,CAAC;AAWD,MAAM,OAAO,CAAC;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAAiB;AACf,QAAM,YAAY,YAAY,YAAY,OAAO;AAEjD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,aAAa,EAAE,MAAM,SAAS,OAAO,UAAU,CAAC,CAAC;AAAA,MAC/D;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAOA,MAAM,WAAW,CAAC;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAAqB;AACnB,QAAM,mBACJ,OAAO,iBAAiB,WAAW,SAAS,cAAc;AAE5D,SACE,qBAAC,QAAG,WAAsB,KAAW,GAAG,OACrC;AAAA,KAAC,CAAC,gBACD,oBAAC,oBAAiB,WAAU,
|
|
4
|
+
"sourcesContent": ["import { cva, type VariantProps } from \"class-variance-authority\";\nimport { Slot as SlotPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport type { Ref } from \"react\";\n\nimport { Tick } from \"../../icons/custom/tick\";\nimport { cn } from \"../../lib/utils\";\n\nconst listVariants = cva(\"text-gray-600 dark:text-gray-100\", {\n variants: {\n size: {\n sm: \"text-sm [&>li]:mt-1\",\n default: \"text-base [&>li]:mt-2\",\n lg: \"text-lg [&>li]:mt-3\",\n },\n variant: {\n unordered: \"list-disc pl-5\",\n ordered: \"list-decimal pl-5\",\n custom: \"list-none pl-0 [&>li>ul]:ml-4\",\n },\n color: {\n default: \"text-gray-600 dark:text-gray-100\",\n modal: \"text-gray-700 dark:text-gray-200\",\n },\n },\n defaultVariants: {\n size: \"default\",\n variant: \"unordered\",\n color: \"default\",\n },\n});\n\nexport interface ListProps\n extends Omit<\n React.HTMLAttributes<HTMLUListElement | HTMLOListElement>,\n \"color\"\n >,\n VariantProps<typeof listVariants> {\n ref?: Ref<HTMLUListElement | HTMLOListElement>;\n}\n\nconst List = ({\n className,\n size,\n variant,\n color,\n ref,\n ...props\n}: ListProps) => {\n const Component = variant === \"ordered\" ? \"ol\" : \"ul\";\n\n return (\n <Component\n className={cn(listVariants({ size, variant, color, className }))}\n ref={ref as React.Ref<HTMLUListElement & HTMLOListElement>}\n {...props}\n />\n );\n};\n\nexport interface ListItemProps extends React.LiHTMLAttributes<HTMLLIElement> {\n customBullet?: React.ReactNode;\n ref?: Ref<HTMLLIElement>;\n}\n\nconst ListItem = ({\n className,\n customBullet,\n children,\n ref,\n ...props\n}: ListItemProps) => {\n const CustomBulletComp =\n typeof customBullet === \"string\" ? \"span\" : SlotPrimitive.Slot;\n\n return (\n <li className={className} ref={ref} {...props}>\n {!!customBullet && (\n <CustomBulletComp className=\"mr-2 shrink-0\">\n {customBullet}\n </CustomBulletComp>\n )}\n {children}\n </li>\n );\n};\n\ninterface ListItemTickProps extends React.HTMLAttributes<HTMLSpanElement> {\n ref?: Ref<HTMLSpanElement>;\n}\n\nconst ListItemTick = ({ className, ref, ...props }: ListItemTickProps) => (\n <span\n className={cn(\n \"inline-flex aspect-square items-center justify-center rounded-[0.375em] bg-green text-white [height:calc(1em_*_1.25)] [margin-top:calc((1em_*_var(--line-height,_1.5)_-_(1em_*_1.25))_/_2)] [width:calc(1em_*_1.25)]\",\n className\n )}\n ref={ref}\n {...props}\n >\n <Tick className=\"[height:calc(1em_*_0.75)] [width:calc(1em_*_0.75)]\" />\n </span>\n);\n\nexport { List, ListItem, ListItemTick, listVariants };\n"],
|
|
5
|
+
"mappings": "AAoDI,cAwBA,YAxBA;AApDJ,SAAS,WAA8B;AACvC,SAAS,QAAQ,qBAAqB;AAItC,SAAS,YAAY;AACrB,SAAS,UAAU;AAEnB,MAAM,eAAe,IAAI,oCAAoC;AAAA,EAC3D,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,IAAI;AAAA,MACJ,SAAS;AAAA,MACT,IAAI;AAAA,IACN;AAAA,IACA,SAAS;AAAA,MACP,WAAW;AAAA,MACX,SAAS;AAAA,MACT,QAAQ;AAAA,IACV;AAAA,IACA,OAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AACF,CAAC;AAWD,MAAM,OAAO,CAAC;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAAiB;AACf,QAAM,YAAY,YAAY,YAAY,OAAO;AAEjD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,aAAa,EAAE,MAAM,SAAS,OAAO,UAAU,CAAC,CAAC;AAAA,MAC/D;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAOA,MAAM,WAAW,CAAC;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAAqB;AACnB,QAAM,mBACJ,OAAO,iBAAiB,WAAW,SAAS,cAAc;AAE5D,SACE,qBAAC,QAAG,WAAsB,KAAW,GAAG,OACrC;AAAA,KAAC,CAAC,gBACD,oBAAC,oBAAiB,WAAU,iBACzB,wBACH;AAAA,IAED;AAAA,KACH;AAEJ;AAMA,MAAM,eAAe,CAAC,EAAE,WAAW,KAAK,GAAG,MAAM,MAC/C;AAAA,EAAC;AAAA;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACC,GAAG;AAAA,IAEJ,8BAAC,QAAK,WAAU,sDAAqD;AAAA;AACvE;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/lib/utils.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { type ClassValue } from "clsx";
|
|
|
5
5
|
* This ensures proper handling of conflicting classes.
|
|
6
6
|
*
|
|
7
7
|
* Fluid classes are expanded to conflict at multiple modifier levels:
|
|
8
|
-
* -
|
|
8
|
+
* - `@md:fl-text-2xl/3xl` conflicts with BOTH `@md:text-5xl` AND `text-4xl`
|
|
9
9
|
* - This ensures fluid classes override all related text sizing
|
|
10
10
|
*/
|
|
11
11
|
export declare function cn(...inputs: ClassValue[]): string;
|
package/dist/lib/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAQ,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAQ,MAAM,MAAM,CAAC;AA8E7C;;;;;;;;GAQG;AACH,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAqBzC"}
|
package/dist/lib/utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { clsx } from "clsx";
|
|
2
2
|
import { extendTailwindMerge, mergeConfigs } from "tailwind-merge";
|
|
3
3
|
const isFluidTextSize = (classPart) => {
|
|
4
|
-
const fluidPattern =
|
|
4
|
+
const fluidPattern = /^fl-text-([a-z0-9]+)\/([a-z0-9]+)$/;
|
|
5
5
|
const match = classPart.match(fluidPattern);
|
|
6
6
|
if (!match) {
|
|
7
7
|
return false;
|
|
@@ -29,24 +29,14 @@ const customTwMerge = extendTailwindMerge((config) => {
|
|
|
29
29
|
extend: {
|
|
30
30
|
classGroups: {
|
|
31
31
|
// Extend font-size class group to support fluid syntax
|
|
32
|
-
// Top-level validator receives the full className (e.g., "
|
|
32
|
+
// Top-level validator receives the full className (e.g., "fl-text-2xl/3xl")
|
|
33
33
|
"font-size": [isFluidTextSize]
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
// After processing: ~@md:~text-4xl/5xl → modifiers:[@md], baseClassName:~text-4xl/5xl
|
|
39
|
-
// We keep the ~ on baseClassName because tailwind-merge needs it to recognize fluid syntax
|
|
36
|
+
// Parse className to extract modifiers and base class name
|
|
37
|
+
// For @md:fl-text-4xl/5xl → modifiers:[@md], baseClassName:fl-text-4xl/5xl
|
|
40
38
|
experimentalParseClassName: ({ className, parseClassName }) => {
|
|
41
|
-
|
|
42
|
-
const cleanedModifiers = parsed.modifiers.map(
|
|
43
|
-
(modifier) => modifier.startsWith("~") ? modifier.slice(1) : modifier
|
|
44
|
-
);
|
|
45
|
-
return {
|
|
46
|
-
...parsed,
|
|
47
|
-
modifiers: cleanedModifiers
|
|
48
|
-
// Keep baseClassName as-is (preserving ~) so fluid syntax isn't misinterpreted as line-height
|
|
49
|
-
};
|
|
39
|
+
return parseClassName(className);
|
|
50
40
|
}
|
|
51
41
|
});
|
|
52
42
|
});
|
|
@@ -54,11 +44,11 @@ function cn(...inputs) {
|
|
|
54
44
|
const classList = clsx(inputs);
|
|
55
45
|
const expanded = classList.split(" ").flatMap((className) => {
|
|
56
46
|
const fluidWithModifier = className.match(
|
|
57
|
-
|
|
47
|
+
/^(@[\w-]+):fl-text-(\w+)\/(\w+)$/
|
|
58
48
|
);
|
|
59
49
|
if (fluidWithModifier) {
|
|
60
50
|
const [, modifier, startSize, endSize] = fluidWithModifier;
|
|
61
|
-
const baseFluid =
|
|
51
|
+
const baseFluid = `fl-text-${startSize}/${endSize}`;
|
|
62
52
|
return [className, baseFluid];
|
|
63
53
|
}
|
|
64
54
|
return [className];
|
package/dist/lib/utils.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/utils.ts"],
|
|
4
|
-
"sourcesContent": ["import { type ClassValue, clsx } from \"clsx\";\nimport { extendTailwindMerge, mergeConfigs } from \"tailwind-merge\";\n\n/**\n * Fluid-tailwind support for tailwind-merge.\n * Adapted from @fluid-tailwind/tailwind-merge source code.\n *\n * **How it works:**\n * 1. Fluid classes with modifiers (e.g.,
|
|
5
|
-
"mappings": "AAAA,SAA0B,YAAY;AACtC,SAAS,qBAAqB,oBAAoB;AA4BlD,MAAM,kBAAkB,CAAC,cAA+B;AAEtD,QAAM,eAAe;AACrB,QAAM,QAAQ,UAAU,MAAM,YAAY;AAE1C,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,QAAM,CAAC,EAAE,WAAW,OAAO,IAAI;AAC/B,QAAM,aAAa;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,WAAW,SAAS,SAAS,KAAK,WAAW,SAAS,OAAO;AACtE;AAKA,MAAM,gBAAgB,oBAAoB,CAAC,WAAW;AACpD,SAAO,aAAa,QAAQ;AAAA,IAC1B,QAAQ;AAAA,MACN,aAAa;AAAA;AAAA;AAAA,QAGX,aAAa,CAAC,eAAe;AAAA,MAC/B;AAAA,IACF;AAAA;AAAA;AAAA
|
|
4
|
+
"sourcesContent": ["import { type ClassValue, clsx } from \"clsx\";\nimport { extendTailwindMerge, mergeConfigs } from \"tailwind-merge\";\n\n/**\n * Fluid-tailwind support for tailwind-merge.\n * Adapted from @fluid-tailwind/tailwind-merge source code.\n *\n * **How it works:**\n * 1. Fluid classes with modifiers (e.g., `@md:fl-text-2xl/3xl`) are expanded\n * to include both the modified and base versions\n * 2. This ensures they conflict at multiple modifier levels:\n * - `@md:fl-text-2xl/3xl` conflicts with `@md:text-5xl` (responsive)\n * - `@md:fl-text-2xl/3xl` conflicts with `text-4xl` (base)\n * 3. Simple fluid classes (e.g., `fl-text-2xl/3xl`) conflict with `text-4xl`\n *\n * **Supported patterns:**\n * - `fl-text-4xl/5xl` conflicts with `text-4xl` \u2713\n * - `@md:fl-text-4xl/5xl` conflicts with BOTH `@md:text-5xl` AND `text-4xl` \u2713\n * - Fluid text sizing with any container query breakpoint \u2713\n *\n * This implementation ensures that passing a fluid className overrides all\n * related text sizing classes, as expected in a component library.\n */\n\n/**\n * Validator for fluid text sizes with `/` separator.\n * This matches the full className including prefix and postfix.\n * Tailwind-merge class groups can receive full className for top-level validators.\n */\nconst isFluidTextSize = (classPart: string): boolean => {\n // Match full fluid class patterns: fl-text-X/Y\n const fluidPattern = /^fl-text-([a-z0-9]+)\\/([a-z0-9]+)$/;\n const match = classPart.match(fluidPattern);\n\n if (!match) {\n return false;\n }\n\n const [, startSize, endSize] = match;\n const validSizes = [\n \"xs\",\n \"sm\",\n \"base\",\n \"lg\",\n \"xl\",\n \"2xl\",\n \"3xl\",\n \"4xl\",\n \"5xl\",\n \"6xl\",\n \"7xl\",\n \"8xl\",\n \"9xl\",\n ];\n\n return validSizes.includes(startSize) && validSizes.includes(endSize);\n};\n\n/**\n * Custom tailwind-merge with fluid text size support.\n */\nconst customTwMerge = extendTailwindMerge((config) => {\n return mergeConfigs(config, {\n extend: {\n classGroups: {\n // Extend font-size class group to support fluid syntax\n // Top-level validator receives the full className (e.g., \"fl-text-2xl/3xl\")\n \"font-size\": [isFluidTextSize],\n },\n },\n // Parse className to extract modifiers and base class name\n // For @md:fl-text-4xl/5xl \u2192 modifiers:[@md], baseClassName:fl-text-4xl/5xl\n experimentalParseClassName: ({ className, parseClassName }) => {\n return parseClassName(className);\n },\n });\n});\n\n/**\n * Merge Tailwind CSS classes with clsx and tailwind-merge.\n * Supports both standard Tailwind classes and fluid-tailwind text sizing.\n * This ensures proper handling of conflicting classes.\n *\n * Fluid classes are expanded to conflict at multiple modifier levels:\n * - `@md:fl-text-2xl/3xl` conflicts with BOTH `@md:text-5xl` AND `text-4xl`\n * - This ensures fluid classes override all related text sizing\n */\nexport function cn(...inputs: ClassValue[]) {\n const classList = clsx(inputs);\n\n // Expand fluid classes to conflict at multiple modifier levels\n const expanded = classList.split(\" \").flatMap((className) => {\n // Match fluid classes with modifiers: @md:fl-text-2xl/3xl\n const fluidWithModifier = className.match(\n /^(@[\\w-]+):fl-text-(\\w+)\\/(\\w+)$/\n );\n if (fluidWithModifier) {\n const [, modifier, startSize, endSize] = fluidWithModifier;\n const baseFluid = `fl-text-${startSize}/${endSize}`;\n // Return both the modified version and base version\n // This makes it conflict with both @md:text-* and text-*\n return [className, baseFluid];\n }\n\n return [className];\n });\n\n return customTwMerge(expanded.join(\" \"));\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAA0B,YAAY;AACtC,SAAS,qBAAqB,oBAAoB;AA4BlD,MAAM,kBAAkB,CAAC,cAA+B;AAEtD,QAAM,eAAe;AACrB,QAAM,QAAQ,UAAU,MAAM,YAAY;AAE1C,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,QAAM,CAAC,EAAE,WAAW,OAAO,IAAI;AAC/B,QAAM,aAAa;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,WAAW,SAAS,SAAS,KAAK,WAAW,SAAS,OAAO;AACtE;AAKA,MAAM,gBAAgB,oBAAoB,CAAC,WAAW;AACpD,SAAO,aAAa,QAAQ;AAAA,IAC1B,QAAQ;AAAA,MACN,aAAa;AAAA;AAAA;AAAA,QAGX,aAAa,CAAC,eAAe;AAAA,MAC/B;AAAA,IACF;AAAA;AAAA;AAAA,IAGA,4BAA4B,CAAC,EAAE,WAAW,eAAe,MAAM;AAC7D,aAAO,eAAe,SAAS;AAAA,IACjC;AAAA,EACF,CAAC;AACH,CAAC;AAWM,SAAS,MAAM,QAAsB;AAC1C,QAAM,YAAY,KAAK,MAAM;AAG7B,QAAM,WAAW,UAAU,MAAM,GAAG,EAAE,QAAQ,CAAC,cAAc;AAE3D,UAAM,oBAAoB,UAAU;AAAA,MAClC;AAAA,IACF;AACA,QAAI,mBAAmB;AACrB,YAAM,CAAC,EAAE,UAAU,WAAW,OAAO,IAAI;AACzC,YAAM,YAAY,WAAW,SAAS,IAAI,OAAO;AAGjD,aAAO,CAAC,WAAW,SAAS;AAAA,IAC9B;AAEA,WAAO,CAAC,SAAS;AAAA,EACnB,CAAC;AAED,SAAO,cAAc,SAAS,KAAK,GAAG,CAAC;AACzC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/styles/index.css
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Cadence Design System - Styles Entry Point
|
|
3
|
+
*
|
|
4
|
+
* Main stylesheet for Cadence. Import after the Tailwind config.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* // app.css
|
|
8
|
+
* @import "tailwindcss";
|
|
9
|
+
* @import "@music-vine/cadence/tailwind.config";
|
|
10
|
+
* @import "@music-vine/cadence/styles";
|
|
11
|
+
*
|
|
12
|
+
* @source "./src/**\/*.{js,ts,jsx,tsx}";
|
|
13
|
+
*/
|
|
4
14
|
|
|
15
|
+
/* ================================
|
|
16
|
+
Base Layer - CSS Variables
|
|
17
|
+
================================ */
|
|
5
18
|
@layer base {
|
|
6
19
|
:root {
|
|
7
20
|
/* Brand Colors - Themeable */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@music-vine/cadence",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -17,9 +17,6 @@
|
|
|
17
17
|
],
|
|
18
18
|
"theme": [
|
|
19
19
|
"./dist/theme/index.d.ts"
|
|
20
|
-
],
|
|
21
|
-
"tailwind.config": [
|
|
22
|
-
"./tailwind.config.ts"
|
|
23
20
|
]
|
|
24
21
|
}
|
|
25
22
|
},
|
|
@@ -45,14 +42,11 @@
|
|
|
45
42
|
"types": "./dist/theme/index.d.ts"
|
|
46
43
|
},
|
|
47
44
|
"./styles": "./dist/styles/index.css",
|
|
48
|
-
"./
|
|
49
|
-
"./tailwind.config": "./tailwind.config.ts",
|
|
50
|
-
"./tailwind.config.v4": "./tailwind.config.v4.css"
|
|
45
|
+
"./tailwind.config": "./tailwind.config.css"
|
|
51
46
|
},
|
|
52
47
|
"files": [
|
|
53
48
|
"dist",
|
|
54
|
-
"tailwind.config.
|
|
55
|
-
"tailwind.config.v4.css"
|
|
49
|
+
"tailwind.config.css"
|
|
56
50
|
],
|
|
57
51
|
"repository": {
|
|
58
52
|
"type": "git",
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Cadence Design System - Tailwind CSS
|
|
2
|
+
* Cadence Design System - Tailwind CSS Configuration
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Use this instead of tailwind.config.ts when using Tailwind v4.
|
|
4
|
+
* CSS-first configuration for Tailwind CSS v4+.
|
|
6
5
|
*
|
|
7
6
|
* @example
|
|
8
7
|
* // app.css
|
|
9
8
|
* @import "tailwindcss";
|
|
10
|
-
* @import "@music-vine/cadence/tailwind.config
|
|
11
|
-
* @import "@music-vine/cadence/styles
|
|
9
|
+
* @import "@music-vine/cadence/tailwind.config";
|
|
10
|
+
* @import "@music-vine/cadence/styles";
|
|
12
11
|
*
|
|
13
12
|
* @source "./src/**\/*.{js,ts,jsx,tsx}";
|
|
14
13
|
*/
|
|
15
14
|
|
|
15
|
+
/* ================================
|
|
16
|
+
Plugins
|
|
17
|
+
================================ */
|
|
18
|
+
@plugin "tailwindcss-animate";
|
|
19
|
+
|
|
16
20
|
/* ================================
|
|
17
21
|
Dark Mode Configuration
|
|
18
22
|
================================ */
|
|
@@ -32,20 +36,24 @@
|
|
|
32
36
|
--breakpoint-3xl: 125rem; /* 2000px */
|
|
33
37
|
|
|
34
38
|
/* ================================
|
|
35
|
-
|
|
39
|
+
Container Query Breakpoints
|
|
40
|
+
(matches @tailwindcss/container-queries v3 plugin defaults)
|
|
36
41
|
================================ */
|
|
37
|
-
--
|
|
38
|
-
--
|
|
39
|
-
--
|
|
40
|
-
--
|
|
41
|
-
--
|
|
42
|
+
--container-xs: 20rem; /* 320px */
|
|
43
|
+
--container-sm: 24rem; /* 384px */
|
|
44
|
+
--container-md: 28rem; /* 448px */
|
|
45
|
+
--container-lg: 32rem; /* 512px */
|
|
46
|
+
--container-xl: 36rem; /* 576px */
|
|
47
|
+
--container-2xl: 42rem; /* 672px */
|
|
48
|
+
--container-3xl: 48rem; /* 768px */
|
|
49
|
+
--container-4xl: 56rem; /* 896px */
|
|
50
|
+
--container-5xl: 64rem; /* 1024px */
|
|
51
|
+
--container-6xl: 72rem; /* 1152px */
|
|
52
|
+
--container-7xl: 80rem; /* 1280px */
|
|
42
53
|
|
|
43
54
|
/* ================================
|
|
44
55
|
Base Colors
|
|
45
56
|
================================ */
|
|
46
|
-
--color-inherit: inherit;
|
|
47
|
-
--color-current: currentColor;
|
|
48
|
-
--color-transparent: transparent;
|
|
49
57
|
--color-black: #151A20;
|
|
50
58
|
--color-white: #FFF;
|
|
51
59
|
|
|
@@ -257,6 +265,19 @@
|
|
|
257
265
|
--animate-slide-up-and-fade: slideUpAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
258
266
|
}
|
|
259
267
|
|
|
268
|
+
/* ================================
|
|
269
|
+
Brand Colors (Themeable via CSS custom properties)
|
|
270
|
+
Uses @theme inline so var() references are inlined directly into
|
|
271
|
+
utility declarations instead of being resolved at build time.
|
|
272
|
+
================================ */
|
|
273
|
+
@theme inline {
|
|
274
|
+
--color-brand-primary: var(--brand-primary, #F23D75);
|
|
275
|
+
--color-brand-primary-hover: var(--brand-primary-hover, #DF1F64);
|
|
276
|
+
--color-brand-primary-active: var(--brand-primary-active, #BC1454);
|
|
277
|
+
--color-brand-secondary: var(--brand-secondary, #FFF1F4);
|
|
278
|
+
--color-brand-secondary-hover: var(--brand-secondary-hover, #FFE4EA);
|
|
279
|
+
}
|
|
280
|
+
|
|
260
281
|
/* ================================
|
|
261
282
|
Keyframe Animations
|
|
262
283
|
================================ */
|