UNPKG

lightswind

Version:

A collection of beautifully crafted React Components, Blocks & Templates for Modern Developers. Create stunning web applications effortlessly by using our 160+ professional and animated react components.

206 lines 9.79 kB
"use strict"; "use client"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.TabsContent = exports.TabsTrigger = exports.TabsList = exports.Tabs = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const React = __importStar(require("react")); const utils_1 = require("@/components/lib/utils"); const framer_motion_1 = require("framer-motion"); const TabsContext = React.createContext({ value: "", onValueChange: () => { }, updateIndicator: () => { }, scheduleUpdateIndicator: () => { }, indicatorStyle: {}, mounted: false, registerTabTrigger: () => { }, registerTabsList: () => { }, }); const Tabs = React.forwardRef(({ className, defaultValue, value, onValueChange, children, ...props }, ref) => { const [internalValue, setInternalValue] = React.useState(defaultValue || ""); const [indicatorStyle, setIndicatorStyle] = React.useState({}); const [mounted, setMounted] = React.useState(false); const tabsListRef = React.useRef(null); const tabTriggerRefs = React.useRef(new Map()); const controlled = value !== undefined; const currentValue = controlled ? value : internalValue; const registerTabsList = React.useCallback((element) => { tabsListRef.current = element; }, []); const registerTabTrigger = React.useCallback((value, element) => { if (element) { tabTriggerRefs.current.set(value, element); } else { tabTriggerRefs.current.delete(value); } }, []); const updateIndicator = React.useCallback(() => { if (tabsListRef.current && currentValue) { const activeTab = tabTriggerRefs.current.get(currentValue); if (activeTab) { const tabRect = activeTab.getBoundingClientRect(); const listRect = tabsListRef.current.getBoundingClientRect(); // Only update if both rects have valid dimensions (element is visible) if (tabRect.width > 0 && listRect.width > 0) { setIndicatorStyle({ left: `${tabRect.left - listRect.left}px`, width: `${tabRect.width}px`, }); } } } }, [currentValue]); const scheduleUpdateIndicator = React.useCallback(() => { // Use rAF to defer until after browser has painted layout requestAnimationFrame(() => { updateIndicator(); }); }, [updateIndicator]); React.useEffect(() => { setMounted(true); // Schedule update after paint so dimensions are correct even inside popovers/dialogs scheduleUpdateIndicator(); window.addEventListener("resize", scheduleUpdateIndicator); return () => window.removeEventListener("resize", scheduleUpdateIndicator); }, [scheduleUpdateIndicator]); // Watch for when the tabs list becomes visible in the DOM // (e.g., when a Popover or Dialog opens), then re-compute the indicator React.useEffect(() => { const listEl = tabsListRef.current; if (!listEl) return; const observer = new IntersectionObserver((entries) => { if (entries[0]?.isIntersecting) { scheduleUpdateIndicator(); } }, { threshold: 0.1 }); observer.observe(listEl); return () => observer.disconnect(); }, [scheduleUpdateIndicator]); const handleValueChange = React.useCallback((newValue) => { if (!controlled) setInternalValue(newValue); onValueChange?.(newValue); }, [controlled, onValueChange]); return ((0, jsx_runtime_1.jsx)(TabsContext.Provider, { value: { value: currentValue, onValueChange: handleValueChange, updateIndicator, scheduleUpdateIndicator, indicatorStyle, mounted, registerTabTrigger, registerTabsList, }, children: (0, jsx_runtime_1.jsx)("div", { ref: ref, className: (0, utils_1.cn)("w-full", className), ...props, children: children }) })); }); exports.Tabs = Tabs; Tabs.displayName = "Tabs"; const TabsList = React.forwardRef(({ className, ...props }, ref) => { const { indicatorStyle, registerTabsList, mounted } = React.useContext(TabsContext); return ((0, jsx_runtime_1.jsxs)("div", { ref: (el) => { if (typeof ref === "function") ref(el); else if (ref) ref.current = el; registerTabsList(el); }, className: (0, utils_1.cn)(`relative inline-flex h-8 items-center justify-center rounded-full bg-muted text-primary`, className), ...props, children: [mounted && ((0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { layout: true, className: "tabs-bg-indicator absolute top-0 left-0 h-full rounded-full bg-gradient-tabs", style: { ...indicatorStyle, position: "absolute", top: 0, borderRadius: "9999px", height: "100%", zIndex: 0, }, transition: { type: "spring", stiffness: 300, damping: 30 } })), props.children] })); }); exports.TabsList = TabsList; TabsList.displayName = "TabsList"; const TabsTrigger = React.forwardRef(({ className, value, ...props }, ref) => { const { value: selectedValue, onValueChange, registerTabTrigger, updateIndicator, scheduleUpdateIndicator } = React.useContext(TabsContext); const isActive = selectedValue === value; const triggerRef = React.useRef(null); React.useEffect(() => { registerTabTrigger(value, triggerRef.current); return () => registerTabTrigger(value, null); }, [value, registerTabTrigger]); React.useEffect(() => { // Use scheduleUpdateIndicator for deferred measurement (fixes popover/dialog initial state) if (isActive) scheduleUpdateIndicator(); }, [isActive, scheduleUpdateIndicator]); return ((0, jsx_runtime_1.jsx)("button", { ref: (el) => { if (typeof ref === "function") ref(el); else if (ref) ref.current = el; triggerRef.current = el; }, type: "button", role: "tab", "aria-selected": isActive, "data-state": isActive ? "active" : "inactive", "data-value": value, className: (0, utils_1.cn)(`relative z-10 inline-flex items-center justify-center whitespace-nowrap rounded-full px-3 py-0.5 md:py-1.5 text-xs lg:text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50`, isActive ? "text-white dark:text-black" : "", className), onClick: () => onValueChange(value), ...props })); }); exports.TabsTrigger = TabsTrigger; TabsTrigger.displayName = "TabsTrigger"; const TabsContent = React.forwardRef(({ className, value, ...props }, ref) => { const { value: selectedValue, updateIndicator } = React.useContext(TabsContext); const isActive = selectedValue === value; const contentRef = React.useRef(null); // Trigger updateIndicator when content resizes React.useEffect(() => { if (!isActive || !contentRef.current) return; const observer = new ResizeObserver(() => { updateIndicator(); }); observer.observe(contentRef.current); return () => observer.disconnect(); }, [isActive, updateIndicator]); return ((0, jsx_runtime_1.jsx)(framer_motion_1.AnimatePresence, { mode: "wait", children: isActive && ((0, jsx_runtime_1.jsx)("div", { ref: (el) => { contentRef.current = el; if (typeof ref === "function") ref(el); else if (ref) ref.current = el; }, role: "tabpanel", "data-state": "active", "data-value": value, className: (0, utils_1.cn)(`mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 mx-auto w-full`, className), style: { scrollbarWidth: "none", msOverflowStyle: "none", }, ...props }, value)) })); }); exports.TabsContent = TabsContent; TabsContent.displayName = "TabsContent"; //# sourceMappingURL=tabs.js.map