From b0173c82e6ba34cd29a7cd7e4baaa32e2d15f72a Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Wed, 22 Apr 2026 12:24:05 +0300 Subject: [PATCH] =?UTF-8?q?UI=20-=20=D0=9C=D0=95=D0=96=D0=9F=D0=A0=D0=9E?= =?UTF-8?q?=D0=95=D0=9A=D0=A2=D0=9D=D0=90=D0=AF=20=D0=9A=D0=9E=D0=9C=D0=9C?= =?UTF-8?q?=D0=A3=D0=9D=D0=98=D0=9A=D0=90=D0=A6=D0=98=D0=AF:=20=D0=BC?= =?UTF-8?q?=D0=B8=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20P0=20action-menu?= =?UTF-8?q?=20=D0=BD=D0=B0=20ActionDropdown?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../external-contours/actions-menu.tsx | 107 ++++++++++-------- .../core/components/cycles/quick-actions.tsx | 60 ++-------- .../quick-action-dropdowns/helper.tsx | 66 +---------- .../issues/layout-quick-actions.tsx | 41 ++----- .../core/components/modules/quick-actions.tsx | 60 ++-------- .../components/pages/dropdowns/actions.tsx | 35 ++---- .../core/components/views/quick-actions.tsx | 59 ++-------- 7 files changed, 107 insertions(+), 321 deletions(-) diff --git a/plane-src/apps/web/ce/components/projects/external-contours/actions-menu.tsx b/plane-src/apps/web/ce/components/projects/external-contours/actions-menu.tsx index 2cf098b..ee21468 100644 --- a/plane-src/apps/web/ce/components/projects/external-contours/actions-menu.tsx +++ b/plane-src/apps/web/ce/components/projects/external-contours/actions-menu.tsx @@ -8,7 +8,8 @@ import { MoreHorizontal, Bell, BellOff } from "lucide-react"; import { useTranslation } from "@plane/i18n"; import { getIconButtonStyling } from "@plane/propel/icon-button"; import { CheckCircleFilledIcon, CloseCircleFilledIcon, CopyLinkIcon, NewTabIcon } from "@plane/propel/icons"; -import { CustomMenu } from "@plane/ui"; +import type { TContextMenuItem } from "@plane/ui"; +import { ActionDropdown } from "@plane/ui"; type Props = { canOpenTargetWorkItem: boolean; @@ -38,54 +39,70 @@ export const ExternalContourActionsMenu = (props: Props) => { } = props; const { t } = useTranslation(); - return ( - } - customButtonClassName={getIconButtonStyling("secondary", "lg")} - placement="bottom-start" - > - {includeDecisionActions && canReviewClosedRequest && onAccept && ( - -
- - {t("external_contours_page.actions.accept")} -
-
- )} - - {includeDecisionActions && canReviewClosedRequest && onDecline && ( - -
- - {t("external_contours_page.actions.decline")} -
-
- )} - - + const items: TContextMenuItem[] = [ + { + key: "accept", + action: () => onAccept?.(), + shouldRender: includeDecisionActions && canReviewClosedRequest && !!onAccept, + customContent: ( +
+ + {t("external_contours_page.actions.accept")} +
+ ), + }, + { + key: "decline", + action: () => onDecline?.(), + shouldRender: includeDecisionActions && canReviewClosedRequest && !!onDecline, + customContent: ( +
+ + {t("external_contours_page.actions.decline")} +
+ ), + }, + { + key: "copy", + action: onCopy, + customContent: (
{t("external_contours_page.actions.copy")}
-
+ ), + }, + { + key: "open", + action: () => onOpenTarget?.(), + shouldRender: canOpenTargetWorkItem && !!onOpenTarget, + customContent: ( +
+ + {t("external_contours_page.actions.open")} +
+ ), + }, + { + key: "toggle-subscription", + action: () => onToggleSubscription?.(), + shouldRender: canOpenTargetWorkItem && !!onToggleSubscription, + disabled: isSubscriptionLoading || isSubscribed === undefined, + customContent: ( +
+ {isSubscribed ? : } + {isSubscribed ? t("common.actions.unsubscribe") : t("common.actions.subscribe")} +
+ ), + }, + ]; - {canOpenTargetWorkItem && onOpenTarget && ( - -
- - {t("external_contours_page.actions.open")} -
-
- )} - - {canOpenTargetWorkItem && onToggleSubscription && ( - -
- {isSubscribed ? : } - {isSubscribed ? t("common.actions.unsubscribe") : t("common.actions.subscribe")} -
-
- )} -
+ return ( + } + buttonClassName={getIconButtonStyling("secondary", "lg")} + placement="bottom-start" + /> ); }; diff --git a/plane-src/apps/web/core/components/cycles/quick-actions.tsx b/plane-src/apps/web/core/components/cycles/quick-actions.tsx index 0c4e8be..c2ef6fa 100644 --- a/plane-src/apps/web/core/components/cycles/quick-actions.tsx +++ b/plane-src/apps/web/core/components/cycles/quick-actions.tsx @@ -10,10 +10,10 @@ import { MoreHorizontal } from "lucide-react"; // ui import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; -import { IconButton } from "@plane/propel/icon-button"; +import { getIconButtonStyling } from "@plane/propel/icon-button"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import type { TContextMenuItem } from "@plane/ui"; -import { ContextMenu, CustomMenu } from "@plane/ui"; +import { ActionDropdown, ContextMenu } from "@plane/ui"; import { copyUrlToClipboard, cn } from "@plane/utils"; // hooks import { useCycleMenuItems } from "@/components/common/quick-actions-helper"; @@ -101,15 +101,6 @@ export const CycleQuickActions = observer(function CycleQuickActions(props: Prop const MENU_ITEMS: TContextMenuItem[] = Array.isArray(menuResult) ? menuResult : menuResult.items; const additionalModals = Array.isArray(menuResult) ? null : menuResult.modals; - const CONTEXT_MENU_ITEMS = MENU_ITEMS.map(function CONTEXT_MENU_ITEMS(item) { - return { - ...item, - action: () => { - item.action(); - }, - }; - }); - return ( <> {cycleDetails && ( @@ -138,48 +129,13 @@ export const CycleQuickActions = observer(function CycleQuickActions(props: Prop {additionalModals} )} - - } + + } placement="bottom-end" - closeOnSelect - maxHeight="lg" - buttonClassName={customClassName} - > - {MENU_ITEMS.map((item) => { - if (item.shouldRender === false) return null; - return ( - { - item.action(); - }} - className={cn( - "flex items-center gap-2", - { - "text-placeholder": item.disabled, - }, - item.className - )} - disabled={item.disabled} - > - {item.icon && } -
-
{item.title}
- {item.description && ( -

- {item.description} -

- )} -
-
- ); - })} -
+ buttonClassName={cn(getIconButtonStyling("tertiary", "lg"), customClassName)} + /> ); }); diff --git a/plane-src/apps/web/core/components/issues/issue-layouts/quick-action-dropdowns/helper.tsx b/plane-src/apps/web/core/components/issues/issue-layouts/quick-action-dropdowns/helper.tsx index 3c19633..a7f33ba 100644 --- a/plane-src/apps/web/core/components/issues/issue-layouts/quick-action-dropdowns/helper.tsx +++ b/plane-src/apps/web/core/components/issues/issue-layouts/quick-action-dropdowns/helper.tsx @@ -4,7 +4,6 @@ * See the LICENSE file for details. */ -import type { ReactNode } from "react"; import { useMemo } from "react"; import { XCircle, ArchiveRestoreIcon } from "lucide-react"; // plane imports @@ -12,8 +11,7 @@ import { useTranslation } from "@plane/i18n"; import { LinkIcon, CopyIcon, NewTabIcon, EditIcon, ArchiveIcon, TrashIcon } from "@plane/propel/icons"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import type { EIssuesStoreType, TIssue } from "@plane/types"; -import { CustomMenu, type TContextMenuItem } from "@plane/ui"; -import { cn } from "@plane/utils"; +import type { TContextMenuItem } from "@plane/ui"; import { copyUrlToClipboard, generateWorkItemLink } from "@plane/utils"; // types import { createCopyMenuWithDuplication } from "@/plane-web/components/issues/issue-layouts/quick-action-dropdowns"; @@ -82,68 +80,6 @@ export interface MenuItemFactoryProps { storeType?: EIssuesStoreType; } -export const QUICK_ACTION_MENU_LAYER_CLASS_NAME = "z-[220]"; - -const renderQuickActionMenuItemContent = (item: TContextMenuItem) => - item.customContent ?? ( -
- {item.icon && } -
- {item.title &&
{item.title}
} - {item.description && ( -

- {item.description} -

- )} -
-
- ); - -export const renderQuickActionMenuItems = (items: TContextMenuItem[]): ReactNode[] => - items.map((item) => { - if (item.shouldRender === false) return null; - - if (item.nestedMenuItems?.some((nestedItem) => nestedItem.shouldRender !== false)) { - return ( - - {renderQuickActionMenuItems(item.nestedMenuItems)} - - ); - } - - return ( - item.action()} - className={cn( - "flex items-center gap-2", - { - "text-placeholder": item.disabled, - }, - item.className - )} - disabled={item.disabled} - > - {renderQuickActionMenuItemContent(item)} - - ); - }); - // Common action handlers hook export const useIssueActionHandlers = (props: MenuItemFactoryProps) => { const { issue, workspaceSlug, projectIdentifier, handleRestore } = props; diff --git a/plane-src/apps/web/core/components/issues/layout-quick-actions.tsx b/plane-src/apps/web/core/components/issues/layout-quick-actions.tsx index c68f9e9..22cf806 100644 --- a/plane-src/apps/web/core/components/issues/layout-quick-actions.tsx +++ b/plane-src/apps/web/core/components/issues/layout-quick-actions.tsx @@ -5,13 +5,13 @@ */ import { observer } from "mobx-react"; -import { TOAST_TYPE, setToast } from "@plane/propel/toast"; -import type { TContextMenuItem } from "@plane/ui"; -import { CustomMenu } from "@plane/ui"; -import { copyUrlToClipboard, cn } from "@plane/utils"; -import { useLayoutMenuItems } from "@/components/common/quick-actions-helper"; import { Ellipsis } from "lucide-react"; -import { IconButton } from "@plane/propel/icon-button"; +import { TOAST_TYPE, setToast } from "@plane/propel/toast"; +import { getIconButtonStyling } from "@plane/propel/icon-button"; +import type { TContextMenuItem } from "@plane/ui"; +import { ActionDropdown } from "@plane/ui"; +import { copyUrlToClipboard } from "@plane/utils"; +import { useLayoutMenuItems } from "@/components/common/quick-actions-helper"; type Props = { workspaceSlug: string; @@ -49,31 +49,12 @@ export const LayoutQuickActions = observer(function LayoutQuickActions(props: Pr return ( <> {additionalModals} - } - > - {MENU_ITEMS.map((item) => { - if (item.shouldRender === false) return null; - return ( - - {item.icon && } - {item.title} - - ); - })} - + button={} + buttonClassName={getIconButtonStyling("tertiary", "lg")} + /> ); }); diff --git a/plane-src/apps/web/core/components/modules/quick-actions.tsx b/plane-src/apps/web/core/components/modules/quick-actions.tsx index f62e5cc..6d6012e 100644 --- a/plane-src/apps/web/core/components/modules/quick-actions.tsx +++ b/plane-src/apps/web/core/components/modules/quick-actions.tsx @@ -9,10 +9,10 @@ import { observer } from "mobx-react"; import { MoreHorizontal } from "lucide-react"; // plane imports import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; -import { IconButton } from "@plane/propel/icon-button"; +import { getIconButtonStyling } from "@plane/propel/icon-button"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import type { TContextMenuItem } from "@plane/ui"; -import { ContextMenu, CustomMenu } from "@plane/ui"; +import { ActionDropdown, ContextMenu } from "@plane/ui"; import { copyUrlToClipboard, cn } from "@plane/utils"; // components import { useModuleMenuItems } from "@/components/common/quick-actions-helper"; @@ -101,16 +101,6 @@ export const ModuleQuickActions = observer(function ModuleQuickActions(props: Pr const MENU_ITEMS: TContextMenuItem[] = Array.isArray(menuResult) ? menuResult : menuResult.items; const additionalModals = Array.isArray(menuResult) ? null : menuResult.modals; - const CONTEXT_MENU_ITEMS = MENU_ITEMS.map(function CONTEXT_MENU_ITEMS(item) { - return { - ...item, - - onClick: () => { - item.action(); - }, - }; - }); - return ( <> {moduleDetails && ( @@ -133,47 +123,13 @@ export const ModuleQuickActions = observer(function ModuleQuickActions(props: Pr {additionalModals} )} - - } + + } placement="bottom-end" - closeOnSelect - buttonClassName={customClassName} - > - {MENU_ITEMS.map((item) => { - if (item.shouldRender === false) return null; - return ( - { - item.action(); - }} - className={cn( - "flex items-center gap-2", - { - "text-placeholder": item.disabled, - }, - item.className - )} - disabled={item.disabled} - > - {item.icon && } -
-
{item.title}
- {item.description && ( -

- {item.description} -

- )} -
-
- ); - })} -
+ buttonClassName={cn(getIconButtonStyling("tertiary", "lg"), customClassName)} + /> ); }); diff --git a/plane-src/apps/web/core/components/pages/dropdowns/actions.tsx b/plane-src/apps/web/core/components/pages/dropdowns/actions.tsx index aec992c..b4763c7 100644 --- a/plane-src/apps/web/core/components/pages/dropdowns/actions.tsx +++ b/plane-src/apps/web/core/components/pages/dropdowns/actions.tsx @@ -7,16 +7,15 @@ import { useMemo, useState } from "react"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; -import { ArchiveRestoreIcon, FileOutput, LockKeyhole, LockKeyholeOpen } from "lucide-react"; +import { ArchiveRestoreIcon, FileOutput, LockKeyhole, LockKeyholeOpen, MoreHorizontal } from "lucide-react"; // constants import { EPageAccess } from "@plane/constants"; // plane editor import { LinkIcon, CopyIcon, LockIcon, NewTabIcon, ArchiveIcon, TrashIcon, GlobeIcon } from "@plane/propel/icons"; +import { getIconButtonStyling } from "@plane/propel/icon-button"; // plane ui import type { TContextMenuItem } from "@plane/ui"; -import { ContextMenu, CustomMenu } from "@plane/ui"; -// components -import { cn } from "@plane/utils"; +import { ActionDropdown, ContextMenu } from "@plane/ui"; import { DeletePageModal } from "@/components/pages/modals/delete-page-modal"; // hooks import { usePageOperations } from "@/hooks/use-page-operations"; @@ -188,28 +187,12 @@ export const PageActions = observer(function PageActions(props: Props) { storeType={storeType} /> {parentRef && } - - {arrangedOptions.map((item) => { - if (item.shouldRender === false) return null; - return ( - { - item.action?.(); - }} - className={cn("flex items-center gap-2", item.className)} - disabled={item.disabled} - > - {item.customContent ?? ( - <> - {item.icon && } - {item.title} - - )} - - ); - })} - + } + buttonClassName={getIconButtonStyling("tertiary", "lg")} + /> ); }); diff --git a/plane-src/apps/web/core/components/views/quick-actions.tsx b/plane-src/apps/web/core/components/views/quick-actions.tsx index ea85af9..efecee3 100644 --- a/plane-src/apps/web/core/components/views/quick-actions.tsx +++ b/plane-src/apps/web/core/components/views/quick-actions.tsx @@ -9,12 +9,12 @@ import { observer } from "mobx-react"; import { MoreHorizontal } from "lucide-react"; // types import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; -import { IconButton } from "@plane/propel/icon-button"; +import { getIconButtonStyling } from "@plane/propel/icon-button"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import type { IProjectView } from "@plane/types"; // ui import type { TContextMenuItem } from "@plane/ui"; -import { ContextMenu, CustomMenu } from "@plane/ui"; +import { ActionDropdown, ContextMenu } from "@plane/ui"; import { copyUrlToClipboard, cn } from "@plane/utils"; // helpers import { useViewMenuItems } from "@/components/common/quick-actions-helper"; @@ -79,15 +79,6 @@ export const ViewQuickActions = observer(function ViewQuickActions(props: Props) if (publishContextMenu) MENU_ITEMS.splice(2, 0, publishContextMenu); - const CONTEXT_MENU_ITEMS = MENU_ITEMS.map(function CONTEXT_MENU_ITEMS(item) { - return { - ...item, - action: () => { - item.action(); - }, - }; - }); - return ( <> setDeleteViewModal(false)} /> setPublishModalOpen(false)} view={view} /> {additionalModals} - - } + + } placement="bottom-end" - closeOnSelect - buttonClassName={customClassName} - > - {MENU_ITEMS.map((item) => { - if (item.shouldRender === false) return null; - return ( - { - item.action(); - }} - className={cn( - "flex items-center gap-2", - { - "text-placeholder": item.disabled, - }, - item.className - )} - disabled={item.disabled} - > - {item.icon && } -
-
{item.title}
- {item.description && ( -

- {item.description} -

- )} -
-
- ); - })} -
+ buttonClassName={cn(getIconButtonStyling("tertiary", "lg"), customClassName)} + /> ); });