@craftzbay/ui 0.10.0 → 0.11.1
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 +18 -12
- package/dist-lib/__tests__/helpers/color.d.ts +9 -0
- package/dist-lib/__tests__/helpers/theme.d.ts +18 -0
- package/dist-lib/components/ui/Accordion.js +18 -7
- package/dist-lib/components/ui/Accordion.js.map +1 -1
- package/dist-lib/components/ui/Alert.d.ts +16 -0
- package/dist-lib/components/ui/Alert.js +47 -34
- package/dist-lib/components/ui/Alert.js.map +1 -1
- package/dist-lib/components/ui/Avatar.d.ts +1 -1
- package/dist-lib/components/ui/Avatar.js +45 -47
- package/dist-lib/components/ui/Avatar.js.map +1 -1
- package/dist-lib/components/ui/Badge.d.ts +3 -1
- package/dist-lib/components/ui/Badge.js +24 -15
- package/dist-lib/components/ui/Badge.js.map +1 -1
- package/dist-lib/components/ui/Breadcrumbs.d.ts +2 -0
- package/dist-lib/components/ui/Breadcrumbs.js +46 -28
- package/dist-lib/components/ui/Breadcrumbs.js.map +1 -1
- package/dist-lib/components/ui/Button.d.ts +5 -3
- package/dist-lib/components/ui/Button.js +59 -45
- package/dist-lib/components/ui/Button.js.map +1 -1
- package/dist-lib/components/ui/Calendar.d.ts +1 -1
- package/dist-lib/components/ui/Calendar.js +87 -59
- package/dist-lib/components/ui/Calendar.js.map +1 -1
- package/dist-lib/components/ui/Card.d.ts +2 -0
- package/dist-lib/components/ui/Card.js +48 -67
- package/dist-lib/components/ui/Card.js.map +1 -1
- package/dist-lib/components/ui/Carousel.d.ts +6 -6
- package/dist-lib/components/ui/Carousel.js +133 -102
- package/dist-lib/components/ui/Carousel.js.map +1 -1
- package/dist-lib/components/ui/Chart.d.ts +49 -10
- package/dist-lib/components/ui/Chart.js +415 -181
- package/dist-lib/components/ui/Chart.js.map +1 -1
- package/dist-lib/components/ui/Checkbox.js +42 -28
- package/dist-lib/components/ui/Checkbox.js.map +1 -1
- package/dist-lib/components/ui/Combobox.d.ts +2 -0
- package/dist-lib/components/ui/Combobox.js +142 -111
- package/dist-lib/components/ui/Combobox.js.map +1 -1
- package/dist-lib/components/ui/CommandPalette.d.ts +6 -3
- package/dist-lib/components/ui/CommandPalette.js +92 -87
- package/dist-lib/components/ui/CommandPalette.js.map +1 -1
- package/dist-lib/components/ui/ContextMenu.d.ts +7 -2
- package/dist-lib/components/ui/ContextMenu.js +77 -62
- package/dist-lib/components/ui/ContextMenu.js.map +1 -1
- package/dist-lib/components/ui/DataGrid.d.ts +17 -4
- package/dist-lib/components/ui/DataGrid.js +65 -50
- package/dist-lib/components/ui/DataGrid.js.map +1 -1
- package/dist-lib/components/ui/DatePicker.js +118 -99
- package/dist-lib/components/ui/DatePicker.js.map +1 -1
- package/dist-lib/components/ui/DesignSystemProvider.d.ts +94 -40
- package/dist-lib/components/ui/DesignSystemProvider.js +111 -55
- package/dist-lib/components/ui/DesignSystemProvider.js.map +1 -1
- package/dist-lib/components/ui/Dialog.d.ts +18 -3
- package/dist-lib/components/ui/Dialog.js +105 -80
- package/dist-lib/components/ui/Dialog.js.map +1 -1
- package/dist-lib/components/ui/Drawer.d.ts +17 -2
- package/dist-lib/components/ui/Drawer.js +89 -55
- package/dist-lib/components/ui/Drawer.js.map +1 -1
- package/dist-lib/components/ui/DropdownMenu.js +14 -9
- package/dist-lib/components/ui/DropdownMenu.js.map +1 -1
- package/dist-lib/components/ui/EmptyState.d.ts +2 -0
- package/dist-lib/components/ui/EmptyState.js +30 -16
- package/dist-lib/components/ui/EmptyState.js.map +1 -1
- package/dist-lib/components/ui/ErrorState.d.ts +13 -2
- package/dist-lib/components/ui/ErrorState.js +44 -23
- package/dist-lib/components/ui/ErrorState.js.map +1 -1
- package/dist-lib/components/ui/FileUpload.js +59 -56
- package/dist-lib/components/ui/FileUpload.js.map +1 -1
- package/dist-lib/components/ui/Form.d.ts +15 -2
- package/dist-lib/components/ui/Form.js +97 -71
- package/dist-lib/components/ui/Form.js.map +1 -1
- package/dist-lib/components/ui/Icon.d.ts +1 -1
- package/dist-lib/components/ui/Icon.js +20 -14
- package/dist-lib/components/ui/Icon.js.map +1 -1
- package/dist-lib/components/ui/IconButton.d.ts +2 -2
- package/dist-lib/components/ui/IconButton.js +28 -20
- package/dist-lib/components/ui/IconButton.js.map +1 -1
- package/dist-lib/components/ui/Input.d.ts +3 -1
- package/dist-lib/components/ui/Input.js +102 -79
- package/dist-lib/components/ui/Input.js.map +1 -1
- package/dist-lib/components/ui/Kbd.js +1 -1
- package/dist-lib/components/ui/Kbd.js.map +1 -1
- package/dist-lib/components/ui/MultiSelect.js +121 -107
- package/dist-lib/components/ui/MultiSelect.js.map +1 -1
- package/dist-lib/components/ui/Pagination.js +67 -62
- package/dist-lib/components/ui/Pagination.js.map +1 -1
- package/dist-lib/components/ui/Popover.js +14 -14
- package/dist-lib/components/ui/Popover.js.map +1 -1
- package/dist-lib/components/ui/Progress.d.ts +3 -3
- package/dist-lib/components/ui/Progress.js +77 -72
- package/dist-lib/components/ui/Progress.js.map +1 -1
- package/dist-lib/components/ui/RadioGroup.d.ts +1 -1
- package/dist-lib/components/ui/RadioGroup.js +69 -52
- package/dist-lib/components/ui/RadioGroup.js.map +1 -1
- package/dist-lib/components/ui/RelativeTime.d.ts +19 -0
- package/dist-lib/components/ui/RelativeTime.js +31 -0
- package/dist-lib/components/ui/RelativeTime.js.map +1 -0
- package/dist-lib/components/ui/ScrollArea.d.ts +9 -1
- package/dist-lib/components/ui/ScrollArea.js +43 -27
- package/dist-lib/components/ui/ScrollArea.js.map +1 -1
- package/dist-lib/components/ui/Select.d.ts +6 -1
- package/dist-lib/components/ui/Select.js +79 -71
- package/dist-lib/components/ui/Select.js.map +1 -1
- package/dist-lib/components/ui/Separator.js +1 -1
- package/dist-lib/components/ui/Separator.js.map +1 -1
- package/dist-lib/components/ui/Sheet.d.ts +9 -0
- package/dist-lib/components/ui/Sheet.js +69 -46
- package/dist-lib/components/ui/Sheet.js.map +1 -1
- package/dist-lib/components/ui/Sidebar.d.ts +12 -9
- package/dist-lib/components/ui/Sidebar.js +152 -109
- package/dist-lib/components/ui/Sidebar.js.map +1 -1
- package/dist-lib/components/ui/Skeleton.d.ts +13 -2
- package/dist-lib/components/ui/Skeleton.js +15 -14
- package/dist-lib/components/ui/Skeleton.js.map +1 -1
- package/dist-lib/components/ui/Slider.js +41 -29
- package/dist-lib/components/ui/Slider.js.map +1 -1
- package/dist-lib/components/ui/Snackbar.js +23 -22
- package/dist-lib/components/ui/Snackbar.js.map +1 -1
- package/dist-lib/components/ui/Spinner.js +18 -6
- package/dist-lib/components/ui/Spinner.js.map +1 -1
- package/dist-lib/components/ui/Stepper.d.ts +6 -0
- package/dist-lib/components/ui/Stepper.js +71 -48
- package/dist-lib/components/ui/Stepper.js.map +1 -1
- package/dist-lib/components/ui/Switch.js +46 -43
- package/dist-lib/components/ui/Switch.js.map +1 -1
- package/dist-lib/components/ui/Table.d.ts +33 -5
- package/dist-lib/components/ui/Table.js +141 -119
- package/dist-lib/components/ui/Table.js.map +1 -1
- package/dist-lib/components/ui/Tabs.js +50 -48
- package/dist-lib/components/ui/Tabs.js.map +1 -1
- package/dist-lib/components/ui/TagInput.js +45 -45
- package/dist-lib/components/ui/TagInput.js.map +1 -1
- package/dist-lib/components/ui/Textarea.js +65 -54
- package/dist-lib/components/ui/Textarea.js.map +1 -1
- package/dist-lib/components/ui/Timeline.js +39 -35
- package/dist-lib/components/ui/Timeline.js.map +1 -1
- package/dist-lib/components/ui/Toast.js +44 -57
- package/dist-lib/components/ui/Toast.js.map +1 -1
- package/dist-lib/components/ui/Tooltip.d.ts +4 -1
- package/dist-lib/components/ui/Tooltip.js +24 -22
- package/dist-lib/components/ui/Tooltip.js.map +1 -1
- package/dist-lib/components/ui/TopNav.d.ts +7 -3
- package/dist-lib/components/ui/TopNav.js +39 -22
- package/dist-lib/components/ui/TopNav.js.map +1 -1
- package/dist-lib/components/ui/Tree.d.ts +15 -10
- package/dist-lib/components/ui/Tree.js +82 -79
- package/dist-lib/components/ui/Tree.js.map +1 -1
- package/dist-lib/hooks/use-debounce.d.ts +19 -0
- package/dist-lib/hooks/use-debounce.js +36 -0
- package/dist-lib/hooks/use-debounce.js.map +1 -0
- package/dist-lib/hooks/use-delayed-loading.d.ts +21 -0
- package/dist-lib/hooks/use-delayed-loading.js +20 -0
- package/dist-lib/hooks/use-delayed-loading.js.map +1 -0
- package/dist-lib/hooks/use-field-ids.d.ts +2 -1
- package/dist-lib/hooks/use-field-ids.js +9 -9
- package/dist-lib/hooks/use-field-ids.js.map +1 -1
- package/dist-lib/hooks/use-media-query.d.ts +3 -1
- package/dist-lib/hooks/use-media-query.js +16 -12
- package/dist-lib/hooks/use-media-query.js.map +1 -1
- package/dist-lib/hooks/use-modifier-key.d.ts +13 -0
- package/dist-lib/hooks/use-modifier-key.js +19 -0
- package/dist-lib/hooks/use-modifier-key.js.map +1 -0
- package/dist-lib/hooks/use-toast.d.ts +7 -2
- package/dist-lib/hooks/use-toast.js +61 -30
- package/dist-lib/hooks/use-toast.js.map +1 -1
- package/dist-lib/icons/index.d.ts +9 -0
- package/dist-lib/icons/index.js +148 -130
- package/dist-lib/icons/index.js.map +1 -1
- package/dist-lib/illustrations/index.d.ts +1 -0
- package/dist-lib/illustrations/index.js +15 -6
- package/dist-lib/illustrations/index.js.map +1 -1
- package/dist-lib/index.d.ts +6 -0
- package/dist-lib/index.js +280 -260
- package/dist-lib/index.js.map +1 -1
- package/dist-lib/lib/format.d.ts +65 -0
- package/dist-lib/lib/format.js +95 -0
- package/dist-lib/lib/format.js.map +1 -0
- package/dist-lib/lib/return-focus.d.ts +11 -0
- package/dist-lib/lib/return-focus.js +12 -0
- package/dist-lib/lib/return-focus.js.map +1 -0
- package/dist-lib/lib/strings.d.ts +62 -1
- package/dist-lib/lib/strings.js +75 -19
- package/dist-lib/lib/strings.js.map +1 -1
- package/dist-lib/lib/strings.mn.js +50 -5
- package/dist-lib/lib/strings.mn.js.map +1 -1
- package/dist-lib/styles.css +1 -1
- package/dist-lib/theme.css +141 -78
- package/package.json +11 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sidebar.js","sources":["../../../src/components/ui/Sidebar.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n forwardRef,\n useContext,\n useState,\n type AnchorHTMLAttributes,\n type ButtonHTMLAttributes,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { ChevronDown, ChevronsLeft, ChevronsRight } from '@/icons';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\nimport { Tooltip } from './Tooltip';\n\ninterface SidebarContextValue {\n collapsed: boolean;\n}\nconst SidebarContext = createContext<SidebarContextValue>({ collapsed: false });\n\n/** Read the parent Sidebar's collapsed state. Useful for brand/footer slots\n * that need to swap between full and compact rendering. */\nexport function useSidebar(): SidebarContextValue {\n return useContext(SidebarContext);\n}\n\nexport interface SidebarProps extends HTMLAttributes<HTMLElement> {\n /** Default state on first mount. Use `defaultCollapsed` for uncontrolled. */\n defaultCollapsed?: boolean;\n /** Controlled collapsed state. */\n collapsed?: boolean;\n /** Called when the user toggles via the rail or keyboard. */\n onCollapsedChange?: (next: boolean) => void;\n /** Header slot pinned to the top (brand, workspace switcher, etc.). */\n header?: ReactNode;\n /** Footer slot pinned to the bottom (user card, version, etc.). */\n footer?: ReactNode;\n}\n\n/**\n * App-level navigation rail. Holds `SidebarSection` → `SidebarItem` lists,\n * and optionally a `footer`. Supports collapse to icon-only on desktop.\n * Below `md` the rail is hidden — pair it with a Sheet / Drawer for mobile.\n *\n * @example\n * <Sidebar footer={<UserCard />}>\n * <SidebarSection label=\"Workspace\">\n * <SidebarItem icon={<Home />} active>Home</SidebarItem>\n * <SidebarItem icon={<Folder />}>Projects</SidebarItem>\n * </SidebarSection>\n * <SidebarSection label=\"Account\">\n * <SidebarItem icon={<Settings />}>Settings</SidebarItem>\n * </SidebarSection>\n * </Sidebar>\n *\n * @do Use 1–3 sections. More than that signals the IA needs restructuring.\n * @dont Hide critical navigation under the collapsed state — keep icons\n * always visible with tooltips.\n */\nexport const Sidebar = forwardRef<HTMLElement, SidebarProps>(function Sidebar(\n {\n defaultCollapsed = false,\n collapsed: controlled,\n onCollapsedChange,\n header,\n footer,\n className,\n children,\n ...props\n },\n ref,\n) {\n const strings = useStrings();\n const [internal, setInternal] = useState(defaultCollapsed);\n const collapsed = controlled ?? internal;\n const setCollapsed = (next: boolean) => {\n if (controlled === undefined) setInternal(next);\n onCollapsedChange?.(next);\n };\n\n return (\n <SidebarContext.Provider value={{ collapsed }}>\n <aside\n ref={ref}\n aria-label={props['aria-label'] ?? strings.sidebar.label}\n className={cn(\n 'border-border bg-background-subtle sticky top-0 hidden h-screen shrink-0 flex-col gap-2 border-r md:flex',\n 'transition-[width] duration-[var(--duration-base)] ease-[var(--ease-out)]',\n collapsed ? 'w-14' : 'w-60',\n className,\n )}\n {...props}\n >\n {header && (\n <div\n className={cn(\n 'border-border flex h-14 shrink-0 items-center overflow-hidden border-b',\n collapsed ? 'justify-center px-2' : 'px-3',\n )}\n >\n {header}\n </div>\n )}\n <div className=\"flex-1 overflow-y-auto py-3\">{children}</div>\n {footer && <div className=\"border-border border-t p-2\">{footer}</div>}\n <button\n type=\"button\"\n onClick={() => setCollapsed(!collapsed)}\n aria-label={collapsed ? strings.sidebar.expand : strings.sidebar.collapse}\n aria-expanded={!collapsed}\n className={cn(\n 'text-foreground-subtle mx-2 mb-2 flex h-8 items-center gap-2 rounded-md px-2',\n 'hover:bg-background-muted hover:text-foreground outline-none',\n 'focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2',\n 'transition-colors duration-[var(--duration-fast)]',\n )}\n >\n {collapsed ? (\n <ChevronsRight className=\"size-4\" aria-hidden />\n ) : (\n <ChevronsLeft className=\"size-4\" aria-hidden />\n )}\n {!collapsed && <span className=\"text-xs font-medium\">{strings.sidebar.collapseShort}</span>}\n </button>\n </aside>\n </SidebarContext.Provider>\n );\n});\nSidebar.displayName = 'Sidebar';\n\nexport interface SidebarSectionProps extends HTMLAttributes<HTMLDivElement> {\n /** Visible section header. Hidden when collapsed. */\n label?: ReactNode;\n}\n\nexport function SidebarSection({ label, className, children, ...props }: SidebarSectionProps) {\n const { collapsed } = useContext(SidebarContext);\n return (\n <div className={cn('mb-3', className)} {...props}>\n {label && !collapsed && (\n <div className=\"text-foreground-subtle px-4 pt-2 pb-1 text-xs font-medium tracking-wide uppercase\">\n {label}\n </div>\n )}\n <ul className=\"flex flex-col gap-px\">{children}</ul>\n </div>\n );\n}\nSidebarSection.displayName = 'SidebarSection';\n\ninterface SidebarItemBaseProps {\n /** Lucide icon shown to the left. */\n icon?: ReactNode;\n /** Mark as the current page. */\n active?: boolean;\n /** Trailing badge / counter slot. */\n trailing?: ReactNode;\n /** Render a sub-item indented under a parent. */\n sub?: boolean;\n /**\n * Tooltip text shown while the sidebar is collapsed. Defaults to `children`\n * when that is a plain string.\n */\n tooltip?: ReactNode;\n}\n\ntype SidebarItemAnchorProps = SidebarItemBaseProps &\n AnchorHTMLAttributes<HTMLAnchorElement> & {\n /** Renders an `<a>`. Omit to render a `<button>`. */\n href: string;\n };\n\ntype SidebarItemButtonProps = SidebarItemBaseProps &\n ButtonHTMLAttributes<HTMLButtonElement> & {\n href?: undefined;\n };\n\n/** Discriminated on `href`: with it the item is an anchor, without it a button. */\nexport type SidebarItemProps = SidebarItemAnchorProps | SidebarItemButtonProps;\n\nexport function SidebarItem(props: SidebarItemProps) {\n const { icon, active, trailing, sub, tooltip, className, children, ...rest } = props;\n const { collapsed } = useContext(SidebarContext);\n\n const classes = cn(\n 'flex h-8 w-full items-center gap-2 rounded-md px-2 text-sm outline-none',\n 'transition-colors duration-[var(--duration-fast)]',\n 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n active\n ? 'bg-background-muted text-foreground font-medium'\n : 'text-foreground-muted hover:bg-background-muted hover:text-foreground',\n sub && !collapsed && 'ml-6',\n collapsed && 'justify-center',\n className,\n );\n\n const labelTitle = typeof children === 'string' ? children : undefined;\n\n const content = (\n <>\n {icon && <span className=\"flex shrink-0 items-center [&_svg]:size-4\">{icon}</span>}\n {collapsed ? (\n <span className=\"sr-only\">{children}</span>\n ) : (\n <span className=\"flex-1 truncate text-left\" title={labelTitle}>\n {children}\n </span>\n )}\n {!collapsed && trailing && <span className=\"ml-auto\">{trailing}</span>}\n </>\n );\n\n let element: ReactNode;\n if (rest.href !== undefined) {\n const { href, ...anchorProps } = rest as Omit<\n SidebarItemAnchorProps,\n keyof SidebarItemBaseProps\n >;\n element = (\n <a\n href={href}\n aria-current={active ? 'page' : undefined}\n className={classes}\n {...anchorProps}\n >\n {content}\n </a>\n );\n } else {\n const {\n href: _href,\n type,\n ...buttonProps\n } = rest as Omit<SidebarItemButtonProps, keyof SidebarItemBaseProps>;\n element = (\n <button\n type={type ?? 'button'}\n aria-current={active ? 'page' : undefined}\n className={classes}\n {...buttonProps}\n >\n {content}\n </button>\n );\n }\n\n const tip = tooltip ?? labelTitle;\n return (\n <li className=\"px-2\">\n {collapsed && tip ? (\n <Tooltip label={tip} side=\"right\">\n {element}\n </Tooltip>\n ) : (\n element\n )}\n </li>\n );\n}\nSidebarItem.displayName = 'SidebarItem';\n\n/** Lightweight collapsible sub-section inside the sidebar. */\nexport interface SidebarGroupProps {\n icon?: ReactNode;\n label: ReactNode;\n defaultOpen?: boolean;\n children: ReactNode;\n}\n\nexport function SidebarGroup({ icon, label, defaultOpen = true, children }: SidebarGroupProps) {\n const { collapsed } = useContext(SidebarContext);\n const [open, setOpen] = useState(defaultOpen);\n if (collapsed) return <>{children}</>;\n return (\n <li>\n <button\n type=\"button\"\n onClick={() => setOpen((o) => !o)}\n aria-expanded={open}\n className={cn(\n 'text-foreground-muted mx-2 flex h-8 w-[calc(100%-1rem)] items-center gap-2 rounded-md px-2 text-sm outline-none',\n 'transition-colors duration-[var(--duration-fast)]',\n 'hover:bg-background-muted hover:text-foreground',\n 'focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2',\n )}\n >\n {icon && <span className=\"flex shrink-0 items-center [&_svg]:size-4\">{icon}</span>}\n <span className=\"flex-1 text-left\">{label}</span>\n <ChevronDown\n className={cn('size-3.5 transition-transform', open && 'rotate-180')}\n aria-hidden\n />\n </button>\n {open && <ul className=\"flex flex-col gap-px\">{children}</ul>}\n </li>\n );\n}\nSidebarGroup.displayName = 'SidebarGroup';\n"],"names":["SidebarContext","createContext","useSidebar","useContext","Sidebar","forwardRef","defaultCollapsed","controlled","onCollapsedChange","header","footer","className","children","props","ref","strings","useStrings","internal","setInternal","useState","collapsed","setCollapsed","next","jsxs","cn","jsx","ChevronsRight","ChevronsLeft","SidebarSection","label","SidebarItem","icon","active","trailing","sub","tooltip","rest","classes","labelTitle","content","Fragment","element","href","anchorProps","_href","type","buttonProps","tip","Tooltip","SidebarGroup","defaultOpen","open","setOpen","o","ChevronDown"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Sidebar.js","sources":["../../../src/components/ui/Sidebar.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n forwardRef,\n useContext,\n useId,\n useState,\n type AnchorHTMLAttributes,\n type ButtonHTMLAttributes,\n type HTMLAttributes,\n type ReactNode,\n type Ref,\n} from 'react';\nimport { Slot } from '@radix-ui/react-slot';\nimport { ChevronDown, ChevronsLeft, ChevronsRight } from '@/icons';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\nimport { Tooltip } from './Tooltip';\n\ninterface SidebarContextValue {\n collapsed: boolean;\n}\nconst SidebarContext = createContext<SidebarContextValue>({ collapsed: false });\n\n/** Read the parent Sidebar's collapsed state. Useful for brand/footer slots\n * that need to swap between full and compact rendering. */\nexport function useSidebar(): SidebarContextValue {\n return useContext(SidebarContext);\n}\n\nexport interface SidebarProps extends HTMLAttributes<HTMLElement> {\n /** Default state on first mount. Use `defaultCollapsed` for uncontrolled. */\n defaultCollapsed?: boolean;\n /** Controlled collapsed state. */\n collapsed?: boolean;\n /** Called when the user toggles via the rail or keyboard. */\n onCollapsedChange?: (next: boolean) => void;\n /** Header slot pinned to the top (brand, workspace switcher, etc.). */\n header?: ReactNode;\n /** Footer slot pinned to the bottom (user card, version, etc.). */\n footer?: ReactNode;\n}\n\n/**\n * App-level navigation rail. Holds `SidebarSection` → `SidebarItem` lists,\n * and optionally a `footer`. Supports collapse to icon-only on desktop.\n * Below `md` the rail is hidden — pair it with a Sheet / Drawer for mobile.\n *\n * @example\n * <Sidebar footer={<UserCard />}>\n * <SidebarSection label=\"Workspace\">\n * <SidebarItem icon={<Home />} active>Home</SidebarItem>\n * <SidebarItem icon={<Folder />}>Projects</SidebarItem>\n * </SidebarSection>\n * <SidebarSection label=\"Account\">\n * <SidebarItem icon={<Settings />}>Settings</SidebarItem>\n * </SidebarSection>\n * </Sidebar>\n *\n * @do Use 1–3 sections. More than that signals the IA needs restructuring.\n * @dont Hide critical navigation under the collapsed state — keep icons\n * always visible with tooltips.\n */\nexport const Sidebar = forwardRef<HTMLElement, SidebarProps>(function Sidebar(\n {\n defaultCollapsed = false,\n collapsed: controlled,\n onCollapsedChange,\n header,\n footer,\n className,\n children,\n 'aria-label': ariaLabel,\n ...props\n },\n ref,\n) {\n const strings = useStrings();\n const [internal, setInternal] = useState(defaultCollapsed);\n const collapsed = controlled ?? internal;\n const setCollapsed = (next: boolean) => {\n if (controlled === undefined) setInternal(next);\n onCollapsedChange?.(next);\n };\n\n return (\n <SidebarContext.Provider value={{ collapsed }}>\n <nav\n ref={ref}\n aria-label={ariaLabel ?? strings.sidebar.label}\n className={cn(\n 'border-border bg-background-subtle sticky top-0 hidden h-dvh shrink-0 flex-col gap-2 border-r md:flex',\n // Deliberate motion exception: the rail's own width transitions (layout).\n // Only the rail and the main column it flanks move; no content inside reflows.\n 'transition-[width] duration-[var(--duration-base)] ease-[var(--ease-out)] motion-reduce:transition-none',\n collapsed ? 'w-14' : 'w-60',\n className,\n )}\n {...props}\n >\n {header && (\n <div\n className={cn(\n 'border-border flex h-14 shrink-0 items-center overflow-hidden border-b',\n collapsed ? 'justify-center px-2' : 'px-3',\n )}\n >\n {header}\n </div>\n )}\n <div className=\"flex-1 overflow-y-auto py-3\">{children}</div>\n {footer && <div className=\"border-border border-t p-2\">{footer}</div>}\n <button\n type=\"button\"\n onClick={() => setCollapsed(!collapsed)}\n aria-label={collapsed ? strings.sidebar.expand : strings.sidebar.collapse}\n aria-expanded={!collapsed}\n className={cn(\n 'text-foreground-subtle mx-2 mb-2 flex h-8 items-center gap-2 rounded-md px-2',\n 'hover:bg-background-muted hover:text-foreground outline-none',\n 'focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2',\n 'transition-colors duration-[var(--duration-fast)]',\n )}\n >\n {collapsed ? (\n <ChevronsRight className=\"size-4\" aria-hidden />\n ) : (\n <ChevronsLeft className=\"size-4\" aria-hidden />\n )}\n {!collapsed && (\n <span className=\"text-xs font-medium\">{strings.sidebar.collapseShort}</span>\n )}\n </button>\n </nav>\n </SidebarContext.Provider>\n );\n});\nSidebar.displayName = 'Sidebar';\n\nexport interface SidebarSectionProps extends HTMLAttributes<HTMLDivElement> {\n /** Visible section header. Hidden when collapsed. */\n label?: ReactNode;\n}\n\nexport function SidebarSection({ label, className, children, ...props }: SidebarSectionProps) {\n const { collapsed } = useContext(SidebarContext);\n const labelId = useId();\n return (\n <div className={cn('mb-3', className)} {...props}>\n {label && (\n // Kept in the DOM (sr-only) while collapsed so the list stays labelled.\n <div\n id={labelId}\n className={cn(\n 'text-foreground-subtle px-4 pt-2 pb-1 text-xs font-medium',\n collapsed && 'sr-only',\n )}\n >\n {label}\n </div>\n )}\n <ul aria-labelledby={label ? labelId : undefined} className=\"flex flex-col gap-px\">\n {children}\n </ul>\n </div>\n );\n}\nSidebarSection.displayName = 'SidebarSection';\n\ninterface SidebarItemBaseProps {\n /** Lucide icon shown to the left. */\n icon?: ReactNode;\n /** Mark as the current page. */\n active?: boolean;\n /** Trailing badge / counter slot. */\n trailing?: ReactNode;\n /** Render a sub-item indented under a parent. */\n sub?: boolean;\n /**\n * Tooltip text shown while the sidebar is collapsed. Defaults to `children`\n * when that is a plain string.\n */\n tooltip?: ReactNode;\n /**\n * Render the child element (router `<Link>`) instead of `<a>`/`<button>`.\n * The child receives className, aria-current and the item content.\n */\n asChild?: boolean;\n}\n\ntype SidebarItemAnchorProps = SidebarItemBaseProps &\n AnchorHTMLAttributes<HTMLAnchorElement> & {\n /** Renders an `<a>`. Omit to render a `<button>`. */\n href: string;\n };\n\ntype SidebarItemButtonProps = SidebarItemBaseProps &\n ButtonHTMLAttributes<HTMLButtonElement> & {\n href?: undefined;\n };\n\n/** Discriminated on `href`: with it the item is an anchor, without it a button. */\nexport type SidebarItemProps = SidebarItemAnchorProps | SidebarItemButtonProps;\n\nexport const SidebarItem = forwardRef<HTMLElement, SidebarItemProps>(\n function SidebarItem(props, ref) {\n const { icon, active, trailing, sub, tooltip, asChild, className, children, ...rest } = props;\n const { collapsed } = useContext(SidebarContext);\n\n const classes = cn(\n 'flex h-8 w-full items-center gap-2 rounded-md px-2 text-sm outline-none',\n 'transition-colors duration-[var(--duration-fast)]',\n 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n active\n ? 'bg-background-muted text-foreground font-medium'\n : 'text-foreground-muted hover:bg-background-muted hover:text-foreground',\n sub && !collapsed && 'ml-6',\n collapsed && 'justify-center',\n className,\n );\n\n const labelTitle = typeof children === 'string' ? children : undefined;\n const tip = tooltip ?? labelTitle;\n // Collapsed: keep an sr-only label so the control always has a name —\n // the Tooltip only wires aria-describedby while it is open.\n const showTooltip = collapsed && !!tip;\n\n const content = (\n <>\n {icon && <span className=\"flex shrink-0 items-center [&_svg]:size-4\">{icon}</span>}\n {collapsed ? (\n <span className=\"sr-only\">{children}</span>\n ) : (\n <span className=\"flex-1 truncate text-left\" title={labelTitle}>\n {children}\n </span>\n )}\n {!collapsed && trailing && <span className=\"ml-auto\">{trailing}</span>}\n </>\n );\n\n let element: ReactNode;\n if (asChild) {\n const { href: _href, type: _type, ...slotProps } = rest as Record<string, unknown>;\n element = (\n <Slot\n ref={ref}\n aria-current={active ? 'page' : undefined}\n className={classes}\n {...(slotProps as HTMLAttributes<HTMLElement>)}\n >\n {children && typeof children === 'object' && 'props' in (children as object)\n ? children\n : null}\n </Slot>\n );\n } else if (rest.href !== undefined) {\n const { href, ...anchorProps } = rest as Omit<\n SidebarItemAnchorProps,\n keyof SidebarItemBaseProps\n >;\n element = (\n <a\n ref={ref as Ref<HTMLAnchorElement>}\n href={href}\n aria-current={active ? 'page' : undefined}\n className={classes}\n {...anchorProps}\n >\n {content}\n </a>\n );\n } else {\n const {\n href: _href,\n type,\n ...buttonProps\n } = rest as Omit<SidebarItemButtonProps, keyof SidebarItemBaseProps>;\n element = (\n <button\n ref={ref as Ref<HTMLButtonElement>}\n type={type ?? 'button'}\n aria-current={active ? 'page' : undefined}\n className={classes}\n {...buttonProps}\n >\n {content}\n </button>\n );\n }\n\n return (\n <li className=\"px-2\">\n {showTooltip ? (\n <Tooltip label={tip} side=\"right\">\n {element}\n </Tooltip>\n ) : (\n element\n )}\n </li>\n );\n },\n);\nSidebarItem.displayName = 'SidebarItem';\n\n/** Lightweight collapsible sub-section inside the sidebar. */\nexport interface SidebarGroupProps extends Omit<HTMLAttributes<HTMLLIElement>, 'children'> {\n icon?: ReactNode;\n label: ReactNode;\n /** Initial open state (uncontrolled). */\n defaultOpen?: boolean;\n /** Controlled open state. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n children: ReactNode;\n}\n\nexport const SidebarGroup = forwardRef<HTMLLIElement, SidebarGroupProps>(function SidebarGroup(\n {\n icon,\n label,\n defaultOpen = true,\n open: controlled,\n onOpenChange,\n className,\n children,\n ...props\n },\n ref,\n) {\n const { collapsed } = useContext(SidebarContext);\n const [internal, setInternal] = useState(defaultOpen);\n const open = controlled ?? internal;\n const listId = useId();\n const toggle = () => {\n const next = !open;\n if (controlled === undefined) setInternal(next);\n onOpenChange?.(next);\n };\n if (collapsed) return <>{children}</>;\n return (\n <li ref={ref} className={className} {...props}>\n <button\n type=\"button\"\n onClick={toggle}\n aria-controls={open ? listId : undefined}\n aria-expanded={open}\n className={cn(\n 'text-foreground-muted mx-2 flex h-8 w-[calc(100%-1rem)] items-center gap-2 rounded-md px-2 text-sm outline-none',\n 'transition-colors duration-[var(--duration-fast)]',\n 'hover:bg-background-muted hover:text-foreground',\n 'focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2',\n )}\n >\n {icon && <span className=\"flex shrink-0 items-center [&_svg]:size-4\">{icon}</span>}\n <span className=\"flex-1 text-left\">{label}</span>\n <ChevronDown\n className={cn('size-3.5 transition-transform', open && 'rotate-180')}\n aria-hidden\n />\n </button>\n {open && (\n <ul id={listId} className=\"flex flex-col gap-px\">\n {children}\n </ul>\n )}\n </li>\n );\n});\nSidebarGroup.displayName = 'SidebarGroup';\n"],"names":["SidebarContext","createContext","useSidebar","useContext","Sidebar","forwardRef","defaultCollapsed","controlled","onCollapsedChange","header","footer","className","children","ariaLabel","props","ref","strings","useStrings","internal","setInternal","useState","collapsed","setCollapsed","next","jsxs","cn","jsx","ChevronsRight","ChevronsLeft","SidebarSection","label","labelId","useId","SidebarItem","icon","active","trailing","sub","tooltip","asChild","rest","classes","labelTitle","tip","showTooltip","content","Fragment","element","_href","_type","slotProps","Slot","href","anchorProps","type","buttonProps","Tooltip","SidebarGroup","defaultOpen","onOpenChange","open","listId","toggle","ChevronDown"],"mappings":"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"}
|
|
@@ -2,9 +2,19 @@ import { HTMLAttributes } from 'react';
|
|
|
2
2
|
export interface SkeletonProps extends HTMLAttributes<HTMLDivElement> {
|
|
3
3
|
/** Predefined silhouette presets. */
|
|
4
4
|
variant?: 'text' | 'circle' | 'card' | 'avatar';
|
|
5
|
+
/**
|
|
6
|
+
* Render nothing until this many ms have elapsed, so sub-300ms loads never
|
|
7
|
+
* flash a placeholder. Default 300. Pass 0 to render immediately.
|
|
8
|
+
*/
|
|
9
|
+
delay?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Once shown, keep the placeholder mounted for at least this many ms so it
|
|
12
|
+
* never flickers. Default 500.
|
|
13
|
+
*/
|
|
14
|
+
minVisible?: number;
|
|
5
15
|
}
|
|
6
16
|
/**
|
|
7
|
-
* Animated placeholder. Use `
|
|
17
|
+
* Animated placeholder. Use `variant="text"` / `variant="avatar"` for common
|
|
8
18
|
* shapes; pass `className` directly for one-off sizes.
|
|
9
19
|
*
|
|
10
20
|
* @example List item
|
|
@@ -21,6 +31,7 @@ export interface SkeletonProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
21
31
|
*
|
|
22
32
|
* @do Match the silhouette of the eventual content so the layout doesn't
|
|
23
33
|
* shift when data arrives.
|
|
24
|
-
* @dont
|
|
34
|
+
* @dont Pass `delay={0}` for data that usually arrives in under 300ms — the
|
|
35
|
+
* default delay exists so fast loads never flash a placeholder.
|
|
25
36
|
*/
|
|
26
37
|
export declare const Skeleton: import('react').ForwardRefExoticComponent<SkeletonProps & import('react').RefAttributes<HTMLDivElement>>;
|
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as
|
|
3
|
-
import { forwardRef as
|
|
4
|
-
import { cn as
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import { jsx as t } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef as l } from "react";
|
|
4
|
+
import { cn as s } from "../../lib/utils.js";
|
|
5
|
+
import { useDelayedLoading as m } from "../../hooks/use-delayed-loading.js";
|
|
6
|
+
const c = l(function({ className: o, variant: e, delay: r = 300, minVisible: d = 500, ...u }, n) {
|
|
7
|
+
return m(r, { minVisible: d }) ? /* @__PURE__ */ t(
|
|
7
8
|
"div",
|
|
8
9
|
{
|
|
9
|
-
ref:
|
|
10
|
+
ref: n,
|
|
10
11
|
"aria-hidden": !0,
|
|
11
|
-
className:
|
|
12
|
-
"
|
|
12
|
+
className: s(
|
|
13
|
+
"bg-background-muted animate-pulse",
|
|
13
14
|
e === "text" && "h-3 rounded-sm",
|
|
14
|
-
e === "circle" && "rounded-full
|
|
15
|
+
e === "circle" && "aspect-square rounded-full",
|
|
15
16
|
e === "avatar" && "size-8 rounded-full",
|
|
16
17
|
e === "card" && "rounded-lg",
|
|
17
18
|
!e && "rounded-md",
|
|
18
|
-
|
|
19
|
+
o
|
|
19
20
|
),
|
|
20
|
-
...
|
|
21
|
+
...u
|
|
21
22
|
}
|
|
22
|
-
);
|
|
23
|
+
) : null;
|
|
23
24
|
});
|
|
24
|
-
|
|
25
|
+
c.displayName = "Skeleton";
|
|
25
26
|
export {
|
|
26
|
-
|
|
27
|
+
c as Skeleton
|
|
27
28
|
};
|
|
28
29
|
//# sourceMappingURL=Skeleton.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Skeleton.js","sources":["../../../src/components/ui/Skeleton.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type HTMLAttributes } from 'react';\nimport { cn } from '@/lib/utils';\n\nexport interface SkeletonProps extends HTMLAttributes<HTMLDivElement> {\n /** Predefined silhouette presets. */\n variant?: 'text' | 'circle' | 'card' | 'avatar';\n}\n\n/**\n * Animated placeholder. Use `
|
|
1
|
+
{"version":3,"file":"Skeleton.js","sources":["../../../src/components/ui/Skeleton.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type HTMLAttributes } from 'react';\nimport { cn } from '@/lib/utils';\nimport { useDelayedLoading } from '@/hooks/use-delayed-loading';\n\nexport interface SkeletonProps extends HTMLAttributes<HTMLDivElement> {\n /** Predefined silhouette presets. */\n variant?: 'text' | 'circle' | 'card' | 'avatar';\n /**\n * Render nothing until this many ms have elapsed, so sub-300ms loads never\n * flash a placeholder. Default 300. Pass 0 to render immediately.\n */\n delay?: number;\n /**\n * Once shown, keep the placeholder mounted for at least this many ms so it\n * never flickers. Default 500.\n */\n minVisible?: number;\n}\n\n/**\n * Animated placeholder. Use `variant=\"text\"` / `variant=\"avatar\"` for common\n * shapes; pass `className` directly for one-off sizes.\n *\n * @example List item\n * <div className=\"flex items-center gap-3\">\n * <Skeleton variant=\"avatar\" />\n * <div className=\"space-y-1.5 flex-1\">\n * <Skeleton variant=\"text\" className=\"w-1/3\" />\n * <Skeleton variant=\"text\" className=\"w-1/2\" />\n * </div>\n * </div>\n *\n * @example Card\n * <Skeleton variant=\"card\" className=\"h-32\" />\n *\n * @do Match the silhouette of the eventual content so the layout doesn't\n * shift when data arrives.\n * @dont Pass `delay={0}` for data that usually arrives in under 300ms — the\n * default delay exists so fast loads never flash a placeholder.\n */\nexport const Skeleton = forwardRef<HTMLDivElement, SkeletonProps>(function Skeleton(\n { className, variant, delay = 300, minVisible = 500, ...props },\n ref,\n) {\n const show = useDelayedLoading(delay, { minVisible });\n if (!show) return null;\n return (\n <div\n ref={ref}\n aria-hidden\n className={cn(\n 'bg-background-muted animate-pulse',\n variant === 'text' && 'h-3 rounded-sm',\n variant === 'circle' && 'aspect-square rounded-full',\n variant === 'avatar' && 'size-8 rounded-full',\n variant === 'card' && 'rounded-lg',\n !variant && 'rounded-md',\n className,\n )}\n {...props}\n />\n );\n});\nSkeleton.displayName = 'Skeleton';\n"],"names":["Skeleton","forwardRef","className","variant","delay","minVisible","props","ref","useDelayedLoading","jsx","cn"],"mappings":";;;;;AA0CO,MAAMA,IAAWC,EAA0C,SAChE,EAAE,WAAAC,GAAW,SAAAC,GAAS,OAAAC,IAAQ,KAAK,YAAAC,IAAa,KAAK,GAAGC,EAAA,GACxDC,GACA;AAEA,SADaC,EAAkBJ,GAAO,EAAE,YAAAC,GAAY,IAGlD,gBAAAI;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAF;AAAA,MACA,eAAW;AAAA,MACX,WAAWG;AAAA,QACT;AAAA,QACAP,MAAY,UAAU;AAAA,QACtBA,MAAY,YAAY;AAAA,QACxBA,MAAY,YAAY;AAAA,QACxBA,MAAY,UAAU;AAAA,QACtB,CAACA,KAAW;AAAA,QACZD;AAAA,MAAA;AAAA,MAED,GAAGI;AAAA,IAAA;AAAA,EAAA,IAdU;AAiBpB,CAAC;AACDN,EAAS,cAAc;"}
|
|
@@ -1,43 +1,55 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsxs as
|
|
3
|
-
import { forwardRef as
|
|
4
|
-
import * as
|
|
5
|
-
import { cn as
|
|
6
|
-
import { useStrings as
|
|
7
|
-
const
|
|
8
|
-
function({
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
2
|
+
import { jsxs as u, jsx as i } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef as k, useState as R, useId as j } from "react";
|
|
4
|
+
import * as s from "@radix-ui/react-slider";
|
|
5
|
+
import { cn as h } from "../../lib/utils.js";
|
|
6
|
+
import { useStrings as I } from "../../hooks/use-strings.js";
|
|
7
|
+
const z = k(
|
|
8
|
+
function({
|
|
9
|
+
className: N,
|
|
10
|
+
label: e,
|
|
11
|
+
showValue: b,
|
|
12
|
+
formatValue: o = (l) => String(l),
|
|
13
|
+
value: n,
|
|
14
|
+
defaultValue: g,
|
|
15
|
+
onValueChange: a,
|
|
16
|
+
...v
|
|
17
|
+
}, y) {
|
|
18
|
+
const [l, S] = R(g ?? [0]), p = n !== void 0, r = p ? n : l, d = r.length > 1, c = j(), f = I(), m = typeof e == "string" ? e : void 0, w = (t) => d ? t === 0 ? f.slider.minimum : f.slider.maximum : m ?? v["aria-label"] ?? f.slider.value;
|
|
19
|
+
return /* @__PURE__ */ u("div", { className: h("flex flex-col gap-2", N), children: [
|
|
20
|
+
(e || b) && /* @__PURE__ */ u("div", { className: "flex items-center justify-between", children: [
|
|
21
|
+
e && /* @__PURE__ */ i("span", { id: c, className: "text-foreground text-sm font-medium", children: e }),
|
|
22
|
+
b && /* @__PURE__ */ i("span", { className: "tabular text-foreground-muted font-mono text-xs", children: d ? `${o(r[0])} – ${o(r[1])}` : o(r[0]) })
|
|
14
23
|
] }),
|
|
15
|
-
/* @__PURE__ */
|
|
16
|
-
|
|
24
|
+
/* @__PURE__ */ u(
|
|
25
|
+
s.Root,
|
|
17
26
|
{
|
|
18
|
-
ref:
|
|
19
|
-
value:
|
|
20
|
-
defaultValue:
|
|
27
|
+
ref: y,
|
|
28
|
+
value: n,
|
|
29
|
+
defaultValue: g,
|
|
30
|
+
onValueChange: (t) => {
|
|
31
|
+
p || S(t), a == null || a(t);
|
|
32
|
+
},
|
|
21
33
|
className: "relative flex w-full touch-none items-center py-3.5 select-none",
|
|
22
|
-
"aria-labelledby": e && !
|
|
23
|
-
...
|
|
34
|
+
"aria-labelledby": e && !m ? c : void 0,
|
|
35
|
+
...v,
|
|
24
36
|
children: [
|
|
25
|
-
/* @__PURE__ */
|
|
26
|
-
r.map((
|
|
27
|
-
|
|
37
|
+
/* @__PURE__ */ i(s.Track, { className: "bg-background-muted relative h-1 w-full grow overflow-hidden rounded-full", children: /* @__PURE__ */ i(s.Range, { className: "bg-accent absolute h-full" }) }),
|
|
38
|
+
r.map((t, x) => /* @__PURE__ */ i(
|
|
39
|
+
s.Thumb,
|
|
28
40
|
{
|
|
29
|
-
className:
|
|
41
|
+
className: h(
|
|
30
42
|
"border-accent bg-card relative block size-4 rounded-full border-2 shadow-sm",
|
|
31
43
|
// 44px touch target without growing the visible thumb.
|
|
32
44
|
'before:absolute before:top-1/2 before:left-1/2 before:size-11 before:-translate-x-1/2 before:-translate-y-1/2 before:rounded-full before:content-[""]',
|
|
33
45
|
"focus-visible:ring-ring focus-visible:ring-offset-background outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
|
|
34
46
|
"transition-transform duration-[var(--duration-fast)] ease-[var(--ease-out)]",
|
|
35
|
-
"hover:scale-110 disabled:pointer-events-none disabled:opacity-50"
|
|
47
|
+
"hover:scale-110 data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
|
|
36
48
|
),
|
|
37
|
-
"aria-label": x
|
|
38
|
-
"aria-labelledby": !
|
|
49
|
+
"aria-label": w(x),
|
|
50
|
+
"aria-labelledby": !d && e && !m ? c : void 0
|
|
39
51
|
},
|
|
40
|
-
|
|
52
|
+
x
|
|
41
53
|
))
|
|
42
54
|
]
|
|
43
55
|
}
|
|
@@ -45,8 +57,8 @@ const S = N(
|
|
|
45
57
|
] });
|
|
46
58
|
}
|
|
47
59
|
);
|
|
48
|
-
|
|
60
|
+
z.displayName = "Slider";
|
|
49
61
|
export {
|
|
50
|
-
|
|
62
|
+
z as Slider
|
|
51
63
|
};
|
|
52
64
|
//# sourceMappingURL=Slider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Slider.js","sources":["../../../src/components/ui/Slider.tsx"],"sourcesContent":["'use client';\n\nimport {\n forwardRef,\n useId,\n type ComponentPropsWithoutRef,\n type ElementRef,\n type ReactNode,\n} from 'react';\nimport * as SliderPrimitive from '@radix-ui/react-slider';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\n\nexport interface SliderProps extends Omit<\n ComponentPropsWithoutRef<typeof SliderPrimitive.Root>,\n 'value' | 'defaultValue'\n> {\n /**\n * Controlled value. Pass `[n]` for a single-thumb slider, `[a, b]` for a range slider.\n */\n value?: number[];\n defaultValue?: number[];\n /** Inline label rendered above the slider. */\n label?: ReactNode;\n /** Show the numeric value next to the label. */\n showValue?: boolean;\n /** Format the displayed value (e.g. `(v) => \\`${v}%\\``). */\n formatValue?: (value: number) => string;\n}\n\n/**\n * Single- or range-thumb slider. Pass one item in `value` for a single\n * thumb, two for a range.\n *\n * @example Single value\n * <Slider label=\"Volume\" showValue defaultValue={[60]} max={100} step={1} />\n *\n * @example Price range\n * <Slider label=\"Price\"\n * defaultValue={[50, 250]}\n * min={0} max={500} step={10}\n * formatValue={(v) => \\`$\\${v}\\`}\n * showValue />\n *\n * @do Use `showValue` when the absolute number matters (price, volume, weight).\n * @dont Hide the value when users will need to reason about exact thresholds.\n */\nexport const Slider = forwardRef<ElementRef<typeof SliderPrimitive.Root>, SliderProps>(\n function Slider(\n {
|
|
1
|
+
{"version":3,"file":"Slider.js","sources":["../../../src/components/ui/Slider.tsx"],"sourcesContent":["'use client';\n\nimport {\n forwardRef,\n useId,\n useState,\n type ComponentPropsWithoutRef,\n type ElementRef,\n type ReactNode,\n} from 'react';\nimport * as SliderPrimitive from '@radix-ui/react-slider';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\n\nexport interface SliderProps extends Omit<\n ComponentPropsWithoutRef<typeof SliderPrimitive.Root>,\n 'value' | 'defaultValue'\n> {\n /**\n * Controlled value. Pass `[n]` for a single-thumb slider, `[a, b]` for a range slider.\n */\n value?: number[];\n defaultValue?: number[];\n /** Inline label rendered above the slider. */\n label?: ReactNode;\n /** Show the numeric value next to the label. */\n showValue?: boolean;\n /** Format the displayed value (e.g. `(v) => \\`${v}%\\``). */\n formatValue?: (value: number) => string;\n}\n\n/**\n * Single- or range-thumb slider. Pass one item in `value` for a single\n * thumb, two for a range.\n *\n * @example Single value\n * <Slider label=\"Volume\" showValue defaultValue={[60]} max={100} step={1} />\n *\n * @example Price range\n * <Slider label=\"Price\"\n * defaultValue={[50, 250]}\n * min={0} max={500} step={10}\n * formatValue={(v) => \\`$\\${v}\\`}\n * showValue />\n *\n * @do Use `showValue` when the absolute number matters (price, volume, weight).\n * @dont Hide the value when users will need to reason about exact thresholds.\n */\nexport const Slider = forwardRef<ElementRef<typeof SliderPrimitive.Root>, SliderProps>(\n function Slider(\n {\n className,\n label,\n showValue,\n formatValue = (v) => String(v),\n value,\n defaultValue,\n onValueChange,\n ...props\n },\n ref,\n ) {\n // Uncontrolled sliders track their own value so `showValue` stays live.\n const [internal, setInternal] = useState<number[]>(defaultValue ?? [0]);\n const isControlled = value !== undefined;\n const currentValue = isControlled ? value : internal;\n const isRange = currentValue.length > 1;\n const labelId = useId();\n const strings = useStrings();\n // Only a string label can double as aria-label; ReactNode labels are\n // referenced by id instead.\n const stringLabel = typeof label === 'string' ? label : undefined;\n const thumbLabel = (i: number) =>\n isRange\n ? i === 0\n ? strings.slider.minimum\n : strings.slider.maximum\n : (stringLabel ?? props['aria-label'] ?? strings.slider.value);\n\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n {(label || showValue) && (\n <div className=\"flex items-center justify-between\">\n {label && (\n <span id={labelId} className=\"text-foreground text-sm font-medium\">\n {label}\n </span>\n )}\n {showValue && (\n <span className=\"tabular text-foreground-muted font-mono text-xs\">\n {isRange\n ? `${formatValue(currentValue[0])} – ${formatValue(currentValue[1])}`\n : formatValue(currentValue[0])}\n </span>\n )}\n </div>\n )}\n\n <SliderPrimitive.Root\n ref={ref}\n value={value}\n defaultValue={defaultValue}\n onValueChange={(v) => {\n if (!isControlled) setInternal(v);\n onValueChange?.(v);\n }}\n className=\"relative flex w-full touch-none items-center py-3.5 select-none\"\n aria-labelledby={label && !stringLabel ? labelId : undefined}\n {...props}\n >\n <SliderPrimitive.Track className=\"bg-background-muted relative h-1 w-full grow overflow-hidden rounded-full\">\n <SliderPrimitive.Range className=\"bg-accent absolute h-full\" />\n </SliderPrimitive.Track>\n {currentValue.map((_, i) => (\n <SliderPrimitive.Thumb\n key={i}\n className={cn(\n 'border-accent bg-card relative block size-4 rounded-full border-2 shadow-sm',\n // 44px touch target without growing the visible thumb.\n 'before:absolute before:top-1/2 before:left-1/2 before:size-11 before:-translate-x-1/2 before:-translate-y-1/2 before:rounded-full before:content-[\"\"]',\n 'focus-visible:ring-ring focus-visible:ring-offset-background outline-none focus-visible:ring-2 focus-visible:ring-offset-2',\n 'transition-transform duration-[var(--duration-fast)] ease-[var(--ease-out)]',\n 'hover:scale-110 data-[disabled]:pointer-events-none data-[disabled]:opacity-50',\n )}\n aria-label={thumbLabel(i)}\n aria-labelledby={!isRange && label && !stringLabel ? labelId : undefined}\n />\n ))}\n </SliderPrimitive.Root>\n </div>\n );\n },\n);\n\nSlider.displayName = 'Slider';\n"],"names":["Slider","forwardRef","className","label","showValue","formatValue","v","value","defaultValue","onValueChange","props","ref","internal","setInternal","useState","isControlled","currentValue","isRange","labelId","useId","strings","useStrings","stringLabel","thumbLabel","i","cn","jsxs","SliderPrimitive","jsx","_"],"mappings":";;;;;;AAgDO,MAAMA,IAASC;AAAA,EACpB,SACE;AAAA,IACE,WAAAC;AAAA,IACA,OAAAC;AAAA,IACA,WAAAC;AAAA,IACA,aAAAC,IAAc,CAACC,MAAM,OAAOA,CAAC;AAAA,IAC7B,OAAAC;AAAA,IACA,cAAAC;AAAA,IACA,eAAAC;AAAA,IACA,GAAGC;AAAA,EAAA,GAELC,GACA;AAEA,UAAM,CAACC,GAAUC,CAAW,IAAIC,EAAmBN,KAAgB,CAAC,CAAC,CAAC,GAChEO,IAAeR,MAAU,QACzBS,IAAeD,IAAeR,IAAQK,GACtCK,IAAUD,EAAa,SAAS,GAChCE,IAAUC,EAAA,GACVC,IAAUC,EAAA,GAGVC,IAAc,OAAOnB,KAAU,WAAWA,IAAQ,QAClDoB,IAAa,CAACC,MAClBP,IACIO,MAAM,IACJJ,EAAQ,OAAO,UACfA,EAAQ,OAAO,UAChBE,KAAeZ,EAAM,YAAY,KAAKU,EAAQ,OAAO;AAE5D,6BACG,OAAA,EAAI,WAAWK,EAAG,uBAAuBvB,CAAS,GAC/C,UAAA;AAAA,OAAAC,KAASC,MACT,gBAAAsB,EAAC,OAAA,EAAI,WAAU,qCACZ,UAAA;AAAA,QAAAvB,uBACE,QAAA,EAAK,IAAIe,GAAS,WAAU,uCAC1B,UAAAf,GACH;AAAA,QAEDC,uBACE,QAAA,EAAK,WAAU,mDACb,UAAAa,IACG,GAAGZ,EAAYW,EAAa,CAAC,CAAC,CAAC,MAAMX,EAAYW,EAAa,CAAC,CAAC,CAAC,KACjEX,EAAYW,EAAa,CAAC,CAAC,EAAA,CACjC;AAAA,MAAA,GAEJ;AAAA,MAGF,gBAAAU;AAAA,QAACC,EAAgB;AAAA,QAAhB;AAAA,UACC,KAAAhB;AAAA,UACA,OAAAJ;AAAA,UACA,cAAAC;AAAA,UACA,eAAe,CAACF,MAAM;AACpB,YAAKS,KAAcF,EAAYP,CAAC,GAChCG,KAAA,QAAAA,EAAgBH;AAAA,UAClB;AAAA,UACA,WAAU;AAAA,UACV,mBAAiBH,KAAS,CAACmB,IAAcJ,IAAU;AAAA,UAClD,GAAGR;AAAA,UAEJ,UAAA;AAAA,YAAA,gBAAAkB,EAACD,EAAgB,OAAhB,EAAsB,WAAU,6EAC/B,UAAA,gBAAAC,EAACD,EAAgB,OAAhB,EAAsB,WAAU,4BAAA,CAA4B,EAAA,CAC/D;AAAA,YACCX,EAAa,IAAI,CAACa,GAAGL,MACpB,gBAAAI;AAAA,cAACD,EAAgB;AAAA,cAAhB;AAAA,gBAEC,WAAWF;AAAA,kBACT;AAAA;AAAA,kBAEA;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA,gBAAA;AAAA,gBAEF,cAAYF,EAAWC,CAAC;AAAA,gBACxB,mBAAiB,CAACP,KAAWd,KAAS,CAACmB,IAAcJ,IAAU;AAAA,cAAA;AAAA,cAV1DM;AAAA,YAAA,CAYR;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IACH,GACF;AAAA,EAEJ;AACF;AAEAxB,EAAO,cAAc;"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsxs as
|
|
3
|
-
import { forwardRef as
|
|
2
|
+
import { jsxs as i, jsx as e } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef as m } from "react";
|
|
4
4
|
import { X as b, XCircle as g, AlertTriangle as x, CheckCircle2 as h, Info as p } from "../../icons/index.js";
|
|
5
|
-
import { cn as
|
|
6
|
-
import { useStrings as
|
|
7
|
-
import { cva as
|
|
8
|
-
const w =
|
|
5
|
+
import { cn as v } from "../../lib/utils.js";
|
|
6
|
+
import { useStrings as k } from "../../hooks/use-strings.js";
|
|
7
|
+
import { cva as N } from "class-variance-authority";
|
|
8
|
+
const w = N(
|
|
9
9
|
[
|
|
10
10
|
"relative pointer-events-auto w-full max-w-md",
|
|
11
11
|
"flex items-start gap-3 rounded-md border bg-card p-3 shadow-md",
|
|
@@ -25,32 +25,33 @@ const w = v(
|
|
|
25
25
|
}
|
|
26
26
|
), z = {
|
|
27
27
|
default: null,
|
|
28
|
-
info: /* @__PURE__ */ e(p, { className: "
|
|
29
|
-
success: /* @__PURE__ */ e(h, { className: "
|
|
30
|
-
warning: /* @__PURE__ */ e(x, { className: "
|
|
31
|
-
danger: /* @__PURE__ */ e(g, { className: "
|
|
32
|
-
},
|
|
33
|
-
const
|
|
34
|
-
return /* @__PURE__ */
|
|
35
|
-
|
|
36
|
-
/* @__PURE__ */
|
|
28
|
+
info: /* @__PURE__ */ e(p, { className: "text-info-text mt-0.5 size-4 shrink-0", "aria-hidden": !0 }),
|
|
29
|
+
success: /* @__PURE__ */ e(h, { className: "text-success-text mt-0.5 size-4 shrink-0", "aria-hidden": !0 }),
|
|
30
|
+
warning: /* @__PURE__ */ e(x, { className: "text-warning-text mt-0.5 size-4 shrink-0", "aria-hidden": !0 }),
|
|
31
|
+
danger: /* @__PURE__ */ e(g, { className: "text-danger-text mt-0.5 size-4 shrink-0", "aria-hidden": !0 })
|
|
32
|
+
}, S = m(function({ className: o, variant: r = "default", title: t, action: d, onClose: s, icon: a, children: n, ...c }, l) {
|
|
33
|
+
const u = k(), f = a === !1 ? null : a ?? z[r ?? "default"];
|
|
34
|
+
return /* @__PURE__ */ i("div", { ref: l, role: "status", className: v(w({ variant: r }), o), ...c, children: [
|
|
35
|
+
f,
|
|
36
|
+
/* @__PURE__ */ i("div", { className: "min-w-0 flex-1", children: [
|
|
37
37
|
t && /* @__PURE__ */ e("p", { className: "font-medium", children: t }),
|
|
38
38
|
n && /* @__PURE__ */ e("p", { className: "text-foreground-muted", children: n })
|
|
39
39
|
] }),
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
d,
|
|
41
|
+
s && /* @__PURE__ */ e(
|
|
42
42
|
"button",
|
|
43
43
|
{
|
|
44
44
|
type: "button",
|
|
45
|
-
onClick:
|
|
46
|
-
"aria-label":
|
|
47
|
-
className: "
|
|
48
|
-
children: /* @__PURE__ */ e(b, { className: "size-4" })
|
|
45
|
+
onClick: s,
|
|
46
|
+
"aria-label": u.snackbar.dismiss,
|
|
47
|
+
className: "text-foreground-muted hover:bg-background-muted hover:text-foreground focus-visible:ring-ring focus-visible:ring-offset-card rounded p-1 outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
|
|
48
|
+
children: /* @__PURE__ */ e(b, { className: "size-4", "aria-hidden": !0 })
|
|
49
49
|
}
|
|
50
50
|
)
|
|
51
51
|
] });
|
|
52
52
|
});
|
|
53
|
+
S.displayName = "Snackbar";
|
|
53
54
|
export {
|
|
54
|
-
|
|
55
|
+
S as Snackbar
|
|
55
56
|
};
|
|
56
57
|
//# sourceMappingURL=Snackbar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Snackbar.js","sources":["../../../src/components/ui/Snackbar.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type HTMLAttributes, type ReactNode } from 'react';\nimport { CheckCircle2, Info, AlertTriangle, XCircle, X } from '@/icons';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\nimport { cva, type VariantProps } from '@/lib/cva';\n\nconst snackbar = cva(\n [\n 'relative pointer-events-auto w-full max-w-md',\n 'flex items-start gap-3 rounded-md border bg-card p-3 shadow-md',\n 'text-sm text-foreground',\n ],\n {\n variants: {\n variant: {\n default: 'border-border',\n info: 'border-info-border-soft',\n success: 'border-success-border-soft',\n warning: 'border-warning-border-soft',\n danger: 'border-danger-border-soft',\n },\n },\n defaultVariants: { variant: 'default' },\n },\n);\n\nconst iconForVariant: Record<NonNullable<VariantProps<typeof snackbar>['variant']>, ReactNode> = {\n default: null,\n info: <Info className=\"
|
|
1
|
+
{"version":3,"file":"Snackbar.js","sources":["../../../src/components/ui/Snackbar.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type HTMLAttributes, type ReactNode } from 'react';\nimport { CheckCircle2, Info, AlertTriangle, XCircle, X } from '@/icons';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\nimport { cva, type VariantProps } from '@/lib/cva';\n\nconst snackbar = cva(\n [\n 'relative pointer-events-auto w-full max-w-md',\n 'flex items-start gap-3 rounded-md border bg-card p-3 shadow-md',\n 'text-sm text-foreground',\n ],\n {\n variants: {\n variant: {\n default: 'border-border',\n info: 'border-info-border-soft',\n success: 'border-success-border-soft',\n warning: 'border-warning-border-soft',\n danger: 'border-danger-border-soft',\n },\n },\n defaultVariants: { variant: 'default' },\n },\n);\n\nconst iconForVariant: Record<NonNullable<VariantProps<typeof snackbar>['variant']>, ReactNode> = {\n default: null,\n info: <Info className=\"text-info-text mt-0.5 size-4 shrink-0\" aria-hidden />,\n success: <CheckCircle2 className=\"text-success-text mt-0.5 size-4 shrink-0\" aria-hidden />,\n warning: <AlertTriangle className=\"text-warning-text mt-0.5 size-4 shrink-0\" aria-hidden />,\n danger: <XCircle className=\"text-danger-text mt-0.5 size-4 shrink-0\" aria-hidden />,\n};\n\nexport interface SnackbarProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'title'>, VariantProps<typeof snackbar> {\n /** Headline. */\n title?: ReactNode;\n /** Action button rendered on the right. */\n action?: ReactNode;\n /** Show a close (×) button on the right. */\n onClose?: () => void;\n /** Override the variant's default icon. Pass `false` to hide. */\n icon?: ReactNode | false;\n}\n\n/**\n * Persistent inline notification. Unlike Toast, Snackbar does **not** auto-\n * dismiss. Pair with an `action` button or an `onClose` handler.\n */\nexport const Snackbar = forwardRef<HTMLDivElement, SnackbarProps>(function Snackbar(\n { className, variant = 'default', title, action, onClose, icon, children, ...props },\n ref,\n) {\n const strings = useStrings();\n const renderedIcon = icon === false ? null : (icon ?? iconForVariant[variant ?? 'default']);\n\n return (\n <div ref={ref} role=\"status\" className={cn(snackbar({ variant }), className)} {...props}>\n {renderedIcon}\n <div className=\"min-w-0 flex-1\">\n {title && <p className=\"font-medium\">{title}</p>}\n {children && <p className=\"text-foreground-muted\">{children}</p>}\n </div>\n {action}\n {onClose && (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label={strings.snackbar.dismiss}\n className=\"text-foreground-muted hover:bg-background-muted hover:text-foreground focus-visible:ring-ring focus-visible:ring-offset-card rounded p-1 outline-none focus-visible:ring-2 focus-visible:ring-offset-2\"\n >\n <X className=\"size-4\" aria-hidden />\n </button>\n )}\n </div>\n );\n});\nSnackbar.displayName = 'Snackbar';\n"],"names":["snackbar","cva","iconForVariant","jsx","Info","CheckCircle2","AlertTriangle","XCircle","Snackbar","forwardRef","className","variant","title","action","onClose","icon","children","props","ref","strings","useStrings","renderedIcon","jsxs","cn","X"],"mappings":";;;;;;;AAQA,MAAMA,IAAWC;AAAA,EACf;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAAA,EAEF;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,QACT,QAAQ;AAAA,MAAA;AAAA,IACV;AAAA,IAEF,iBAAiB,EAAE,SAAS,UAAA;AAAA,EAAU;AAE1C,GAEMC,IAA2F;AAAA,EAC/F,SAAS;AAAA,EACT,MAAM,gBAAAC,EAACC,GAAA,EAAK,WAAU,yCAAwC,eAAW,IAAC;AAAA,EAC1E,SAAS,gBAAAD,EAACE,GAAA,EAAa,WAAU,4CAA2C,eAAW,IAAC;AAAA,EACxF,SAAS,gBAAAF,EAACG,GAAA,EAAc,WAAU,4CAA2C,eAAW,IAAC;AAAA,EACzF,QAAQ,gBAAAH,EAACI,GAAA,EAAQ,WAAU,2CAA0C,eAAW,GAAA,CAAC;AACnF,GAkBaC,IAAWC,EAA0C,SAChE,EAAE,WAAAC,GAAW,SAAAC,IAAU,WAAW,OAAAC,GAAO,QAAAC,GAAQ,SAAAC,GAAS,MAAAC,GAAM,UAAAC,GAAU,GAAGC,EAAA,GAC7EC,GACA;AACA,QAAMC,IAAUC,EAAA,GACVC,IAAeN,MAAS,KAAQ,OAAQA,KAAQb,EAAeS,KAAW,SAAS;AAEzF,SACE,gBAAAW,EAAC,OAAA,EAAI,KAAAJ,GAAU,MAAK,UAAS,WAAWK,EAAGvB,EAAS,EAAE,SAAAW,GAAS,GAAGD,CAAS,GAAI,GAAGO,GAC/E,UAAA;AAAA,IAAAI;AAAA,IACD,gBAAAC,EAAC,OAAA,EAAI,WAAU,kBACZ,UAAA;AAAA,MAAAV,KAAS,gBAAAT,EAAC,KAAA,EAAE,WAAU,eAAe,UAAAS,GAAM;AAAA,MAC3CI,KAAY,gBAAAb,EAAC,KAAA,EAAE,WAAU,yBAAyB,UAAAa,EAAA,CAAS;AAAA,IAAA,GAC9D;AAAA,IACCH;AAAA,IACAC,KACC,gBAAAX;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,SAASW;AAAA,QACT,cAAYK,EAAQ,SAAS;AAAA,QAC7B,WAAU;AAAA,QAEV,UAAA,gBAAAhB,EAACqB,GAAA,EAAE,WAAU,UAAS,eAAW,GAAA,CAAC;AAAA,MAAA;AAAA,IAAA;AAAA,EACpC,GAEJ;AAEJ,CAAC;AACDhB,EAAS,cAAc;"}
|
|
@@ -7,20 +7,32 @@ const u = { sm: "size-3.5", md: "size-4", lg: "size-6" }, f = {
|
|
|
7
7
|
accent: "text-accent",
|
|
8
8
|
neutral: "text-foreground-subtle",
|
|
9
9
|
"on-accent": "text-on-accent"
|
|
10
|
-
}, x = p(function({ className: t, tone:
|
|
11
|
-
const
|
|
10
|
+
}, x = p(function({ className: t, tone: o = "accent", size: r = "md", label: i, decorative: e, ...s }, a) {
|
|
11
|
+
const c = m();
|
|
12
12
|
return /* @__PURE__ */ l(
|
|
13
13
|
"svg",
|
|
14
14
|
{
|
|
15
|
-
ref:
|
|
15
|
+
ref: a,
|
|
16
16
|
viewBox: "0 0 24 24",
|
|
17
17
|
role: e ? void 0 : "status",
|
|
18
|
-
"aria-label": e ? void 0 : i ??
|
|
18
|
+
"aria-label": e ? void 0 : i ?? c.spinner.loading,
|
|
19
19
|
"aria-hidden": e || void 0,
|
|
20
|
-
|
|
20
|
+
"data-motion-keep": "",
|
|
21
|
+
className: d("animate-spin", u[r], f[o], t),
|
|
21
22
|
...s,
|
|
22
23
|
children: [
|
|
23
|
-
/* @__PURE__ */ n(
|
|
24
|
+
/* @__PURE__ */ n(
|
|
25
|
+
"circle",
|
|
26
|
+
{
|
|
27
|
+
cx: "12",
|
|
28
|
+
cy: "12",
|
|
29
|
+
r: "10",
|
|
30
|
+
stroke: "currentColor",
|
|
31
|
+
strokeWidth: "2.5",
|
|
32
|
+
strokeOpacity: 0.2,
|
|
33
|
+
fill: "none"
|
|
34
|
+
}
|
|
35
|
+
),
|
|
24
36
|
/* @__PURE__ */ n(
|
|
25
37
|
"path",
|
|
26
38
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Spinner.js","sources":["../../../src/components/ui/Spinner.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type SVGProps } from 'react';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\n\nexport interface SpinnerProps extends SVGProps<SVGSVGElement> {\n /** Visual weight. */\n tone?: 'accent' | 'neutral' | 'on-accent';\n /** Pixel size. */\n size?: 'sm' | 'md' | 'lg';\n /** Accessible label. Required unless `decorative` is true. */\n label?: string;\n /** Hide from assistive tech (set when a parent already announces busy state). */\n decorative?: boolean;\n}\n\nconst sizeMap = { sm: 'size-3.5', md: 'size-4', lg: 'size-6' };\nconst toneMap = {\n accent: 'text-accent',\n neutral: 'text-foreground-subtle',\n 'on-accent': 'text-on-accent',\n};\n\n/**\n * Small indeterminate progress indicator. Use when the operation duration is\n * unknown and Progress isn't appropriate.\n *\n * @example\n * <Spinner label=\"Loading users\" />\n * <Button loading>Saving…</Button> // uses Spinner internally\n *\n * @do Provide a `label` so the busy state is announced.\n * @dont Use a Spinner for tasks that take 300ms+; use Skeleton instead.\n */\nexport const Spinner = forwardRef<SVGSVGElement, SpinnerProps>(function Spinner(\n { className, tone = 'accent', size = 'md', label, decorative, ...props },\n ref,\n) {\n const strings = useStrings();\n return (\n <svg\n ref={ref}\n viewBox=\"0 0 24 24\"\n role={decorative ? undefined : 'status'}\n aria-label={decorative ? undefined : (label ?? strings.spinner.loading)}\n aria-hidden={decorative || undefined}\n className={cn('animate-spin', sizeMap[size], toneMap[tone], className)}\n {...props}\n >\n <circle
|
|
1
|
+
{"version":3,"file":"Spinner.js","sources":["../../../src/components/ui/Spinner.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type SVGProps } from 'react';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\n\nexport interface SpinnerProps extends SVGProps<SVGSVGElement> {\n /** Visual weight. */\n tone?: 'accent' | 'neutral' | 'on-accent';\n /** Pixel size. */\n size?: 'sm' | 'md' | 'lg';\n /** Accessible label. Required unless `decorative` is true. */\n label?: string;\n /** Hide from assistive tech (set when a parent already announces busy state). */\n decorative?: boolean;\n}\n\nconst sizeMap = { sm: 'size-3.5', md: 'size-4', lg: 'size-6' };\nconst toneMap = {\n accent: 'text-accent',\n neutral: 'text-foreground-subtle',\n 'on-accent': 'text-on-accent',\n};\n\n/**\n * Small indeterminate progress indicator. Use when the operation duration is\n * unknown and Progress isn't appropriate.\n *\n * @example\n * <Spinner label=\"Loading users\" />\n * <Button loading>Saving…</Button> // uses Spinner internally\n *\n * @do Provide a `label` so the busy state is announced.\n * @dont Use a Spinner for tasks that take 300ms+; use Skeleton instead.\n */\nexport const Spinner = forwardRef<SVGSVGElement, SpinnerProps>(function Spinner(\n { className, tone = 'accent', size = 'md', label, decorative, ...props },\n ref,\n) {\n const strings = useStrings();\n return (\n <svg\n ref={ref}\n viewBox=\"0 0 24 24\"\n role={decorative ? undefined : 'status'}\n aria-label={decorative ? undefined : (label ?? strings.spinner.loading)}\n aria-hidden={decorative || undefined}\n // Under prefers-reduced-motion theme.css swaps the spin for a slow\n // (1.6s) opacity pulse via this attribute — busy state stays perceivable.\n data-motion-keep=\"\"\n className={cn('animate-spin', sizeMap[size], toneMap[tone], className)}\n {...props}\n >\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeOpacity={0.2}\n fill=\"none\"\n />\n <path\n d=\"M12 2a10 10 0 0 1 10 10\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n fill=\"none\"\n />\n </svg>\n );\n});\nSpinner.displayName = 'Spinner';\n"],"names":["sizeMap","toneMap","Spinner","forwardRef","className","tone","size","label","decorative","props","ref","strings","useStrings","jsxs","cn","jsx"],"mappings":";;;;;AAiBA,MAAMA,IAAU,EAAE,IAAI,YAAY,IAAI,UAAU,IAAI,SAAA,GAC9CC,IAAU;AAAA,EACd,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,aAAa;AACf,GAaaC,IAAUC,EAAwC,SAC7D,EAAE,WAAAC,GAAW,MAAAC,IAAO,UAAU,MAAAC,IAAO,MAAM,OAAAC,GAAO,YAAAC,GAAY,GAAGC,EAAA,GACjEC,GACA;AACA,QAAMC,IAAUC,EAAA;AAChB,SACE,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAH;AAAA,MACA,SAAQ;AAAA,MACR,MAAMF,IAAa,SAAY;AAAA,MAC/B,cAAYA,IAAa,SAAaD,KAASI,EAAQ,QAAQ;AAAA,MAC/D,eAAaH,KAAc;AAAA,MAG3B,oBAAiB;AAAA,MACjB,WAAWM,EAAG,gBAAgBd,EAAQM,CAAI,GAAGL,EAAQI,CAAI,GAAGD,CAAS;AAAA,MACpE,GAAGK;AAAA,MAEJ,UAAA;AAAA,QAAA,gBAAAM;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,IAAG;AAAA,YACH,IAAG;AAAA,YACH,GAAE;AAAA,YACF,QAAO;AAAA,YACP,aAAY;AAAA,YACZ,eAAe;AAAA,YACf,MAAK;AAAA,UAAA;AAAA,QAAA;AAAA,QAEP,gBAAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,GAAE;AAAA,YACF,QAAO;AAAA,YACP,aAAY;AAAA,YACZ,eAAc;AAAA,YACd,MAAK;AAAA,UAAA;AAAA,QAAA;AAAA,MACP;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC;AACDb,EAAQ,cAAc;"}
|
|
@@ -12,6 +12,12 @@ export interface StepperProps extends HTMLAttributes<HTMLOListElement> {
|
|
|
12
12
|
current: number;
|
|
13
13
|
/** Layout direction. */
|
|
14
14
|
orientation?: 'horizontal' | 'vertical';
|
|
15
|
+
/** sr-only state words announced after each title (i18n). */
|
|
16
|
+
stateLabels?: {
|
|
17
|
+
complete: string;
|
|
18
|
+
current: string;
|
|
19
|
+
upcoming: string;
|
|
20
|
+
};
|
|
15
21
|
}
|
|
16
22
|
/**
|
|
17
23
|
* Multi-step progress indicator. Use the horizontal variant in narrow flows
|