bolt.diy/app/components/ui/Breadcrumbs.tsx
Stijnus 870bfc58ee
Some checks are pending
Docker Publish / docker-build-publish (push) Waiting to run
Update Stable Branch / prepare-release (push) Waiting to run
feat: github fix and ui improvements (#1685)
* feat: Add reusable UI components and fix GitHub repository display

* style: Fix linting issues in UI components

* fix: Add close icon to GitHub Connection Required dialog

* fix: Add CloseButton component to fix white background issue in dialog close icons

* Fix close button styling in dialog components to address ghost white issue in dark mode

* fix: update icon color to tertiary for consistency

The icon color was changed from `text-bolt-elements-icon-info` to `text-bolt-elements-icon-tertiary`

* fix: improve repository selection dialog tab styling for dark mode

- Update tab menu styling to prevent white background in dark mode
- Use explicit color values for better dark/light mode compatibility
- Improve hover and active states for better visual hierarchy
- Remove unused Tabs imports

---------

Co-authored-by: KevIsDev <zennerd404@gmail.com>
2025-05-09 15:23:20 +02:00

102 lines
2.9 KiB
TypeScript

import React from 'react';
import { classNames } from '~/utils/classNames';
import { motion } from 'framer-motion';
interface BreadcrumbItem {
label: string;
href?: string;
icon?: string;
onClick?: () => void;
}
interface BreadcrumbsProps {
items: BreadcrumbItem[];
className?: string;
separator?: string;
maxItems?: number;
renderItem?: (item: BreadcrumbItem, index: number, isLast: boolean) => React.ReactNode;
}
export function Breadcrumbs({
items,
className,
separator = 'i-ph:caret-right',
maxItems = 0,
renderItem,
}: BreadcrumbsProps) {
const displayItems =
maxItems > 0 && items.length > maxItems
? [
...items.slice(0, 1),
{ label: '...', onClick: undefined, href: undefined },
...items.slice(-Math.max(1, maxItems - 2)),
]
: items;
const defaultRenderItem = (item: BreadcrumbItem, index: number, isLast: boolean) => {
const content = (
<div className="flex items-center gap-1.5">
{item.icon && <span className={classNames(item.icon, 'w-3.5 h-3.5')} />}
<span
className={classNames(
isLast
? 'font-medium text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary-dark'
: 'text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary-dark hover:text-bolt-elements-textPrimary dark:hover:text-bolt-elements-textPrimary-dark',
item.onClick || item.href ? 'cursor-pointer' : '',
)}
>
{item.label}
</span>
</div>
);
if (item.href && !isLast) {
return (
<motion.a href={item.href} className="hover:underline" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
{content}
</motion.a>
);
}
if (item.onClick && !isLast) {
return (
<motion.button
type="button"
onClick={item.onClick}
className="hover:underline"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
{content}
</motion.button>
);
}
return content;
};
return (
<nav className={classNames('flex items-center', className)} aria-label="Breadcrumbs">
<ol className="flex items-center gap-1.5">
{displayItems.map((item, index) => {
const isLast = index === displayItems.length - 1;
return (
<li key={index} className="flex items-center">
{renderItem ? renderItem(item, index, isLast) : defaultRenderItem(item, index, isLast)}
{!isLast && (
<span
className={classNames(
separator,
'w-3 h-3 mx-1 text-bolt-elements-textTertiary dark:text-bolt-elements-textTertiary-dark',
)}
/>
)}
</li>
);
})}
</ol>
</nav>
);
}