Skip to content

Commit

Permalink
Add breadcrumb animation
Browse files Browse the repository at this point in the history
  • Loading branch information
josepjaume committed Jan 20, 2025
1 parent f3bb275 commit ff1a5ff
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 29 deletions.
107 changes: 93 additions & 14 deletions lib/experimental/Navigation/Header/Breadcrumbs/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Documents, Recruitment } from "@/icons/modules"
import type { Meta, StoryObj } from "@storybook/react"
import { useState } from "react"
import Breadcrumbs from "./index"

const meta: Meta<typeof Breadcrumbs> = {
Expand All @@ -14,53 +15,131 @@ type Story = StoryObj<typeof Breadcrumbs>
export const Default: Story = {
args: {
breadcrumbs: [
{ label: "Recruitment", href: "/recruitment", icon: Recruitment },
{ label: "Candidates", href: "/recruitment/candidates" },
{ label: "Dani Moreno" },
{
id: "recruitment",
label: "Recruitment",
href: "/recruitment",
icon: Recruitment,
},
{
id: "candidates",
label: "Candidates",
href: "/recruitment/candidates",
},
{ id: "dani-moreno", label: "Dani Moreno" },
],
},
}

export const LoadingLastItem: Story = {
args: {
breadcrumbs: [
{ label: "Recruitment", href: "/recruitment", icon: Recruitment },
{ label: "Candidates", href: "/recruitment/candidates" },
{ loading: true },
{
id: "recruitment",
label: "Recruitment",
href: "/recruitment",
icon: Recruitment,
},
{
id: "candidates",
label: "Candidates",
href: "/recruitment/candidates",
},
{ id: "loading", label: "Loading", loading: true },
],
},
}

export const LoadingLastTwoItems: Story = {
args: {
breadcrumbs: [
{ label: "Recruitment", href: "/recruitment", icon: Recruitment },
{ loading: true },
{ loading: true },
{
id: "recruitment",
label: "Recruitment",
href: "/recruitment",
icon: Recruitment,
},
{ id: "loading-1", label: "Loading", loading: true },
{ id: "loading-2", label: "Loading", loading: true },
],
},
}

export const LongBreadcrumbs: Story = {
args: {
breadcrumbs: [
{ label: "Documents", href: "/", icon: Documents },
{ label: "Employee Documents", href: "/documents" },
{ label: "Human Resources", href: "/documents/hr" },
{ label: "Recruitment", href: "/documents/hr/recruitment" },
{ label: "Candidates", href: "/documents/hr/recruitment/candidates" },
{ id: "documents", label: "Documents", href: "/", icon: Documents },
{
id: "employee-documents",
label: "Employee Documents",
href: "/documents",
},
{
id: "human-resources",
label: "Human Resources",
href: "/documents/hr",
},
{
id: "recruitment",
label: "Recruitment",
href: "/documents/hr/recruitment",
},
{
id: "candidates",
label: "Candidates",
href: "/documents/hr/recruitment/candidates",
},
{
id: "dani-moreno",
label: "Dani Moreno",
href: "/dani-moreno",
},
{
id: "applications",
label: "Applications",
href: "/dani-moreno/applications",
},
{
id: "interviews",
label: "Interviews",
href: "/dani-moreno/applications/interviews",
},
],
},
}

export const Interactive: Story = {
render: () => {
const [showLast, setShowLast] = useState(true)

const baseBreadcrumbs = [
{
id: "recruitment",
label: "Recruitment",
href: "/recruitment",
icon: Recruitment,
},
{
id: "candidates",
label: "Candidates",
href: "/recruitment/candidates",
},
]

const breadcrumbs = showLast
? [...baseBreadcrumbs, { id: "dani-moreno", label: "Dani Moreno" }]
: baseBreadcrumbs

return (
<div className="space-y-4">
<button
onClick={() => setShowLast((prev) => !prev)}
className="rounded-md bg-f1-background-secondary px-4 py-2 text-sm font-medium text-f1-foreground hover:bg-f1-background-secondary/80"
>
{showLast ? "Remove Last Item" : "Add Last Item"}
</button>
<Breadcrumbs breadcrumbs={breadcrumbs} />
</div>
)
},
}
11 changes: 8 additions & 3 deletions lib/experimental/Navigation/Header/Breadcrumbs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ import { NavigationItem } from "../../utils"
import { IconType } from "@/components/Utilities/Icon"
import { useEffect, useRef, useState } from "react"

export type BreadcrumbItemType =
export type BreadcrumbItemType = { id: string } & (
| (NavigationItem & {
icon?: IconType
})
| {
loading: true
}
)

function BreadcrumbSkeleton() {
return (
Expand Down Expand Up @@ -102,7 +103,7 @@ function BreadcrumbItem({
showSeparator = true,
}: BreadcrumbItemProps) {
return (
<ShadBreadcrumbItem>
<ShadBreadcrumbItem key={item.id}>
<div className="flex items-center">
<BreadcrumbContent item={item} isLast={isLast} />
</div>
Expand Down Expand Up @@ -198,22 +199,26 @@ export default function Breadcrumbs({ breadcrumbs }: BreadcrumbsProps) {
const lastItems = breadcrumbs.slice(-visibleCount + 1)
const collapsedItems = breadcrumbs.slice(1, -visibleCount + 1)

console.log(lastItems)

return (
<Breadcrumb ref={containerRef} className="w-full">
<BreadcrumbList>
<BreadcrumbItem
key={firstItem.id}
item={firstItem}
isLast={false}
showSeparator={!("loading" in firstItem)}
/>
{collapsedItems.length > 0 && (
<CollapsedBreadcrumbItem
key="collapsed-items"
items={collapsedItems as DropdownItemWithoutIcon[]}
/>
)}
{lastItems.map((item, index) => (
<BreadcrumbItem
key={index}
key={item.id}
item={item}
isLast={index === lastItems.length - 1}
showSeparator={index !== lastItems.length - 1}
Expand Down
30 changes: 18 additions & 12 deletions lib/ui/breadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Slot } from "@radix-ui/react-slot"
import { AnimatePresence, HTMLMotionProps, motion } from "framer-motion"
import { ChevronRight, MoreHorizontal } from "lucide-react"
import * as React from "react"

Expand All @@ -16,28 +17,33 @@ Breadcrumb.displayName = "Breadcrumb"
const BreadcrumbList = React.forwardRef<
HTMLOListElement,
React.ComponentPropsWithoutRef<"ol">
>(({ className, ...props }, ref) => (
>(({ className, children, ...props }, ref) => (
<ol
ref={ref}
className={cn(
"flex list-none flex-nowrap items-center gap-1 text-f1-foreground-secondary",
className
)}
{...props}
/>
>
<AnimatePresence>{children}</AnimatePresence>
</ol>
))
BreadcrumbList.displayName = "BreadcrumbList"

const BreadcrumbItem = React.forwardRef<
HTMLLIElement,
React.ComponentPropsWithoutRef<"li">
>(({ className, ...props }, ref) => (
<li
ref={ref}
className={cn("inline-flex items-center gap-0.5", className)}
{...props}
/>
))
const BreadcrumbItem = React.forwardRef<HTMLLIElement, HTMLMotionProps<"li">>(
({ className, ...props }, ref) => (
<motion.li
ref={ref}
className={cn("inline-flex items-center gap-0.5", className)}
initial={{ opacity: 0, translateX: -8 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: -8 }}
transition={{ duration: 1 }}
{...props}
/>
)
)
BreadcrumbItem.displayName = "BreadcrumbItem"

const BreadcrumbLink = React.forwardRef<
Expand Down

0 comments on commit ff1a5ff

Please sign in to comment.