@aster-ui/prefixed 0.12.52 → 0.12.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"Drawer.js","sources":["../../src/components/Drawer.tsx"],"sourcesContent":["import React, {\n useEffect,\n useRef,\n useId,\n useCallback,\n useState,\n forwardRef,\n useImperativeHandle,\n createContext,\n useContext,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { Skeleton } from './Skeleton'\n\n// DaisyUI classes\nconst dBtn = 'd-btn'\nconst dBtnGhost = 'd-btn-ghost'\nconst dBtnSm = 'd-btn-sm'\nconst dBtnSquare = 'd-btn-square'\n\nexport type DrawerPlacement = 'top' | 'right' | 'bottom' | 'left'\nexport type DrawerSize = 'default' | 'large' | number\n\nexport interface DrawerPushConfig {\n /** Distance to push parent drawer (default: 180) */\n distance?: number\n}\n\nexport interface DrawerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Drawer content */\n children: React.ReactNode\n /** Whether the drawer is visible */\n open?: boolean\n /** Callback when drawer is closed */\n onClose?: (e?: React.MouseEvent | React.KeyboardEvent) => void\n /** Callback after open/close animation completes */\n afterOpenChange?: (open: boolean) => void\n /** Drawer title */\n title?: React.ReactNode\n /** Direction drawer slides from */\n placement?: DrawerPlacement\n /** Preset size or custom width/height in pixels */\n size?: DrawerSize\n /** Custom width (overrides size for left/right placement) */\n width?: number | string\n /** Custom height (overrides size for top/bottom placement) */\n height?: number | string\n /** Whether to show close button */\n closable?: boolean\n /** Whether to show mask/backdrop */\n mask?: boolean\n /** Whether clicking mask closes drawer */\n maskClosable?: boolean\n /** Whether ESC closes drawer */\n keyboard?: boolean\n /** Footer content */\n footer?: React.ReactNode\n /** Extra content in header (right side) */\n extra?: React.ReactNode\n /** CSS class for drawer panel */\n className?: string\n /** CSS class for drawer wrapper */\n rootClassName?: string\n /** Style for drawer panel */\n style?: React.CSSProperties\n /** Style for drawer header */\n headerStyle?: React.CSSProperties\n /** Style for drawer body/content area */\n bodyStyle?: React.CSSProperties\n /** Style for drawer footer */\n footerStyle?: React.CSSProperties\n /** Style for drawer wrapper (includes mask) */\n rootStyle?: React.CSSProperties\n /** Style for mask/backdrop */\n maskStyle?: React.CSSProperties\n /** z-index of drawer */\n zIndex?: number\n /** Destroy content when closed */\n destroyOnClose?: boolean\n /** Pre-render drawer content (keep in DOM even when closed) */\n forceRender?: boolean\n /** Where to place initial focus */\n initialFocus?: 'close' | 'content'\n /** Show loading skeleton */\n loading?: boolean\n /** Custom container for portal (false to disable portal) */\n getContainer?: HTMLElement | (() => HTMLElement) | false\n /** Nested drawer push behavior */\n push?: boolean | DrawerPushConfig\n /** Test ID for testing */\n 'data-testid'?: string\n}\n\nexport interface DrawerRef {\n /** The drawer panel element */\n nativeElement: HTMLDivElement | null\n}\n\n// Context for nested drawer push behavior\ninterface DrawerContextValue {\n push: boolean | DrawerPushConfig\n pushDistance: number\n}\n\nconst DrawerContext = createContext<DrawerContextValue | null>(null)\n\nfunction useDrawerContext() {\n return useContext(DrawerContext)\n}\n\n/**\n * Drawer - A panel that slides in from the edge of the screen.\n * Use for forms, details, or task panels.\n * For responsive sidebar navigation, use ResponsiveDrawer instead.\n */\nexport const Drawer = forwardRef<DrawerRef, DrawerProps>(\n (\n {\n children,\n open = false,\n onClose,\n afterOpenChange,\n title,\n placement = 'right',\n size = 'default',\n width,\n height,\n closable = true,\n mask = true,\n maskClosable = true,\n keyboard = true,\n footer,\n extra,\n className = '',\n rootClassName = '',\n style,\n headerStyle,\n bodyStyle,\n footerStyle,\n rootStyle,\n maskStyle,\n zIndex = 1000,\n destroyOnClose = false,\n forceRender = false,\n initialFocus = 'close',\n loading = false,\n getContainer,\n push = { distance: 180 },\n 'data-testid': testId,\n ...rest\n },\n ref\n ) => {\n const drawerRef = useRef<HTMLDivElement>(null)\n const closeButtonRef = useRef<HTMLButtonElement>(null)\n const contentRef = useRef<HTMLDivElement>(null)\n const previousActiveElement = useRef<HTMLElement | null>(null)\n const titleId = useId()\n const contentId = useId()\n const [mounted, setMounted] = useState(false)\n const [shouldRender, setShouldRender] = useState(open || forceRender)\n const [isAnimating, setIsAnimating] = useState(false)\n\n // Get parent drawer context for nested push behavior\n const parentDrawer = useDrawerContext()\n\n // Expose ref\n useImperativeHandle(ref, () => ({\n nativeElement: drawerRef.current,\n }))\n\n // Handle SSR - only render portal after mounting in browser\n useEffect(() => {\n setMounted(true)\n }, [])\n\n // Calculate dimensions\n const getSizeValue = (): number => {\n if (typeof size === 'number') return size\n return size === 'large' ? 736 : 378\n }\n\n const getDimension = (): { width?: string; height?: string } => {\n const isHorizontal = placement === 'left' || placement === 'right'\n const sizeValue = getSizeValue()\n\n if (isHorizontal) {\n const w = width ?? sizeValue\n return { width: typeof w === 'number' ? `${w}px` : w }\n } else {\n const h = height ?? sizeValue\n return { height: typeof h === 'number' ? `${h}px` : h }\n }\n }\n\n // Calculate push distance for nested drawers\n const getPushDistance = (): number => {\n if (!push) return 0\n if (typeof push === 'boolean') return push ? 180 : 0\n return push.distance ?? 180\n }\n\n // Focus trap\n const trapFocus = useCallback((e: KeyboardEvent) => {\n if (!drawerRef.current || e.key !== 'Tab' || typeof document === 'undefined') return\n\n const focusableElements = drawerRef.current.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n )\n const firstElement = focusableElements[0]\n const lastElement = focusableElements[focusableElements.length - 1]\n\n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault()\n lastElement?.focus()\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault()\n firstElement?.focus()\n }\n }, [])\n\n // Handle ESC key\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (keyboard && e.key === 'Escape' && onClose) {\n e.preventDefault()\n onClose()\n }\n },\n [keyboard, onClose]\n )\n\n // Handle animation end\n const handleTransitionEnd = useCallback(() => {\n setIsAnimating(false)\n afterOpenChange?.(open)\n\n if (!open && destroyOnClose) {\n setShouldRender(false)\n }\n }, [open, afterOpenChange, destroyOnClose])\n\n // Open/close effects\n useEffect(() => {\n if (typeof document === 'undefined') return\n\n if (open) {\n setShouldRender(true)\n setIsAnimating(true)\n previousActiveElement.current = document.activeElement as HTMLElement\n document.body.style.overflow = 'hidden'\n\n // Set initial focus\n const focusTimeout = setTimeout(() => {\n if (initialFocus === 'close' && closeButtonRef.current) {\n closeButtonRef.current.focus()\n } else if (contentRef.current) {\n const firstFocusable = contentRef.current.querySelector<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n )\n firstFocusable?.focus()\n }\n }, 0)\n\n // Add event listeners\n document.addEventListener('keydown', handleKeyDown)\n document.addEventListener('keydown', trapFocus)\n\n return () => {\n clearTimeout(focusTimeout)\n document.body.style.overflow = ''\n document.removeEventListener('keydown', handleKeyDown)\n document.removeEventListener('keydown', trapFocus)\n }\n } else {\n setIsAnimating(true)\n // Restore focus to previously focused element if it's still in the DOM\n const prevElement = previousActiveElement.current\n if (prevElement && document.body.contains(prevElement)) {\n prevElement.focus()\n }\n }\n }, [open, handleKeyDown, trapFocus, initialFocus])\n\n const handleMaskClick = (e: React.MouseEvent) => {\n if (maskClosable && onClose) {\n onClose(e)\n }\n }\n\n // Position classes\n const placementClasses: Record<DrawerPlacement, string> = {\n top: 'inset-x-0 top-0',\n right: 'inset-y-0 right-0',\n bottom: 'inset-x-0 bottom-0',\n left: 'inset-y-0 left-0',\n }\n\n // Transform for animation\n const getTransform = (isOpen: boolean): string => {\n // Apply push offset from child drawer\n const pushOffset = parentDrawer && open ? parentDrawer.pushDistance : 0\n\n if (isOpen) {\n if (pushOffset === 0) return 'translate(0, 0)'\n switch (placement) {\n case 'right':\n return `translateX(-${pushOffset}px)`\n case 'left':\n return `translateX(${pushOffset}px)`\n case 'top':\n return `translateY(${pushOffset}px)`\n case 'bottom':\n return `translateY(-${pushOffset}px)`\n }\n }\n\n switch (placement) {\n case 'top':\n return 'translateY(-100%)'\n case 'right':\n return 'translateX(100%)'\n case 'bottom':\n return 'translateY(100%)'\n case 'left':\n return 'translateX(-100%)'\n }\n }\n\n const dimension = getDimension()\n\n // Get container element\n const getContainerElement = (): HTMLElement | null => {\n if (getContainer === false) return null\n if (typeof getContainer === 'function') return getContainer()\n if (getContainer) return getContainer\n return typeof document !== 'undefined' ? document.body : null\n }\n\n // Generate test IDs\n const getTestId = (suffix: string) => (testId ? `${testId}-${suffix}` : undefined)\n\n const drawerContent = (\n <DrawerContext.Provider value={{ push, pushDistance: getPushDistance() }}>\n <div\n className={`fixed inset-0 ${open ? '' : 'pointer-events-none'} ${rootClassName}`}\n style={{ zIndex, ...rootStyle }}\n role=\"presentation\"\n data-state={open ? 'open' : 'closed'}\n data-testid={testId}\n {...rest}\n >\n {/* Backdrop/Mask */}\n {mask && (\n <div\n className={`absolute inset-0 bg-black transition-opacity duration-300 ${\n open ? 'opacity-50' : 'opacity-0'\n }`}\n style={maskStyle}\n onClick={handleMaskClick}\n aria-hidden=\"true\"\n data-testid={getTestId('mask')}\n />\n )}\n\n {/* Drawer Panel */}\n <div\n ref={drawerRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-describedby={contentId}\n className={`fixed flex flex-col bg-base-100 shadow-xl transition-transform duration-300 ease-in-out ${placementClasses[placement]} ${className}`}\n style={{\n ...dimension,\n transform: getTransform(open),\n ...style,\n }}\n onTransitionEnd={handleTransitionEnd}\n data-testid={getTestId('panel')}\n >\n {/* Header */}\n {(title || closable || extra) && (\n <div\n className=\"flex items-center justify-between px-6 py-4 border-b border-base-300\"\n style={headerStyle}\n data-testid={getTestId('header')}\n >\n {title && (\n <h2 id={titleId} className=\"text-lg font-semibold\">\n {title}\n </h2>\n )}\n <div className=\"flex items-center gap-2 ml-auto\">\n {extra}\n {closable && (\n <button\n ref={closeButtonRef}\n type=\"button\"\n className={`${dBtn} ${dBtnGhost} ${dBtnSm} ${dBtnSquare}`}\n onClick={onClose}\n aria-label=\"Close drawer\"\n data-testid={getTestId('close')}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-5 w-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n )}\n </div>\n </div>\n )}\n\n {/* Content */}\n <div\n ref={contentRef}\n id={contentId}\n className=\"flex-1 overflow-auto p-6\"\n style={bodyStyle}\n data-testid={getTestId('body')}\n >\n {loading ? (\n <div className=\"space-y-4\" data-testid={getTestId('skeleton')}>\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-5/6\" />\n <Skeleton className=\"h-4 w-2/3\" />\n <Skeleton className=\"h-32 w-full\" />\n </div>\n ) : (\n children\n )}\n </div>\n\n {/* Footer */}\n {footer && (\n <div\n className=\"px-6 py-4 border-t border-base-300\"\n style={footerStyle}\n data-testid={getTestId('footer')}\n >\n {footer}\n </div>\n )}\n </div>\n </div>\n </DrawerContext.Provider>\n )\n\n // Don't render during SSR or when not needed\n if (!mounted) return null\n if (!shouldRender && !open && !forceRender) return null\n\n // Render without portal if getContainer is false\n const container = getContainerElement()\n if (container === null) return drawerContent\n\n return createPortal(drawerContent, container)\n }\n)\n\nDrawer.displayName = 'Drawer'\n"],"names":["dBtn","dBtnGhost","dBtnSm","dBtnSquare","DrawerContext","createContext","useDrawerContext","useContext","Drawer","forwardRef","children","open","onClose","afterOpenChange","title","placement","size","width","height","closable","mask","maskClosable","keyboard","footer","extra","className","rootClassName","style","headerStyle","bodyStyle","footerStyle","rootStyle","maskStyle","zIndex","destroyOnClose","forceRender","initialFocus","loading","getContainer","push","testId","rest","ref","drawerRef","useRef","closeButtonRef","contentRef","previousActiveElement","titleId","useId","contentId","mounted","setMounted","useState","shouldRender","setShouldRender","isAnimating","setIsAnimating","parentDrawer","useImperativeHandle","useEffect","getSizeValue","getDimension","isHorizontal","sizeValue","w","h","getPushDistance","trapFocus","useCallback","focusableElements","firstElement","lastElement","handleKeyDown","handleTransitionEnd","focusTimeout","prevElement","handleMaskClick","placementClasses","getTransform","isOpen","pushOffset","dimension","getContainerElement","getTestId","suffix","drawerContent","jsx","jsxs","Skeleton","container","createPortal"],"mappings":"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"}
1
+ {"version":3,"file":"Drawer.js","sources":["../../src/components/Drawer.tsx"],"sourcesContent":["import React, {\n useEffect,\n useRef,\n useId,\n useCallback,\n useState,\n forwardRef,\n useImperativeHandle,\n createContext,\n useContext,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { Skeleton } from './Skeleton'\n\n// DaisyUI classes\nconst dBtn = 'd-btn'\nconst dBtnGhost = 'd-btn-ghost'\nconst dBtnSm = 'd-btn-sm'\nconst dBtnSquare = 'd-btn-square'\n\nexport type DrawerPlacement = 'top' | 'right' | 'bottom' | 'left'\nexport type DrawerSize = 'default' | 'large' | number\n\nexport interface DrawerPushConfig {\n /** Distance to push parent drawer (default: 180) */\n distance?: number\n}\n\nexport interface DrawerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Drawer content */\n children: React.ReactNode\n /** Whether the drawer is visible */\n open?: boolean\n /** Callback when drawer is closed */\n onClose?: (e?: React.MouseEvent | React.KeyboardEvent) => void\n /** Callback after open/close animation completes */\n afterOpenChange?: (open: boolean) => void\n /** Drawer title */\n title?: React.ReactNode\n /** Direction drawer slides from */\n placement?: DrawerPlacement\n /** Preset size or custom width/height in pixels */\n size?: DrawerSize\n /** Custom width (overrides size for left/right placement) */\n width?: number | string\n /** Custom height (overrides size for top/bottom placement) */\n height?: number | string\n /** Whether to show close button */\n closable?: boolean\n /** Whether to show mask/backdrop */\n mask?: boolean\n /** Whether clicking mask closes drawer */\n maskClosable?: boolean\n /** Whether ESC closes drawer */\n keyboard?: boolean\n /** Footer content */\n footer?: React.ReactNode\n /** Extra content in header (right side) */\n extra?: React.ReactNode\n /** CSS class for drawer panel */\n className?: string\n /** CSS class for drawer wrapper */\n rootClassName?: string\n /** Style for drawer panel */\n style?: React.CSSProperties\n /** Style for drawer header */\n headerStyle?: React.CSSProperties\n /** Style for drawer body/content area */\n bodyStyle?: React.CSSProperties\n /** Style for drawer footer */\n footerStyle?: React.CSSProperties\n /** Style for drawer wrapper (includes mask) */\n rootStyle?: React.CSSProperties\n /** Style for mask/backdrop */\n maskStyle?: React.CSSProperties\n /** z-index of drawer */\n zIndex?: number\n /** Destroy content when closed */\n destroyOnClose?: boolean\n /** Pre-render drawer content (keep in DOM even when closed) */\n forceRender?: boolean\n /** Where to place initial focus */\n initialFocus?: 'close' | 'content'\n /** Show loading skeleton */\n loading?: boolean\n /** Custom container for portal (false to disable portal) */\n getContainer?: HTMLElement | (() => HTMLElement) | false\n /** Nested drawer push behavior */\n push?: boolean | DrawerPushConfig\n /** Test ID for testing */\n 'data-testid'?: string\n}\n\nexport interface DrawerRef {\n /** The drawer panel element */\n nativeElement: HTMLDivElement | null\n}\n\n// Context for nested drawer push behavior\ninterface DrawerContextValue {\n push: boolean | DrawerPushConfig\n pushDistance: number\n}\n\nconst DrawerContext = createContext<DrawerContextValue | null>(null)\n\nfunction useDrawerContext() {\n return useContext(DrawerContext)\n}\n\n/**\n * Drawer - A panel that slides in from the edge of the screen.\n * Use for forms, details, or task panels.\n * For responsive sidebar navigation, use ResponsiveDrawer instead.\n */\nexport const Drawer = forwardRef<DrawerRef, DrawerProps>(\n (\n {\n children,\n open = false,\n onClose,\n afterOpenChange,\n title,\n placement = 'right',\n size = 'default',\n width,\n height,\n closable = true,\n mask = true,\n maskClosable = true,\n keyboard = true,\n footer,\n extra,\n className = '',\n rootClassName = '',\n style,\n headerStyle,\n bodyStyle,\n footerStyle,\n rootStyle,\n maskStyle,\n zIndex = 1000,\n destroyOnClose = false,\n forceRender = false,\n initialFocus = 'close',\n loading = false,\n getContainer,\n push = { distance: 180 },\n 'data-testid': testId,\n ...rest\n },\n ref\n ) => {\n const drawerRef = useRef<HTMLDivElement>(null)\n const closeButtonRef = useRef<HTMLButtonElement>(null)\n const contentRef = useRef<HTMLDivElement>(null)\n const previousActiveElement = useRef<HTMLElement | null>(null)\n const titleId = useId()\n const contentId = useId()\n const [mounted, setMounted] = useState(false)\n const [shouldRender, setShouldRender] = useState(open || forceRender)\n const [, setIsAnimating] = useState(false)\n\n // Get parent drawer context for nested push behavior\n const parentDrawer = useDrawerContext()\n\n // Expose ref\n useImperativeHandle(ref, () => ({\n nativeElement: drawerRef.current,\n }))\n\n // Handle SSR - only render portal after mounting in browser\n useEffect(() => {\n setMounted(true)\n }, [])\n\n // Calculate dimensions\n const getSizeValue = (): number => {\n if (typeof size === 'number') return size\n return size === 'large' ? 736 : 378\n }\n\n const getDimension = (): { width?: string; height?: string } => {\n const isHorizontal = placement === 'left' || placement === 'right'\n const sizeValue = getSizeValue()\n\n if (isHorizontal) {\n const w = width ?? sizeValue\n return { width: typeof w === 'number' ? `${w}px` : w }\n } else {\n const h = height ?? sizeValue\n return { height: typeof h === 'number' ? `${h}px` : h }\n }\n }\n\n // Calculate push distance for nested drawers\n const getPushDistance = (): number => {\n if (!push) return 0\n if (typeof push === 'boolean') return push ? 180 : 0\n return push.distance ?? 180\n }\n\n // Focus trap\n const trapFocus = useCallback((e: KeyboardEvent) => {\n if (!drawerRef.current || e.key !== 'Tab' || typeof document === 'undefined') return\n\n const focusableElements = drawerRef.current.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n )\n const firstElement = focusableElements[0]\n const lastElement = focusableElements[focusableElements.length - 1]\n\n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault()\n lastElement?.focus()\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault()\n firstElement?.focus()\n }\n }, [])\n\n // Handle ESC key\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (keyboard && e.key === 'Escape' && onClose) {\n e.preventDefault()\n onClose()\n }\n },\n [keyboard, onClose]\n )\n\n // Handle animation end\n const handleTransitionEnd = useCallback(() => {\n setIsAnimating(false)\n afterOpenChange?.(open)\n\n if (!open && destroyOnClose) {\n setShouldRender(false)\n }\n }, [open, afterOpenChange, destroyOnClose])\n\n // Open/close effects\n useEffect(() => {\n if (typeof document === 'undefined') return\n\n if (open) {\n setShouldRender(true)\n setIsAnimating(true)\n previousActiveElement.current = document.activeElement as HTMLElement\n document.body.style.overflow = 'hidden'\n\n // Set initial focus\n const focusTimeout = setTimeout(() => {\n if (initialFocus === 'close' && closeButtonRef.current) {\n closeButtonRef.current.focus()\n } else if (contentRef.current) {\n const firstFocusable = contentRef.current.querySelector<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n )\n firstFocusable?.focus()\n }\n }, 0)\n\n // Add event listeners\n document.addEventListener('keydown', handleKeyDown)\n document.addEventListener('keydown', trapFocus)\n\n return () => {\n clearTimeout(focusTimeout)\n document.body.style.overflow = ''\n document.removeEventListener('keydown', handleKeyDown)\n document.removeEventListener('keydown', trapFocus)\n }\n } else {\n setIsAnimating(true)\n // Restore focus to previously focused element if it's still in the DOM\n const prevElement = previousActiveElement.current\n if (prevElement && document.body.contains(prevElement)) {\n prevElement.focus()\n }\n }\n }, [open, handleKeyDown, trapFocus, initialFocus])\n\n const handleMaskClick = (e: React.MouseEvent) => {\n if (maskClosable && onClose) {\n onClose(e)\n }\n }\n\n // Position classes\n const placementClasses: Record<DrawerPlacement, string> = {\n top: 'inset-x-0 top-0',\n right: 'inset-y-0 right-0',\n bottom: 'inset-x-0 bottom-0',\n left: 'inset-y-0 left-0',\n }\n\n // Transform for animation\n const getTransform = (isOpen: boolean): string => {\n // Apply push offset from child drawer\n const pushOffset = parentDrawer && open ? parentDrawer.pushDistance : 0\n\n if (isOpen) {\n if (pushOffset === 0) return 'translate(0, 0)'\n switch (placement) {\n case 'right':\n return `translateX(-${pushOffset}px)`\n case 'left':\n return `translateX(${pushOffset}px)`\n case 'top':\n return `translateY(${pushOffset}px)`\n case 'bottom':\n return `translateY(-${pushOffset}px)`\n }\n }\n\n switch (placement) {\n case 'top':\n return 'translateY(-100%)'\n case 'right':\n return 'translateX(100%)'\n case 'bottom':\n return 'translateY(100%)'\n case 'left':\n return 'translateX(-100%)'\n }\n }\n\n const dimension = getDimension()\n\n // Get container element\n const getContainerElement = (): HTMLElement | null => {\n if (getContainer === false) return null\n if (typeof getContainer === 'function') return getContainer()\n if (getContainer) return getContainer\n return typeof document !== 'undefined' ? document.body : null\n }\n\n // Generate test IDs\n const getTestId = (suffix: string) => (testId ? `${testId}-${suffix}` : undefined)\n\n const drawerContent = (\n <DrawerContext.Provider value={{ push, pushDistance: getPushDistance() }}>\n <div\n className={`fixed inset-0 ${open ? '' : 'pointer-events-none'} ${rootClassName}`}\n style={{ zIndex, ...rootStyle }}\n role=\"presentation\"\n data-state={open ? 'open' : 'closed'}\n data-testid={testId}\n {...rest}\n >\n {/* Backdrop/Mask */}\n {mask && (\n <div\n className={`absolute inset-0 bg-black transition-opacity duration-300 ${\n open ? 'opacity-50' : 'opacity-0'\n }`}\n style={maskStyle}\n onClick={handleMaskClick}\n aria-hidden=\"true\"\n data-testid={getTestId('mask')}\n />\n )}\n\n {/* Drawer Panel */}\n <div\n ref={drawerRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-describedby={contentId}\n className={`fixed flex flex-col bg-base-100 shadow-xl transition-transform duration-300 ease-in-out ${placementClasses[placement]} ${className}`}\n style={{\n ...dimension,\n transform: getTransform(open),\n ...style,\n }}\n onTransitionEnd={handleTransitionEnd}\n data-testid={getTestId('panel')}\n >\n {/* Header */}\n {(title || closable || extra) && (\n <div\n className=\"flex items-center justify-between px-6 py-4 border-b border-base-300\"\n style={headerStyle}\n data-testid={getTestId('header')}\n >\n {title && (\n <h2 id={titleId} className=\"text-lg font-semibold\">\n {title}\n </h2>\n )}\n <div className=\"flex items-center gap-2 ml-auto\">\n {extra}\n {closable && (\n <button\n ref={closeButtonRef}\n type=\"button\"\n className={`${dBtn} ${dBtnGhost} ${dBtnSm} ${dBtnSquare}`}\n onClick={onClose}\n aria-label=\"Close drawer\"\n data-testid={getTestId('close')}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-5 w-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n )}\n </div>\n </div>\n )}\n\n {/* Content */}\n <div\n ref={contentRef}\n id={contentId}\n className=\"flex-1 overflow-auto p-6\"\n style={bodyStyle}\n data-testid={getTestId('body')}\n >\n {loading ? (\n <div className=\"space-y-4\" data-testid={getTestId('skeleton')}>\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-5/6\" />\n <Skeleton className=\"h-4 w-2/3\" />\n <Skeleton className=\"h-32 w-full\" />\n </div>\n ) : (\n children\n )}\n </div>\n\n {/* Footer */}\n {footer && (\n <div\n className=\"px-6 py-4 border-t border-base-300\"\n style={footerStyle}\n data-testid={getTestId('footer')}\n >\n {footer}\n </div>\n )}\n </div>\n </div>\n </DrawerContext.Provider>\n )\n\n // Don't render during SSR or when not needed\n if (!mounted) return null\n if (!shouldRender && !open && !forceRender) return null\n\n // Render without portal if getContainer is false\n const container = getContainerElement()\n if (container === null) return drawerContent\n\n return createPortal(drawerContent, container)\n }\n)\n\nDrawer.displayName = 'Drawer'\n"],"names":["dBtn","dBtnGhost","dBtnSm","dBtnSquare","DrawerContext","createContext","useDrawerContext","useContext","Drawer","forwardRef","children","open","onClose","afterOpenChange","title","placement","size","width","height","closable","mask","maskClosable","keyboard","footer","extra","className","rootClassName","style","headerStyle","bodyStyle","footerStyle","rootStyle","maskStyle","zIndex","destroyOnClose","forceRender","initialFocus","loading","getContainer","push","testId","rest","ref","drawerRef","useRef","closeButtonRef","contentRef","previousActiveElement","titleId","useId","contentId","mounted","setMounted","useState","shouldRender","setShouldRender","setIsAnimating","parentDrawer","useImperativeHandle","useEffect","getSizeValue","getDimension","isHorizontal","sizeValue","w","h","getPushDistance","trapFocus","useCallback","focusableElements","firstElement","lastElement","handleKeyDown","handleTransitionEnd","focusTimeout","prevElement","handleMaskClick","placementClasses","getTransform","isOpen","pushOffset","dimension","getContainerElement","getTestId","suffix","drawerContent","jsx","jsxs","Skeleton","container","createPortal"],"mappings":"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"}
@@ -1,60 +1,61 @@
1
- import { jsx as e, jsxs as b } from "react/jsx-runtime";
2
- import { useRef as R, useEffect as C } from "react";
3
- import w from "qrcode";
4
- const Q = "d-loading", j = "d-loading-spinner", F = "d-loading-lg", L = "d-btn", $ = "d-btn-sm", k = "d-btn-primary", B = ({
5
- value: o,
1
+ import { jsx as e, jsxs as C } from "react/jsx-runtime";
2
+ import { useRef as Q, useEffect as j } from "react";
3
+ import k from "qrcode";
4
+ import { useTheme as B } from "../hooks/useTheme.js";
5
+ const L = "d-loading", $ = "d-loading-spinner", E = "d-loading-lg", I = "d-btn", M = "d-btn-sm", V = "d-btn-primary", G = ({
6
+ value: d,
6
7
  size: t = 160,
7
- errorLevel: g = "M",
8
+ errorLevel: h = "M",
8
9
  icon: m,
9
10
  iconSize: n = 40,
10
11
  type: f = "canvas",
11
- color: v = "#000000",
12
- bgColor: h = "#FFFFFF",
12
+ color: y,
13
+ bgColor: R,
13
14
  bordered: a = !0,
14
15
  status: s = "active",
15
- onRefresh: x,
16
- className: y = "",
16
+ onRefresh: v,
17
+ className: w = "",
17
18
  ...r
18
19
  }) => {
19
- const c = R(null);
20
- C(() => {
21
- if (s !== "active" || !o || f !== "canvas") return;
20
+ const c = Q(null), { colors: x } = B(), p = y ?? x.foreground, g = R ?? x.background;
21
+ j(() => {
22
+ if (s !== "active" || !d || f !== "canvas") return;
22
23
  (async () => {
23
24
  if (c.current)
24
25
  try {
25
- if (await w.toCanvas(c.current, o, {
26
+ if (await k.toCanvas(c.current, d, {
26
27
  width: t,
27
28
  margin: 1,
28
29
  color: {
29
- dark: v,
30
- light: h
30
+ dark: p,
31
+ light: g
31
32
  },
32
- errorCorrectionLevel: g
33
+ errorCorrectionLevel: h
33
34
  }), m && c.current) {
34
- const i = c.current.getContext("2d");
35
- if (i) {
36
- const d = new Image();
37
- d.crossOrigin = "anonymous", d.onload = () => {
38
- const u = (t - n) / 2, N = (t - n) / 2;
39
- i.fillStyle = h, i.fillRect(u - 4, N - 4, n + 8, n + 8), i.drawImage(d, u, N, n, n);
40
- }, d.src = m;
35
+ const l = c.current.getContext("2d");
36
+ if (l) {
37
+ const i = new Image();
38
+ i.crossOrigin = "anonymous", i.onload = () => {
39
+ const b = (t - n) / 2, N = (t - n) / 2;
40
+ l.fillStyle = g, l.fillRect(b - 4, N - 4, n + 8, n + 8), l.drawImage(i, b, N, n, n);
41
+ }, i.src = m;
41
42
  }
42
43
  }
43
- } catch (p) {
44
- console.error("QR Code generation error:", p);
44
+ } catch (u) {
45
+ console.error("QR Code generation error:", u);
45
46
  }
46
47
  })();
47
- }, [o, t, g, m, n, f, v, h, s]);
48
- const l = [
48
+ }, [d, t, h, m, n, f, p, g, s]);
49
+ const o = [
49
50
  "inline-flex items-center justify-center",
50
51
  a && "border border-base-content/20 p-3",
51
52
  "bg-base-100",
52
- y
53
+ w
53
54
  ].filter(Boolean).join(" ");
54
- return s === "loading" ? /* @__PURE__ */ e("div", { className: l, style: { width: t + (a ? 24 : 0), height: t + (a ? 24 : 0) }, "data-state": "loading", ...r, children: /* @__PURE__ */ b("div", { className: "flex flex-col items-center justify-center gap-2", children: [
55
- /* @__PURE__ */ e("span", { className: `${Q} ${j} ${F}` }),
55
+ return s === "loading" ? /* @__PURE__ */ e("div", { className: o, style: { width: t + (a ? 24 : 0), height: t + (a ? 24 : 0) }, "data-state": "loading", ...r, children: /* @__PURE__ */ C("div", { className: "flex flex-col items-center justify-center gap-2", children: [
56
+ /* @__PURE__ */ e("span", { className: `${L} ${$} ${E}` }),
56
57
  /* @__PURE__ */ e("span", { className: "text-sm text-base-content/70", children: "Loading..." })
57
- ] }) }) : s === "expired" ? /* @__PURE__ */ e("div", { className: l, style: { width: t + (a ? 24 : 0), height: t + (a ? 24 : 0) }, "data-state": "expired", ...r, children: /* @__PURE__ */ b("div", { className: "flex flex-col items-center justify-center gap-2", children: [
58
+ ] }) }) : s === "expired" ? /* @__PURE__ */ e("div", { className: o, style: { width: t + (a ? 24 : 0), height: t + (a ? 24 : 0) }, "data-state": "expired", ...r, children: /* @__PURE__ */ C("div", { className: "flex flex-col items-center justify-center gap-2", children: [
58
59
  /* @__PURE__ */ e("svg", { className: "w-12 h-12 text-base-content/30", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ e(
59
60
  "path",
60
61
  {
@@ -64,12 +65,12 @@ const Q = "d-loading", j = "d-loading-spinner", F = "d-loading-lg", L = "d-btn",
64
65
  }
65
66
  ) }),
66
67
  /* @__PURE__ */ e("span", { className: "text-sm text-base-content/70", children: "QR Code Expired" }),
67
- x && /* @__PURE__ */ e("button", { className: `${L} ${$} ${k}`, onClick: x, children: "Refresh" })
68
- ] }) }) : f === "canvas" ? /* @__PURE__ */ e("div", { className: "inline-block", "data-state": "active", ...r, children: /* @__PURE__ */ e("div", { className: l, children: /* @__PURE__ */ e("canvas", { ref: c, style: { display: "block" } }) }) }) : /* @__PURE__ */ e("div", { className: "inline-block", "data-state": "active", ...r, children: /* @__PURE__ */ e("div", { className: l, children: /* @__PURE__ */ e("div", { style: { width: t, height: t }, className: "bg-base-content/5", children: /* @__PURE__ */ e("span", { className: "text-xs text-base-content/50", children: "SVG mode placeholder" }) }) }) });
68
+ v && /* @__PURE__ */ e("button", { className: `${I} ${M} ${V}`, onClick: v, children: "Refresh" })
69
+ ] }) }) : f === "canvas" ? /* @__PURE__ */ e("div", { className: "inline-block", "data-state": "active", ...r, children: /* @__PURE__ */ e("div", { className: o, children: /* @__PURE__ */ e("canvas", { ref: c, style: { display: "block" } }) }) }) : /* @__PURE__ */ e("div", { className: "inline-block", "data-state": "active", ...r, children: /* @__PURE__ */ e("div", { className: o, children: /* @__PURE__ */ e("div", { style: { width: t, height: t }, className: "bg-base-content/5", children: /* @__PURE__ */ e("span", { className: "text-xs text-base-content/50", children: "SVG mode placeholder" }) }) }) });
69
70
  };
70
- B.displayName = "QRCode";
71
+ G.displayName = "QRCode";
71
72
  export {
72
- B as QRCode,
73
- B as default
73
+ G as QRCode,
74
+ G as default
74
75
  };
75
76
  //# sourceMappingURL=QRCode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"QRCode.js","sources":["../../src/components/QRCode.tsx"],"sourcesContent":["import React, { useEffect, useRef } from 'react'\nimport QRCodeLib from 'qrcode'\n\n// DaisyUI classes\nconst dLoading = 'd-loading'\nconst dLoadingSpinner = 'd-loading-spinner'\nconst dLoadingLg = 'd-loading-lg'\nconst dBtn = 'd-btn'\nconst dBtnSm = 'd-btn-sm'\nconst dBtnPrimary = 'd-btn-primary'\n\nexport type QRCodeErrorLevel = 'L' | 'M' | 'Q' | 'H'\nexport type QRCodeType = 'canvas' | 'svg'\nexport type QRCodeStatus = 'active' | 'loading' | 'expired'\n\nexport interface QRCodeProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color'> {\n value: string\n size?: number\n errorLevel?: QRCodeErrorLevel\n icon?: string\n iconSize?: number\n type?: QRCodeType\n color?: string\n bgColor?: string\n bordered?: boolean\n status?: QRCodeStatus\n onRefresh?: () => void\n}\n\nexport const QRCode: React.FC<QRCodeProps> = ({\n value,\n size = 160,\n errorLevel = 'M',\n icon,\n iconSize = 40,\n type = 'canvas',\n color = '#000000',\n bgColor = '#FFFFFF',\n bordered = true,\n status = 'active',\n onRefresh,\n className = '',\n ...rest\n}) => {\n const canvasRef = useRef<HTMLCanvasElement>(null)\n\n useEffect(() => {\n if (status !== 'active' || !value || type !== 'canvas') return\n\n const generateQRCode = async () => {\n if (!canvasRef.current) return\n\n try {\n await QRCodeLib.toCanvas(canvasRef.current, value, {\n width: size,\n margin: 1,\n color: {\n dark: color,\n light: bgColor,\n },\n errorCorrectionLevel: errorLevel,\n })\n\n if (icon && canvasRef.current) {\n const canvas = canvasRef.current\n const ctx = canvas.getContext('2d')\n if (ctx) {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n img.onload = () => {\n const iconX = (size - iconSize) / 2\n const iconY = (size - iconSize) / 2\n ctx.fillStyle = bgColor\n ctx.fillRect(iconX - 4, iconY - 4, iconSize + 8, iconSize + 8)\n ctx.drawImage(img, iconX, iconY, iconSize, iconSize)\n }\n img.src = icon\n }\n }\n } catch (error) {\n console.error('QR Code generation error:', error)\n }\n }\n\n generateQRCode()\n }, [value, size, errorLevel, icon, iconSize, type, color, bgColor, status])\n\n // Download functionality can be implemented by consumers\n // by accessing the canvas ref and converting to data URL\n\n const containerClasses = [\n 'inline-flex items-center justify-center',\n bordered && 'border border-base-content/20 p-3',\n 'bg-base-100',\n className,\n ]\n .filter(Boolean)\n .join(' ')\n\n if (status === 'loading') {\n return (\n <div className={containerClasses} style={{ width: size + (bordered ? 24 : 0), height: size + (bordered ? 24 : 0) }} data-state=\"loading\" {...rest}>\n <div className=\"flex flex-col items-center justify-center gap-2\">\n <span className={`${dLoading} ${dLoadingSpinner} ${dLoadingLg}`}></span>\n <span className=\"text-sm text-base-content/70\">Loading...</span>\n </div>\n </div>\n )\n }\n\n if (status === 'expired') {\n return (\n <div className={containerClasses} style={{ width: size + (bordered ? 24 : 0), height: size + (bordered ? 24 : 0) }} data-state=\"expired\" {...rest}>\n <div className=\"flex flex-col items-center justify-center gap-2\">\n <svg className=\"w-12 h-12 text-base-content/30\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path\n fillRule=\"evenodd\"\n d=\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z\"\n clipRule=\"evenodd\"\n />\n </svg>\n <span className=\"text-sm text-base-content/70\">QR Code Expired</span>\n {onRefresh && (\n <button className={`${dBtn} ${dBtnSm} ${dBtnPrimary}`} onClick={onRefresh}>\n Refresh\n </button>\n )}\n </div>\n </div>\n )\n }\n\n if (type === 'canvas') {\n return (\n <div className=\"inline-block\" data-state=\"active\" {...rest}>\n <div className={containerClasses}>\n <canvas ref={canvasRef} style={{ display: 'block' }} />\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"inline-block\" data-state=\"active\" {...rest}>\n <div className={containerClasses}>\n <div style={{ width: size, height: size }} className=\"bg-base-content/5\">\n <span className=\"text-xs text-base-content/50\">SVG mode placeholder</span>\n </div>\n </div>\n </div>\n )\n}\n\nQRCode.displayName = 'QRCode'\n\nexport default QRCode\n"],"names":["dLoading","dLoadingSpinner","dLoadingLg","dBtn","dBtnSm","dBtnPrimary","QRCode","value","size","errorLevel","icon","iconSize","type","color","bgColor","bordered","status","onRefresh","className","rest","canvasRef","useRef","useEffect","QRCodeLib","ctx","img","iconX","iconY","error","containerClasses","jsx","jsxs"],"mappings":"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"}
1
+ {"version":3,"file":"QRCode.js","sources":["../../src/components/QRCode.tsx"],"sourcesContent":["import React, { useEffect, useRef } from 'react'\nimport QRCodeLib from 'qrcode'\nimport { useTheme } from '../hooks/useTheme'\n\n// DaisyUI classes\nconst dLoading = 'd-loading'\nconst dLoadingSpinner = 'd-loading-spinner'\nconst dLoadingLg = 'd-loading-lg'\nconst dBtn = 'd-btn'\nconst dBtnSm = 'd-btn-sm'\nconst dBtnPrimary = 'd-btn-primary'\n\nexport type QRCodeErrorLevel = 'L' | 'M' | 'Q' | 'H'\nexport type QRCodeType = 'canvas' | 'svg'\nexport type QRCodeStatus = 'active' | 'loading' | 'expired'\n\nexport interface QRCodeProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color'> {\n value: string\n size?: number\n errorLevel?: QRCodeErrorLevel\n icon?: string\n iconSize?: number\n type?: QRCodeType\n color?: string\n bgColor?: string\n bordered?: boolean\n status?: QRCodeStatus\n onRefresh?: () => void\n}\n\nexport const QRCode: React.FC<QRCodeProps> = ({\n value,\n size = 160,\n errorLevel = 'M',\n icon,\n iconSize = 40,\n type = 'canvas',\n color,\n bgColor,\n bordered = true,\n status = 'active',\n onRefresh,\n className = '',\n ...rest\n}) => {\n const canvasRef = useRef<HTMLCanvasElement>(null)\n const { colors } = useTheme()\n\n // Theme-aware default colors from DaisyUI CSS variables\n const effectiveColor = color ?? colors.foreground\n const effectiveBgColor = bgColor ?? colors.background\n\n useEffect(() => {\n if (status !== 'active' || !value || type !== 'canvas') return\n\n const generateQRCode = async () => {\n if (!canvasRef.current) return\n\n try {\n await QRCodeLib.toCanvas(canvasRef.current, value, {\n width: size,\n margin: 1,\n color: {\n dark: effectiveColor,\n light: effectiveBgColor,\n },\n errorCorrectionLevel: errorLevel,\n })\n\n if (icon && canvasRef.current) {\n const canvas = canvasRef.current\n const ctx = canvas.getContext('2d')\n if (ctx) {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n img.onload = () => {\n const iconX = (size - iconSize) / 2\n const iconY = (size - iconSize) / 2\n ctx.fillStyle = effectiveBgColor\n ctx.fillRect(iconX - 4, iconY - 4, iconSize + 8, iconSize + 8)\n ctx.drawImage(img, iconX, iconY, iconSize, iconSize)\n }\n img.src = icon\n }\n }\n } catch (error) {\n console.error('QR Code generation error:', error)\n }\n }\n\n generateQRCode()\n }, [value, size, errorLevel, icon, iconSize, type, effectiveColor, effectiveBgColor, status])\n\n // Download functionality can be implemented by consumers\n // by accessing the canvas ref and converting to data URL\n\n const containerClasses = [\n 'inline-flex items-center justify-center',\n bordered && 'border border-base-content/20 p-3',\n 'bg-base-100',\n className,\n ]\n .filter(Boolean)\n .join(' ')\n\n if (status === 'loading') {\n return (\n <div className={containerClasses} style={{ width: size + (bordered ? 24 : 0), height: size + (bordered ? 24 : 0) }} data-state=\"loading\" {...rest}>\n <div className=\"flex flex-col items-center justify-center gap-2\">\n <span className={`${dLoading} ${dLoadingSpinner} ${dLoadingLg}`}></span>\n <span className=\"text-sm text-base-content/70\">Loading...</span>\n </div>\n </div>\n )\n }\n\n if (status === 'expired') {\n return (\n <div className={containerClasses} style={{ width: size + (bordered ? 24 : 0), height: size + (bordered ? 24 : 0) }} data-state=\"expired\" {...rest}>\n <div className=\"flex flex-col items-center justify-center gap-2\">\n <svg className=\"w-12 h-12 text-base-content/30\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path\n fillRule=\"evenodd\"\n d=\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z\"\n clipRule=\"evenodd\"\n />\n </svg>\n <span className=\"text-sm text-base-content/70\">QR Code Expired</span>\n {onRefresh && (\n <button className={`${dBtn} ${dBtnSm} ${dBtnPrimary}`} onClick={onRefresh}>\n Refresh\n </button>\n )}\n </div>\n </div>\n )\n }\n\n if (type === 'canvas') {\n return (\n <div className=\"inline-block\" data-state=\"active\" {...rest}>\n <div className={containerClasses}>\n <canvas ref={canvasRef} style={{ display: 'block' }} />\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"inline-block\" data-state=\"active\" {...rest}>\n <div className={containerClasses}>\n <div style={{ width: size, height: size }} className=\"bg-base-content/5\">\n <span className=\"text-xs text-base-content/50\">SVG mode placeholder</span>\n </div>\n </div>\n </div>\n )\n}\n\nQRCode.displayName = 'QRCode'\n\nexport default QRCode\n"],"names":["dLoading","dLoadingSpinner","dLoadingLg","dBtn","dBtnSm","dBtnPrimary","QRCode","value","size","errorLevel","icon","iconSize","type","color","bgColor","bordered","status","onRefresh","className","rest","canvasRef","useRef","colors","useTheme","effectiveColor","effectiveBgColor","useEffect","QRCodeLib","ctx","img","iconX","iconY","error","containerClasses","jsx","jsxs"],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { default as React } from 'react';
2
+ export interface ThemeProviderProps {
3
+ children: React.ReactNode;
4
+ /** Default theme. Use "system" to follow browser preference. */
5
+ defaultTheme?: string;
6
+ /** localStorage key for persisting theme. Set to false to disable persistence. */
7
+ storageKey?: string | false;
8
+ /** Light theme to use when system preference is light */
9
+ lightTheme?: string;
10
+ /** Dark theme to use when system preference is dark */
11
+ darkTheme?: string;
12
+ /** Custom function to determine if a theme is dark */
13
+ isDarkTheme?: (theme: string) => boolean;
14
+ }
15
+ export interface ThemeColors {
16
+ background: string;
17
+ foreground: string;
18
+ primary: string;
19
+ primaryContent: string;
20
+ secondary: string;
21
+ accent: string;
22
+ info: string;
23
+ success: string;
24
+ warning: string;
25
+ error: string;
26
+ }
27
+ export interface ThemeContextValue {
28
+ /** The theme setting (what user selected: "system", "light", "dark", etc.) */
29
+ theme: string;
30
+ /** The actual applied theme after resolving "system" */
31
+ resolvedTheme: string;
32
+ /** Whether the resolved theme is dark */
33
+ isDark: boolean;
34
+ /** Set the theme */
35
+ setTheme: (theme: string) => void;
36
+ /** Computed theme colors as hex values (for canvas/non-CSS contexts) */
37
+ colors: ThemeColors;
38
+ /** The system preference ("light" or "dark") */
39
+ systemTheme: 'light' | 'dark';
40
+ }
41
+ export declare function ThemeProvider({ children, defaultTheme, storageKey, lightTheme, darkTheme, isDarkTheme, }: ThemeProviderProps): import("react/jsx-runtime").JSX.Element;
42
+ /**
43
+ * Hook to access theme context.
44
+ * Must be used within a ThemeProvider.
45
+ */
46
+ export declare function useThemeContext(): ThemeContextValue;
47
+ /**
48
+ * Check if ThemeProvider is present in the tree.
49
+ */
50
+ export declare function useHasThemeProvider(): boolean;
@@ -0,0 +1,128 @@
1
+ import { jsx as E } from "react/jsx-runtime";
2
+ import { useState as a, useMemo as m, useCallback as k, useEffect as l, createContext as P, useContext as v } from "react";
3
+ const L = /* @__PURE__ */ new Set([
4
+ "dark",
5
+ "synthwave",
6
+ "halloween",
7
+ "forest",
8
+ "black",
9
+ "luxury",
10
+ "dracula",
11
+ "business",
12
+ "night",
13
+ "coffee",
14
+ "dim",
15
+ "sunset"
16
+ ]), h = P(void 0);
17
+ function M(t) {
18
+ if (typeof document > "u") return "#000000";
19
+ const e = document.createElement("canvas");
20
+ e.width = e.height = 1;
21
+ const n = e.getContext("2d");
22
+ if (!n) return "#000000";
23
+ n.fillStyle = t, n.fillRect(0, 0, 1, 1);
24
+ const [f, o, i] = n.getImageData(0, 0, 1, 1).data;
25
+ return `#${((1 << 24) + (f << 16) + (o << 8) + i).toString(16).slice(1)}`;
26
+ }
27
+ function b() {
28
+ if (typeof document > "u")
29
+ return {
30
+ background: "#ffffff",
31
+ foreground: "#000000",
32
+ primary: "#6366f1",
33
+ primaryContent: "#ffffff",
34
+ secondary: "#f000b8",
35
+ accent: "#37cdbe",
36
+ info: "#3abff8",
37
+ success: "#36d399",
38
+ warning: "#fbbd23",
39
+ error: "#f87272"
40
+ };
41
+ const t = getComputedStyle(document.documentElement), e = (n, f) => {
42
+ const o = t.getPropertyValue(n).trim();
43
+ return o ? M(o) : f;
44
+ };
45
+ return {
46
+ background: e("--color-base-100", "#ffffff"),
47
+ foreground: e("--color-base-content", "#000000"),
48
+ primary: e("--color-primary", "#6366f1"),
49
+ primaryContent: e("--color-primary-content", "#ffffff"),
50
+ secondary: e("--color-secondary", "#f000b8"),
51
+ accent: e("--color-accent", "#37cdbe"),
52
+ info: e("--color-info", "#3abff8"),
53
+ success: e("--color-success", "#36d399"),
54
+ warning: e("--color-warning", "#fbbd23"),
55
+ error: e("--color-error", "#f87272")
56
+ };
57
+ }
58
+ function A() {
59
+ return typeof window > "u" ? "light" : window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
60
+ }
61
+ function H(t) {
62
+ if (!t || typeof window > "u") return null;
63
+ try {
64
+ return localStorage.getItem(t);
65
+ } catch {
66
+ return null;
67
+ }
68
+ }
69
+ function I(t, e) {
70
+ if (!(!t || typeof window > "u"))
71
+ try {
72
+ localStorage.setItem(t, e);
73
+ } catch {
74
+ }
75
+ }
76
+ function j({
77
+ children: t,
78
+ defaultTheme: e = "system",
79
+ storageKey: n = "asterui-theme",
80
+ lightTheme: f = "light",
81
+ darkTheme: o = "dark",
82
+ isDarkTheme: i
83
+ }) {
84
+ const [u, w] = a(() => H(n) || e), [d, C] = a(A), c = m(() => u === "system" ? d === "dark" ? o : f : u, [u, d, f, o]), g = m(() => i ? i(c) : L.has(c), [c, i]), [y, S] = a(b), p = k((r) => {
85
+ w(r), I(n, r);
86
+ }, [n]);
87
+ l(() => {
88
+ typeof document > "u" || (document.documentElement.setAttribute("data-theme", c), requestAnimationFrame(() => {
89
+ S(b());
90
+ }));
91
+ }, [c]), l(() => {
92
+ if (typeof window > "u") return;
93
+ const r = window.matchMedia("(prefers-color-scheme: dark)"), s = (T) => {
94
+ C(T.matches ? "dark" : "light");
95
+ };
96
+ return r.addEventListener("change", s), () => r.removeEventListener("change", s);
97
+ }, []), l(() => {
98
+ if (!n || typeof window > "u") return;
99
+ const r = (s) => {
100
+ s.key === n && s.newValue && w(s.newValue);
101
+ };
102
+ return window.addEventListener("storage", r), () => window.removeEventListener("storage", r);
103
+ }, [n]);
104
+ const x = m(() => ({
105
+ theme: u,
106
+ resolvedTheme: c,
107
+ isDark: g,
108
+ setTheme: p,
109
+ colors: y,
110
+ systemTheme: d
111
+ }), [u, c, g, p, y, d]);
112
+ return /* @__PURE__ */ E(h.Provider, { value: x, children: t });
113
+ }
114
+ function q() {
115
+ const t = v(h);
116
+ if (!t)
117
+ throw new Error("useThemeContext must be used within a ThemeProvider");
118
+ return t;
119
+ }
120
+ function D() {
121
+ return v(h) !== void 0;
122
+ }
123
+ export {
124
+ j as ThemeProvider,
125
+ D as useHasThemeProvider,
126
+ q as useThemeContext
127
+ };
128
+ //# sourceMappingURL=ThemeProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThemeProvider.js","sources":["../../src/components/ThemeProvider.tsx"],"sourcesContent":["import React, { createContext, useContext, useEffect, useState, useCallback, useMemo } from 'react'\n\n// Common dark themes in DaisyUI\nconst DARK_THEMES = new Set([\n 'dark', 'synthwave', 'halloween', 'forest', 'black', 'luxury', 'dracula',\n 'business', 'night', 'coffee', 'dim', 'sunset'\n])\n\nexport interface ThemeProviderProps {\n children: React.ReactNode\n /** Default theme. Use \"system\" to follow browser preference. */\n defaultTheme?: string\n /** localStorage key for persisting theme. Set to false to disable persistence. */\n storageKey?: string | false\n /** Light theme to use when system preference is light */\n lightTheme?: string\n /** Dark theme to use when system preference is dark */\n darkTheme?: string\n /** Custom function to determine if a theme is dark */\n isDarkTheme?: (theme: string) => boolean\n}\n\nexport interface ThemeColors {\n background: string\n foreground: string\n primary: string\n primaryContent: string\n secondary: string\n accent: string\n info: string\n success: string\n warning: string\n error: string\n}\n\nexport interface ThemeContextValue {\n /** The theme setting (what user selected: \"system\", \"light\", \"dark\", etc.) */\n theme: string\n /** The actual applied theme after resolving \"system\" */\n resolvedTheme: string\n /** Whether the resolved theme is dark */\n isDark: boolean\n /** Set the theme */\n setTheme: (theme: string) => void\n /** Computed theme colors as hex values (for canvas/non-CSS contexts) */\n colors: ThemeColors\n /** The system preference (\"light\" or \"dark\") */\n systemTheme: 'light' | 'dark'\n}\n\nconst ThemeContext = createContext<ThemeContextValue | undefined>(undefined)\n\n// Convert any CSS color to hex\nfunction colorToHex(color: string): string {\n if (typeof document === 'undefined') return '#000000'\n const canvas = document.createElement('canvas')\n canvas.width = canvas.height = 1\n const ctx = canvas.getContext('2d')\n if (!ctx) return '#000000'\n ctx.fillStyle = color\n ctx.fillRect(0, 0, 1, 1)\n const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data\n return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`\n}\n\nfunction getThemeColors(): ThemeColors {\n if (typeof document === 'undefined') {\n return {\n background: '#ffffff',\n foreground: '#000000',\n primary: '#6366f1',\n primaryContent: '#ffffff',\n secondary: '#f000b8',\n accent: '#37cdbe',\n info: '#3abff8',\n success: '#36d399',\n warning: '#fbbd23',\n error: '#f87272',\n }\n }\n\n const style = getComputedStyle(document.documentElement)\n const getColor = (varName: string, fallback: string): string => {\n const value = style.getPropertyValue(varName).trim()\n return value ? colorToHex(value) : fallback\n }\n\n return {\n background: getColor('--color-base-100', '#ffffff'),\n foreground: getColor('--color-base-content', '#000000'),\n primary: getColor('--color-primary', '#6366f1'),\n primaryContent: getColor('--color-primary-content', '#ffffff'),\n secondary: getColor('--color-secondary', '#f000b8'),\n accent: getColor('--color-accent', '#37cdbe'),\n info: getColor('--color-info', '#3abff8'),\n success: getColor('--color-success', '#36d399'),\n warning: getColor('--color-warning', '#fbbd23'),\n error: getColor('--color-error', '#f87272'),\n }\n}\n\nfunction getSystemTheme(): 'light' | 'dark' {\n if (typeof window === 'undefined') return 'light'\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n}\n\nfunction getStoredTheme(key: string | false): string | null {\n if (!key || typeof window === 'undefined') return null\n try {\n return localStorage.getItem(key)\n } catch {\n return null\n }\n}\n\nfunction storeTheme(key: string | false, theme: string): void {\n if (!key || typeof window === 'undefined') return\n try {\n localStorage.setItem(key, theme)\n } catch {\n // Ignore storage errors\n }\n}\n\nexport function ThemeProvider({\n children,\n defaultTheme = 'system',\n storageKey = 'asterui-theme',\n lightTheme = 'light',\n darkTheme = 'dark',\n isDarkTheme,\n}: ThemeProviderProps) {\n // Initialize theme from storage or default\n const [theme, setThemeState] = useState<string>(() => {\n const stored = getStoredTheme(storageKey)\n return stored || defaultTheme\n })\n\n // Track system preference\n const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>(getSystemTheme)\n\n // Resolve the actual theme\n const resolvedTheme = useMemo(() => {\n if (theme === 'system') {\n return systemTheme === 'dark' ? darkTheme : lightTheme\n }\n return theme\n }, [theme, systemTheme, lightTheme, darkTheme])\n\n // Determine if dark\n const isDark = useMemo(() => {\n if (isDarkTheme) return isDarkTheme(resolvedTheme)\n return DARK_THEMES.has(resolvedTheme)\n }, [resolvedTheme, isDarkTheme])\n\n // Track colors (updated after theme applies)\n const [colors, setColors] = useState<ThemeColors>(getThemeColors)\n\n // Set theme function\n const setTheme = useCallback((newTheme: string) => {\n setThemeState(newTheme)\n storeTheme(storageKey, newTheme)\n }, [storageKey])\n\n // Apply theme to document\n useEffect(() => {\n if (typeof document === 'undefined') return\n document.documentElement.setAttribute('data-theme', resolvedTheme)\n\n // Update colors after a frame to ensure CSS has applied\n requestAnimationFrame(() => {\n setColors(getThemeColors())\n })\n }, [resolvedTheme])\n\n // Listen for system preference changes\n useEffect(() => {\n if (typeof window === 'undefined') return\n\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n const handleChange = (e: MediaQueryListEvent) => {\n setSystemTheme(e.matches ? 'dark' : 'light')\n }\n\n mediaQuery.addEventListener('change', handleChange)\n return () => mediaQuery.removeEventListener('change', handleChange)\n }, [])\n\n // Listen for storage changes (cross-tab sync)\n useEffect(() => {\n if (!storageKey || typeof window === 'undefined') return\n\n const handleStorage = (e: StorageEvent) => {\n if (e.key === storageKey && e.newValue) {\n setThemeState(e.newValue)\n }\n }\n\n window.addEventListener('storage', handleStorage)\n return () => window.removeEventListener('storage', handleStorage)\n }, [storageKey])\n\n const value = useMemo<ThemeContextValue>(() => ({\n theme,\n resolvedTheme,\n isDark,\n setTheme,\n colors,\n systemTheme,\n }), [theme, resolvedTheme, isDark, setTheme, colors, systemTheme])\n\n return (\n <ThemeContext.Provider value={value}>\n {children}\n </ThemeContext.Provider>\n )\n}\n\n/**\n * Hook to access theme context.\n * Must be used within a ThemeProvider.\n */\nexport function useThemeContext(): ThemeContextValue {\n const context = useContext(ThemeContext)\n if (!context) {\n throw new Error('useThemeContext must be used within a ThemeProvider')\n }\n return context\n}\n\n/**\n * Check if ThemeProvider is present in the tree.\n */\nexport function useHasThemeProvider(): boolean {\n return useContext(ThemeContext) !== undefined\n}\n"],"names":["DARK_THEMES","ThemeContext","createContext","colorToHex","color","canvas","ctx","r","g","b","getThemeColors","style","getColor","varName","fallback","value","getSystemTheme","getStoredTheme","key","storeTheme","theme","ThemeProvider","children","defaultTheme","storageKey","lightTheme","darkTheme","isDarkTheme","setThemeState","useState","systemTheme","setSystemTheme","resolvedTheme","useMemo","isDark","colors","setColors","setTheme","useCallback","newTheme","useEffect","mediaQuery","handleChange","e","handleStorage","jsx","useThemeContext","context","useContext","useHasThemeProvider"],"mappings":"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"}