{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "table", "type": "registry:block", "title": "Table", "description": "A structured table for dashboards, billing, and admin surfaces.", "author": "Wensity ", "dependencies": [ "@tabler/icons-react", "clsx", "framer-motion", "tailwind-merge" ], "registryDependencies": [], "files": [ { "path": "registry/wensity/lib/utils.ts", "type": "registry:lib", "target": "@lib/utils.ts", "content": "import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n" }, { "path": "registry/wensity/table.tsx", "type": "registry:component", "target": "@components/wensity/table.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\nimport {\n IconChevronDown,\n IconChevronUp,\n IconSelector,\n} from \"@tabler/icons-react\";\nimport {\n AnimatePresence,\n LayoutGroup,\n motion,\n useReducedMotion,\n} from \"framer-motion\";\nimport { cn } from \"@/lib/utils\";\n\nconst tableSurfaceClass = cn(\n \"border border-[var(--border)] bg-[var(--primitive-surface-elevated)]\",\n \"[box-shadow:var(--primitive-shadow-raised)] rounded-[var(--primitive-radius-surface)]\",\n);\n\nexport type TableDensity = \"compact\" | \"comfortable\" | \"spacious\";\nexport type SortDirection = \"asc\" | \"desc\" | null;\n\nexport interface TableProps extends React.HTMLAttributes {\n stickyHeader?: boolean;\n density?: TableDensity;\n}\n\nexport interface TableBodyProps extends React.HTMLAttributes {\n /** Animate row reordering when sort order changes. */\n animatedSort?: boolean;\n}\n\nexport interface TableRowProps extends React.HTMLAttributes {\n selected?: boolean;\n expanded?: boolean;\n /** Click anywhere on the row to toggle selection. */\n selectable?: boolean;\n onSelectedChange?: (selected: boolean) => void;\n /** Click anywhere on the row to toggle expansion. */\n expandable?: boolean;\n onExpandedChange?: (expanded: boolean) => void;\n}\n\nexport interface TableHeadProps extends React.ThHTMLAttributes {\n sortable?: boolean;\n sortDirection?: SortDirection;\n onSort?: () => void;\n}\n\nexport type TableCellProps = React.TdHTMLAttributes;\n\nexport interface TableExpandableContentProps\n extends React.HTMLAttributes {\n open?: boolean;\n colSpan: number;\n}\n\nconst EASE_OUT = \"cubic-bezier(0.16, 1, 0.3, 1)\";\nconst EASE_MORPH = [0.32, 0.72, 0, 1] as const;\nconst SORT_LAYOUT_MS = 0.36;\n\n// E-10 density scale: compact 11px/xs, comfortable xs/sm, spacious sm/sm.\nconst densityClasses: Record = {\n compact: { head: \"h-9 px-3 text-[11px]\", cell: \"h-10 px-3 text-xs\" },\n comfortable: { head: \"h-11 px-4 text-xs\", cell: \"h-12 px-4 text-sm\" },\n spacious: { head: \"h-12 px-5 text-sm\", cell: \"h-14 px-5 text-sm\" },\n};\n\ntype TableContextValue = {\n density: TableDensity;\n stickyHeader: boolean;\n animatedSort: boolean;\n layoutGroupId: string;\n};\n\nconst TableContext = React.createContext({\n density: \"comfortable\",\n stickyHeader: false,\n animatedSort: false,\n layoutGroupId: \"wensity-table\",\n});\n\nfunction useTableContext() {\n return React.useContext(TableContext);\n}\n\nfunction isInteractiveTarget(target: EventTarget | null) {\n if (!(target instanceof HTMLElement)) return false;\n return Boolean(\n target.closest(\n 'button, a, input, label, textarea, select, [role=\"checkbox\"], [data-slot=\"table-select-cell\"], [data-prevent-row-action=\"true\"]',\n ),\n );\n}\n\nfunction SortIndicator({ direction }: { direction: SortDirection }) {\n const shouldReduceMotion = useReducedMotion();\n\n return (\n \n \n {direction === \"asc\" ? (\n \n \n \n ) : direction === \"desc\" ? (\n \n \n \n ) : (\n \n \n \n )}\n \n \n );\n}\n\nexport const Table = React.forwardRef(\n ({ className, stickyHeader = false, density = \"comfortable\", children, ...props }, ref) => {\n const layoutGroupId = React.useId();\n\n return (\n \n
\n {children}
\n
\n \n );\n },\n);\nTable.displayName = \"Table\";\n\nexport const TableCaption = React.forwardRef<\n HTMLTableCaptionElement,\n React.HTMLAttributes\n>(({ className, ...props }, ref) => (\n \n));\nTableCaption.displayName = \"TableCaption\";\n\nexport const TableHeader = React.forwardRef>(\n ({ className, ...props }, ref) => {\n const { stickyHeader } = useTableContext();\n return (\n \n );\n },\n);\nTableHeader.displayName = \"TableHeader\";\n\nexport const TableBody = React.forwardRef(\n ({ className, animatedSort = false, children, ...props }, ref) => {\n const parentContext = useTableContext();\n const sortEnabled = animatedSort || parentContext.animatedSort;\n\n return (\n \n \n {sortEnabled ? (\n {children}\n ) : (\n children\n )}\n \n \n );\n },\n);\nTableBody.displayName = \"TableBody\";\n\nexport const TableFooter = React.forwardRef>(\n ({ className, ...props }, ref) => (\n \n ),\n);\nTableFooter.displayName = \"TableFooter\";\n\nexport const TableRow = React.forwardRef(\n (\n {\n className,\n selected = false,\n expanded = false,\n selectable = false,\n onSelectedChange,\n expandable = false,\n onExpandedChange,\n onClick,\n children,\n style,\n ...props\n },\n ref,\n ) => {\n const { animatedSort } = useTableContext();\n const shouldReduceMotion = useReducedMotion();\n const isInteractive = selectable || expandable;\n\n const handleClick = (event: React.MouseEvent) => {\n onClick?.(event);\n if (event.defaultPrevented || isInteractiveTarget(event.target)) return;\n\n if (selectable) {\n onSelectedChange?.(!selected);\n } else if (expandable) {\n onExpandedChange?.(!expanded);\n }\n };\n\n const sharedClass = cn(\n \"border-b border-[var(--border)]\",\n \"transition-[background-color,box-shadow] duration-200 ease-[var(--table-ease-out)] motion-reduce:transition-none\",\n \"hover:bg-[var(--primitive-surface-hover)]\",\n isInteractive && \"cursor-pointer\",\n selected &&\n \"bg-[var(--primitive-surface-selected)] hover:bg-[var(--primitive-surface-selected)]\",\n expanded &&\n !selected &&\n \"bg-[var(--primitive-surface-hover)] hover:bg-[var(--primitive-surface-hover)]\",\n className,\n );\n\n const sharedProps = {\n \"data-slot\": \"table-row\",\n \"data-selected\": selected ? \"true\" : undefined,\n \"data-expanded\": expanded ? \"true\" : undefined,\n \"data-selectable\": selectable ? \"true\" : undefined,\n \"data-expandable\": expandable ? \"true\" : undefined,\n onClick: isInteractive ? handleClick : onClick,\n className: sharedClass,\n style: { [\"--table-ease-out\" as string]: EASE_OUT, ...style },\n ...props,\n };\n\n if (animatedSort && !shouldReduceMotion) {\n const motionRowProps = sharedProps as React.ComponentPropsWithoutRef;\n\n return (\n \n {children}\n \n );\n }\n\n return (\n \n {children}\n \n );\n },\n);\nTableRow.displayName = \"TableRow\";\n\nexport const TableHead = React.forwardRef(\n (\n {\n className,\n sortable = false,\n sortDirection = null,\n onSort,\n children,\n \"aria-sort\": ariaSort,\n ...props\n },\n ref,\n ) => {\n const { density } = useTableContext();\n const isActive = Boolean(sortDirection);\n const resolvedAriaSort =\n sortDirection === \"asc\"\n ? \"ascending\"\n : sortDirection === \"desc\"\n ? \"descending\"\n : ariaSort;\n\n const content = (\n <>\n {children}\n {sortable ? : null}\n \n );\n\n const sharedClass = cn(\n \"relative whitespace-nowrap font-medium tracking-[-0.01em] text-[var(--primitive-text-secondary,var(--muted-foreground))]\",\n densityClasses[density].head,\n sortable && \"cursor-pointer select-none\",\n sortable &&\n \"transition-[color,background-color] duration-[360ms] ease-[var(--table-ease-out)] hover:text-[var(--foreground)] motion-reduce:transition-none\",\n isActive &&\n \"text-[var(--foreground)] after:absolute after:inset-x-3 after:bottom-0 after:h-px after:origin-left after:scale-x-100 after:bg-[color-mix(in_srgb,var(--foreground)_22%,transparent)] after:transition-transform after:duration-[360ms] after:ease-[var(--table-ease-out)]\",\n className,\n );\n\n if (sortable) {\n return (\n \n \n {content}\n \n \n );\n }\n\n return (\n \n {content}\n \n );\n },\n);\nTableHead.displayName = \"TableHead\";\n\nexport const TableCell = React.forwardRef(\n ({ className, ...props }, ref) => {\n const { density, animatedSort } = useTableContext();\n const shouldReduceMotion = useReducedMotion();\n\n const cellClass = cn(\n \"align-middle text-[var(--foreground)]\",\n densityClasses[density].cell,\n animatedSort &&\n !shouldReduceMotion &&\n \"transition-[opacity,transform,filter] duration-[360ms] ease-[var(--table-ease-out)] motion-reduce:transition-none\",\n className,\n );\n\n return (\n \n );\n },\n);\nTableCell.displayName = \"TableCell\";\n\nexport interface TableSelectCellProps extends Omit {\n checked?: boolean;\n indeterminate?: boolean;\n onCheckedChange?: (checked: boolean) => void;\n ariaLabel?: string;\n}\n\nfunction TableCheckbox({\n checked,\n indeterminate,\n onCheckedChange,\n ariaLabel,\n}: {\n checked: boolean;\n indeterminate: boolean;\n onCheckedChange?: (checked: boolean) => void;\n ariaLabel: string;\n}) {\n const inputRef = React.useRef(null);\n\n React.useEffect(() => {\n if (inputRef.current) inputRef.current.indeterminate = indeterminate;\n }, [indeterminate]);\n\n return (\n \n onCheckedChange?.(event.currentTarget.checked)}\n className=\"sr-only\"\n />\n \n {indeterminate ? (\n \n ) : (\n \n )}\n \n \n );\n}\n\nexport const TableSelectCell = React.forwardRef(\n (\n {\n checked = false,\n indeterminate = false,\n onCheckedChange,\n ariaLabel = \"Select row\",\n className,\n onClick,\n ...props\n },\n ref,\n ) => {\n const { density } = useTableContext();\n\n return (\n {\n event.stopPropagation();\n onClick?.(event);\n }}\n {...props}\n >\n onCheckedChange?.(value === true)}\n ariaLabel={ariaLabel}\n />\n \n );\n },\n);\nTableSelectCell.displayName = \"TableSelectCell\";\n\nexport interface TableExpandTriggerCellProps extends TableCellProps {\n expanded?: boolean;\n onToggle?: () => void;\n label?: string;\n}\n\nexport const TableExpandTriggerCell = React.forwardRef<\n HTMLTableCellElement,\n TableExpandTriggerCellProps\n>(({ expanded = false, onToggle, label = \"Toggle row details\", className, children, onClick, ...props }, ref) => {\n const { density } = useTableContext();\n\n return (\n {\n event.stopPropagation();\n onClick?.(event);\n }}\n {...props}\n >\n {\n event.stopPropagation();\n onToggle?.();\n }}\n className={cn(\n \"inline-flex size-7 items-center justify-center rounded-[var(--primitive-radius-control-sm)]\",\n \"text-[var(--muted-foreground)] transition-[color,transform] duration-[360ms] ease-[var(--table-ease-out)] motion-reduce:transition-none\",\n \"hover:bg-[var(--primitive-surface-hover)] hover:text-[var(--foreground)]\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--primitive-ring)]\",\n )}\n style={{ [\"--table-ease-out\" as string]: EASE_OUT }}\n >\n \n \n {children}\n \n );\n});\nTableExpandTriggerCell.displayName = \"TableExpandTriggerCell\";\n\nexport const TableExpandableContent = React.forwardRef<\n HTMLTableRowElement,\n TableExpandableContentProps\n>(({ className, open = false, colSpan, children, ...props }, ref) => {\n return (\n \n \n \n
\n
{children}
\n
\n \n \n \n );\n});\nTableExpandableContent.displayName = \"TableExpandableContent\";\n" } ], "cssVars": { "theme": { "font-sans": "var(--font-satoshi, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif)", "font-display": "var(--font-satoshi, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif)", "font-heading": "var(--font-cabinet, ui-sans-serif, system-ui, sans-serif)", "color-background": "var(--background)", "color-foreground": "var(--foreground)", "color-surface": "var(--surface)", "color-surface-muted": "var(--surface-muted)", "color-border": "var(--border)", "color-border-strong": "var(--border-strong)", "color-muted": "var(--muted)", "color-muted-foreground": "var(--muted-foreground)", "color-ring": "var(--ring)", "color-chili-50": "#fff1ee", "color-chili-100": "#ffe2db", "color-chili-200": "#ffa896", "color-chili-300": "#ff8a73", "color-chili-400": "#f15a45", "color-chili-500": "#cd1c18", "color-chili-600": "#b31614", "color-chili-700": "#9b1313", "color-chili-800": "#6a0d0e", "color-chili-900": "#38000a", "color-chili-950": "#1f0006", "color-primitive-surface-elevated": "var(--primitive-surface-elevated)", "color-primitive-surface-overlay": "var(--primitive-surface-overlay)", "color-primitive-surface-hover": "var(--primitive-surface-hover)", "color-primitive-surface-active": "var(--primitive-surface-active)", "color-primitive-surface-selected": "var(--primitive-surface-selected)", "color-primitive-border-subtle": "var(--primitive-border-subtle)", "color-primitive-ring": "var(--primitive-ring)", "color-primitive-text-secondary": "var(--primitive-text-secondary)", "color-primitive-text-placeholder": "var(--primitive-text-placeholder)", "color-primitive-text-inverse": "var(--primitive-text-inverse)", "color-primitive-destructive": "var(--primitive-destructive)", "color-primitive-destructive-hover": "var(--primitive-destructive-hover)", "color-primitive-destructive-active": "var(--primitive-destructive-active)", "color-primitive-destructive-foreground": "var(--primitive-destructive-foreground)", "color-primitive-destructive-surface": "var(--primitive-destructive-surface)", "color-primitive-destructive-border": "var(--primitive-destructive-border)", "color-primitive-success": "var(--primitive-success)", "color-primitive-warning": "var(--primitive-warning)", "color-primitive-info": "var(--primitive-info)", "color-primitive-control-solid": "var(--primitive-control-solid)", "color-primitive-control-solid-hover": "var(--primitive-control-solid-hover)", "color-primitive-control-solid-active": "var(--primitive-control-solid-active)", "color-primitive-control-solid-foreground": "var(--primitive-control-solid-foreground)", "color-primitive-chart-1": "var(--primitive-chart-1)", "color-primitive-chart-2": "var(--primitive-chart-2)", "color-primitive-chart-3": "var(--primitive-chart-3)", "color-primitive-chart-4": "var(--primitive-chart-4)", "color-primitive-chart-5": "var(--primitive-chart-5)", "color-primitive-chart-6": "var(--primitive-chart-6)", "radius-primitive": "var(--primitive-radius)", "radius-primitive-control": "var(--primitive-radius-control)", "radius-primitive-control-sm": "var(--primitive-radius-control-sm)", "radius-primitive-surface": "var(--primitive-radius-surface)", "radius-primitive-item": "var(--primitive-radius-item)", "font-primitive-sans": "var(--primitive-font-sans)", "font-primitive-display": "var(--primitive-font-display)", "font-primitive-mono": "var(--primitive-font-mono)", "spacing-primitive-control-height-sm": "var(--primitive-control-height-sm)", "spacing-primitive-control-height-md": "var(--primitive-control-height-md)", "spacing-primitive-control-height-lg": "var(--primitive-control-height-lg)" }, "light": { "background": "#fafafa", "foreground": "#0a0a0a", "surface": "#ffffff", "surface-muted": "#f4f4f5", "border": "rgba(10, 10, 10, 0.08)", "border-strong": "rgba(10, 10, 10, 0.16)", "muted": "#f4f4f5", "muted-foreground": "#52525b", "ring": "#cd1c18", "pattern-fg": "rgba(10, 10, 10, 0.07)", "llb-primary": "#18181b", "llb-primary-fg": "#ffffff", "llb-success": "#1f883d", "llb-error": "#cf222e", "primitive-surface-elevated": "#ffffff", "primitive-surface-overlay": "#ffffff", "primitive-surface-hover": "color-mix(in srgb, var(--foreground) 4%, transparent)", "primitive-surface-active": "color-mix(in srgb, var(--foreground) 7%, transparent)", "primitive-surface-selected": "color-mix(in srgb, var(--foreground) 6%, transparent)", "primitive-border-subtle": "color-mix(in srgb, var(--border) 60%, transparent)", "primitive-ring": "color-mix(in srgb, var(--foreground) 45%, transparent)", "primitive-text-secondary": "color-mix(in srgb, var(--foreground) 72%, transparent)", "primitive-text-placeholder": "color-mix(in srgb, var(--muted-foreground) 85%, transparent)", "primitive-radius": "0.875rem", "primitive-font-sans": "var(--font-sans)", "primitive-font-display": "var(--font-display)", "primitive-font-mono": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace", "primitive-control-solid": "#24292d", "primitive-control-solid-hover": "#2c3237", "primitive-control-solid-active": "#1e2326", "primitive-control-solid-foreground": "#ffffff", "primitive-chart-1": "#2a78d6", "primitive-chart-2": "#1baf7a", "primitive-chart-3": "#eda100", "primitive-chart-4": "#008300", "primitive-chart-5": "#4a3aa7", "primitive-chart-6": "#e34948", "primitive-text-inverse": "#ffffff", "primitive-text-hint": "0.6875rem", "primitive-destructive": "#dc2626", "primitive-destructive-hover": "#b91c1c", "primitive-destructive-active": "#991b1b", "primitive-destructive-foreground": "#ffffff", "primitive-destructive-surface": "rgba(220, 38, 38, 0.10)", "primitive-destructive-border": "rgba(220, 38, 38, 0.45)", "primitive-success": "#047857", "primitive-success-surface": "rgba(4, 120, 87, 0.10)", "primitive-success-border": "rgba(4, 120, 87, 0.45)", "primitive-warning": "#b45309", "primitive-warning-surface": "rgba(180, 83, 9, 0.10)", "primitive-warning-border": "rgba(180, 83, 9, 0.45)", "primitive-info": "#0369a1", "primitive-info-surface": "rgba(3, 105, 161, 0.10)", "primitive-info-border": "rgba(3, 105, 161, 0.45)", "primitive-radius-control": "var(--primitive-radius)", "primitive-radius-control-sm": "max(0px, calc(var(--primitive-radius) - 4px))", "primitive-radius-surface": "calc(var(--primitive-radius) + min(2px, var(--primitive-radius)))", "primitive-radius-item": "max(0px, calc(var(--primitive-radius-surface) - 6px))", "primitive-control-height-sm": "2rem", "primitive-control-height-md": "2.25rem", "primitive-control-height-lg": "2.5rem", "primitive-shadow-raised": "0 1px 2px rgba(0, 0, 0, 0.06), 0 8px 24px -12px rgba(0, 0, 0, 0.08)", "primitive-shadow-overlay": "0 1px 2px rgba(0, 0, 0, 0.06), 0 18px 48px -24px rgba(0, 0, 0, 0.35)", "primitive-shadow-modal": "0 1px 2px rgba(0, 0, 0, 0.08), 0 24px 64px -28px rgba(0, 0, 0, 0.42)", "primitive-z-overlay": "100", "primitive-z-popover": "130", "primitive-z-toast": "140", "primitive-backdrop": "rgba(0, 0, 0, 0.6)", "primitive-ease": "cubic-bezier(0.23, 1, 0.32, 1)" }, "dark": { "background": "#0a0a0b", "foreground": "#f5f5f6", "surface": "#111113", "surface-muted": "#18181b", "border": "rgba(255, 255, 255, 0.08)", "border-strong": "rgba(255, 255, 255, 0.14)", "muted": "#1c1c1f", "muted-foreground": "#a1a1aa", "ring": "#cd1c18", "pattern-fg": "rgba(255, 255, 255, 0.06)", "llb-primary": "#f5f5f6", "llb-primary-fg": "#0a0a0b", "llb-success": "#2da44e", "llb-error": "#f85149", "primitive-surface-elevated": "#0f0f10", "primitive-surface-overlay": "#141415", "primitive-surface-hover": "color-mix(in srgb, var(--foreground) 5%, transparent)", "primitive-surface-active": "color-mix(in srgb, var(--foreground) 10%, transparent)", "primitive-surface-selected": "color-mix(in srgb, var(--foreground) 8%, transparent)", "primitive-border-subtle": "color-mix(in srgb, var(--border) 60%, transparent)", "primitive-ring": "color-mix(in srgb, var(--foreground) 45%, transparent)", "primitive-text-secondary": "color-mix(in srgb, var(--foreground) 72%, transparent)", "primitive-text-placeholder": "color-mix(in srgb, var(--muted-foreground) 85%, transparent)", "primitive-radius": "0.875rem", "primitive-font-sans": "var(--font-sans)", "primitive-font-display": "var(--font-display)", "primitive-font-mono": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace", "primitive-control-solid": "#f5f3ee", "primitive-control-solid-hover": "#ffffff", "primitive-control-solid-active": "#e8e4da", "primitive-control-solid-foreground": "#151719", "primitive-chart-1": "#3987e5", "primitive-chart-2": "#199e70", "primitive-chart-3": "#c98500", "primitive-chart-4": "#008300", "primitive-chart-5": "#9085e9", "primitive-chart-6": "#e66767", "primitive-text-inverse": "#151719", "primitive-text-hint": "0.6875rem", "primitive-destructive": "#e11d2e", "primitive-destructive-hover": "#c1121f", "primitive-destructive-active": "#a4161a", "primitive-destructive-foreground": "#ffffff", "primitive-destructive-surface": "rgba(225, 29, 46, 0.12)", "primitive-destructive-border": "rgba(225, 29, 46, 0.52)", "primitive-success": "#34d399", "primitive-success-surface": "rgba(52, 211, 153, 0.12)", "primitive-success-border": "rgba(52, 211, 153, 0.45)", "primitive-warning": "#fbbf24", "primitive-warning-surface": "rgba(251, 191, 36, 0.12)", "primitive-warning-border": "rgba(251, 191, 36, 0.45)", "primitive-info": "#38bdf8", "primitive-info-surface": "rgba(56, 189, 248, 0.12)", "primitive-info-border": "rgba(56, 189, 248, 0.45)", "primitive-radius-control": "var(--primitive-radius)", "primitive-radius-control-sm": "max(0px, calc(var(--primitive-radius) - 4px))", "primitive-radius-surface": "calc(var(--primitive-radius) + min(2px, var(--primitive-radius)))", "primitive-radius-item": "max(0px, calc(var(--primitive-radius-surface) - 6px))", "primitive-control-height-sm": "2rem", "primitive-control-height-md": "2.25rem", "primitive-control-height-lg": "2.5rem", "primitive-shadow-raised": "0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px -12px rgba(0, 0, 0, 0.6)", "primitive-shadow-overlay": "0 1px 2px rgba(0, 0, 0, 0.4), 0 20px 56px -28px rgba(0, 0, 0, 0.72)", "primitive-shadow-modal": "0 1px 2px rgba(0, 0, 0, 0.45), 0 28px 72px -32px rgba(0, 0, 0, 0.8)", "primitive-z-overlay": "100", "primitive-z-popover": "130", "primitive-z-toast": "140", "primitive-backdrop": "rgba(0, 0, 0, 0.6)", "primitive-ease": "cubic-bezier(0.23, 1, 0.32, 1)" } }, "css": { "@layer base": { ":where([data-wensity-primitive])": { "font-family": "var(--primitive-font-sans)" } }, "@utility scrollbar-hidden": { "scrollbar-width": "none", "-ms-overflow-style": "none", "&::-webkit-scrollbar": { "display": "none" } }, "@keyframes wensity-marquee-x": { "from": { "transform": "translate3d(0, 0, 0)" }, "to": { "transform": "translate3d(-50%, 0, 0)" } }, "@keyframes wensity-marquee-x-reverse": { "from": { "transform": "translate3d(-50%, 0, 0)" }, "to": { "transform": "translate3d(0, 0, 0)" } }, "@keyframes wensity-morph-rot-cw": { "from": { "transform": "rotate(0deg)" }, "to": { "transform": "rotate(360deg)" } }, "@keyframes wensity-morph-rot-ccw": { "from": { "transform": "rotate(0deg)" }, "to": { "transform": "rotate(-360deg)" } } }, "docs": "Free Wensity component. Installs to @components/wensity/table.tsx. For Pro components and updates, use pnpm dlx wensity@latest add table.", "categories": [ "UI Primitives", "wensity", "free" ], "meta": { "wensity": { "slug": "table", "access": "free", "category": "UI Primitives", "kind": "component", "componentUrl": "https://ui.wensity.com/primitives/table", "shadcnUrl": "https://ui.wensity.com/r/table", "cliInstall": "pnpm dlx wensity@latest add table" } } }