{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "resizable", "type": "registry:block", "title": "Resizable", "description": "Resizable panel layouts with splitters, snapping, and persistence.", "author": "Wensity ", "dependencies": [ "clsx", "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/resizable.tsx", "type": "registry:component", "target": "@components/wensity/resizable.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\n\n/* ─── Resizable Group ───────────────────────────────────────── */\n\nexport interface ResizableGroupProps extends React.HTMLAttributes {\n direction?: \"horizontal\" | \"vertical\";\n storageKey?: string;\n}\n\ntype ResizablePanelConfig = {\n minSize: number;\n maxSize: number;\n collapsible: boolean;\n};\n\nfunction readStoredSizes(storageKey: string | undefined, count: number): number[] | null {\n if (!storageKey || typeof window === \"undefined\") return null;\n try {\n const saved = localStorage.getItem(storageKey);\n if (!saved) return null;\n const parsed = JSON.parse(saved);\n if (Array.isArray(parsed) && parsed.length === count) return parsed;\n } catch {\n // SSR / private mode / quota — fall through to defaults\n }\n return null;\n}\n\nfunction writeStoredSizes(storageKey: string | undefined, nextSizes: number[]) {\n if (!storageKey || typeof window === \"undefined\") return;\n try {\n localStorage.setItem(storageKey, JSON.stringify(nextSizes));\n } catch {\n // Ignore persistence failures in private / restricted storage.\n }\n}\n\nexport const ResizableGroup = React.forwardRef(\n ({ className, direction = \"horizontal\", storageKey, children, ...props }, ref) => {\n const containerRef = React.useRef(null);\n const combinedRef = useForkRef(ref, containerRef);\n\n const [sizes, setSizes] = React.useState([]);\n\n // Retrieve list of resizable panels inside group\n const getPanels = React.useCallback(() => {\n const arr: React.ReactElement[] = [];\n React.Children.forEach(children, (child) => {\n if (\n React.isValidElement(child) &&\n (child.type === ResizablePanel || (child.type as any).displayName === \"ResizablePanel\")\n ) {\n arr.push(child as React.ReactElement);\n }\n });\n return arr;\n }, [children]);\n\n const persistSizes = React.useCallback(\n (nextSizes: number[]) => {\n writeStoredSizes(storageKey, nextSizes);\n },\n [storageKey]\n );\n\n const getPanelConfig = React.useCallback(\n (index: number): ResizablePanelConfig => {\n const panel = getPanels()[index]?.props ?? {};\n\n return {\n minSize: panel.minSize ?? 10,\n maxSize: panel.maxSize ?? 100,\n collapsible: panel.collapsible ?? false,\n };\n },\n [getPanels]\n );\n\n const resizePanelPair = React.useCallback(\n (baseSizes: number[], handleIndex: number, targetLeftSize: number) => {\n if (\n baseSizes.length === 0 ||\n handleIndex < 0 ||\n handleIndex + 1 >= baseSizes.length\n ) {\n return baseSizes;\n }\n\n const nextSizes = [...baseSizes];\n const sizeLeft = baseSizes[handleIndex];\n const sizeRight = baseSizes[handleIndex + 1];\n const totalCombined = sizeLeft + sizeRight;\n\n if (!Number.isFinite(totalCombined) || totalCombined <= 0) {\n return baseSizes;\n }\n\n const leftPanel = getPanelConfig(handleIndex);\n const rightPanel = getPanelConfig(handleIndex + 1);\n\n let newSizeLeft = targetLeftSize;\n let newSizeRight = totalCombined - targetLeftSize;\n\n // Left panel sizing constraints\n if (\n leftPanel.collapsible &&\n newSizeLeft < leftPanel.minSize / 2\n ) {\n newSizeRight += newSizeLeft;\n newSizeLeft = 0;\n } else {\n newSizeLeft = Math.max(\n leftPanel.minSize,\n Math.min(leftPanel.maxSize, newSizeLeft)\n );\n newSizeRight = totalCombined - newSizeLeft;\n }\n\n // Right panel sizing constraints, if left did not collapse\n if (newSizeLeft > 0) {\n if (\n rightPanel.collapsible &&\n newSizeRight < rightPanel.minSize / 2\n ) {\n newSizeLeft += newSizeRight;\n newSizeRight = 0;\n } else {\n newSizeRight = Math.max(\n rightPanel.minSize,\n Math.min(rightPanel.maxSize, newSizeRight)\n );\n newSizeLeft = totalCombined - newSizeRight;\n }\n }\n\n nextSizes[handleIndex] = newSizeLeft;\n nextSizes[handleIndex + 1] = newSizeRight;\n\n return nextSizes;\n },\n [getPanelConfig]\n );\n\n const getHandleValue = React.useCallback(\n (baseSizes: number[], handleIndex: number) => {\n const sizeLeft = baseSizes[handleIndex] ?? 0;\n const sizeRight = baseSizes[handleIndex + 1] ?? 0;\n const totalCombined = sizeLeft + sizeRight;\n const leftPanel = getPanelConfig(handleIndex);\n const rightPanel = getPanelConfig(handleIndex + 1);\n const min = leftPanel.collapsible ? 0 : leftPanel.minSize;\n const maxByRight = totalCombined - (rightPanel.collapsible ? 0 : rightPanel.minSize);\n const max = Math.max(min, Math.min(leftPanel.maxSize, maxByRight));\n const now = Math.max(min, Math.min(max, sizeLeft));\n\n return {\n min: roundSize(min),\n max: roundSize(max),\n now: roundSize(now),\n };\n },\n [getPanelConfig]\n );\n\n // Initial sizes calculations & load from storage\n React.useEffect(() => {\n const panels = getPanels();\n const count = panels.length;\n if (count === 0) return;\n\n const stored = readStoredSizes(storageKey, count);\n if (stored) {\n setSizes(stored);\n return;\n }\n\n // Fallback: calculate sizes using defaultSize or split equally\n let remaining = 100;\n let undefCount = 0;\n const initial = panels.map((p) => {\n if (p.props.defaultSize !== undefined) {\n remaining -= p.props.defaultSize;\n return p.props.defaultSize;\n }\n undefCount++;\n return -1;\n });\n\n const fallback = undefCount > 0 ? Math.max(0, remaining / undefCount) : 0;\n const finalSizes = initial.map((s) => (s === -1 ? fallback : s));\n setSizes(finalSizes);\n }, [storageKey, getPanels]);\n\n const activeResizeRef = React.useRef<{\n handleIndex: number;\n initialPointer: number;\n initialSizes: number[];\n totalSize: number;\n lastSizes: number[];\n } | null>(null);\n\n const startResize = (handleIndex: number, e: React.PointerEvent) => {\n e.preventDefault();\n const container = containerRef.current;\n if (!container || handleIndex < 0 || handleIndex + 1 >= sizes.length) return;\n\n const rect = container.getBoundingClientRect();\n const totalSize = direction === \"horizontal\" ? rect.width : rect.height;\n if (totalSize <= 0) return;\n\n const initialPointer = direction === \"horizontal\" ? e.clientX : e.clientY;\n\n activeResizeRef.current = {\n handleIndex,\n initialPointer,\n initialSizes: [...sizes],\n totalSize,\n lastSizes: [...sizes],\n };\n\n // Drag capture starts\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n container.classList.add(\"select-none\");\n };\n\n const handlePointerMove = (e: React.PointerEvent) => {\n const active = activeResizeRef.current;\n if (!active) return;\n\n const currentPointer = direction === \"horizontal\" ? e.clientX : e.clientY;\n const deltaPx = currentPointer - active.initialPointer;\n const deltaPct = (deltaPx / active.totalSize) * 100;\n\n const nextSizes = resizePanelPair(\n active.initialSizes,\n active.handleIndex,\n active.initialSizes[active.handleIndex] + deltaPct\n );\n\n active.lastSizes = nextSizes;\n setSizes(nextSizes);\n };\n\n const handlePointerUp = (e: React.PointerEvent) => {\n const active = activeResizeRef.current;\n if (!active) return;\n\n (e.target as HTMLElement).releasePointerCapture(e.pointerId);\n containerRef.current?.classList.remove(\"select-none\");\n persistSizes(active.lastSizes);\n activeResizeRef.current = null;\n };\n\n const handleKeyDown = (handleIndex: number, e: React.KeyboardEvent) => {\n const step = e.shiftKey ? 10 : 2;\n let delta: number | null = null;\n let boundary: \"min\" | \"max\" | null = null;\n\n if (direction === \"horizontal\") {\n if (e.key === \"ArrowLeft\") {\n delta = -step;\n } else if (e.key === \"ArrowRight\") {\n delta = step;\n }\n } else if (e.key === \"ArrowUp\") {\n delta = -step;\n } else if (e.key === \"ArrowDown\") {\n delta = step;\n }\n\n if (e.key === \"Home\") {\n boundary = \"min\";\n } else if (e.key === \"End\") {\n boundary = \"max\";\n }\n\n if (delta === null && boundary === null) return;\n\n e.preventDefault();\n\n setSizes((currentSizes) => {\n const targetLeftSize =\n boundary === \"min\"\n ? getHandleValue(currentSizes, handleIndex).min\n : boundary === \"max\"\n ? getHandleValue(currentSizes, handleIndex).max\n : (currentSizes[handleIndex] ?? 0) + delta!;\n\n const nextSizes = resizePanelPair(\n currentSizes,\n handleIndex,\n targetLeftSize\n );\n persistSizes(nextSizes);\n return nextSizes;\n });\n };\n\n const toggleCollapse = (idx: number) => {\n if (sizes.length === 0) return;\n\n const panels = getPanels();\n const pLeft = panels[idx]?.props ?? {};\n const pRight = panels[idx + 1]?.props ?? {};\n\n const minLeft = pLeft.minSize ?? 10;\n const minRight = pRight.minSize ?? 10;\n\n const totalCombined = sizes[idx] + sizes[idx + 1];\n const nextSizes = [...sizes];\n\n // Double-click toggle collapsible panel logic\n if (sizes[idx] === 0 && pLeft.collapsible) {\n nextSizes[idx] = minLeft;\n nextSizes[idx + 1] = totalCombined - minLeft;\n } else if (sizes[idx + 1] === 0 && pRight.collapsible) {\n nextSizes[idx + 1] = minRight;\n nextSizes[idx] = totalCombined - minRight;\n } else {\n if (pLeft.collapsible) {\n nextSizes[idx] = 0;\n nextSizes[idx + 1] = totalCombined;\n } else if (pRight.collapsible) {\n nextSizes[idx + 1] = 0;\n nextSizes[idx] = totalCombined;\n }\n }\n\n setSizes(nextSizes);\n persistSizes(nextSizes);\n };\n\n let panelCounter = 0;\n\n return (\n \n {React.Children.map(children, (child) => {\n if (!React.isValidElement(child)) return child;\n\n if (\n child.type === ResizablePanel ||\n (child.type as any).displayName === \"ResizablePanel\"\n ) {\n const currentIdx = panelCounter++;\n const currentSize = sizes[currentIdx];\n return React.cloneElement(child as React.ReactElement, {\n size: currentSize,\n direction,\n });\n }\n\n if (\n child.type === ResizableHandle ||\n (child.type as any).displayName === \"ResizableHandle\"\n ) {\n const currentIdx = panelCounter - 1;\n const handleValue = getHandleValue(sizes, currentIdx);\n return React.cloneElement(child as React.ReactElement, {\n direction,\n \"aria-valuemin\": handleValue.min,\n \"aria-valuemax\": handleValue.max,\n \"aria-valuenow\": handleValue.now,\n \"aria-valuetext\": `${handleValue.now}%`,\n onPointerDown: (e: React.PointerEvent) => startResize(currentIdx, e),\n onPointerMove: handlePointerMove,\n onPointerUp: handlePointerUp,\n onKeyDown: (e: React.KeyboardEvent) => handleKeyDown(currentIdx, e),\n onDoubleClick: () => toggleCollapse(currentIdx),\n });\n }\n\n return child;\n })}\n \n );\n }\n);\nResizableGroup.displayName = \"ResizableGroup\";\n\n/* ─── Resizable Panel ───────────────────────────────────────── */\n\nexport interface ResizablePanelProps extends React.HTMLAttributes {\n defaultSize?: number;\n minSize?: number;\n maxSize?: number;\n collapsible?: boolean;\n size?: number; // Injected by Group\n direction?: \"horizontal\" | \"vertical\"; // Injected by Group\n}\n\nexport const ResizablePanel = React.forwardRef(\n ({ className, defaultSize, minSize, maxSize, collapsible, size, direction, style, ...props }, ref) => {\n const currentSize = size !== undefined ? size : defaultSize;\n\n const panelStyle: React.CSSProperties = {\n ...style,\n flexGrow: currentSize !== undefined ? currentSize : 1,\n flexShrink: currentSize !== undefined ? 0 : 1,\n flexBasis: currentSize !== undefined ? `${currentSize}%` : \"0px\",\n };\n\n if (currentSize === 0) {\n panelStyle.display = \"none\";\n }\n\n return (\n \n );\n }\n);\nResizablePanel.displayName = \"ResizablePanel\";\n\n/* ─── Resizable Handle ──────────────────────────────────────── */\n\nexport interface ResizableHandleProps extends React.HTMLAttributes {\n direction?: \"horizontal\" | \"vertical\"; // Injected by Group\n}\n\nexport const ResizableHandle = React.forwardRef(\n ({ className, direction = \"horizontal\", ...props }, ref) => {\n const isHorizontal = direction === \"horizontal\";\n\n return (\n \n {/* Visual line / grip */}\n \n \n );\n }\n);\nResizableHandle.displayName = \"ResizableHandle\";\n\n/* ─── Ref Forker Helper ─────────────────────────────────────── */\n\nfunction useForkRef(\n refA: React.Ref | undefined,\n refB: React.Ref | undefined\n): React.RefCallback | null {\n return React.useMemo(() => {\n if (refA == null && refB == null) return null;\n return (value) => {\n setRef(refA, value);\n setRef(refB, value);\n };\n }, [refA, refB]);\n}\n\nfunction setRef(ref: React.Ref | undefined, value: T | null) {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref && \"current\" in ref) {\n (ref as React.MutableRefObject).current = value;\n }\n}\n\nfunction roundSize(value: number) {\n return Math.round(value * 100) / 100;\n}\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-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/resizable.tsx. For Pro components and updates, use pnpm dlx wensity@latest add resizable.", "categories": [ "UI Primitives", "wensity", "free" ], "meta": { "wensity": { "slug": "resizable", "access": "free", "category": "UI Primitives", "kind": "component", "componentUrl": "https://ui.wensity.com/primitives/resizable", "shadcnUrl": "https://ui.wensity.com/r/resizable", "cliInstall": "pnpm dlx wensity@latest add resizable" } } }