mirror of
https://github.com/outline/outline.git
synced 2025-12-20 10:09:43 -06:00
This PR moves the entire project to Typescript. Due to the ~1000 ignores this will lead to a messy codebase for a while, but the churn is worth it – all of those ignore comments are places that were never type-safe previously. closes #1282
89 lines
2.2 KiB
TypeScript
89 lines
2.2 KiB
TypeScript
import { GoToIcon } from "outline-icons";
|
|
import * as React from "react";
|
|
import { Link } from "react-router-dom";
|
|
import styled from "styled-components";
|
|
import Flex from "~/components/Flex";
|
|
import BreadcrumbMenu from "~/menus/BreadcrumbMenu";
|
|
import { MenuInternalLink } from "~/types";
|
|
|
|
export type Crumb = {
|
|
title: React.ReactNode;
|
|
icon?: React.ReactNode;
|
|
to?: string;
|
|
};
|
|
|
|
type Props = {
|
|
items: Crumb[];
|
|
max?: number;
|
|
children?: React.ReactNode;
|
|
highlightFirstItem?: boolean;
|
|
};
|
|
|
|
function Breadcrumb({ items, highlightFirstItem, children, max = 2 }: Props) {
|
|
const totalItems = items.length;
|
|
const topLevelItems: Crumb[] = [...items];
|
|
let overflowItems;
|
|
|
|
// chop middle breadcrumbs and present a "..." menu instead
|
|
if (totalItems > max) {
|
|
const halfMax = Math.floor(max / 2);
|
|
overflowItems = topLevelItems.splice(halfMax, totalItems - max);
|
|
|
|
topLevelItems.splice(halfMax, 0, {
|
|
title: <BreadcrumbMenu items={overflowItems as MenuInternalLink[]} />,
|
|
});
|
|
}
|
|
|
|
return (
|
|
<Flex justify="flex-start" align="center">
|
|
{topLevelItems.map((item, index) => (
|
|
<React.Fragment key={item.to || index}>
|
|
{item.icon}
|
|
{item.to ? (
|
|
<Item
|
|
to={item.to}
|
|
$withIcon={!!item.icon}
|
|
$highlight={!!highlightFirstItem && index === 0}
|
|
>
|
|
{item.title}
|
|
</Item>
|
|
) : (
|
|
item.title
|
|
)}
|
|
{index !== topLevelItems.length - 1 || !!children ? <Slash /> : null}
|
|
</React.Fragment>
|
|
))}
|
|
{children}
|
|
</Flex>
|
|
);
|
|
}
|
|
|
|
const Slash = styled(GoToIcon)`
|
|
flex-shrink: 0;
|
|
fill: ${(props) => props.theme.divider};
|
|
`;
|
|
|
|
const Item = styled(Link)<{ $highlight: boolean; $withIcon: boolean }>`
|
|
display: flex;
|
|
flex-shrink: 1;
|
|
min-width: 0;
|
|
color: ${(props) => props.theme.text};
|
|
font-size: 15px;
|
|
height: 24px;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
font-weight: ${(props) => (props.$highlight ? "500" : "inherit")};
|
|
margin-left: ${(props) => (props.$withIcon ? "4px" : "0")};
|
|
|
|
svg {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
&:hover {
|
|
text-decoration: underline;
|
|
}
|
|
`;
|
|
|
|
export default Breadcrumb;
|