diff --git a/web/src/components/ui/breadcrumb/Breadcrumb.stories.tsx b/web/src/components/ui/breadcrumb/Breadcrumb.stories.tsx new file mode 100644 index 000000000..220a35603 --- /dev/null +++ b/web/src/components/ui/breadcrumb/Breadcrumb.stories.tsx @@ -0,0 +1,60 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Breadcrumb } from './Breadcrumb'; +import { BusterAppRoutes } from '@/routes/busterRoutes/busterAppRoutes'; + +const meta: Meta = { + title: 'Base/Breadcrumb', + component: Breadcrumb, + tags: ['autodocs'], + parameters: { + layout: 'centered' + } +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + items: [ + { label: 'Home', route: { route: BusterAppRoutes.APP_ROOT } }, + { label: 'Datasets', route: { route: BusterAppRoutes.APP_DATASETS } }, + { label: 'Current Dataset' } + ] + } +}; + +export const WithDropdown: Story = { + args: { + items: [ + { label: 'Home', route: { route: BusterAppRoutes.APP_ROOT } }, + { + label: null, + dropdown: [ + { label: 'Dataset A', route: { route: BusterAppRoutes.APP_DATASETS } }, + { label: 'Dataset B', route: { route: BusterAppRoutes.APP_DATASETS } }, + { label: 'Dataset C', route: { route: BusterAppRoutes.APP_DATASETS } } + ] + }, + { label: 'Current Dataset' } + ] + } +}; + +export const CustomActiveIndex: Story = { + args: { + items: [ + { label: 'Home', route: { route: BusterAppRoutes.APP_ROOT } }, + { label: 'Datasets', route: { route: BusterAppRoutes.APP_DATASETS } }, + { label: 'Settings', route: { route: BusterAppRoutes.SETTINGS_GENERAL } }, + { label: 'Profile' } + ], + activeIndex: 2 + } +}; + +export const SingleItem: Story = { + args: { + items: [{ label: 'Home' }] + } +}; diff --git a/web/src/components/ui/breadcrumb/Breadcrumb.tsx b/web/src/components/ui/breadcrumb/Breadcrumb.tsx new file mode 100644 index 000000000..f39a513c3 --- /dev/null +++ b/web/src/components/ui/breadcrumb/Breadcrumb.tsx @@ -0,0 +1,108 @@ +import React, { useMemo } from 'react'; + +import { + Breadcrumb as BreadcrumbBase, + BreadcrumbList, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbPage, + BreadcrumbSeparator, + BreadcrumbEllipsis +} from './BreadcrumbBase'; +import { createBusterRoute } from '@/routes/busterRoutes'; +import { Dropdown, DropdownItem } from '../dropdown/Dropdown'; +import Link from 'next/link'; + +type CreateBusterRouteParams = Parameters[0]; + +interface BreadcrumbItem { + label: string | null; //if null, it will be an ellipsis + route?: CreateBusterRouteParams; + dropdown?: { label: string; route: CreateBusterRouteParams }[]; +} + +interface BreadcrumbProps { + items: BreadcrumbItem[]; + activeIndex?: number; //default will be the last item +} + +export const Breadcrumb = React.memo( + React.forwardRef(({ items, activeIndex }, ref) => { + const chosenIndex = activeIndex ?? items.length - 1; + const lastItemIndex = items.length - 1; + + console.log('chosenIndex', chosenIndex); + + return ( + + + {items.map((item, index) => ( + + ))} + + + ); + }) +); + +const BreadcrumbItemSelector: React.FC<{ + item: BreadcrumbItem; + isActive: boolean; + isLast: boolean; +}> = ({ item, isActive, isLast }) => { + const ChosenComponent = useMemo(() => { + if (item.dropdown) { + return ; + } + if (isActive) { + return {item.label}; + } + + return ( + + {item.route ? ( + {item.label} + ) : ( + {item.label} + )} + + ); + }, [isActive, item.label, item.route, item.dropdown]); + + return ( + <> + {ChosenComponent} + {!isLast && } + + ); +}; + +const BreadcrumbDropdown: React.FC<{ + items: { label: string; route: CreateBusterRouteParams }[]; +}> = ({ items }) => { + const dropdownItems: DropdownItem[] = useMemo(() => { + return items.map((item) => { + const route = createBusterRoute(item.route); + return { + label: {item.label}, + value: route + }; + }); + }, [items]); + + return ( + +
+ + Toggle menu +
+
+ ); +}; + +Breadcrumb.displayName = 'Breadcrumb'; diff --git a/web/src/components/ui/breadcrumb/BreadcrumbBase.tsx b/web/src/components/ui/breadcrumb/BreadcrumbBase.tsx new file mode 100644 index 000000000..27b7f9385 --- /dev/null +++ b/web/src/components/ui/breadcrumb/BreadcrumbBase.tsx @@ -0,0 +1,101 @@ +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { ChevronRight, Dots } from '../icons'; +import Link from 'next/link'; + +import { cn } from '@/lib/utils'; + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<'nav'> & { + separator?: React.ReactNode; + } +>(({ ...props }, ref) =>