[WIKI-511] [WIKI-572] fix: extended page root and editor body (#7661)
* fix: better refactoring of page root and editor body * fix: editor sideeffects * fix: add comments json field * fix: props name * fix: review changes * fix: extra checks * fix: type changes * fix: remove editor ref current * fix: renaming * fix: link check
This commit is contained in:
parent
f42eeec2c0
commit
fd5ba6c7b8
39 changed files with 492 additions and 106 deletions
|
|
@ -13,7 +13,7 @@ import { useIssueEmbed } from "@/plane-web/hooks/use-issue-embed";
|
|||
import { EditorMentionsRoot } from "../embeds/mentions";
|
||||
|
||||
type DocumentEditorWrapperProps = MakeOptional<
|
||||
Omit<IDocumentEditorProps, "fileHandler" | "mentionHandler" | "embedHandler" | "user">,
|
||||
Omit<IDocumentEditorProps, "fileHandler" | "mentionHandler" | "embedHandler" | "user" | "extendedEditorProps">,
|
||||
"disabledExtensions" | "editable" | "flaggedExtensions"
|
||||
> & {
|
||||
embedHandler?: Partial<IDocumentEditorProps["embedHandler"]>;
|
||||
|
|
@ -45,7 +45,9 @@ export const DocumentEditor = forwardRef<EditorRefApi, DocumentEditorWrapperProp
|
|||
// store hooks
|
||||
const { getUserDetails } = useMember();
|
||||
// editor flaggings
|
||||
const { document: documentEditorExtensions } = useEditorFlagging(workspaceSlug);
|
||||
const { document: documentEditorExtensions } = useEditorFlagging({
|
||||
workspaceSlug: workspaceSlug?.toString() ?? "",
|
||||
});
|
||||
// use editor mention
|
||||
const { fetchMentions } = useEditorMention({
|
||||
searchEntity: editable ? async (payload) => await props.searchMentionCallback(payload) : async () => ({}),
|
||||
|
|
@ -83,6 +85,7 @@ export const DocumentEditor = forwardRef<EditorRefApi, DocumentEditorWrapperProp
|
|||
issue: issueEmbedProps,
|
||||
...embedHandler,
|
||||
}}
|
||||
extendedEditorProps={{}}
|
||||
{...rest}
|
||||
containerClassName={cn("relative pl-3 pb-3", containerClassName)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import { WorkspaceService } from "@/plane-web/services";
|
|||
const workspaceService = new WorkspaceService();
|
||||
|
||||
type LiteTextEditorWrapperProps = MakeOptional<
|
||||
Omit<ILiteTextEditorProps, "fileHandler" | "mentionHandler">,
|
||||
Omit<ILiteTextEditorProps, "fileHandler" | "mentionHandler" | "extendedEditorProps">,
|
||||
"disabledExtensions" | "flaggedExtensions"
|
||||
> & {
|
||||
workspaceSlug: string;
|
||||
|
|
@ -68,7 +68,9 @@ export const LiteTextEditor = React.forwardRef<EditorRefApi, LiteTextEditorWrapp
|
|||
// states
|
||||
const [isFocused, setIsFocused] = useState(showToolbarInitially);
|
||||
// editor flaggings
|
||||
const { liteText: liteTextEditorExtensions } = useEditorFlagging(workspaceSlug?.toString());
|
||||
const { liteText: liteTextEditorExtensions } = useEditorFlagging({
|
||||
workspaceSlug: workspaceSlug?.toString() ?? "",
|
||||
});
|
||||
// store hooks
|
||||
const { getUserDetails } = useMember();
|
||||
// use editor mention
|
||||
|
|
@ -126,6 +128,7 @@ export const LiteTextEditor = React.forwardRef<EditorRefApi, LiteTextEditorWrapp
|
|||
containerClassName={cn(containerClassName, "relative", {
|
||||
"p-2": !editable,
|
||||
})}
|
||||
extendedEditorProps={{}}
|
||||
{...rest}
|
||||
/>
|
||||
{showToolbar && editable && (
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { useMember } from "@/hooks/store/use-member";
|
|||
import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging";
|
||||
|
||||
type RichTextEditorWrapperProps = MakeOptional<
|
||||
Omit<IRichTextEditorProps, "fileHandler" | "mentionHandler">,
|
||||
Omit<IRichTextEditorProps, "fileHandler" | "mentionHandler" | "extendedEditorProps">,
|
||||
"disabledExtensions" | "editable" | "flaggedExtensions"
|
||||
> & {
|
||||
workspaceSlug: string;
|
||||
|
|
@ -42,7 +42,9 @@ export const RichTextEditor = forwardRef<EditorRefApi, RichTextEditorWrapperProp
|
|||
// store hooks
|
||||
const { getUserDetails } = useMember();
|
||||
// editor flaggings
|
||||
const { richText: richTextEditorExtensions } = useEditorFlagging(workspaceSlug?.toString());
|
||||
const { richText: richTextEditorExtensions } = useEditorFlagging({
|
||||
workspaceSlug: workspaceSlug?.toString() ?? "",
|
||||
});
|
||||
// use editor mention
|
||||
const { fetchMentions } = useEditorMention({
|
||||
searchEntity: editable ? async (payload) => await props.searchMentionCallback(payload) : async () => ({}),
|
||||
|
|
@ -73,6 +75,7 @@ export const RichTextEditor = forwardRef<EditorRefApi, RichTextEditorWrapperProp
|
|||
display_name: getUserDetails(id)?.display_name ?? "",
|
||||
}),
|
||||
}}
|
||||
extendedEditorProps={{}}
|
||||
{...rest}
|
||||
containerClassName={cn("relative pl-3 pb-3", containerClassName)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { StickyEditorToolbar } from "./toolbar";
|
|||
|
||||
interface StickyEditorWrapperProps
|
||||
extends Omit<
|
||||
ILiteTextEditorProps,
|
||||
Omit<ILiteTextEditorProps, "extendedEditorProps">,
|
||||
"disabledExtensions" | "editable" | "flaggedExtensions" | "fileHandler" | "mentionHandler"
|
||||
> {
|
||||
workspaceSlug: string;
|
||||
|
|
@ -51,7 +51,9 @@ export const StickyEditor = React.forwardRef<EditorRefApi, StickyEditorWrapperPr
|
|||
// states
|
||||
const [isFocused, setIsFocused] = useState(showToolbarInitially);
|
||||
// editor flaggings
|
||||
const { liteText: liteTextEditorExtensions } = useEditorFlagging(workspaceSlug?.toString());
|
||||
const { liteText: liteTextEditorExtensions } = useEditorFlagging({
|
||||
workspaceSlug: workspaceSlug?.toString() ?? "",
|
||||
});
|
||||
// editor config
|
||||
const { getEditorFileHandlers } = useEditorConfig();
|
||||
function isMutableRefObject<T>(ref: React.ForwardedRef<T>): ref is React.MutableRefObject<T | null> {
|
||||
|
|
@ -79,6 +81,7 @@ export const StickyEditor = React.forwardRef<EditorRefApi, StickyEditorWrapperPr
|
|||
mentionHandler={{
|
||||
renderComponent: () => <></>,
|
||||
}}
|
||||
extendedEditorProps={{}}
|
||||
containerClassName={cn(containerClassName, "relative")}
|
||||
{...rest}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -24,8 +24,13 @@ import { useUser } from "@/hooks/store/user";
|
|||
import { usePageFilters } from "@/hooks/use-page-filters";
|
||||
// plane web components
|
||||
import { EditorAIMenu } from "@/plane-web/components/pages";
|
||||
// plane web types
|
||||
import type { TExtendedEditorExtensionsConfig } from "@/plane-web/hooks/pages";
|
||||
// plane web store
|
||||
import { EPageStoreType } from "@/plane-web/hooks/store";
|
||||
// plane web hooks
|
||||
import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging";
|
||||
|
||||
import { useIssueEmbed } from "@/plane-web/hooks/use-issue-embed";
|
||||
// store
|
||||
import type { TPageInstance } from "@/store/pages/base-page";
|
||||
|
|
@ -41,6 +46,7 @@ export type TEditorBodyConfig = {
|
|||
|
||||
export type TEditorBodyHandlers = {
|
||||
fetchEntity: (payload: TSearchEntityRequestPayload) => Promise<TSearchResponse>;
|
||||
getRedirectionLink: (pageId?: string) => string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
|
|
@ -54,7 +60,11 @@ type Props = {
|
|||
isNavigationPaneOpen: boolean;
|
||||
page: TPageInstance;
|
||||
webhookConnectionParams: TWebhookConnectionQueryParams;
|
||||
projectId: string;
|
||||
workspaceSlug: string;
|
||||
storeType: EPageStoreType;
|
||||
|
||||
extendedEditorProps: TExtendedEditorExtensionsConfig;
|
||||
};
|
||||
|
||||
export const PageEditorBody: React.FC<Props> = observer((props) => {
|
||||
|
|
@ -67,8 +77,11 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
|||
handlers,
|
||||
isNavigationPaneOpen,
|
||||
page,
|
||||
storeType,
|
||||
webhookConnectionParams,
|
||||
projectId,
|
||||
workspaceSlug,
|
||||
extendedEditorProps,
|
||||
} = props;
|
||||
// store hooks
|
||||
const { data: currentUser } = useUser();
|
||||
|
|
@ -93,7 +106,10 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
|||
searchEntity: handlers.fetchEntity,
|
||||
});
|
||||
// editor flaggings
|
||||
const { document: documentEditorExtensions } = useEditorFlagging(workspaceSlug);
|
||||
const { document: documentEditorExtensions } = useEditorFlagging({
|
||||
workspaceSlug,
|
||||
storeType,
|
||||
});
|
||||
// page filters
|
||||
const { fontSize, fontStyle, isFullWidth } = usePageFilters();
|
||||
// translation
|
||||
|
|
@ -115,7 +131,7 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
|||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
workspaceId={workspaceId}
|
||||
workspaceSlug={workspaceSlug?.toString() ?? ""}
|
||||
workspaceSlug={workspaceSlug}
|
||||
/>
|
||||
),
|
||||
[editorRef, workspaceId, workspaceSlug]
|
||||
|
|
@ -202,7 +218,7 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
|||
)}
|
||||
<div className="page-header-container group/page-header">
|
||||
<div className={blockWidthClassName}>
|
||||
<PageEditorHeaderRoot page={page} />
|
||||
<PageEditorHeaderRoot page={page} projectId={projectId} />
|
||||
<PageEditorTitle
|
||||
editorRef={editorRef}
|
||||
readOnly={!isContentEditable}
|
||||
|
|
@ -240,6 +256,7 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
|||
menu: getAIMenu,
|
||||
}}
|
||||
onAssetChange={updateAssetsList}
|
||||
extendedEditorProps={extendedEditorProps}
|
||||
/>
|
||||
</div>
|
||||
</Row>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { PageEditorHeaderLogoPicker } from "./logo-picker";
|
|||
|
||||
type Props = {
|
||||
page: TPageInstance;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
// plane imports
|
||||
import type { EditorRefApi } from "@plane/editor";
|
||||
import type { TDocumentPayload, TPage, TPageVersion, TWebhookConnectionQueryParams } from "@plane/types";
|
||||
|
|
@ -9,19 +8,20 @@ import { useAppRouter } from "@/hooks/use-app-router";
|
|||
import { usePageFallback } from "@/hooks/use-page-fallback";
|
||||
import { useQueryParams } from "@/hooks/use-query-params";
|
||||
// plane web import
|
||||
import type { TPageNavigationPaneTab } from "@/plane-web/components/pages/navigation-pane";
|
||||
import { PageModals } from "@/plane-web/components/pages";
|
||||
import { usePagesPaneExtensions, useExtendedEditorProps } from "@/plane-web/hooks/pages";
|
||||
import { EPageStoreType } from "@/plane-web/hooks/store";
|
||||
// store
|
||||
import type { TPageInstance } from "@/store/pages/base-page";
|
||||
// local imports
|
||||
import {
|
||||
PAGE_NAVIGATION_PANE_TAB_KEYS,
|
||||
PAGE_NAVIGATION_PANE_TABS_QUERY_PARAM,
|
||||
PAGE_NAVIGATION_PANE_VERSION_QUERY_PARAM,
|
||||
PageNavigationPaneRoot,
|
||||
} from "../navigation-pane";
|
||||
import { PageVersionsOverlay } from "../version";
|
||||
import { PagesVersionEditor } from "../version/editor";
|
||||
import { PageEditorBody, TEditorBodyConfig, TEditorBodyHandlers } from "./editor-body";
|
||||
import { PageEditorBody, type TEditorBodyConfig, type TEditorBodyHandlers } from "./editor-body";
|
||||
import { PageEditorToolbarRoot } from "./toolbar";
|
||||
|
||||
export type TPageRootHandlers = {
|
||||
|
|
@ -29,7 +29,7 @@ export type TPageRootHandlers = {
|
|||
fetchAllVersions: (pageId: string) => Promise<TPageVersion[] | undefined>;
|
||||
fetchDescriptionBinary: () => Promise<any>;
|
||||
fetchVersionDetails: (pageId: string, versionId: string) => Promise<TPageVersion | undefined>;
|
||||
getRedirectionLink: (pageId: string) => string;
|
||||
restoreVersion: (pageId: string, versionId: string) => Promise<void>;
|
||||
updateDescription: (document: TDocumentPayload) => Promise<void>;
|
||||
} & TEditorBodyHandlers;
|
||||
|
||||
|
|
@ -39,12 +39,14 @@ type TPageRootProps = {
|
|||
config: TPageRootConfig;
|
||||
handlers: TPageRootHandlers;
|
||||
page: TPageInstance;
|
||||
storeType: EPageStoreType;
|
||||
webhookConnectionParams: TWebhookConnectionQueryParams;
|
||||
projectId: string;
|
||||
workspaceSlug: string;
|
||||
};
|
||||
|
||||
export const PageRoot = observer((props: TPageRootProps) => {
|
||||
const { config, handlers, page, webhookConnectionParams, workspaceSlug } = props;
|
||||
const { config, handlers, page, projectId, storeType, webhookConnectionParams, workspaceSlug } = props;
|
||||
// states
|
||||
const [editorReady, setEditorReady] = useState(false);
|
||||
const [hasConnectionFailed, setHasConnectionFailed] = useState(false);
|
||||
|
|
@ -52,8 +54,6 @@ export const PageRoot = observer((props: TPageRootProps) => {
|
|||
const editorRef = useRef<EditorRefApi>(null);
|
||||
// router
|
||||
const router = useAppRouter();
|
||||
// search params
|
||||
const searchParams = useSearchParams();
|
||||
// derived values
|
||||
const {
|
||||
isContentEditable,
|
||||
|
|
@ -66,7 +66,6 @@ export const PageRoot = observer((props: TPageRootProps) => {
|
|||
hasConnectionFailed,
|
||||
updatePageDescription: handlers.updateDescription,
|
||||
});
|
||||
// update query params
|
||||
const { updateQueryParams } = useQueryParams();
|
||||
|
||||
const handleEditorReady = useCallback(
|
||||
|
|
@ -85,10 +84,31 @@ export const PageRoot = observer((props: TPageRootProps) => {
|
|||
}, 0);
|
||||
}, [isContentEditable, setEditorRef]);
|
||||
|
||||
const handleRestoreVersion = useCallback(async (descriptionHTML: string) => {
|
||||
editorRef.current?.clearEditor();
|
||||
editorRef.current?.setEditorValue(descriptionHTML);
|
||||
}, []);
|
||||
// Get extensions and navigation logic from hook
|
||||
const { editorExtensionHandlers, navigationPaneExtensions, handleOpenNavigationPane, isNavigationPaneOpen } =
|
||||
usePagesPaneExtensions({
|
||||
page,
|
||||
editorRef,
|
||||
});
|
||||
|
||||
// Get extended editor extensions configuration
|
||||
const extendedEditorProps = useExtendedEditorProps({
|
||||
workspaceSlug,
|
||||
page,
|
||||
storeType,
|
||||
fetchEntity: handlers.fetchEntity,
|
||||
getRedirectionLink: handlers.getRedirectionLink,
|
||||
extensionHandlers: editorExtensionHandlers,
|
||||
projectId,
|
||||
});
|
||||
|
||||
const handleRestoreVersion = useCallback(
|
||||
async (descriptionHTML: string) => {
|
||||
editorRef.current?.clearEditor();
|
||||
editorRef.current?.setEditorValue(descriptionHTML);
|
||||
},
|
||||
[editorRef]
|
||||
);
|
||||
|
||||
// reset editor ref on unmount
|
||||
useEffect(
|
||||
|
|
@ -98,19 +118,6 @@ export const PageRoot = observer((props: TPageRootProps) => {
|
|||
[setEditorRef]
|
||||
);
|
||||
|
||||
const navigationPaneQueryParam = searchParams.get(
|
||||
PAGE_NAVIGATION_PANE_TABS_QUERY_PARAM
|
||||
) as TPageNavigationPaneTab | null;
|
||||
const isValidNavigationPaneTab =
|
||||
!!navigationPaneQueryParam && PAGE_NAVIGATION_PANE_TAB_KEYS.includes(navigationPaneQueryParam);
|
||||
|
||||
const handleOpenNavigationPane = useCallback(() => {
|
||||
const updatedRoute = updateQueryParams({
|
||||
paramsToAdd: { [PAGE_NAVIGATION_PANE_TABS_QUERY_PARAM]: "outline" },
|
||||
});
|
||||
router.push(updatedRoute);
|
||||
}, [router, updateQueryParams]);
|
||||
|
||||
const handleCloseNavigationPane = useCallback(() => {
|
||||
const updatedRoute = updateQueryParams({
|
||||
paramsToRemove: [PAGE_NAVIGATION_PANE_TABS_QUERY_PARAM, PAGE_NAVIGATION_PANE_VERSION_QUERY_PARAM],
|
||||
|
|
@ -127,10 +134,11 @@ export const PageRoot = observer((props: TPageRootProps) => {
|
|||
handleRestore={handleRestoreVersion}
|
||||
pageId={page.id ?? ""}
|
||||
restoreEnabled={isContentEditable}
|
||||
storeType={storeType}
|
||||
/>
|
||||
<PageEditorToolbarRoot
|
||||
handleOpenNavigationPane={handleOpenNavigationPane}
|
||||
isNavigationPaneOpen={isValidNavigationPaneTab}
|
||||
isNavigationPaneOpen={isNavigationPaneOpen}
|
||||
page={page}
|
||||
/>
|
||||
<PageEditorBody
|
||||
|
|
@ -141,21 +149,27 @@ export const PageRoot = observer((props: TPageRootProps) => {
|
|||
handleEditorReady={handleEditorReady}
|
||||
handleOpenNavigationPane={handleOpenNavigationPane}
|
||||
handlers={handlers}
|
||||
isNavigationPaneOpen={isValidNavigationPaneTab}
|
||||
isNavigationPaneOpen={isNavigationPaneOpen}
|
||||
page={page}
|
||||
projectId={projectId}
|
||||
storeType={storeType}
|
||||
webhookConnectionParams={webhookConnectionParams}
|
||||
workspaceSlug={workspaceSlug}
|
||||
extendedEditorProps={extendedEditorProps}
|
||||
/>
|
||||
</div>
|
||||
<PageNavigationPaneRoot
|
||||
storeType={storeType}
|
||||
handleClose={handleCloseNavigationPane}
|
||||
isNavigationPaneOpen={isValidNavigationPaneTab}
|
||||
isNavigationPaneOpen={isNavigationPaneOpen}
|
||||
page={page}
|
||||
versionHistory={{
|
||||
fetchAllVersions: handlers.fetchAllVersions,
|
||||
fetchVersionDetails: handlers.fetchVersionDetails,
|
||||
}}
|
||||
extensions={navigationPaneExtensions}
|
||||
/>
|
||||
<PageModals page={page} storeType={storeType} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { ORDERED_PAGE_NAVIGATION_TABS_LIST } from "@/plane-web/components/pages/navigation-pane";
|
||||
|
||||
export * from "./root";
|
||||
export * from "./types";
|
||||
|
||||
export const PAGE_NAVIGATION_PANE_WIDTH = 294;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,11 +11,14 @@ import { useQueryParams } from "@/hooks/use-query-params";
|
|||
// plane web components
|
||||
import { TPageNavigationPaneTab } from "@/plane-web/components/pages/navigation-pane";
|
||||
// store
|
||||
import { type EPageStoreType } from "@/plane-web/hooks/store";
|
||||
import type { TPageInstance } from "@/store/pages/base-page";
|
||||
// local imports
|
||||
import { TPageRootHandlers } from "../editor/page-root";
|
||||
import { PageNavigationPaneTabPanelsRoot } from "./tab-panels/root";
|
||||
import { PageNavigationPaneTabsList } from "./tabs-list";
|
||||
import { INavigationPaneExtension } from "./types/extensions";
|
||||
|
||||
import {
|
||||
PAGE_NAVIGATION_PANE_TAB_KEYS,
|
||||
PAGE_NAVIGATION_PANE_TABS_QUERY_PARAM,
|
||||
|
|
@ -28,10 +31,14 @@ type Props = {
|
|||
isNavigationPaneOpen: boolean;
|
||||
page: TPageInstance;
|
||||
versionHistory: Pick<TPageRootHandlers, "fetchAllVersions" | "fetchVersionDetails">;
|
||||
// Generic extension system for additional navigation pane content
|
||||
extensions?: INavigationPaneExtension[];
|
||||
storeType: EPageStoreType;
|
||||
};
|
||||
|
||||
export const PageNavigationPaneRoot: React.FC<Props> = observer((props) => {
|
||||
const { handleClose, isNavigationPaneOpen, page, versionHistory } = props;
|
||||
const { handleClose, isNavigationPaneOpen, page, versionHistory, extensions = [], storeType } = props;
|
||||
|
||||
// navigation
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
|
@ -43,6 +50,21 @@ export const PageNavigationPaneRoot: React.FC<Props> = observer((props) => {
|
|||
) as TPageNavigationPaneTab | null;
|
||||
const activeTab: TPageNavigationPaneTab = navigationPaneQueryParam || "outline";
|
||||
const selectedIndex = PAGE_NAVIGATION_PANE_TAB_KEYS.indexOf(activeTab);
|
||||
|
||||
// Check if any extension is currently active based on query parameters
|
||||
const ActiveExtension = extensions.find((extension) => {
|
||||
const paneTabValue = searchParams.get(PAGE_NAVIGATION_PANE_TABS_QUERY_PARAM);
|
||||
const hasVersionParam = searchParams.get(PAGE_NAVIGATION_PANE_VERSION_QUERY_PARAM);
|
||||
|
||||
// Extension is active ONLY when paneTab matches AND no regular navigation params are present
|
||||
return paneTabValue === extension.triggerParam && !hasVersionParam;
|
||||
});
|
||||
|
||||
// Don't show tabs when an extension is active
|
||||
const showNavigationTabs = !ActiveExtension && isNavigationPaneOpen;
|
||||
|
||||
// Use extension width if available, otherwise fall back to default
|
||||
const paneWidth = ActiveExtension?.width ?? PAGE_NAVIGATION_PANE_WIDTH;
|
||||
// translation
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
|
@ -61,10 +83,10 @@ export const PageNavigationPaneRoot: React.FC<Props> = observer((props) => {
|
|||
|
||||
return (
|
||||
<aside
|
||||
className="flex-shrink-0 h-full flex flex-col bg-custom-background-100 pt-3.5 border-l border-custom-border-200 transition-all duration-300 ease-in-out"
|
||||
className="flex-shrink-0 h-full flex flex-col bg-custom-background-100 pt-3.5 border-l border-custom-border-200 transition-all duration-300 ease-out"
|
||||
style={{
|
||||
width: `${PAGE_NAVIGATION_PANE_WIDTH}px`,
|
||||
marginRight: isNavigationPaneOpen ? "0px" : `-${PAGE_NAVIGATION_PANE_WIDTH}px`,
|
||||
width: `${paneWidth}px`,
|
||||
marginRight: isNavigationPaneOpen ? "0px" : `-${paneWidth}px`,
|
||||
}}
|
||||
>
|
||||
<div className="mb-3.5 px-3.5">
|
||||
|
|
@ -79,10 +101,17 @@ export const PageNavigationPaneRoot: React.FC<Props> = observer((props) => {
|
|||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Tab.Group as={React.Fragment} selectedIndex={selectedIndex} onChange={handleTabChange}>
|
||||
<PageNavigationPaneTabsList />
|
||||
<PageNavigationPaneTabPanelsRoot page={page} versionHistory={versionHistory} />
|
||||
</Tab.Group>
|
||||
|
||||
<div className="flex-1 flex flex-col overflow-hidden animate-slide-in-right">
|
||||
{ActiveExtension ? (
|
||||
<ActiveExtension.component page={page} extensionData={ActiveExtension.data} storeType={storeType} />
|
||||
) : showNavigationTabs ? (
|
||||
<Tab.Group as={React.Fragment} selectedIndex={selectedIndex} onChange={handleTabChange}>
|
||||
<PageNavigationPaneTabsList />
|
||||
<PageNavigationPaneTabPanelsRoot page={page} versionHistory={versionHistory} />
|
||||
</Tab.Group>
|
||||
) : null}
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { ReactNode } from "react";
|
||||
import { EPageStoreType } from "@/plane-web/hooks/store";
|
||||
import { TPageInstance } from "@/store/pages/base-page";
|
||||
|
||||
export interface INavigationPaneExtensionProps<T = any> {
|
||||
page: TPageInstance;
|
||||
extensionData?: T;
|
||||
storeType: EPageStoreType;
|
||||
}
|
||||
|
||||
export interface INavigationPaneExtensionComponent<T = any> {
|
||||
(props: INavigationPaneExtensionProps<T>): ReactNode;
|
||||
}
|
||||
|
||||
export interface INavigationPaneExtension<T = any> {
|
||||
id: string;
|
||||
triggerParam: string;
|
||||
component: INavigationPaneExtensionComponent<T>;
|
||||
data?: T;
|
||||
width?: number;
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// Export generic extension system interfaces
|
||||
export type {
|
||||
INavigationPaneExtensionProps,
|
||||
INavigationPaneExtensionComponent,
|
||||
INavigationPaneExtension,
|
||||
} from "./extensions";
|
||||
|
|
@ -9,10 +9,13 @@ import { DocumentEditor } from "@/components/editor/document/editor";
|
|||
// hooks
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
import { usePageFilters } from "@/hooks/use-page-filters";
|
||||
// plane web hooks
|
||||
import { EPageStoreType } from "@/plane-web/hooks/store";
|
||||
|
||||
export type TVersionEditorProps = {
|
||||
activeVersion: string | null;
|
||||
versionDetails: TPageVersion | undefined;
|
||||
storeType: EPageStoreType;
|
||||
};
|
||||
|
||||
export const PagesVersionEditor: React.FC<TVersionEditorProps> = observer((props) => {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import { EyeIcon, TriangleAlert } from "lucide-react";
|
|||
import { TPageVersion } from "@plane/types";
|
||||
import { Button, setToast, TOAST_TYPE } from "@plane/ui";
|
||||
import { renderFormattedDate, renderFormattedTime } from "@plane/utils";
|
||||
// helpers
|
||||
import { EPageStoreType } from "@/plane-web/hooks/store";
|
||||
// local imports
|
||||
import { TVersionEditorProps } from "./editor";
|
||||
|
||||
|
|
@ -17,11 +19,20 @@ type Props = {
|
|||
handleRestore: (descriptionHTML: string) => Promise<void>;
|
||||
pageId: string;
|
||||
restoreEnabled: boolean;
|
||||
storeType: EPageStoreType;
|
||||
};
|
||||
|
||||
export const PageVersionsMainContent: React.FC<Props> = observer((props) => {
|
||||
const { activeVersion, editorComponent, fetchVersionDetails, handleClose, handleRestore, pageId, restoreEnabled } =
|
||||
props;
|
||||
const {
|
||||
activeVersion,
|
||||
editorComponent,
|
||||
fetchVersionDetails,
|
||||
handleClose,
|
||||
handleRestore,
|
||||
pageId,
|
||||
restoreEnabled,
|
||||
storeType,
|
||||
} = props;
|
||||
// states
|
||||
const [isRestoring, setIsRestoring] = useState(false);
|
||||
const [isRetrying, setIsRetrying] = useState(false);
|
||||
|
|
@ -107,7 +118,7 @@ export const PageVersionsMainContent: React.FC<Props> = observer((props) => {
|
|||
)}
|
||||
</div>
|
||||
<div className="pt-8 h-full overflow-y-scroll vertical-scrollbar scrollbar-sm">
|
||||
<VersionEditor activeVersion={activeVersion} versionDetails={versionDetails} />
|
||||
<VersionEditor activeVersion={activeVersion} storeType={storeType} versionDetails={versionDetails} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import { TPageVersion } from "@plane/types";
|
|||
import { cn } from "@plane/utils";
|
||||
// hooks
|
||||
import { useQueryParams } from "@/hooks/use-query-params";
|
||||
// plane web imports
|
||||
import { EPageStoreType } from "@/plane-web/hooks/store";
|
||||
// local imports
|
||||
import { PAGE_NAVIGATION_PANE_VERSION_QUERY_PARAM, PAGE_NAVIGATION_PANE_WIDTH } from "../navigation-pane";
|
||||
import { TVersionEditorProps } from "./editor";
|
||||
|
|
@ -17,10 +19,11 @@ type Props = {
|
|||
handleRestore: (descriptionHTML: string) => Promise<void>;
|
||||
pageId: string;
|
||||
restoreEnabled: boolean;
|
||||
storeType: EPageStoreType;
|
||||
};
|
||||
|
||||
export const PageVersionsOverlay: React.FC<Props> = observer((props) => {
|
||||
const { editorComponent, fetchVersionDetails, handleRestore, pageId, restoreEnabled } = props;
|
||||
const { editorComponent, fetchVersionDetails, handleRestore, pageId, restoreEnabled, storeType } = props;
|
||||
// navigation
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
|
@ -57,6 +60,7 @@ export const PageVersionsOverlay: React.FC<Props> = observer((props) => {
|
|||
handleRestore={handleRestore}
|
||||
pageId={pageId}
|
||||
restoreEnabled={restoreEnabled}
|
||||
storeType={storeType}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -30,4 +30,14 @@ export class ProjectPageVersionService extends APIService {
|
|||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async restoreVersion(workspaceSlug: string, projectId: string, pageId: string, versionId: string): Promise<void> {
|
||||
return this.post(
|
||||
`/api/workspaces/${workspaceSlug}/projects/${projectId}/pages/${pageId}/versions/${versionId}/restore/`
|
||||
)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ export class BasePage extends ExtendedBasePage implements TBasePage {
|
|||
updated_by: string | undefined;
|
||||
created_at: Date | undefined;
|
||||
updated_at: Date | undefined;
|
||||
deleted_at: Date | undefined;
|
||||
// helpers
|
||||
oldName: string = "";
|
||||
// services
|
||||
|
|
@ -130,6 +131,7 @@ export class BasePage extends ExtendedBasePage implements TBasePage {
|
|||
this.created_at = page?.created_at || undefined;
|
||||
this.updated_at = page?.updated_at || undefined;
|
||||
this.oldName = page?.name || "";
|
||||
this.deleted_at = page?.deleted_at || undefined;
|
||||
|
||||
makeObservable(this, {
|
||||
// loaders
|
||||
|
|
@ -153,6 +155,7 @@ export class BasePage extends ExtendedBasePage implements TBasePage {
|
|||
updated_by: observable.ref,
|
||||
created_at: observable.ref,
|
||||
updated_at: observable.ref,
|
||||
deleted_at: observable.ref,
|
||||
// helpers
|
||||
oldName: observable.ref,
|
||||
setIsSubmitting: action,
|
||||
|
|
@ -227,6 +230,7 @@ export class BasePage extends ExtendedBasePage implements TBasePage {
|
|||
updated_by: this.updated_by,
|
||||
created_at: this.created_at,
|
||||
updated_at: this.updated_at,
|
||||
deleted_at: this.deleted_at,
|
||||
...this.asJSONExtended,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue