snapshot of https://github.com/makeplane/plane.git @ d868349a55de79979eb8b98701bf1702c67ad634 (2026-09-15)
Some checks failed
CodeQL / Analyze (javascript) (push) Failing after 16s
CodeQL / Analyze (python) (push) Failing after 3s
i18n sync check / check:sync (push) Failing after 3s
React Doctor / react-doctor (push) Failing after 16s
Branch Build CE / Build-Push Admin Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Admin Manifest (push) Has been cancelled
Branch Build CE / Build-Push Web Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Web Manifest (push) Has been cancelled
Branch Build CE / Build-Push Space Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Proxy Manifest (push) Has been cancelled
Branch Build CE / Merge Space Manifest (push) Has been cancelled
Branch Build CE / Prepare AIO Assets (push) Has been cancelled
Branch Build CE / Build Setup (push) Has been cancelled
Branch Build CE / Build-Push Live Collaboration Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Live Collaboration Manifest (push) Has been cancelled
Branch Build CE / Build-Push API Server Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge API Server Manifest (push) Has been cancelled
Branch Build CE / Build-Push Proxy Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Build-Push AIO Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge AIO Manifest (push) Has been cancelled
Branch Build CE / Upload Build Assets (push) Has been cancelled
Branch Build CE / Build Release (push) Has been cancelled
Some checks failed
CodeQL / Analyze (javascript) (push) Failing after 16s
CodeQL / Analyze (python) (push) Failing after 3s
i18n sync check / check:sync (push) Failing after 3s
React Doctor / react-doctor (push) Failing after 16s
Branch Build CE / Build-Push Admin Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Admin Manifest (push) Has been cancelled
Branch Build CE / Build-Push Web Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Web Manifest (push) Has been cancelled
Branch Build CE / Build-Push Space Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Proxy Manifest (push) Has been cancelled
Branch Build CE / Merge Space Manifest (push) Has been cancelled
Branch Build CE / Prepare AIO Assets (push) Has been cancelled
Branch Build CE / Build Setup (push) Has been cancelled
Branch Build CE / Build-Push Live Collaboration Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Live Collaboration Manifest (push) Has been cancelled
Branch Build CE / Build-Push API Server Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge API Server Manifest (push) Has been cancelled
Branch Build CE / Build-Push Proxy Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Build-Push AIO Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge AIO Manifest (push) Has been cancelled
Branch Build CE / Upload Build Assets (push) Has been cancelled
Branch Build CE / Build Release (push) Has been cancelled
This commit is contained in:
200
packages/ui/src/breadcrumbs/breadcrumbs.tsx
Normal file
200
packages/ui/src/breadcrumbs/breadcrumbs.tsx
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import * as React from "react";
|
||||
import { ChevronRightOutline } from "@makeplane/propel/icons";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
import { cn } from "../utils";
|
||||
|
||||
type BreadcrumbsProps = {
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
onBack?: () => void;
|
||||
isLoading?: boolean;
|
||||
};
|
||||
|
||||
export function BreadcrumbItemLoader() {
|
||||
return (
|
||||
<div className="flex h-7 animate-pulse items-center gap-2">
|
||||
<div className="group flex h-full items-center gap-2 rounded-sm px-2 py-1 text-13 font-medium">
|
||||
<span className="h-full w-5 rounded-sm bg-layer-1" />
|
||||
<span className="h-full w-16 rounded-sm bg-layer-1" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Breadcrumbs({ className, children, onBack, isLoading = false }: BreadcrumbsProps) {
|
||||
const [isSmallScreen, setIsSmallScreen] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
const handleResize = () => {
|
||||
setIsSmallScreen(window.innerWidth <= 640); // Adjust this value as per your requirement
|
||||
};
|
||||
|
||||
window.addEventListener("resize", handleResize);
|
||||
handleResize(); // Call it initially to set the correct state
|
||||
return () => window.removeEventListener("resize", handleResize);
|
||||
}, []);
|
||||
|
||||
const childrenArray = React.Children.toArray(children);
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-grow items-center gap-0.5 overflow-hidden", className)}>
|
||||
{!isSmallScreen && (
|
||||
<>
|
||||
{childrenArray.map((child, index) => {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<>
|
||||
<BreadcrumbItemLoader />
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (React.isValidElement<BreadcrumbItemProps>(child)) {
|
||||
return React.cloneElement(child, {
|
||||
isLast: index === childrenArray.length - 1,
|
||||
});
|
||||
}
|
||||
return child;
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSmallScreen && childrenArray.length > 1 && (
|
||||
<>
|
||||
<div className="flex items-center gap-2.5 p-1">
|
||||
{onBack && (
|
||||
<span onClick={onBack} className="text-secondary">
|
||||
...
|
||||
</span>
|
||||
)}
|
||||
<ChevronRightOutline className="h-3.5 w-3.5 flex-shrink-0 text-placeholder" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="flex items-center gap-2.5 p-1">
|
||||
{isLoading ? (
|
||||
<BreadcrumbItemLoader />
|
||||
) : React.isValidElement(childrenArray[childrenArray.length - 1]) ? (
|
||||
React.cloneElement(childrenArray[childrenArray.length - 1] as React.ReactElement<BreadcrumbItemProps>, {
|
||||
isLast: true,
|
||||
})
|
||||
) : (
|
||||
childrenArray[childrenArray.length - 1]
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{isSmallScreen && childrenArray.length === 1 && childrenArray}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// breadcrumb item
|
||||
type BreadcrumbItemProps = {
|
||||
component?: React.ReactNode;
|
||||
showSeparator?: boolean;
|
||||
isLast?: boolean;
|
||||
};
|
||||
|
||||
function BreadcrumbItem(props: BreadcrumbItemProps) {
|
||||
const { component, showSeparator = true, isLast = false } = props;
|
||||
return (
|
||||
<div className="flex h-6 items-center gap-0.5">
|
||||
{component}
|
||||
{showSeparator && !isLast && <BreadcrumbSeparator />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// breadcrumb icon
|
||||
type BreadcrumbIconProps = {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function BreadcrumbIcon(props: BreadcrumbIconProps) {
|
||||
const { children, className } = props;
|
||||
return <div className={cn("flex size-4 items-center justify-start overflow-hidden", className)}>{children}</div>;
|
||||
}
|
||||
|
||||
// breadcrumb label
|
||||
type BreadcrumbLabelProps = {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function BreadcrumbLabel(props: BreadcrumbLabelProps) {
|
||||
const { children, className } = props;
|
||||
return (
|
||||
<div className={cn("relative line-clamp-1 block max-w-[150px] truncate overflow-hidden", className)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// breadcrumb separator
|
||||
type BreadcrumbSeparatorProps = {
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
iconClassName?: string;
|
||||
showDivider?: boolean;
|
||||
};
|
||||
|
||||
function BreadcrumbSeparator(props: BreadcrumbSeparatorProps) {
|
||||
const { className, containerClassName, iconClassName, showDivider = false } = props;
|
||||
return (
|
||||
<div className={cn("relative flex h-full items-center justify-center px-1.5 py-1", className)}>
|
||||
{showDivider && <span className="absolute top-0 -left-0.5 h-full w-[1.8px] bg-surface-1" />}
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-shrink-0 items-center justify-center rounded-sm text-placeholder transition-all",
|
||||
containerClassName
|
||||
)}
|
||||
>
|
||||
<ChevronRightOutline className={cn("h-3.5 w-3.5 flex-shrink-0", iconClassName)} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// breadcrumb wrapper
|
||||
type BreadcrumbItemWrapperProps = {
|
||||
label?: string;
|
||||
disableTooltip?: boolean;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
type?: "link" | "text";
|
||||
isLast?: boolean;
|
||||
};
|
||||
|
||||
function BreadcrumbItemWrapper(props: BreadcrumbItemWrapperProps) {
|
||||
const { label, disableTooltip = false, children, className, type = "link", isLast = false } = props;
|
||||
return (
|
||||
<Tooltip tooltipContent={label} position="bottom" disabled={!label || label === "" || disableTooltip}>
|
||||
<div
|
||||
className={cn(
|
||||
"group flex h-full cursor-default items-center gap-2 rounded-sm px-1.5 py-1 text-13 font-medium",
|
||||
{
|
||||
"text-primary": isLast,
|
||||
"text-tertiary": !isLast,
|
||||
"cursor-pointer hover:bg-layer-transparent-hover hover:text-primary": type === "link" && !isLast,
|
||||
},
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
Breadcrumbs.Item = BreadcrumbItem;
|
||||
Breadcrumbs.Icon = BreadcrumbIcon;
|
||||
Breadcrumbs.Label = BreadcrumbLabel;
|
||||
Breadcrumbs.Separator = BreadcrumbSeparator;
|
||||
Breadcrumbs.ItemWrapper = BreadcrumbItemWrapper;
|
||||
|
||||
export { Breadcrumbs, BreadcrumbItem, BreadcrumbIcon, BreadcrumbLabel, BreadcrumbSeparator, BreadcrumbItemWrapper };
|
||||
Reference in New Issue
Block a user