[WEB-5478] chore: fix types (#8155)
This commit is contained in:
parent
5cfb9538df
commit
0f4309659a
27 changed files with 369 additions and 240 deletions
|
|
@ -51,8 +51,7 @@ const defaultFromData: TFormData = {
|
|||
is_telemetry_enabled: true,
|
||||
};
|
||||
|
||||
export function InstanceSetupForm(props) {
|
||||
const {} = props;
|
||||
export function InstanceSetupForm() {
|
||||
// search params
|
||||
const searchParams = useSearchParams();
|
||||
const firstNameParam = searchParams.get("first_name") || undefined;
|
||||
|
|
|
|||
|
|
@ -104,5 +104,3 @@ export const KanbanIssueBlock = observer(function KanbanIssueBlock(props: IssueB
|
|||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
KanbanIssueBlock.displayName = "KanbanIssueBlock";
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { useRouter } from "next/navigation";
|
|||
import { EUserPermissions, EUserPermissionsLevel, PROJECT_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { EmptyStateDetailed } from "@plane/propel/empty-state";
|
||||
import type { AnalyticsTab } from "@plane/types";
|
||||
import { Tabs } from "@plane/ui";
|
||||
import type { TabItem } from "@plane/ui";
|
||||
// components
|
||||
|
|
@ -44,9 +45,7 @@ function AnalyticsPage({ params }: Route.ComponentProps) {
|
|||
const pageTitle = currentWorkspace?.name
|
||||
? t(`workspace_analytics.page_label`, { workspace: currentWorkspace?.name })
|
||||
: undefined;
|
||||
const ANALYTICS_TABS = useMemo(function ANALYTICS_TABS() {
|
||||
return getAnalyticsTabs(t);
|
||||
});
|
||||
const ANALYTICS_TABS = useMemo<AnalyticsTab[]>(() => getAnalyticsTabs(t), [t]);
|
||||
const tabs: TabItem[] = useMemo(
|
||||
() =>
|
||||
ANALYTICS_TABS.map((tab) => ({
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { useMemo } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { Disclosure } from "@headlessui/react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type { ICycle } from "@plane/types";
|
||||
import { Row } from "@plane/ui";
|
||||
// assets
|
||||
import darkActiveCycleAsset from "@/app/assets/empty-state/cycle/active-dark.webp?url";
|
||||
|
|
@ -27,6 +27,69 @@ interface IActiveCycleDetails {
|
|||
showHeader?: boolean;
|
||||
}
|
||||
|
||||
type ActiveCyclesComponentProps = {
|
||||
cycleId: string | null | undefined;
|
||||
activeCycle: ICycle | null;
|
||||
activeCycleResolvedPath: string;
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
handleFiltersUpdate: (filters: any) => void;
|
||||
cycleIssueDetails?: ActiveCycleIssueDetails | { nextPageResults: boolean };
|
||||
};
|
||||
|
||||
const ActiveCyclesComponent = observer(function ActiveCyclesComponent({
|
||||
cycleId,
|
||||
activeCycle,
|
||||
activeCycleResolvedPath,
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
handleFiltersUpdate,
|
||||
cycleIssueDetails,
|
||||
}: ActiveCyclesComponentProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!cycleId || !activeCycle) {
|
||||
return (
|
||||
<DetailedEmptyState
|
||||
title={t("project_cycles.empty_state.active.title")}
|
||||
description={t("project_cycles.empty_state.active.description")}
|
||||
assetPath={activeCycleResolvedPath}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col border-b border-custom-border-200">
|
||||
<CyclesListItem
|
||||
key={cycleId}
|
||||
cycleId={cycleId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
className="!border-b-transparent"
|
||||
/>
|
||||
<Row className="bg-custom-background-100 pt-3 pb-6">
|
||||
<div className="grid grid-cols-1 bg-custom-background-100 gap-3 lg:grid-cols-2 xl:grid-cols-3">
|
||||
<ActiveCycleProgress
|
||||
handleFiltersUpdate={handleFiltersUpdate}
|
||||
projectId={projectId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
cycle={activeCycle}
|
||||
/>
|
||||
<ActiveCycleProductivity workspaceSlug={workspaceSlug} projectId={projectId} cycle={activeCycle} />
|
||||
<ActiveCycleStats
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
cycle={activeCycle}
|
||||
cycleId={cycleId}
|
||||
handleFiltersUpdate={handleFiltersUpdate}
|
||||
cycleIssueDetails={cycleIssueDetails}
|
||||
/>
|
||||
</div>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const ActiveCycleRoot = observer(function ActiveCycleRoot(props: IActiveCycleDetails) {
|
||||
const { workspaceSlug, projectId, cycleId: propsCycleId, showHeader = true } = props;
|
||||
// theme hook
|
||||
|
|
@ -45,51 +108,6 @@ export const ActiveCycleRoot = observer(function ActiveCycleRoot(props: IActiveC
|
|||
cycleIssueDetails,
|
||||
} = useCyclesDetails({ workspaceSlug, projectId, cycleId });
|
||||
|
||||
const ActiveCyclesComponent = useMemo(function ActiveCyclesComponent() {
|
||||
return (
|
||||
<>
|
||||
{!cycleId || !activeCycle ? (
|
||||
<DetailedEmptyState
|
||||
title={t("project_cycles.empty_state.active.title")}
|
||||
description={t("project_cycles.empty_state.active.description")}
|
||||
assetPath={activeCycleResolvedPath}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex flex-col border-b border-custom-border-200">
|
||||
{cycleId && (
|
||||
<CyclesListItem
|
||||
key={cycleId}
|
||||
cycleId={cycleId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
className="!border-b-transparent"
|
||||
/>
|
||||
)}
|
||||
<Row className="bg-custom-background-100 pt-3 pb-6">
|
||||
<div className="grid grid-cols-1 bg-custom-background-100 gap-3 lg:grid-cols-2 xl:grid-cols-3">
|
||||
<ActiveCycleProgress
|
||||
handleFiltersUpdate={handleFiltersUpdate}
|
||||
projectId={projectId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
cycle={activeCycle}
|
||||
/>
|
||||
<ActiveCycleProductivity workspaceSlug={workspaceSlug} projectId={projectId} cycle={activeCycle} />
|
||||
<ActiveCycleStats
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
cycle={activeCycle}
|
||||
cycleId={cycleId}
|
||||
handleFiltersUpdate={handleFiltersUpdate}
|
||||
cycleIssueDetails={cycleIssueDetails as ActiveCycleIssueDetails}
|
||||
/>
|
||||
</div>
|
||||
</Row>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{showHeader ? (
|
||||
|
|
@ -99,12 +117,30 @@ export const ActiveCycleRoot = observer(function ActiveCycleRoot(props: IActiveC
|
|||
<Disclosure.Button className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 cursor-pointer">
|
||||
<CycleListGroupHeader title={t("project_cycles.active_cycle.label")} type="current" isExpanded={open} />
|
||||
</Disclosure.Button>
|
||||
<Disclosure.Panel>{ActiveCyclesComponent}</Disclosure.Panel>
|
||||
<Disclosure.Panel>
|
||||
<ActiveCyclesComponent
|
||||
cycleId={cycleId}
|
||||
activeCycle={activeCycle}
|
||||
activeCycleResolvedPath={activeCycleResolvedPath}
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
handleFiltersUpdate={handleFiltersUpdate}
|
||||
cycleIssueDetails={cycleIssueDetails}
|
||||
/>
|
||||
</Disclosure.Panel>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
) : (
|
||||
<>{ActiveCyclesComponent}</>
|
||||
<ActiveCyclesComponent
|
||||
cycleId={cycleId}
|
||||
activeCycle={activeCycle}
|
||||
activeCycleResolvedPath={activeCycleResolvedPath}
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
handleFiltersUpdate={handleFiltersUpdate}
|
||||
cycleIssueDetails={cycleIssueDetails}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ export const CommentQuickActions = observer(function CommentQuickActions(props:
|
|||
// translation
|
||||
const { t } = useTranslation();
|
||||
|
||||
const MENU_ITEMS = useMemo(function MENU_ITEMS() {
|
||||
return [
|
||||
const MENU_ITEMS = useMemo<TContextMenuItem[]>(
|
||||
() => [
|
||||
{
|
||||
key: "edit",
|
||||
action: setEditMode,
|
||||
|
|
@ -70,8 +70,9 @@ export const CommentQuickActions = observer(function CommentQuickActions(props:
|
|||
icon: Trash2,
|
||||
shouldRender: canDelete,
|
||||
},
|
||||
];
|
||||
});
|
||||
],
|
||||
[t, setEditMode, canEdit, showCopyLinkOption, activityOperations, comment, showAccessSpecifier, canDelete]
|
||||
);
|
||||
|
||||
return (
|
||||
<CustomMenu ellipsis closeOnSelect>
|
||||
|
|
|
|||
|
|
@ -79,5 +79,3 @@ export const BreadcrumbLink = observer(function BreadcrumbLink(props: Props) {
|
|||
|
||||
return <ItemWrapper {...itemWrapperProps}>{content}</ItemWrapper>;
|
||||
});
|
||||
|
||||
BreadcrumbLink.displayName = "BreadcrumbLink";
|
||||
|
|
|
|||
|
|
@ -21,5 +21,3 @@ export const MultipleSelectGroup = observer(function MultipleSelectGroup(props:
|
|||
|
||||
return <>{children(helpers)}</>;
|
||||
});
|
||||
|
||||
MultipleSelectGroup.displayName = "MultipleSelectGroup";
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export type ActiveCycleStatsProps = {
|
|||
cycle: ICycle | null;
|
||||
cycleId?: string | null;
|
||||
handleFiltersUpdate: (conditions: TWorkItemFilterCondition[]) => void;
|
||||
cycleIssueDetails: ActiveCycleIssueDetails;
|
||||
cycleIssueDetails?: ActiveCycleIssueDetails | { nextPageResults: boolean };
|
||||
};
|
||||
|
||||
export const ActiveCycleStats = observer(function ActiveCycleStats(props: ActiveCycleStatsProps) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ type Props = {
|
|||
};
|
||||
|
||||
export const CalendarIssueBlock = observer(
|
||||
forwardRef<HTMLAnchorElement, Props>((props, ref) => {
|
||||
forwardRef(function CalendarIssueBlock(props: Props, ref: React.ForwardedRef<HTMLAnchorElement>) {
|
||||
const { issue, quickActions, isDragging = false, isEpic = false } = props;
|
||||
// states
|
||||
const [isMenuActive, setIsMenuActive] = useState(false);
|
||||
|
|
|
|||
|
|
@ -299,5 +299,3 @@ export const KanbanIssueBlock = observer(function KanbanIssueBlock(props: IssueB
|
|||
</>
|
||||
);
|
||||
});
|
||||
|
||||
KanbanIssueBlock.displayName = "KanbanIssueBlock";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type { Placement } from "@popperjs/core";
|
||||
import { observer } from "mobx-react";
|
||||
// plane helpers
|
||||
|
|
@ -36,6 +36,126 @@ export interface IIssuePropertyLabels {
|
|||
fullHeight?: boolean;
|
||||
}
|
||||
|
||||
type NoLabelProps = {
|
||||
isMobile: boolean;
|
||||
noLabelBorder: boolean;
|
||||
fullWidth: boolean;
|
||||
placeholderText?: string;
|
||||
};
|
||||
|
||||
const NoLabel = observer(function NoLabel({ isMobile, noLabelBorder, fullWidth, placeholderText }: NoLabelProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
position="top"
|
||||
tooltipHeading={t("common.labels")}
|
||||
tooltipContent="None"
|
||||
isMobile={isMobile}
|
||||
renderByDefault={false}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-full items-center justify-center gap-2 rounded px-2.5 py-1 text-xs hover:bg-custom-background-80",
|
||||
noLabelBorder ? "rounded-none" : "border-[0.5px] border-custom-border-300",
|
||||
fullWidth && "w-full"
|
||||
)}
|
||||
>
|
||||
<LabelPropertyIcon className="h-3.5 w-3.5" />
|
||||
{placeholderText}
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
|
||||
type LabelSummaryProps = {
|
||||
isMobile: boolean;
|
||||
fullWidth: boolean;
|
||||
noLabelBorder: boolean;
|
||||
disabled?: boolean;
|
||||
projectLabels: IIssueLabel[];
|
||||
value: string[];
|
||||
};
|
||||
|
||||
function LabelSummary({ isMobile, fullWidth, noLabelBorder, disabled, projectLabels, value }: LabelSummaryProps) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-5 flex-shrink-0 items-center justify-center rounded px-2.5 text-xs",
|
||||
fullWidth && "w-full",
|
||||
noLabelBorder ? "rounded-none" : "border-[0.5px] border-custom-border-300",
|
||||
disabled ? "cursor-not-allowed" : "cursor-pointer"
|
||||
)}
|
||||
>
|
||||
<Tooltip
|
||||
isMobile={isMobile}
|
||||
position="top"
|
||||
tooltipHeading={t("common.labels")}
|
||||
tooltipContent={projectLabels
|
||||
?.filter((l) => value.includes(l?.id))
|
||||
.map((l) => l?.name)
|
||||
.join(", ")}
|
||||
renderByDefault={false}
|
||||
>
|
||||
<div className="flex h-full items-center gap-1.5 text-custom-text-200">
|
||||
<span className="h-2 w-2 flex-shrink-0 rounded-full bg-custom-primary" />
|
||||
{`${value.length} Labels`}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type LabelItemProps = {
|
||||
label: IIssueLabel;
|
||||
isMobile: boolean;
|
||||
renderByDefault: boolean;
|
||||
disabled?: boolean;
|
||||
fullWidth: boolean;
|
||||
noLabelBorder: boolean;
|
||||
};
|
||||
|
||||
const LabelItem = observer(function LabelItem({
|
||||
label,
|
||||
isMobile,
|
||||
renderByDefault,
|
||||
disabled,
|
||||
fullWidth,
|
||||
noLabelBorder,
|
||||
}: LabelItemProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
position="top"
|
||||
tooltipHeading={t("common.labels")}
|
||||
tooltipContent={label?.name ?? ""}
|
||||
isMobile={isMobile}
|
||||
renderByDefault={renderByDefault}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex overflow-hidden justify-center hover:bg-custom-background-80 max-w-full h-full flex-shrink-0 items-center rounded px-2.5 text-xs",
|
||||
!disabled && "cursor-pointer",
|
||||
fullWidth && "w-full",
|
||||
noLabelBorder ? "rounded-none" : "border-[0.5px] border-custom-border-300"
|
||||
)}
|
||||
>
|
||||
<div className="flex max-w-full items-center gap-1.5 overflow-hidden text-custom-text-200">
|
||||
<span
|
||||
className="h-2 w-2 flex-shrink-0 rounded-full"
|
||||
style={{
|
||||
backgroundColor: label?.color ?? "#000000",
|
||||
}}
|
||||
/>
|
||||
<div className="line-clamp-1 inline-block w-auto max-w-[200px] truncate">{label?.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
|
||||
export const IssuePropertyLabels = observer(function IssuePropertyLabels(props: IIssuePropertyLabels) {
|
||||
const {
|
||||
projectId,
|
||||
|
|
@ -54,8 +174,6 @@ export const IssuePropertyLabels = observer(function IssuePropertyLabels(props:
|
|||
fullWidth = false,
|
||||
fullHeight = false,
|
||||
} = props;
|
||||
// i18n
|
||||
const { t } = useTranslation();
|
||||
// states
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
// refs
|
||||
|
|
@ -83,91 +201,6 @@ export const IssuePropertyLabels = observer(function IssuePropertyLabels(props:
|
|||
let projectLabels: IIssueLabel[] = defaultOptions as IIssueLabel[];
|
||||
if (storeLabels && storeLabels.length > 0) projectLabels = storeLabels;
|
||||
|
||||
const NoLabel = useMemo(function NoLabel() {
|
||||
return (
|
||||
<Tooltip
|
||||
position="top"
|
||||
tooltipHeading={t("common.labels")}
|
||||
tooltipContent="None"
|
||||
isMobile={isMobile}
|
||||
renderByDefault={false}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-full items-center justify-center gap-2 rounded px-2.5 py-1 text-xs hover:bg-custom-background-80",
|
||||
noLabelBorder ? "rounded-none" : "border-[0.5px] border-custom-border-300",
|
||||
fullWidth && "w-full"
|
||||
)}
|
||||
>
|
||||
<LabelPropertyIcon className="h-3.5 w-3.5" />
|
||||
{placeholderText}
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
|
||||
const LabelSummary = useMemo(function LabelSummary() {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-5 flex-shrink-0 items-center justify-center rounded px-2.5 text-xs",
|
||||
fullWidth && "w-full",
|
||||
noLabelBorder ? "rounded-none" : "border-[0.5px] border-custom-border-300",
|
||||
disabled ? "cursor-not-allowed" : "cursor-pointer"
|
||||
)}
|
||||
>
|
||||
<Tooltip
|
||||
isMobile={isMobile}
|
||||
position="top"
|
||||
tooltipHeading={t("common.labels")}
|
||||
tooltipContent={projectLabels
|
||||
?.filter((l) => value.includes(l?.id))
|
||||
.map((l) => l?.name)
|
||||
.join(", ")}
|
||||
renderByDefault={false}
|
||||
>
|
||||
<div className="flex h-full items-center gap-1.5 text-custom-text-200">
|
||||
<span className="h-2 w-2 flex-shrink-0 rounded-full bg-custom-primary" />
|
||||
{`${value.length} Labels`}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const LabelItem = useCallback(function LabelItem({ label }: { label: IIssueLabel }) {
|
||||
return (
|
||||
<Tooltip
|
||||
key={label.id}
|
||||
position="top"
|
||||
tooltipHeading={t("common.labels")}
|
||||
tooltipContent={label?.name ?? ""}
|
||||
isMobile={isMobile}
|
||||
renderByDefault={renderByDefault}
|
||||
>
|
||||
<div
|
||||
key={label?.id}
|
||||
className={cn(
|
||||
"flex overflow-hidden justify-center hover:bg-custom-background-80 max-w-full h-full flex-shrink-0 items-center rounded px-2.5 text-xs",
|
||||
!disabled && "cursor-pointer",
|
||||
fullWidth && "w-full",
|
||||
noLabelBorder ? "rounded-none" : "border-[0.5px] border-custom-border-300"
|
||||
)}
|
||||
>
|
||||
<div className="flex max-w-full items-center gap-1.5 overflow-hidden text-custom-text-200">
|
||||
<span
|
||||
className="h-2 w-2 flex-shrink-0 rounded-full"
|
||||
style={{
|
||||
backgroundColor: label?.color ?? "#000000",
|
||||
}}
|
||||
/>
|
||||
<div className="line-clamp-1 inline-block w-auto max-w-[200px] truncate">{label?.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{value.length > 0 ? (
|
||||
|
|
@ -185,7 +218,16 @@ export const IssuePropertyLabels = observer(function IssuePropertyLabels(props:
|
|||
hideDropdownArrow={hideDropdownArrow}
|
||||
fullWidth={fullWidth}
|
||||
fullHeight={fullHeight}
|
||||
label={<LabelItem label={label} />}
|
||||
label={
|
||||
<LabelItem
|
||||
label={label}
|
||||
isMobile={isMobile}
|
||||
renderByDefault={renderByDefault}
|
||||
disabled={disabled}
|
||||
fullWidth={fullWidth}
|
||||
noLabelBorder={noLabelBorder}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
|
|
@ -198,7 +240,16 @@ export const IssuePropertyLabels = observer(function IssuePropertyLabels(props:
|
|||
placement={placement}
|
||||
fullWidth={fullWidth}
|
||||
fullHeight={fullHeight}
|
||||
label={LabelSummary}
|
||||
label={
|
||||
<LabelSummary
|
||||
isMobile={isMobile}
|
||||
fullWidth={fullWidth}
|
||||
noLabelBorder={noLabelBorder}
|
||||
disabled={disabled}
|
||||
projectLabels={projectLabels}
|
||||
value={value}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
|
|
@ -211,7 +262,14 @@ export const IssuePropertyLabels = observer(function IssuePropertyLabels(props:
|
|||
placement={placement}
|
||||
fullWidth={fullWidth}
|
||||
fullHeight={fullHeight}
|
||||
label={NoLabel}
|
||||
label={
|
||||
<NoLabel
|
||||
isMobile={isMobile}
|
||||
noLabelBorder={noLabelBorder}
|
||||
fullWidth={fullWidth}
|
||||
placeholderText={placeholderText}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,10 @@ const defaultValues: Partial<IIssueLabel> = {
|
|||
};
|
||||
|
||||
export const CreateUpdateLabelInline = observer(
|
||||
forwardRef<HTMLDivElement, TCreateUpdateLabelInlineProps>(function CreateUpdateLabelInline(props, ref) {
|
||||
forwardRef(function CreateUpdateLabelInline(
|
||||
props: TCreateUpdateLabelInlineProps,
|
||||
ref: React.ForwardedRef<HTMLDivElement>
|
||||
) {
|
||||
const { labelForm, setLabelForm, isUpdating, labelOperationsCallbacks, labelToUpdate, onClose } = props;
|
||||
// form info
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import type { FC } from "react";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import { useEffect, useRef } from "react";
|
||||
// plane imports
|
||||
import type { IWorkspaceMemberInvitation } from "@plane/types";
|
||||
import { EOnboardingSteps } from "@plane/types";
|
||||
|
|
@ -16,8 +15,25 @@ type Props = {
|
|||
handleStepChange: (step: EOnboardingSteps, skipInvites?: boolean) => void;
|
||||
};
|
||||
|
||||
function OnboardingStepContent({ currentStep, invitations, handleStepChange }: Props) {
|
||||
switch (currentStep) {
|
||||
case EOnboardingSteps.PROFILE_SETUP:
|
||||
return <ProfileSetupStep handleStepChange={handleStepChange} />;
|
||||
case EOnboardingSteps.ROLE_SETUP:
|
||||
return <RoleSetupStep handleStepChange={handleStepChange} />;
|
||||
case EOnboardingSteps.USE_CASE_SETUP:
|
||||
return <UseCaseSetupStep handleStepChange={handleStepChange} />;
|
||||
case EOnboardingSteps.WORKSPACE_CREATE_OR_JOIN:
|
||||
return <WorkspaceSetupStep invitations={invitations ?? []} handleStepChange={handleStepChange} />;
|
||||
case EOnboardingSteps.INVITE_MEMBERS:
|
||||
return <InviteTeamStep handleStepChange={handleStepChange} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function OnboardingStepRoot(props: Props) {
|
||||
const { currentStep, invitations, handleStepChange } = props;
|
||||
const { currentStep } = props;
|
||||
// ref for the scrollable container
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
|
@ -31,24 +47,12 @@ export function OnboardingStepRoot(props: Props) {
|
|||
}
|
||||
}, [currentStep]);
|
||||
|
||||
// memoized step component mapping
|
||||
const stepComponents = useMemo(
|
||||
() => ({
|
||||
[EOnboardingSteps.PROFILE_SETUP]: <ProfileSetupStep handleStepChange={handleStepChange} />,
|
||||
[EOnboardingSteps.ROLE_SETUP]: <RoleSetupStep handleStepChange={handleStepChange} />,
|
||||
[EOnboardingSteps.USE_CASE_SETUP]: <UseCaseSetupStep handleStepChange={handleStepChange} />,
|
||||
[EOnboardingSteps.WORKSPACE_CREATE_OR_JOIN]: (
|
||||
<WorkspaceSetupStep invitations={invitations ?? []} handleStepChange={handleStepChange} />
|
||||
),
|
||||
[EOnboardingSteps.INVITE_MEMBERS]: <InviteTeamStep handleStepChange={handleStepChange} />,
|
||||
}),
|
||||
[handleStepChange, invitations]
|
||||
);
|
||||
|
||||
return (
|
||||
<div ref={scrollContainerRef} className="flex-1 overflow-y-auto">
|
||||
<div className="flex items-center justify-center min-h-full p-8">
|
||||
<div className="w-full max-w-[24rem]">{stepComponents[currentStep]} </div>
|
||||
<div className="w-full max-w-[24rem]">
|
||||
<OnboardingStepContent {...props} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ export const PageActions = observer(function PageActions(props: Props) {
|
|||
canCurrentUserMovePage,
|
||||
} = page;
|
||||
// menu items
|
||||
const MENU_ITEMS = useMemo(function MENU_ITEMS() {
|
||||
const MENU_ITEMS = useMemo(() => {
|
||||
const menuItems: (TContextMenuItem & { key: TPageActions })[] = [
|
||||
{
|
||||
key: "toggle-lock",
|
||||
|
|
@ -175,13 +175,26 @@ export const PageActions = observer(function PageActions(props: Props) {
|
|||
menuItems.push(...extraOptions);
|
||||
}
|
||||
return menuItems;
|
||||
});
|
||||
}, [
|
||||
extraOptions,
|
||||
is_locked,
|
||||
canCurrentUserLockPage,
|
||||
access,
|
||||
canCurrentUserChangeAccess,
|
||||
archived_at,
|
||||
canCurrentUserDuplicatePage,
|
||||
canCurrentUserArchivePage,
|
||||
canCurrentUserDeletePage,
|
||||
canCurrentUserMovePage,
|
||||
isMovePageEnabled,
|
||||
pageOperations,
|
||||
]);
|
||||
// arrange options
|
||||
const arrangedOptions = useMemo(
|
||||
const arrangedOptions = useMemo<(TContextMenuItem & { key: TPageActions })[]>(
|
||||
() =>
|
||||
optionsOrder
|
||||
.map((key) => MENU_ITEMS.find((item) => item.key === key))
|
||||
.filter((item) => !!item) as (TContextMenuItem & { key: TPageActions })[],
|
||||
.filter((item): item is TContextMenuItem & { key: TPageActions } => !!item),
|
||||
[optionsOrder, MENU_ITEMS]
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ export const PageOptionsDropdown = observer(function PageOptionsDropdown(props:
|
|||
// query params
|
||||
const { updateQueryParams } = useQueryParams();
|
||||
// menu items list
|
||||
const EXTRA_MENU_OPTIONS = useMemo(function EXTRA_MENU_OPTIONS() {
|
||||
return [
|
||||
const EXTRA_MENU_OPTIONS = useMemo<(TContextMenuItem & { key: TPageActions })[]>(
|
||||
() => [
|
||||
{
|
||||
key: "full-screen",
|
||||
action: () => handleFullWidth(!isFullWidth),
|
||||
|
|
@ -106,8 +106,19 @@ export const PageOptionsDropdown = observer(function PageOptionsDropdown(props:
|
|||
icon: ArrowUpToLine,
|
||||
shouldRender: true,
|
||||
},
|
||||
];
|
||||
});
|
||||
],
|
||||
[
|
||||
handleFullWidth,
|
||||
isFullWidth,
|
||||
handleStickyToolbar,
|
||||
isStickyToolbarEnabled,
|
||||
isContentEditable,
|
||||
editorRef,
|
||||
updateQueryParams,
|
||||
router,
|
||||
setIsExportModalOpen,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { range } from "lodash-es";
|
||||
import { Loader } from "@plane/ui";
|
||||
|
||||
export function PageLoader(props) {
|
||||
const {} = props;
|
||||
|
||||
export function PageLoader() {
|
||||
return (
|
||||
<div className="relative w-full h-full flex flex-col">
|
||||
<div className="px-3 border-b border-custom-border-100 py-3">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useState, useMemo } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { TwitterPicker } from "react-color";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import type { IState } from "@plane/types";
|
||||
|
|
@ -12,6 +12,17 @@ type TStateForm = {
|
|||
buttonTitle: string;
|
||||
};
|
||||
|
||||
function PopoverButton({ color }: { color?: string }) {
|
||||
return (
|
||||
<div
|
||||
className="group inline-flex items-center text-base font-medium focus:outline-none h-5 w-5 rounded transition-all"
|
||||
style={{
|
||||
backgroundColor: color ?? "black",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function StateForm(props: TStateForm) {
|
||||
const { data, onSubmit, onCancel, buttonDisabled, buttonTitle } = props;
|
||||
// states
|
||||
|
|
@ -45,22 +56,11 @@ export function StateForm(props: TStateForm) {
|
|||
}
|
||||
};
|
||||
|
||||
const PopoverButton = useMemo(function PopoverButton() {
|
||||
return (
|
||||
<div
|
||||
className="group inline-flex items-center text-base font-medium focus:outline-none h-5 w-5 rounded transition-all"
|
||||
style={{
|
||||
backgroundColor: formData?.color ?? "black",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="relative flex space-x-2 bg-custom-background-100 p-3 rounded">
|
||||
{/* color */}
|
||||
<div className="flex-shrink-0 h-full mt-2">
|
||||
<Popover button={PopoverButton} panelClassName="mt-4 -ml-3">
|
||||
<Popover button={<PopoverButton color={formData?.color} />} panelClassName="mt-4 -ml-3">
|
||||
<TwitterPicker color={formData?.color} onChange={(value) => handleFormData("color", value.hex)} />
|
||||
</Popover>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ export type AppSidebarItemComponent = React.FC<AppSidebarItemProps> & {
|
|||
Button: React.FC<AppSidebarButtonItemProps>;
|
||||
};
|
||||
|
||||
function AppSidebarItem({ variant = "link", item }) {
|
||||
function AppSidebarItem({ variant = "link", item }: AppSidebarItemProps) {
|
||||
if (!item) return null;
|
||||
|
||||
const { icon, isActive, label, href, onClick, disabled } = item;
|
||||
|
|
|
|||
|
|
@ -33,27 +33,27 @@ interface Props {
|
|||
options?: any;
|
||||
}
|
||||
|
||||
function HeadingPrimary({ children }) {
|
||||
function HeadingPrimary({ children }: { children: React.ReactNode }) {
|
||||
return <h1 className="text-lg font-semibold text-custom-text-100">{children}</h1>;
|
||||
}
|
||||
|
||||
function HeadingSecondary({ children }) {
|
||||
function HeadingSecondary({ children }: { children: React.ReactNode }) {
|
||||
return <h3 className="text-base font-semibold text-custom-text-100">{children}</h3>;
|
||||
}
|
||||
|
||||
function Paragraph({ children }) {
|
||||
function Paragraph({ children }: { children: React.ReactNode }) {
|
||||
return <p className="text-sm text-custom-text-200">{children}</p>;
|
||||
}
|
||||
|
||||
function OrderedList({ children }) {
|
||||
function OrderedList({ children }: { children: React.ReactNode }) {
|
||||
return <ol className="mb-4 ml-8 list-decimal text-sm text-custom-text-200">{children}</ol>;
|
||||
}
|
||||
|
||||
function UnorderedList({ children }) {
|
||||
function UnorderedList({ children }: { children: React.ReactNode }) {
|
||||
return <ul className="mb-4 ml-8 list-disc text-sm text-custom-text-200">{children}</ul>;
|
||||
}
|
||||
|
||||
function Link({ href, children }) {
|
||||
function Link({ href, children }: CustomComponentProps) {
|
||||
return (
|
||||
<a href={href} className="underline hover:no-underline" target="_blank" rel="noopener noreferrer">
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -60,51 +60,51 @@ export const useRealtimePageEvents = ({
|
|||
[getUserDetails]
|
||||
);
|
||||
|
||||
const ACTION_HANDLERS = useMemo(function ACTION_HANDLERS() {
|
||||
return {
|
||||
archived: ({ pageIds, data }) => {
|
||||
const ACTION_HANDLERS = useMemo(
|
||||
() => ({
|
||||
archived: ({ pageIds, data }: { pageIds: string[]; data: EventToPayloadMap["archived"] }) => {
|
||||
pageIds.forEach((pageId) => {
|
||||
const pageItem = getPageById(pageId);
|
||||
if (pageItem) pageItem.archive({ archived_at: data.archived_at, shouldSync: false });
|
||||
});
|
||||
},
|
||||
|
||||
unarchived: ({ pageIds }) => {
|
||||
unarchived: ({ pageIds }: { pageIds: string[] }) => {
|
||||
pageIds.forEach((pageId) => {
|
||||
const pageItem = getPageById(pageId);
|
||||
if (pageItem) pageItem.restore({ shouldSync: false });
|
||||
});
|
||||
},
|
||||
|
||||
locked: ({ pageIds }) => {
|
||||
locked: ({ pageIds }: { pageIds: string[] }) => {
|
||||
pageIds.forEach((pageId) => {
|
||||
const pageItem = getPageById(pageId);
|
||||
if (pageItem) pageItem.lock({ shouldSync: false, recursive: false });
|
||||
});
|
||||
},
|
||||
|
||||
unlocked: ({ pageIds }) => {
|
||||
unlocked: ({ pageIds }: { pageIds: string[] }) => {
|
||||
pageIds.forEach((pageId) => {
|
||||
const pageItem = getPageById(pageId);
|
||||
if (pageItem) pageItem.unlock({ shouldSync: false, recursive: false });
|
||||
});
|
||||
},
|
||||
|
||||
"made-public": ({ pageIds }) => {
|
||||
"made-public": ({ pageIds }: { pageIds: string[] }) => {
|
||||
pageIds.forEach((pageId) => {
|
||||
const pageItem = getPageById(pageId);
|
||||
if (pageItem) pageItem.makePublic({ shouldSync: false });
|
||||
});
|
||||
},
|
||||
|
||||
"made-private": ({ pageIds }) => {
|
||||
"made-private": ({ pageIds }: { pageIds: string[] }) => {
|
||||
pageIds.forEach((pageId) => {
|
||||
const pageItem = getPageById(pageId);
|
||||
if (pageItem) pageItem.makePrivate({ shouldSync: false });
|
||||
});
|
||||
},
|
||||
|
||||
deleted: ({ pageIds, data }) => {
|
||||
deleted: ({ pageIds, data }: { pageIds: string[]; data: EventToPayloadMap["deleted"] }) => {
|
||||
pageIds.forEach((pageId) => {
|
||||
const pageItem = getPageById(pageId);
|
||||
if (pageItem) {
|
||||
|
|
@ -123,7 +123,7 @@ export const useRealtimePageEvents = ({
|
|||
});
|
||||
},
|
||||
|
||||
property_updated: ({ pageIds, data }) => {
|
||||
property_updated: ({ pageIds, data }: { pageIds: string[]; data: EventToPayloadMap["property_updated"] }) => {
|
||||
pageIds.forEach((pageId) => {
|
||||
const pageInstance = getPageById(pageId);
|
||||
const { name: updatedName, ...rest } = data;
|
||||
|
|
@ -132,7 +132,7 @@ export const useRealtimePageEvents = ({
|
|||
});
|
||||
},
|
||||
|
||||
error: ({ pageIds, data }) => {
|
||||
error: ({ pageIds, data }: { pageIds: string[]; data: EventToPayloadMap["error"] }) => {
|
||||
const errorType = data.error_type;
|
||||
const errorMessage = data.error_message || "An error occurred";
|
||||
const errorCode = data.error_code;
|
||||
|
|
@ -164,8 +164,9 @@ export const useRealtimePageEvents = ({
|
|||
},
|
||||
|
||||
...customRealtimeEventHandlers,
|
||||
};
|
||||
});
|
||||
}),
|
||||
[getPageById, removePage, page, currentUser, getUserDisplayText, router, handlers, customRealtimeEventHandlers]
|
||||
);
|
||||
|
||||
// The main function that will be returned from this hook
|
||||
const updatePageProperties = useCallback(
|
||||
|
|
@ -181,7 +182,7 @@ export const useRealtimePageEvents = ({
|
|||
if (normalizedPageIds.length === 0) return;
|
||||
|
||||
// Get the handler for this message type
|
||||
const handler = ACTION_HANDLERS[actionType];
|
||||
const handler = ACTION_HANDLERS[actionType] as PageUpdateHandler<T> | undefined;
|
||||
|
||||
if (handler) {
|
||||
// Now TypeScript knows that handler and data match in type
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@
|
|||
"fix:format": "turbo run fix:format",
|
||||
"check": "turbo run check",
|
||||
"check:lint": "turbo run check:lint",
|
||||
"check:format": "turbo run check:format"
|
||||
"check:format": "turbo run check:format",
|
||||
"check:types": "turbo run check:types"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@prettier/plugin-oxc": "0.0.4",
|
||||
|
|
@ -50,4 +51,4 @@
|
|||
"engines": {
|
||||
"node": ">=22.18.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -45,7 +45,10 @@ const getPositionClassNames = (position: DialogPosition) =>
|
|||
"top-8 left-1/2 -translate-x-1/2": position === "top",
|
||||
});
|
||||
|
||||
const DialogPortal = memo(function DialogPortal({ children, ...props }) {
|
||||
const DialogPortal = memo(function DialogPortal({
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof BaseDialog.Portal>) {
|
||||
return (
|
||||
<BaseDialog.Portal data-slot="dialog-portal" {...props}>
|
||||
{children}
|
||||
|
|
@ -54,12 +57,15 @@ const DialogPortal = memo(function DialogPortal({ children, ...props }) {
|
|||
});
|
||||
DialogPortal.displayName = "DialogPortal";
|
||||
|
||||
const DialogOverlay = memo(function DialogOverlay({ className, ...props }) {
|
||||
const DialogOverlay = memo(function DialogOverlay({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof BaseDialog.Backdrop>) {
|
||||
return <BaseDialog.Backdrop data-slot="dialog-overlay" className={cn(OVERLAY_CLASSNAME, className)} {...props} />;
|
||||
});
|
||||
DialogOverlay.displayName = "DialogOverlay";
|
||||
|
||||
const DialogComponent = memo(function DialogComponent({ children, ...props }) {
|
||||
const DialogComponent = memo(function DialogComponent({ children, ...props }: DialogProps) {
|
||||
return (
|
||||
<BaseDialog.Root data-slot="dialog" {...props}>
|
||||
{children}
|
||||
|
|
@ -68,7 +74,10 @@ const DialogComponent = memo(function DialogComponent({ children, ...props }) {
|
|||
});
|
||||
DialogComponent.displayName = "Dialog";
|
||||
|
||||
const DialogTrigger = memo(function DialogTrigger({ children, ...props }) {
|
||||
const DialogTrigger = memo(function DialogTrigger({
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof BaseDialog.Trigger>) {
|
||||
return (
|
||||
<BaseDialog.Trigger data-slot="dialog-trigger" {...props}>
|
||||
{children}
|
||||
|
|
@ -100,7 +109,7 @@ const DialogPanel = forwardRef(function DialogPanel(
|
|||
});
|
||||
DialogPanel.displayName = "DialogPanel";
|
||||
|
||||
const DialogTitle = memo(function DialogTitle({ className, children, ...props }) {
|
||||
const DialogTitle = memo(function DialogTitle({ className, children, ...props }: DialogTitleProps) {
|
||||
return (
|
||||
<BaseDialog.Title
|
||||
data-slot="dialog-title"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from "react";
|
||||
import { memo, useMemo } from "react";
|
||||
import { Popover as BasePopover } from "@base-ui-components/react/popover";
|
||||
import type { TPlacement, TSide, TAlign } from "../utils/placement";
|
||||
import { convertPlacementToSideAndAlign } from "../utils/placement";
|
||||
|
|
@ -13,7 +13,7 @@ export interface PopoverContentProps extends React.ComponentProps<typeof BasePop
|
|||
}
|
||||
|
||||
// PopoverContent component
|
||||
const PopoverContent = React.memo(function PopoverContent({
|
||||
const PopoverContent = memo(function PopoverContent({
|
||||
children,
|
||||
className,
|
||||
placement,
|
||||
|
|
@ -23,9 +23,9 @@ const PopoverContent = React.memo(function PopoverContent({
|
|||
containerRef,
|
||||
positionerClassName,
|
||||
...props
|
||||
}) {
|
||||
}: PopoverContentProps) {
|
||||
// side and align calculations
|
||||
const { finalSide, finalAlign } = React.useMemo(() => {
|
||||
const { finalSide, finalAlign } = useMemo(() => {
|
||||
if (placement) {
|
||||
const converted = convertPlacementToSideAndAlign(placement);
|
||||
return { finalSide: converted.side, finalAlign: converted.align };
|
||||
|
|
@ -45,21 +45,21 @@ const PopoverContent = React.memo(function PopoverContent({
|
|||
});
|
||||
|
||||
// wrapper components
|
||||
const PopoverTrigger = React.memo(function PopoverTrigger(props) {
|
||||
const PopoverTrigger = memo(function PopoverTrigger(props: React.ComponentProps<typeof BasePopover.Trigger>) {
|
||||
return <BasePopover.Trigger data-slot="popover-trigger" {...props} />;
|
||||
});
|
||||
|
||||
const PopoverPortal = React.memo(function PopoverPortal(props) {
|
||||
const PopoverPortal = memo(function PopoverPortal(props: React.ComponentProps<typeof BasePopover.Portal>) {
|
||||
return <BasePopover.Portal data-slot="popover-portal" {...props} />;
|
||||
});
|
||||
|
||||
const PopoverPositioner = React.memo(function PopoverPositioner(props) {
|
||||
const PopoverPositioner = memo(function PopoverPositioner(props: React.ComponentProps<typeof BasePopover.Positioner>) {
|
||||
return <BasePopover.Positioner data-slot="popover-positioner" {...props} />;
|
||||
});
|
||||
|
||||
// compound components
|
||||
const Popover = Object.assign(
|
||||
React.memo<React.ComponentProps<typeof BasePopover.Root>>(function Popover(props) {
|
||||
memo(function Popover(props: React.ComponentProps<typeof BasePopover.Root>) {
|
||||
return <BasePopover.Root data-slot="popover" {...props} />;
|
||||
}),
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,4 +37,4 @@ const Skeleton = Object.assign(SkeletonRoot, { Item: SkeletonItem });
|
|||
SkeletonRoot.displayName = "plane-ui-skeleton";
|
||||
SkeletonItem.displayName = "plane-ui-skeleton-item";
|
||||
|
||||
export { Skeleton };
|
||||
export { Skeleton, SkeletonRoot, SkeletonItem };
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
"@plane/eslint-config": "workspace:*",
|
||||
"@plane/typescript-config": "workspace:*",
|
||||
"@prettier/plugin-oxc": "0.0.4",
|
||||
"@types/node": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"tsdown": "catalog:",
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export function BreadcrumbNavigationDropdown(props: TBreadcrumbNavigationDropdow
|
|||
|
||||
function NavigationButton() {
|
||||
return (
|
||||
<Tooltip tooltipContent={selectedItem.title} position="bottom" disabled={isOpen}>
|
||||
<Tooltip tooltipContent={selectedItem?.title} position="bottom" disabled={isOpen}>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
if (!isLast) {
|
||||
|
|
@ -48,7 +48,7 @@ export function BreadcrumbNavigationDropdown(props: TBreadcrumbNavigationDropdow
|
|||
<div className="flex @4xl:hidden text-custom-text-300">...</div>
|
||||
<div className="hidden @4xl:flex gap-2">
|
||||
{selectedItemIcon && <Breadcrumbs.Icon>{selectedItemIcon}</Breadcrumbs.Icon>}
|
||||
<Breadcrumbs.Label>{selectedItem.title}</Breadcrumbs.Label>
|
||||
<Breadcrumbs.Label>{selectedItem?.title}</Breadcrumbs.Label>
|
||||
</div>
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
|
|
|||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
|
|
@ -1330,6 +1330,9 @@ importers:
|
|||
'@prettier/plugin-oxc':
|
||||
specifier: 0.0.4
|
||||
version: 0.0.4
|
||||
'@types/node':
|
||||
specifier: 'catalog:'
|
||||
version: 22.12.0
|
||||
'@types/react':
|
||||
specifier: 'catalog:'
|
||||
version: 18.3.11
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue