diff --git a/plane-src/apps/web/core/components/common/selection-dropdown.tsx b/plane-src/apps/web/core/components/common/selection-dropdown.tsx index 2bf0dff..acecfbc 100644 --- a/plane-src/apps/web/core/components/common/selection-dropdown.tsx +++ b/plane-src/apps/web/core/components/common/selection-dropdown.tsx @@ -21,7 +21,7 @@ export type TSelectionDropdownOption = { type Props = { disabled?: boolean; - menuButton: ReactNode; + menuButton: ReactNode | ((props: { open: boolean }) => ReactNode); menuButtonWrapperClassName?: string; options: TSelectionDropdownOption[]; placement?: Placement; diff --git a/plane-src/apps/web/core/components/core/description-versions/dropdown-item.tsx b/plane-src/apps/web/core/components/core/description-versions/dropdown-item.tsx index b8abb5b..05d4913 100644 --- a/plane-src/apps/web/core/components/core/description-versions/dropdown-item.tsx +++ b/plane-src/apps/web/core/components/core/description-versions/dropdown-item.tsx @@ -8,7 +8,7 @@ import { observer } from "mobx-react"; // plane imports import { useTranslation } from "@plane/i18n"; import type { TDescriptionVersion } from "@plane/types"; -import { Avatar, CustomMenu } from "@plane/ui"; +import { Avatar } from "@plane/ui"; import { calculateTimeAgo, getFileURL } from "@plane/utils"; // hooks import { useMember } from "@/hooks/store/use-member"; @@ -28,7 +28,7 @@ export const DescriptionVersionsDropdownItem = observer(function DescriptionVers const { t } = useTranslation(); return ( - onClick(version.id)}> + ); }); diff --git a/plane-src/apps/web/core/components/core/description-versions/dropdown.tsx b/plane-src/apps/web/core/components/core/description-versions/dropdown.tsx index 7749066..99c3c41 100644 --- a/plane-src/apps/web/core/components/core/description-versions/dropdown.tsx +++ b/plane-src/apps/web/core/components/core/description-versions/dropdown.tsx @@ -9,12 +9,11 @@ import { History } from "lucide-react"; // plane imports import { useTranslation } from "@plane/i18n"; import type { TDescriptionVersion } from "@plane/types"; -import { CustomMenu } from "@plane/ui"; -import { calculateTimeAgo } from "@plane/utils"; +import { Avatar } from "@plane/ui"; +import { calculateTimeAgo, cn, getFileURL } from "@plane/utils"; // hooks import { useMember } from "@/hooks/store/use-member"; -// local imports -import { DescriptionVersionsDropdownItem } from "./dropdown-item"; +import { SelectionDropdown } from "@/components/common/selection-dropdown"; import type { TDescriptionVersionEntityInformation } from "./root"; type Props = { @@ -34,13 +33,21 @@ export const DescriptionVersionsDropdown = observer(function DescriptionVersions const lastUpdatedByUserDisplayName = latestVersion?.owned_by ? getUserDetails(latestVersion?.owned_by)?.display_name : entityInformation.createdByDisplayName; + const latestVersionId = latestVersion?.id; // translation const { t } = useTranslation(); return ( - + ( +
@@ -50,18 +57,32 @@ export const DescriptionVersionsDropdown = observer(function DescriptionVersions {calculateTimeAgo(lastUpdatedAt)}

+ )} + menuButtonWrapperClassName="rounded-sm" + options={ + versions?.map((version) => { + const versionCreator = version.owned_by ? getUserDetails(version.owned_by) : null; + + return { + key: version.id, + isChecked: version.id === latestVersionId, + onClick: () => onVersionClick(version.id), + icon: ( + + ), + title: ( +

+ {versionCreator?.display_name ?? t("common.deactivated_user")} + {calculateTimeAgo(version.last_saved_at)} +

+ ), + }; + }) ?? [] } - noBorder - noChevron={disabled} - placement="bottom-end" - optionsClassName="w-[300px]" - disabled={disabled} - closeOnSelect - > -

{t("description_versions.previously_edited_by")}

- {versions?.map((version) => ( - - ))} -
+ /> ); }); diff --git a/plane-src/apps/web/core/components/issues/issue-layouts/filters/header/helpers/dropdown.tsx b/plane-src/apps/web/core/components/issues/issue-layouts/filters/header/helpers/dropdown.tsx index 1cf5207..99f90ad 100644 --- a/plane-src/apps/web/core/components/issues/issue-layouts/filters/header/helpers/dropdown.tsx +++ b/plane-src/apps/web/core/components/issues/issue-layouts/filters/header/helpers/dropdown.tsx @@ -20,7 +20,7 @@ type Props = { placement?: Placement; disabled?: boolean; tabIndex?: number; - menuButton?: React.ReactNode; + menuButton?: React.ReactNode | ((props: { open: boolean }) => React.ReactNode); menuButtonWrapperClassName?: string; isFiltersApplied?: boolean; }; @@ -58,7 +58,7 @@ export function FiltersDropdown(props: Props) { className={menuButtonWrapperClassName} disabled={disabled} > - {menuButton} + {typeof menuButton === "function" ? menuButton({ open }) : menuButton} ) : (
diff --git a/plane-src/apps/web/core/components/labels/label-block/label-item-block.tsx b/plane-src/apps/web/core/components/labels/label-block/label-item-block.tsx index 7f8bc07..d1cfaa8 100644 --- a/plane-src/apps/web/core/components/labels/label-block/label-item-block.tsx +++ b/plane-src/apps/web/core/components/labels/label-block/label-item-block.tsx @@ -5,17 +5,17 @@ */ import type { MutableRefObject } from "react"; -import { useRef, useState } from "react"; +import { useState } from "react"; import type { LucideIcon } from "lucide-react"; // plane helpers import { PROJECT_SETTINGS_TRACKER_ELEMENTS } from "@plane/constants"; -import { useOutsideClickDetector } from "@plane/hooks"; import type { ISvgIcons } from "@plane/propel/icons"; import { CloseIcon } from "@plane/propel/icons"; // types import type { IIssueLabel } from "@plane/types"; // ui -import { CustomMenu, DragHandle } from "@plane/ui"; +import type { TContextMenuItem } from "@plane/ui"; +import { ActionDropdown, DragHandle } from "@plane/ui"; // helpers import { cn } from "@plane/utils"; // components @@ -52,11 +52,20 @@ export function LabelItemBlock(props: ILabelItemBlock) { draggable = true, } = props; // states - const [isMenuActive, setIsMenuActive] = useState(true); - // refs - const actionSectionRef = useRef(null); + const [isMenuActive, setIsMenuActive] = useState(false); - useOutsideClickDetector(actionSectionRef, () => setIsMenuActive(false)); + const actionMenuItems: TContextMenuItem[] = customMenuItems + .filter(({ isVisible }) => isVisible) + .map(({ onClick, CustomIcon, text, key }) => ({ + key, + action: () => onClick(label), + customContent: ( + + + {text} + + ), + })); return (
@@ -74,26 +83,13 @@ export function LabelItemBlock(props: ILabelItemBlock) { {!disabled && (
- setIsMenuActive(!isMenuActive)} useCaptureForOutsideClick> - {customMenuItems.map( - ({ isVisible, onClick, CustomIcon, text, key }) => - isVisible && ( - onClick(label)}> - - - {text} - - - ) - )} - + {!isLabelGroup && (
- +
); } @@ -66,8 +66,8 @@ export function BreadcrumbNavigationDropdown(props: TBreadcrumbNavigationDropdow } return ( - { - setIsOpen(!isOpen); - }} - onMenuClose={() => { - setIsOpen(false); - }} - > - {navigationItems.map((item) => { - if (item.shouldRender === false) return null; - return ( - { - e.preventDefault(); - e.stopPropagation(); - if (item.key === selectedItemKey) return; - item.action(); - }} - className={cn( - "flex items-center gap-2", - { - "text-placeholder": item.disabled, - }, - item.className - )} - disabled={item.disabled} - > + onOpenChange={setIsOpen} + items={navigationItems.map((item) => ({ + ...item, + action: () => { + if (item.key === selectedItemKey) return; + item.action(); + }, + customContent: ( +
{item.icon && }
{item.title}
@@ -134,9 +115,9 @@ export function BreadcrumbNavigationDropdown(props: TBreadcrumbNavigationDropdow )}
{item.key === selectedItemKey && } - - ); - })} - +
+ ), + }))} + /> ); } diff --git a/plane-src/packages/ui/src/link/block.tsx b/plane-src/packages/ui/src/link/block.tsx index 6d50522..8ddbceb 100644 --- a/plane-src/packages/ui/src/link/block.tsx +++ b/plane-src/packages/ui/src/link/block.tsx @@ -5,11 +5,12 @@ */ import React from "react"; +import { MoreVertical } from "lucide-react"; // plane utils import { calculateTimeAgo, cn, getIconForLink } from "@plane/utils"; // plane ui import type { TContextMenuItem } from "../dropdowns/context-menu/root"; -import { CustomMenu } from "../dropdowns/custom-menu"; +import { ActionDropdown } from "../dropdowns/action-dropdown"; export type TLinkItemBlockProps = { title: string; @@ -38,36 +39,17 @@ export function LinkItemBlock(props: TLinkItemBlockProps) {
{menuItems && (
- - {menuItems.map((item) => ( - { - e.preventDefault(); - e.stopPropagation(); - item.action(); - }} - className={cn("flex w-full items-center gap-2", { - "text-placeholder": item.disabled, - })} - disabled={item.disabled} - > - {item.icon && } -
-
{item.title}
- {item.description && ( -

- {item.description} -

- )} -
-
- ))} -
+ + + + } + buttonClassName="grid place-items-center" + items={menuItems} + />
)}