UI - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: миграция P0 action-menu на ActionDropdown
This commit is contained in:
parent
54a648bb91
commit
b0173c82e6
|
|
@ -8,7 +8,8 @@ import { MoreHorizontal, Bell, BellOff } from "lucide-react";
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
import { getIconButtonStyling } from "@plane/propel/icon-button";
|
import { getIconButtonStyling } from "@plane/propel/icon-button";
|
||||||
import { CheckCircleFilledIcon, CloseCircleFilledIcon, CopyLinkIcon, NewTabIcon } from "@plane/propel/icons";
|
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 = {
|
type Props = {
|
||||||
canOpenTargetWorkItem: boolean;
|
canOpenTargetWorkItem: boolean;
|
||||||
|
|
@ -38,54 +39,70 @@ export const ExternalContourActionsMenu = (props: Props) => {
|
||||||
} = props;
|
} = props;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
const items: TContextMenuItem[] = [
|
||||||
<CustomMenu
|
{
|
||||||
customButton={<MoreHorizontal className="size-4" />}
|
key: "accept",
|
||||||
customButtonClassName={getIconButtonStyling("secondary", "lg")}
|
action: () => onAccept?.(),
|
||||||
placement="bottom-start"
|
shouldRender: includeDecisionActions && canReviewClosedRequest && !!onAccept,
|
||||||
>
|
customContent: (
|
||||||
{includeDecisionActions && canReviewClosedRequest && onAccept && (
|
<div className="flex items-center gap-2 text-success-secondary">
|
||||||
<CustomMenu.MenuItem onClick={onAccept}>
|
<CheckCircleFilledIcon width={14} height={14} />
|
||||||
<div className="flex items-center gap-2 text-success-secondary">
|
{t("external_contours_page.actions.accept")}
|
||||||
<CheckCircleFilledIcon width={14} height={14} />
|
</div>
|
||||||
{t("external_contours_page.actions.accept")}
|
),
|
||||||
</div>
|
},
|
||||||
</CustomMenu.MenuItem>
|
{
|
||||||
)}
|
key: "decline",
|
||||||
|
action: () => onDecline?.(),
|
||||||
{includeDecisionActions && canReviewClosedRequest && onDecline && (
|
shouldRender: includeDecisionActions && canReviewClosedRequest && !!onDecline,
|
||||||
<CustomMenu.MenuItem onClick={onDecline}>
|
customContent: (
|
||||||
<div className="flex items-center gap-2 text-danger-secondary">
|
<div className="flex items-center gap-2 text-danger-secondary">
|
||||||
<CloseCircleFilledIcon width={14} height={14} />
|
<CloseCircleFilledIcon width={14} height={14} />
|
||||||
{t("external_contours_page.actions.decline")}
|
{t("external_contours_page.actions.decline")}
|
||||||
</div>
|
</div>
|
||||||
</CustomMenu.MenuItem>
|
),
|
||||||
)}
|
},
|
||||||
|
{
|
||||||
<CustomMenu.MenuItem onClick={onCopy}>
|
key: "copy",
|
||||||
|
action: onCopy,
|
||||||
|
customContent: (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<CopyLinkIcon width={14} height={14} />
|
<CopyLinkIcon width={14} height={14} />
|
||||||
{t("external_contours_page.actions.copy")}
|
{t("external_contours_page.actions.copy")}
|
||||||
</div>
|
</div>
|
||||||
</CustomMenu.MenuItem>
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "open",
|
||||||
|
action: () => onOpenTarget?.(),
|
||||||
|
shouldRender: canOpenTargetWorkItem && !!onOpenTarget,
|
||||||
|
customContent: (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<NewTabIcon width={14} height={14} />
|
||||||
|
{t("external_contours_page.actions.open")}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "toggle-subscription",
|
||||||
|
action: () => onToggleSubscription?.(),
|
||||||
|
shouldRender: canOpenTargetWorkItem && !!onToggleSubscription,
|
||||||
|
disabled: isSubscriptionLoading || isSubscribed === undefined,
|
||||||
|
customContent: (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{isSubscribed ? <BellOff width={14} height={14} /> : <Bell width={14} height={14} />}
|
||||||
|
{isSubscribed ? t("common.actions.unsubscribe") : t("common.actions.subscribe")}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
{canOpenTargetWorkItem && onOpenTarget && (
|
return (
|
||||||
<CustomMenu.MenuItem onClick={onOpenTarget}>
|
<ActionDropdown
|
||||||
<div className="flex items-center gap-2">
|
items={items}
|
||||||
<NewTabIcon width={14} height={14} />
|
button={<MoreHorizontal className="size-4" />}
|
||||||
{t("external_contours_page.actions.open")}
|
buttonClassName={getIconButtonStyling("secondary", "lg")}
|
||||||
</div>
|
placement="bottom-start"
|
||||||
</CustomMenu.MenuItem>
|
/>
|
||||||
)}
|
|
||||||
|
|
||||||
{canOpenTargetWorkItem && onToggleSubscription && (
|
|
||||||
<CustomMenu.MenuItem onClick={onToggleSubscription} disabled={isSubscriptionLoading || isSubscribed === undefined}>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
{isSubscribed ? <BellOff width={14} height={14} /> : <Bell width={14} height={14} />}
|
|
||||||
{isSubscribed ? t("common.actions.unsubscribe") : t("common.actions.subscribe")}
|
|
||||||
</div>
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
)}
|
|
||||||
</CustomMenu>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ import { MoreHorizontal } from "lucide-react";
|
||||||
// ui
|
// ui
|
||||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||||
import { useTranslation } from "@plane/i18n";
|
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 { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
import type { TContextMenuItem } from "@plane/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";
|
import { copyUrlToClipboard, cn } from "@plane/utils";
|
||||||
// hooks
|
// hooks
|
||||||
import { useCycleMenuItems } from "@/components/common/quick-actions-helper";
|
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 MENU_ITEMS: TContextMenuItem[] = Array.isArray(menuResult) ? menuResult : menuResult.items;
|
||||||
const additionalModals = Array.isArray(menuResult) ? null : menuResult.modals;
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{cycleDetails && (
|
{cycleDetails && (
|
||||||
|
|
@ -138,48 +129,13 @@ export const CycleQuickActions = observer(function CycleQuickActions(props: Prop
|
||||||
{additionalModals}
|
{additionalModals}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ContextMenu parentRef={parentRef} items={CONTEXT_MENU_ITEMS} />
|
<ContextMenu parentRef={parentRef} items={MENU_ITEMS} />
|
||||||
<CustomMenu
|
<ActionDropdown
|
||||||
customButton={<IconButton variant="tertiary" size="lg" icon={MoreHorizontal} />}
|
items={MENU_ITEMS}
|
||||||
|
button={<MoreHorizontal className="size-4" />}
|
||||||
placement="bottom-end"
|
placement="bottom-end"
|
||||||
closeOnSelect
|
buttonClassName={cn(getIconButtonStyling("tertiary", "lg"), customClassName)}
|
||||||
maxHeight="lg"
|
/>
|
||||||
buttonClassName={customClassName}
|
|
||||||
>
|
|
||||||
{MENU_ITEMS.map((item) => {
|
|
||||||
if (item.shouldRender === false) return null;
|
|
||||||
return (
|
|
||||||
<CustomMenu.MenuItem
|
|
||||||
key={item.key}
|
|
||||||
onClick={() => {
|
|
||||||
item.action();
|
|
||||||
}}
|
|
||||||
className={cn(
|
|
||||||
"flex items-center gap-2",
|
|
||||||
{
|
|
||||||
"text-placeholder": item.disabled,
|
|
||||||
},
|
|
||||||
item.className
|
|
||||||
)}
|
|
||||||
disabled={item.disabled}
|
|
||||||
>
|
|
||||||
{item.icon && <item.icon className={cn("h-3 w-3 flex-shrink-0", item.iconClassName)} />}
|
|
||||||
<div>
|
|
||||||
<h5>{item.title}</h5>
|
|
||||||
{item.description && (
|
|
||||||
<p
|
|
||||||
className={cn("whitespace-pre-line text-tertiary", {
|
|
||||||
"text-placeholder": item.disabled,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{item.description}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</CustomMenu>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
* See the LICENSE file for details.
|
* See the LICENSE file for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ReactNode } from "react";
|
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { XCircle, ArchiveRestoreIcon } from "lucide-react";
|
import { XCircle, ArchiveRestoreIcon } from "lucide-react";
|
||||||
// plane imports
|
// plane imports
|
||||||
|
|
@ -12,8 +11,7 @@ import { useTranslation } from "@plane/i18n";
|
||||||
import { LinkIcon, CopyIcon, NewTabIcon, EditIcon, ArchiveIcon, TrashIcon } from "@plane/propel/icons";
|
import { LinkIcon, CopyIcon, NewTabIcon, EditIcon, ArchiveIcon, TrashIcon } from "@plane/propel/icons";
|
||||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
import type { EIssuesStoreType, TIssue } from "@plane/types";
|
import type { EIssuesStoreType, TIssue } from "@plane/types";
|
||||||
import { CustomMenu, type TContextMenuItem } from "@plane/ui";
|
import type { TContextMenuItem } from "@plane/ui";
|
||||||
import { cn } from "@plane/utils";
|
|
||||||
import { copyUrlToClipboard, generateWorkItemLink } from "@plane/utils";
|
import { copyUrlToClipboard, generateWorkItemLink } from "@plane/utils";
|
||||||
// types
|
// types
|
||||||
import { createCopyMenuWithDuplication } from "@/plane-web/components/issues/issue-layouts/quick-action-dropdowns";
|
import { createCopyMenuWithDuplication } from "@/plane-web/components/issues/issue-layouts/quick-action-dropdowns";
|
||||||
|
|
@ -82,68 +80,6 @@ export interface MenuItemFactoryProps {
|
||||||
storeType?: EIssuesStoreType;
|
storeType?: EIssuesStoreType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const QUICK_ACTION_MENU_LAYER_CLASS_NAME = "z-[220]";
|
|
||||||
|
|
||||||
const renderQuickActionMenuItemContent = (item: TContextMenuItem) =>
|
|
||||||
item.customContent ?? (
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
{item.icon && <item.icon className={cn("h-3 w-3", item.iconClassName)} />}
|
|
||||||
<div>
|
|
||||||
{item.title && <h5>{item.title}</h5>}
|
|
||||||
{item.description && (
|
|
||||||
<p
|
|
||||||
className={cn("whitespace-pre-line text-tertiary", {
|
|
||||||
"text-placeholder": item.disabled,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{item.description}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const renderQuickActionMenuItems = (items: TContextMenuItem[]): ReactNode[] =>
|
|
||||||
items.map((item) => {
|
|
||||||
if (item.shouldRender === false) return null;
|
|
||||||
|
|
||||||
if (item.nestedMenuItems?.some((nestedItem) => nestedItem.shouldRender !== false)) {
|
|
||||||
return (
|
|
||||||
<CustomMenu.SubMenu
|
|
||||||
key={item.key}
|
|
||||||
trigger={renderQuickActionMenuItemContent(item)}
|
|
||||||
disabled={item.disabled}
|
|
||||||
className={cn(
|
|
||||||
"flex items-center gap-2",
|
|
||||||
{
|
|
||||||
"text-placeholder": item.disabled,
|
|
||||||
},
|
|
||||||
item.className
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{renderQuickActionMenuItems(item.nestedMenuItems)}
|
|
||||||
</CustomMenu.SubMenu>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CustomMenu.MenuItem
|
|
||||||
key={item.key}
|
|
||||||
onClick={() => item.action()}
|
|
||||||
className={cn(
|
|
||||||
"flex items-center gap-2",
|
|
||||||
{
|
|
||||||
"text-placeholder": item.disabled,
|
|
||||||
},
|
|
||||||
item.className
|
|
||||||
)}
|
|
||||||
disabled={item.disabled}
|
|
||||||
>
|
|
||||||
{renderQuickActionMenuItemContent(item)}
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Common action handlers hook
|
// Common action handlers hook
|
||||||
export const useIssueActionHandlers = (props: MenuItemFactoryProps) => {
|
export const useIssueActionHandlers = (props: MenuItemFactoryProps) => {
|
||||||
const { issue, workspaceSlug, projectIdentifier, handleRestore } = props;
|
const { issue, workspaceSlug, projectIdentifier, handleRestore } = props;
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { observer } from "mobx-react";
|
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 { 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 = {
|
type Props = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
|
|
@ -49,31 +49,12 @@ export const LayoutQuickActions = observer(function LayoutQuickActions(props: Pr
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{additionalModals}
|
{additionalModals}
|
||||||
<CustomMenu
|
<ActionDropdown
|
||||||
ellipsis
|
items={MENU_ITEMS}
|
||||||
placement="bottom-end"
|
placement="bottom-end"
|
||||||
closeOnSelect
|
button={<Ellipsis className="size-4" />}
|
||||||
maxHeight="lg"
|
buttonClassName={getIconButtonStyling("tertiary", "lg")}
|
||||||
className="flex size-[26px] flex-shrink-0 items-center justify-center rounded"
|
/>
|
||||||
customButton={<IconButton size="lg" variant="tertiary" icon={Ellipsis} />}
|
|
||||||
>
|
|
||||||
{MENU_ITEMS.map((item) => {
|
|
||||||
if (item.shouldRender === false) return null;
|
|
||||||
return (
|
|
||||||
<CustomMenu.MenuItem
|
|
||||||
key={item.key}
|
|
||||||
onClick={item.action}
|
|
||||||
className={cn("flex items-center gap-2", {
|
|
||||||
"text-placeholder": item.disabled,
|
|
||||||
})}
|
|
||||||
disabled={item.disabled}
|
|
||||||
>
|
|
||||||
{item.icon && <item.icon className="h-3 w-3" />}
|
|
||||||
<span>{item.title}</span>
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</CustomMenu>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ import { observer } from "mobx-react";
|
||||||
import { MoreHorizontal } from "lucide-react";
|
import { MoreHorizontal } from "lucide-react";
|
||||||
// plane imports
|
// plane imports
|
||||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
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 { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
import type { TContextMenuItem } from "@plane/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";
|
import { copyUrlToClipboard, cn } from "@plane/utils";
|
||||||
// components
|
// components
|
||||||
import { useModuleMenuItems } from "@/components/common/quick-actions-helper";
|
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 MENU_ITEMS: TContextMenuItem[] = Array.isArray(menuResult) ? menuResult : menuResult.items;
|
||||||
const additionalModals = Array.isArray(menuResult) ? null : menuResult.modals;
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{moduleDetails && (
|
{moduleDetails && (
|
||||||
|
|
@ -133,47 +123,13 @@ export const ModuleQuickActions = observer(function ModuleQuickActions(props: Pr
|
||||||
{additionalModals}
|
{additionalModals}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ContextMenu parentRef={parentRef} items={CONTEXT_MENU_ITEMS} />
|
<ContextMenu parentRef={parentRef} items={MENU_ITEMS} />
|
||||||
<CustomMenu
|
<ActionDropdown
|
||||||
customButton={<IconButton variant="tertiary" size="lg" icon={MoreHorizontal} />}
|
items={MENU_ITEMS}
|
||||||
|
button={<MoreHorizontal className="size-4" />}
|
||||||
placement="bottom-end"
|
placement="bottom-end"
|
||||||
closeOnSelect
|
buttonClassName={cn(getIconButtonStyling("tertiary", "lg"), customClassName)}
|
||||||
buttonClassName={customClassName}
|
/>
|
||||||
>
|
|
||||||
{MENU_ITEMS.map((item) => {
|
|
||||||
if (item.shouldRender === false) return null;
|
|
||||||
return (
|
|
||||||
<CustomMenu.MenuItem
|
|
||||||
key={item.key}
|
|
||||||
onClick={() => {
|
|
||||||
item.action();
|
|
||||||
}}
|
|
||||||
className={cn(
|
|
||||||
"flex items-center gap-2",
|
|
||||||
{
|
|
||||||
"text-placeholder": item.disabled,
|
|
||||||
},
|
|
||||||
item.className
|
|
||||||
)}
|
|
||||||
disabled={item.disabled}
|
|
||||||
>
|
|
||||||
{item.icon && <item.icon className={cn("h-3 w-3 flex-shrink-0", item.iconClassName)} />}
|
|
||||||
<div>
|
|
||||||
<h5>{item.title}</h5>
|
|
||||||
{item.description && (
|
|
||||||
<p
|
|
||||||
className={cn("whitespace-pre-line text-tertiary", {
|
|
||||||
"text-placeholder": item.disabled,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{item.description}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</CustomMenu>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,15 @@
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { ArchiveRestoreIcon, FileOutput, LockKeyhole, LockKeyholeOpen } from "lucide-react";
|
import { ArchiveRestoreIcon, FileOutput, LockKeyhole, LockKeyholeOpen, MoreHorizontal } from "lucide-react";
|
||||||
// constants
|
// constants
|
||||||
import { EPageAccess } from "@plane/constants";
|
import { EPageAccess } from "@plane/constants";
|
||||||
// plane editor
|
// plane editor
|
||||||
import { LinkIcon, CopyIcon, LockIcon, NewTabIcon, ArchiveIcon, TrashIcon, GlobeIcon } from "@plane/propel/icons";
|
import { LinkIcon, CopyIcon, LockIcon, NewTabIcon, ArchiveIcon, TrashIcon, GlobeIcon } from "@plane/propel/icons";
|
||||||
|
import { getIconButtonStyling } from "@plane/propel/icon-button";
|
||||||
// plane ui
|
// plane ui
|
||||||
import type { TContextMenuItem } from "@plane/ui";
|
import type { TContextMenuItem } from "@plane/ui";
|
||||||
import { ContextMenu, CustomMenu } from "@plane/ui";
|
import { ActionDropdown, ContextMenu } from "@plane/ui";
|
||||||
// components
|
|
||||||
import { cn } from "@plane/utils";
|
|
||||||
import { DeletePageModal } from "@/components/pages/modals/delete-page-modal";
|
import { DeletePageModal } from "@/components/pages/modals/delete-page-modal";
|
||||||
// hooks
|
// hooks
|
||||||
import { usePageOperations } from "@/hooks/use-page-operations";
|
import { usePageOperations } from "@/hooks/use-page-operations";
|
||||||
|
|
@ -188,28 +187,12 @@ export const PageActions = observer(function PageActions(props: Props) {
|
||||||
storeType={storeType}
|
storeType={storeType}
|
||||||
/>
|
/>
|
||||||
{parentRef && <ContextMenu parentRef={parentRef} items={arrangedOptions} />}
|
{parentRef && <ContextMenu parentRef={parentRef} items={arrangedOptions} />}
|
||||||
<CustomMenu placement="bottom-end" optionsClassName="max-h-[90vh]" ellipsis closeOnSelect>
|
<ActionDropdown
|
||||||
{arrangedOptions.map((item) => {
|
items={arrangedOptions}
|
||||||
if (item.shouldRender === false) return null;
|
placement="bottom-end"
|
||||||
return (
|
button={<MoreHorizontal className="size-4" />}
|
||||||
<CustomMenu.MenuItem
|
buttonClassName={getIconButtonStyling("tertiary", "lg")}
|
||||||
key={item.key}
|
/>
|
||||||
onClick={() => {
|
|
||||||
item.action?.();
|
|
||||||
}}
|
|
||||||
className={cn("flex items-center gap-2", item.className)}
|
|
||||||
disabled={item.disabled}
|
|
||||||
>
|
|
||||||
{item.customContent ?? (
|
|
||||||
<>
|
|
||||||
{item.icon && <item.icon className="size-3" />}
|
|
||||||
{item.title}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</CustomMenu>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ import { observer } from "mobx-react";
|
||||||
import { MoreHorizontal } from "lucide-react";
|
import { MoreHorizontal } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
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 { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
import type { IProjectView } from "@plane/types";
|
import type { IProjectView } from "@plane/types";
|
||||||
// ui
|
// ui
|
||||||
import type { TContextMenuItem } from "@plane/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";
|
import { copyUrlToClipboard, cn } from "@plane/utils";
|
||||||
// helpers
|
// helpers
|
||||||
import { useViewMenuItems } from "@/components/common/quick-actions-helper";
|
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);
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<CreateUpdateProjectViewModal
|
<CreateUpdateProjectViewModal
|
||||||
|
|
@ -100,47 +91,13 @@ export const ViewQuickActions = observer(function ViewQuickActions(props: Props)
|
||||||
<DeleteProjectViewModal data={view} isOpen={deleteViewModal} onClose={() => setDeleteViewModal(false)} />
|
<DeleteProjectViewModal data={view} isOpen={deleteViewModal} onClose={() => setDeleteViewModal(false)} />
|
||||||
<PublishViewModal isOpen={isPublishModalOpen} onClose={() => setPublishModalOpen(false)} view={view} />
|
<PublishViewModal isOpen={isPublishModalOpen} onClose={() => setPublishModalOpen(false)} view={view} />
|
||||||
{additionalModals}
|
{additionalModals}
|
||||||
<ContextMenu parentRef={parentRef} items={CONTEXT_MENU_ITEMS} />
|
<ContextMenu parentRef={parentRef} items={MENU_ITEMS} />
|
||||||
<CustomMenu
|
<ActionDropdown
|
||||||
customButton={<IconButton variant="tertiary" size="lg" icon={MoreHorizontal} />}
|
items={MENU_ITEMS}
|
||||||
|
button={<MoreHorizontal className="size-4" />}
|
||||||
placement="bottom-end"
|
placement="bottom-end"
|
||||||
closeOnSelect
|
buttonClassName={cn(getIconButtonStyling("tertiary", "lg"), customClassName)}
|
||||||
buttonClassName={customClassName}
|
/>
|
||||||
>
|
|
||||||
{MENU_ITEMS.map((item) => {
|
|
||||||
if (item.shouldRender === false) return null;
|
|
||||||
return (
|
|
||||||
<CustomMenu.MenuItem
|
|
||||||
key={item.key}
|
|
||||||
onClick={() => {
|
|
||||||
item.action();
|
|
||||||
}}
|
|
||||||
className={cn(
|
|
||||||
"flex items-center gap-2",
|
|
||||||
{
|
|
||||||
"text-placeholder": item.disabled,
|
|
||||||
},
|
|
||||||
item.className
|
|
||||||
)}
|
|
||||||
disabled={item.disabled}
|
|
||||||
>
|
|
||||||
{item.icon && <item.icon className={cn("h-3 w-3", item.iconClassName)} />}
|
|
||||||
<div>
|
|
||||||
<h5>{item.title}</h5>
|
|
||||||
{item.description && (
|
|
||||||
<p
|
|
||||||
className={cn("whitespace-pre-line text-tertiary", {
|
|
||||||
"text-placeholder": item.disabled,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{item.description}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</CustomMenu>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue