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

This commit is contained in:
upstream-mirror
2026-09-15 15:14:58 +05:00
commit 23059a1de3
5252 changed files with 573834 additions and 0 deletions

View File

@@ -0,0 +1,223 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { useMemo, useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import {
ArchiveOutline,
CopyOutline,
DeleteOutline,
ExportOutline,
GlobeOutline,
LinkOutline,
LockOutline,
LockedOutline,
NewTabOutline,
RestoreOutline,
UnlockedOutline,
} from "@makeplane/propel/icons";
// constants
import { EPageAccess } from "@plane/constants";
// plane editor
// plane ui
import type { TContextMenuItem } from "@plane/ui";
import { ContextMenu, CustomMenu } from "@plane/ui";
// components
import { cn } from "@plane/utils";
import { DeletePageModal } from "@/components/pages/modals/delete-page-modal";
// hooks
import { usePageOperations } from "@/hooks/use-page-operations";
// plane web hooks
import type { EPageStoreType } from "@/hooks/store";
import { usePageFlag } from "@/hooks/use-page-flag";
// store types
import type { TPageInstance } from "@/store/pages/base-page";
export type TPageActions =
| "full-screen"
| "sticky-toolbar"
| "copy-markdown"
| "toggle-lock"
| "toggle-access"
| "open-in-new-tab"
| "copy-link"
| "make-a-copy"
| "archive-restore"
| "delete"
| "version-history"
| "export"
| "move";
type Props = {
extraOptions?: (TContextMenuItem & { key: TPageActions })[];
optionsOrder: TPageActions[];
page: TPageInstance;
parentRef?: React.RefObject<HTMLElement | null>;
storeType: EPageStoreType;
};
export const PageActions = observer(function PageActions(props: Props) {
const { extraOptions, optionsOrder, page, parentRef, storeType } = props;
// states
const [deletePageModal, setDeletePageModal] = useState(false);
const [movePageModal, setMovePageModal] = useState(false);
// params
const { workspaceSlug } = useParams();
// page flag
const { isMovePageEnabled } = usePageFlag({
workspaceSlug: workspaceSlug?.toString() ?? "",
});
// page operations
const { pageOperations } = usePageOperations({
page,
});
// derived values
const {
access,
archived_at,
is_locked,
canCurrentUserArchivePage,
canCurrentUserChangeAccess,
canCurrentUserDeletePage,
canCurrentUserDuplicatePage,
canCurrentUserLockPage,
canCurrentUserMovePage,
} = page;
// menu items
const MENU_ITEMS = useMemo(
function MENU_ITEMS() {
const menuItems: (TContextMenuItem & { key: TPageActions })[] = [
{
key: "toggle-lock",
action: () => {
pageOperations.toggleLock();
},
title: is_locked ? "Unlock" : "Lock",
icon: is_locked ? UnlockedOutline : LockedOutline,
shouldRender: canCurrentUserLockPage,
},
{
key: "toggle-access",
action: () => {
pageOperations.toggleAccess();
},
title: access === EPageAccess.PUBLIC ? "Make private" : "Make public",
icon: access === EPageAccess.PUBLIC ? LockOutline : GlobeOutline,
shouldRender: canCurrentUserChangeAccess && !archived_at,
},
{
key: "open-in-new-tab",
action: pageOperations.openInNewTab,
title: "Open in new tab",
icon: NewTabOutline,
shouldRender: true,
},
{
key: "copy-link",
action: pageOperations.copyLink,
title: "Copy link",
icon: LinkOutline,
shouldRender: true,
},
{
key: "make-a-copy",
action: () => {
pageOperations.duplicate();
},
title: "Make a copy",
icon: CopyOutline,
shouldRender: canCurrentUserDuplicatePage,
},
{
key: "archive-restore",
action: () => {
pageOperations.toggleArchive();
},
title: archived_at ? "Restore" : "Archive",
icon: archived_at ? RestoreOutline : ArchiveOutline,
shouldRender: canCurrentUserArchivePage,
},
{
key: "delete",
action: () => {
setDeletePageModal(true);
},
title: "Delete",
icon: DeleteOutline,
shouldRender: canCurrentUserDeletePage && !!archived_at,
},
{
key: "move",
action: () => setMovePageModal(true),
title: "Move",
icon: ExportOutline,
shouldRender: canCurrentUserMovePage && isMovePageEnabled,
},
];
if (extraOptions) {
menuItems.push(...extraOptions);
}
return menuItems;
},
[
extraOptions,
is_locked,
canCurrentUserLockPage,
access,
canCurrentUserChangeAccess,
archived_at,
canCurrentUserDuplicatePage,
canCurrentUserArchivePage,
canCurrentUserDeletePage,
canCurrentUserMovePage,
isMovePageEnabled,
pageOperations,
]
);
// arrange options
const arrangedOptions = useMemo<(TContextMenuItem & { key: TPageActions })[]>(
() =>
optionsOrder
.map((key) => MENU_ITEMS.find((item) => item.key === key))
.filter((item): item is TContextMenuItem & { key: TPageActions } => !!item),
[optionsOrder, MENU_ITEMS]
);
return (
<>
<DeletePageModal
isOpen={deletePageModal}
onClose={() => setDeletePageModal(false)}
page={page}
storeType={storeType}
/>
{parentRef && <ContextMenu parentRef={parentRef} items={arrangedOptions} />}
<CustomMenu placement="bottom-end" optionsClassName="max-h-[90vh]" ellipsis closeOnSelect>
{arrangedOptions.map((item) => {
if (item.shouldRender === false) return null;
return (
<CustomMenu.MenuItem
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>
</>
);
});

View File

@@ -0,0 +1,7 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
export * from "./actions";