{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"gantt-nav","type":"registry:ui","title":"Composable navigation - Today, view selector, prev/next, go-to-date, period title, and a free toolbar slot.","description":"Composable navigation - Today, view selector, prev/next, go-to-date, period title, and a free toolbar slot.","dependencies":["date-fns","radix-ui"],"registryDependencies":["button","calendar","dropdown-menu","@neui/gantt","@neui/gantt-lib","@neui/gantt-types","popover","tooltip"],"files":[{"path":"gantt-nav.tsx","type":"registry:ui","content":"// Title: Gantt Nav\n// Description: Composable navigation - Today, view selector, prev/next, go-to-date, period title, and a free toolbar slot.\n\n\"use client\"\n\nimport {\n useState,\n type ButtonHTMLAttributes,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\"\nimport {\n useGanttNavigation,\n useGanttScale,\n useGanttSettings,\n useGanttViewConfig,\n} from \"@/components/neui/gantt/gantt\"\nimport { toZoned } from \"@/components/neui/gantt/gantt-lib\"\nimport type { GanttScale } from \"@/components/neui/gantt/gantt-types\"\nimport { format } from \"date-fns\"\nimport { Slot } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { Calendar } from \"@/components/ui/calendar\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/components/ui/popover\"\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"@/components/ui/tooltip\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nconst GANTT_SCALES: GanttScale[] = [\"day\", \"week\", \"month\", \"quarter\", \"year\"]\n\n/** Configured nav button variant/size (viewConfig.navButtonVariant/Size). */\nfunction useNavButtonProps(): {\n variant: \"ghost\" | \"outline\" | \"secondary\" | \"default\"\n size: \"sm\" | \"default\"\n iconSize: \"icon-sm\" | \"icon\"\n} {\n const viewConfig = useGanttViewConfig()\n return {\n variant: viewConfig.navButtonVariant,\n size: viewConfig.navButtonSize,\n iconSize: viewConfig.navButtonSize === \"sm\" ? \"icon-sm\" : \"icon\",\n }\n}\n\ntype NavButtonProps = Omit<\n ButtonHTMLAttributes,\n \"children\"\n> & {\n children?: ReactNode\n /**\n * Tooltip policy (the part that usually goes wrong on clickable elements):\n * tooltips appear ONLY on hover or keyboard focus-visible - a pointer click\n * never re-triggers them, and buttons that open overlays (the period\n * selector, consumer dialog buttons) get NO tooltip at all so nothing\n * flashes when focus returns. Icon-only buttons default to their accessible\n * label; Today defaults to the actual current date. Pass null to disable.\n */\n tooltip?: ReactNode | null\n asChild?: boolean\n}\n\n/** Hover/focus-visible tooltip wrapper; renders the bare button when disabled. */\nfunction NavTooltip({\n content,\n children,\n}: {\n content: ReactNode | null\n children: React.ReactElement\n}) {\n if (content === null || content === undefined) return children\n return (\n \n {children}\n {content}\n \n )\n}\n\nfunction GanttNavToday({\n className,\n asChild = false,\n children,\n tooltip,\n ...props\n}: NavButtonProps) {\n const { today, isToday } = useGanttNavigation()\n const settings = useGanttSettings()\n const nav = useNavButtonProps()\n // zoned: a system-zone new Date() can name a different day than Today opens\n const defaultTooltip = format(\n toZoned(new Date(), settings.timeZone),\n settings.i18n.formats.dayTitle,\n { locale: settings.locale }\n )\n return (\n \n \n {children ?? settings.i18n.labels.today}\n \n \n )\n}\n\nfunction GanttNavPrev({\n className,\n asChild = false,\n children,\n tooltip,\n ...props\n}: NavButtonProps) {\n const { prev } = useGanttNavigation()\n const settings = useGanttSettings()\n const nav = useNavButtonProps()\n return (\n \n \n {children ?? (\n \n )}\n \n \n )\n}\n\nfunction GanttNavNext({\n className,\n asChild = false,\n children,\n tooltip,\n ...props\n}: NavButtonProps) {\n const { next } = useGanttNavigation()\n const settings = useGanttSettings()\n const nav = useNavButtonProps()\n return (\n \n \n {children ?? (\n \n )}\n \n \n )\n}\n\ninterface GanttTitleProps extends HTMLAttributes {\n format?: (ctx: { title: string }) => ReactNode\n asChild?: boolean\n}\n\nfunction GanttTitle({\n className,\n asChild = false,\n children,\n format: formatTitle,\n ...props\n}: GanttTitleProps) {\n const { title } = useGanttNavigation()\n const Comp = asChild ? Slot.Root : \"div\"\n return (\n \n {children ?? formatTitle?.({ title }) ?? title}\n \n )\n}\n\ninterface GanttScaleSwitcherProps extends Omit<\n ButtonHTMLAttributes,\n \"children\"\n> {\n children?: ReactNode\n /** Hover/focus-visible hint; defaults to the \"Select view\" label. Pass\n * null to disable (overlay-opener policy). */\n tooltip?: ReactNode | null\n /** The offered scales, in menu order. Default: all five. */\n scales?: GanttScale[]\n asChild?: boolean\n}\n\n/**\n * Scale switcher (\"Select view\"): Day / Week / Month / Quarter / Year, a ghost\n * dropdown button (same shape as the event-calendar view switcher).\n */\nfunction GanttScaleSwitcher({\n className,\n asChild,\n children,\n tooltip,\n scales = GANTT_SCALES,\n ...props\n}: GanttScaleSwitcherProps) {\n const { scale, setScale } = useGanttScale()\n const settings = useGanttSettings()\n const nav = useNavButtonProps()\n const labels = settings.i18n.labels\n // Controlled open: selecting a scale swaps the whole track subtree in the\n // same click, so closing must not depend on the menu's internal handler.\n const [open, setOpen] = useState(false)\n // Hover-only tooltip: when the menu closes, Radix focuses the trigger\n // again and a focus-opened tooltip would flash - Radix has no open reason,\n // so focus opens are suppressed at the trigger via preventDefault.\n const [tipOpen, setTipOpen] = useState(false)\n\n const selectScale = (next: GanttScale) => {\n setOpen(false)\n setScale(next)\n }\n\n return (\n {\n setOpen(next)\n if (next) setTipOpen(false)\n }}\n >\n {/* Tooltip on an overlay-opener: hover-only (focus opens ignored) and\n force-closed while the menu is up, so it never lingers or flashes\n when focus returns on close. */}\n setTipOpen(next)}\n >\n \n e.preventDefault()}>\n \n {children ?? (\n <>\n {labels.scales[scale]}\n \n \n )}\n \n \n \n {tipOpen && !open && tooltip !== null && (\n \n {tooltip ?? labels.selectView}\n \n )}\n \n \n {/* Keep the label inside the group so it stays associated with its items */}\n \n \n {labels.selectView}\n \n {scales.map((value) => (\n selectScale(value)}\n >\n {labels.scales[value]}\n \n ))}\n \n \n \n )\n}\n\ninterface GanttDatePickerProps {\n className?: string\n}\n\n/**\n * Compact go-to-date picker (shadcn Calendar in a popover). No tooltip by\n * design: it opens an overlay (see the NavButtonProps tooltip policy).\n */\nfunction GanttDatePicker({ className }: GanttDatePickerProps) {\n const { date, goTo } = useGanttNavigation()\n const settings = useGanttSettings()\n const nav = useNavButtonProps()\n const [open, setOpen] = useState(false)\n const zoned = toZoned(date, settings.timeZone)\n\n return (\n \n \n \n \n \n \n \n {\n if (next) {\n goTo(next)\n setOpen(false)\n }\n }}\n />\n \n \n )\n}\n\ninterface GanttToolbarProps extends HTMLAttributes {\n asChild?: boolean\n}\n\n/** Free slot for consumer toolbar buttons; pure layout shell. */\nfunction GanttToolbar({\n className,\n asChild = false,\n children,\n ...props\n}: GanttToolbarProps) {\n const viewConfig = useGanttViewConfig()\n const Comp = asChild ? Slot.Root : \"div\"\n return (\n \n {children}\n \n )\n}\n\ninterface GanttNavProps extends HTMLAttributes {\n asChild?: boolean\n}\n\n/**\n * Default composed nav (event-calendar parity): Today, time-period switcher,\n * prev/next, title, spacer. GanttDatePicker stays available for custom\n * compositions. Pass children to use it as a pure layout shell instead.\n */\nfunction GanttNav({\n className,\n asChild = false,\n children,\n ...props\n}: GanttNavProps) {\n const viewConfig = useGanttViewConfig()\n const Comp = asChild ? Slot.Root : \"div\"\n return (\n \n {children ?? (\n // Shared provider: first tooltip waits, moving between buttons is instant\n \n \n \n
\n \n \n
\n \n
\n \n )}\n \n )\n}\n\nexport {\n GANTT_SCALES,\n GanttDatePicker,\n GanttNav,\n GanttNavNext,\n GanttNavPrev,\n GanttNavToday,\n GanttScaleSwitcher,\n GanttTitle,\n GanttToolbar,\n}\nexport type {\n GanttNavProps,\n GanttScaleSwitcherProps,\n GanttTitleProps,\n GanttToolbarProps,\n}","target":"components/neui/gantt/gantt-nav.tsx"}]}