style: page editor width and layout updates (#6826)
This commit is contained in:
parent
993713925a
commit
a25cd426a9
23 changed files with 402 additions and 364 deletions
|
|
@ -1,5 +1,7 @@
|
|||
import { Extensions } from "@tiptap/core";
|
||||
import React from "react";
|
||||
// plane imports
|
||||
import { cn } from "@plane/utils";
|
||||
// components
|
||||
import { DocumentContentLoader, PageRenderer } from "@/components/editors";
|
||||
// constants
|
||||
|
|
@ -73,7 +75,11 @@ const CollaborativeDocumentEditor = (props: ICollaborativeDocumentEditor) => {
|
|||
|
||||
if (!editor) return null;
|
||||
|
||||
if (!hasServerSynced && !hasServerConnectionFailed) return <DocumentContentLoader />;
|
||||
const blockWidthClassName = cn("w-full max-w-[720px] mx-auto transition-all duration-200 ease-in-out", {
|
||||
"max-w-[1152px]": displayConfig.wideLayout,
|
||||
});
|
||||
|
||||
if (!hasServerSynced && !hasServerConnectionFailed) return <DocumentContentLoader className={blockWidthClassName} />;
|
||||
|
||||
return (
|
||||
<PageRenderer
|
||||
|
|
@ -81,7 +87,7 @@ const CollaborativeDocumentEditor = (props: ICollaborativeDocumentEditor) => {
|
|||
bubbleMenuEnabled={bubbleMenuEnabled}
|
||||
displayConfig={displayConfig}
|
||||
editor={editor}
|
||||
editorContainerClassName={editorContainerClassNames}
|
||||
editorContainerClassName={cn(editorContainerClassNames, "document-editor")}
|
||||
id={id}
|
||||
tabIndex={tabIndex}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,42 +1,51 @@
|
|||
// ui
|
||||
// plane imports
|
||||
import { Loader } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
|
||||
export const DocumentContentLoader = () => (
|
||||
<div className="size-full px-5">
|
||||
<Loader className="relative space-y-4">
|
||||
<div className="space-y-2">
|
||||
<div className="py-2">
|
||||
<Loader.Item width="100%" height="36px" />
|
||||
</div>
|
||||
<Loader.Item width="80%" height="22px" />
|
||||
<div className="relative flex items-center gap-2">
|
||||
<Loader.Item width="30px" height="30px" />
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
</div>
|
||||
<div className="py-2">
|
||||
<Loader.Item width="60%" height="36px" />
|
||||
</div>
|
||||
<Loader.Item width="70%" height="22px" />
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
<div className="relative flex items-center gap-2">
|
||||
<Loader.Item width="30px" height="30px" />
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
</div>
|
||||
<div className="py-2">
|
||||
<Loader.Item width="50%" height="30px" />
|
||||
</div>
|
||||
<Loader.Item width="100%" height="22px" />
|
||||
<div className="py-2">
|
||||
<Loader.Item width="30%" height="30px" />
|
||||
</div>
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
<div className="relative flex items-center gap-2">
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const DocumentContentLoader = (props: Props) => {
|
||||
const { className } = props;
|
||||
|
||||
return (
|
||||
<div className={cn("size-full", className)}>
|
||||
<Loader className="relative space-y-4">
|
||||
<div className="space-y-2">
|
||||
<div className="py-2">
|
||||
<Loader.Item width="100%" height="36px" />
|
||||
</div>
|
||||
<Loader.Item width="80%" height="22px" />
|
||||
<div className="relative flex items-center gap-2">
|
||||
<Loader.Item width="30px" height="30px" />
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
</div>
|
||||
<div className="py-2">
|
||||
<Loader.Item width="60%" height="36px" />
|
||||
</div>
|
||||
<Loader.Item width="70%" height="22px" />
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
<div className="relative flex items-center gap-2">
|
||||
<Loader.Item width="30px" height="30px" />
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
</div>
|
||||
<div className="py-2">
|
||||
<Loader.Item width="50%" height="30px" />
|
||||
</div>
|
||||
<Loader.Item width="100%" height="22px" />
|
||||
<div className="py-2">
|
||||
<Loader.Item width="30%" height="30px" />
|
||||
</div>
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
<div className="relative flex items-center gap-2">
|
||||
<div className="py-2">
|
||||
<Loader.Item width="30px" height="30px" />
|
||||
</div>
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Loader>
|
||||
</div>
|
||||
);
|
||||
</Loader>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ export const PageRenderer = (props: IPageRenderer) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="frame-renderer flex-grow w-full -mx-5" onMouseOver={handleLinkHover}>
|
||||
<div className="frame-renderer flex-grow w-full" onMouseOver={handleLinkHover}>
|
||||
<EditorContainer
|
||||
displayConfig={displayConfig}
|
||||
editor={editor}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { Extensions } from "@tiptap/core";
|
||||
import { forwardRef, MutableRefObject } from "react";
|
||||
// plane imports
|
||||
import { cn } from "@plane/utils";
|
||||
// components
|
||||
import { PageRenderer } from "@/components/editors";
|
||||
// constants
|
||||
|
|
@ -79,7 +81,7 @@ const DocumentReadOnlyEditor = (props: IDocumentReadOnlyEditor) => {
|
|||
bubbleMenuEnabled={false}
|
||||
displayConfig={displayConfig}
|
||||
editor={editor}
|
||||
editorContainerClassName={editorContainerClassName}
|
||||
editorContainerClassName={cn(editorContainerClassName, "document-editor")}
|
||||
id={id}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ export const EditorContainer: FC<EditorContainerProps> = (props) => {
|
|||
`editor-container cursor-text relative line-spacing-${displayConfig.lineSpacing ?? DEFAULT_DISPLAY_CONFIG.lineSpacing}`,
|
||||
{
|
||||
"active-editor": editor?.isFocused && editor?.isEditable,
|
||||
"wide-layout": displayConfig.wideLayout,
|
||||
},
|
||||
displayConfig.fontSize ?? DEFAULT_DISPLAY_CONFIG.fontSize,
|
||||
displayConfig.fontStyle ?? DEFAULT_DISPLAY_CONFIG.fontStyle,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ export const DEFAULT_DISPLAY_CONFIG: TDisplayConfig = {
|
|||
fontSize: "large-font",
|
||||
fontStyle: "sans-serif",
|
||||
lineSpacing: "regular",
|
||||
wideLayout: false,
|
||||
};
|
||||
|
||||
export const ACCEPTED_FILE_MIME_TYPES = ["image/jpeg", "image/jpg", "image/png", "image/webp", "image/gif"];
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export const isValidHttpUrl = (string: string): boolean => {
|
|||
|
||||
try {
|
||||
url = new URL(string);
|
||||
} catch (_) {
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,4 +29,5 @@ export type TDisplayConfig = {
|
|||
fontStyle?: TEditorFontStyle;
|
||||
fontSize?: TEditorFontSize;
|
||||
lineSpacing?: TEditorLineSpacing;
|
||||
wideLayout?: boolean;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
outline: none;
|
||||
outline: none !important;
|
||||
cursor: text;
|
||||
font-family: var(--font-style);
|
||||
font-size: var(--font-size-regular);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,47 @@
|
|||
:root {
|
||||
/* text colors */
|
||||
--editor-colors-gray-text: #5c5e63;
|
||||
--editor-colors-peach-text: #ff5b59;
|
||||
--editor-colors-pink-text: #f65385;
|
||||
--editor-colors-orange-text: #fd9038;
|
||||
--editor-colors-green-text: #0fc27b;
|
||||
--editor-colors-light-blue-text: #17bee9;
|
||||
--editor-colors-dark-blue-text: #266df0;
|
||||
--editor-colors-purple-text: #9162f9;
|
||||
/* end text colors */
|
||||
|
||||
/* layout */
|
||||
--normal-content-width: 720px;
|
||||
--wide-content-width: 1152px;
|
||||
--normal-content-margin: 20px;
|
||||
--wide-content-margin: 96px;
|
||||
/* end layout */
|
||||
}
|
||||
|
||||
/* text background colors */
|
||||
[data-theme*="light"] {
|
||||
--editor-colors-gray-background: #d6d6d8;
|
||||
--editor-colors-peach-background: #ffd5d7;
|
||||
--editor-colors-pink-background: #fdd4e3;
|
||||
--editor-colors-orange-background: #ffe3cd;
|
||||
--editor-colors-green-background: #c3f0de;
|
||||
--editor-colors-light-blue-background: #c5eff9;
|
||||
--editor-colors-dark-blue-background: #c9dafb;
|
||||
--editor-colors-purple-background: #e3d8fd;
|
||||
}
|
||||
[data-theme*="dark"] {
|
||||
--editor-colors-gray-background: #404144;
|
||||
--editor-colors-peach-background: #593032;
|
||||
--editor-colors-pink-background: #562e3d;
|
||||
--editor-colors-orange-background: #583e2a;
|
||||
--editor-colors-green-background: #1d4a3b;
|
||||
--editor-colors-light-blue-background: #1f495c;
|
||||
--editor-colors-dark-blue-background: #223558;
|
||||
--editor-colors-purple-background: #3d325a;
|
||||
}
|
||||
/* end text background colors */
|
||||
|
||||
/* font size and style */
|
||||
.editor-container {
|
||||
--color-placeholder: rgba(var(--color-text-100), 0.5);
|
||||
|
||||
|
|
@ -47,6 +91,8 @@
|
|||
/* end font sizes and line heights */
|
||||
|
||||
/* font styles */
|
||||
--font-style: "Inter", sans-serif;
|
||||
|
||||
&.sans-serif {
|
||||
--font-style: "Inter", sans-serif;
|
||||
}
|
||||
|
|
@ -102,3 +148,94 @@
|
|||
}
|
||||
/* end spacing */
|
||||
}
|
||||
/* end font size and style */
|
||||
|
||||
/* layout config */
|
||||
#page-header-container {
|
||||
container-name: page-header-container;
|
||||
container-type: inline-size;
|
||||
|
||||
.page-header-content {
|
||||
--header-width: var(--normal-content-width);
|
||||
|
||||
&.wide-layout {
|
||||
--header-width: var(--wide-content-width);
|
||||
}
|
||||
|
||||
padding-left: calc(((100% - var(--header-width)) / 2) - 10px);
|
||||
}
|
||||
}
|
||||
|
||||
#page-content-container {
|
||||
container-name: page-content-container;
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
.editor-container.document-editor {
|
||||
--editor-content-width: var(--normal-content-width);
|
||||
|
||||
&.wide-layout {
|
||||
--editor-content-width: var(--wide-content-width);
|
||||
}
|
||||
|
||||
.ProseMirror {
|
||||
max-width: var(--editor-content-width);
|
||||
margin: 0 auto;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* keep a static padding of 96px for wide layouts for container width >912px and <1344px */
|
||||
@container page-header-container (min-width: 912px) and (max-width: 1344px) {
|
||||
.page-header-content.wide-layout {
|
||||
padding-left: var(--wide-content-margin) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* keep a static padding of 96px for wide layouts for container width <912px */
|
||||
@container page-header-container (max-width: 912) {
|
||||
.page-header-content.wide-layout {
|
||||
padding-left: var(--wide-content-margin) !important;
|
||||
}
|
||||
}
|
||||
/* end layout config */
|
||||
|
||||
/* keep a static padding of 20px for wide layouts for container width <760px */
|
||||
@container page-header-container (max-width: 760px) {
|
||||
.page-header-content {
|
||||
padding-left: var(--normal-content-margin) !important;
|
||||
}
|
||||
}
|
||||
/* end layout config */
|
||||
|
||||
/* keep a static padding of 96px for wide layouts for container width >912px and <1344px */
|
||||
@container page-content-container (min-width: 912px) and (max-width: 1344px) {
|
||||
.editor-container.wide-layout,
|
||||
.page-title-container {
|
||||
padding-left: var(--wide-content-margin);
|
||||
padding-right: var(--wide-content-margin);
|
||||
}
|
||||
}
|
||||
|
||||
/* keep a static padding of 20px for wide layouts for container width <912px */
|
||||
@container page-content-container (max-width: 912px) {
|
||||
.editor-container.wide-layout,
|
||||
.page-title-container {
|
||||
padding-left: var(--normal-content-margin);
|
||||
padding-right: var(--normal-content-margin);
|
||||
}
|
||||
}
|
||||
|
||||
/* keep a static padding of 20px for normal layouts for container width <760px */
|
||||
@container page-content-container (max-width: 760px) {
|
||||
.editor-container:not(.wide-layout),
|
||||
.page-title-container {
|
||||
padding-left: var(--normal-content-margin);
|
||||
padding-right: var(--normal-content-margin);
|
||||
}
|
||||
|
||||
.page-summary-container {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
/* end layout config */
|
||||
|
|
|
|||
|
|
@ -8,27 +8,25 @@ import { cn } from "@/helpers/common.helper";
|
|||
export const IssueEmbedUpgradeCard: React.FC<any> = (props) => (
|
||||
<div
|
||||
className={cn(
|
||||
"w-full h-20 bg-custom-background-80 rounded-md border-[0.5px] border-custom-border-200 shadow-custom-shadow-2xs",
|
||||
"w-full bg-custom-background-80 rounded-md border-[0.5px] border-custom-border-200 shadow-custom-shadow-2xs flex items-center justify-between gap-5 px-5 py-2 max-md:flex-wrap",
|
||||
{
|
||||
"border-2": props.selected,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-5 mt-2.5 pl-4 pr-5 py-3 w-full max-md:max-w-full max-md:flex-wrap rounded-md">
|
||||
<div className="flex items-center gap-4">
|
||||
<ProIcon className="flex-shrink-0 size-4" />
|
||||
<p className="text-custom-text !text-base">
|
||||
Embed and access work items in pages seamlessly, upgrade to Plane Pro now.
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="https://plane.so/pro"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={cn(getButtonStyling("primary", "md"), "no-underline")}
|
||||
>
|
||||
Upgrade
|
||||
</a>
|
||||
<div className="flex items-center gap-4">
|
||||
<ProIcon className="flex-shrink-0 size-4" />
|
||||
<p className="text-custom-text !text-base">
|
||||
Embed and access issues in pages seamlessly, upgrade to Plane Pro now.
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="https://plane.so/pro"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={cn(getButtonStyling("primary", "md"), "no-underline")}
|
||||
>
|
||||
Upgrade
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Dispatch, SetStateAction, useCallback, useMemo } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// document-editor
|
||||
// plane imports
|
||||
import {
|
||||
CollaborativeDocumentEditorWithRef,
|
||||
EditorRefApi,
|
||||
|
|
@ -10,15 +10,14 @@ import {
|
|||
TRealtimeConfig,
|
||||
TServerHandler,
|
||||
} from "@plane/editor";
|
||||
// plane types
|
||||
import { TSearchEntityRequestPayload, TSearchResponse, TWebhookConnectionQueryParams } from "@plane/types";
|
||||
// plane ui
|
||||
import { Row } from "@plane/ui";
|
||||
import { ERowVariant, Row } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
// components
|
||||
import { EditorMentionsRoot } from "@/components/editor";
|
||||
import { PageContentBrowser, PageContentLoader, PageEditorTitle } from "@/components/pages";
|
||||
// helpers
|
||||
import { cn, LIVE_BASE_PATH, LIVE_BASE_URL } from "@/helpers/common.helper";
|
||||
import { LIVE_BASE_PATH, LIVE_BASE_URL } from "@/helpers/common.helper";
|
||||
import { generateRandomColor } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useEditorMention } from "@/hooks/editor";
|
||||
|
|
@ -48,7 +47,6 @@ type Props = {
|
|||
handleEditorReady: Dispatch<SetStateAction<boolean>>;
|
||||
handlers: TEditorBodyHandlers;
|
||||
page: TPageInstance;
|
||||
sidePeekVisible: boolean;
|
||||
webhookConnectionParams: TWebhookConnectionQueryParams;
|
||||
workspaceSlug: string;
|
||||
};
|
||||
|
|
@ -61,7 +59,6 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
|||
handleEditorReady,
|
||||
handlers,
|
||||
page,
|
||||
sidePeekVisible,
|
||||
webhookConnectionParams,
|
||||
workspaceSlug,
|
||||
} = props;
|
||||
|
|
@ -91,8 +88,9 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
|||
() => ({
|
||||
fontSize,
|
||||
fontStyle,
|
||||
wideLayout: isFullWidth,
|
||||
}),
|
||||
[fontSize, fontStyle]
|
||||
[fontSize, fontStyle, isFullWidth]
|
||||
);
|
||||
|
||||
const getAIMenu = useCallback(
|
||||
|
|
@ -152,69 +150,70 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
|||
[currentUser?.display_name, currentUser?.id]
|
||||
);
|
||||
|
||||
if (pageId === undefined || !realtimeConfig) return <PageContentLoader />;
|
||||
const blockWidthClassName = cn(
|
||||
"block bg-transparent w-full max-w-[720px] mx-auto transition-all duration-200 ease-in-out",
|
||||
{
|
||||
"max-w-[1152px]": isFullWidth,
|
||||
}
|
||||
);
|
||||
|
||||
if (pageId === undefined || !realtimeConfig) return <PageContentLoader className={blockWidthClassName} />;
|
||||
|
||||
return (
|
||||
<div className="flex items-center h-full w-full overflow-y-auto">
|
||||
<Row
|
||||
className={cn("sticky top-0 hidden h-full flex-shrink-0 -translate-x-full py-5 duration-200 md:block", {
|
||||
"translate-x-0": sidePeekVisible,
|
||||
"w-[10rem] lg:w-[14rem]": !isFullWidth,
|
||||
"w-[5%]": isFullWidth,
|
||||
})}
|
||||
>
|
||||
{!isFullWidth && <PageContentBrowser editorRef={editorRef.current} />}
|
||||
</Row>
|
||||
<div
|
||||
className={cn("size-full pt-5 duration-200", {
|
||||
"md:w-[calc(100%-10rem)] xl:w-[calc(100%-28rem)]": !isFullWidth,
|
||||
"md:w-[90%]": isFullWidth,
|
||||
})}
|
||||
>
|
||||
<div className="size-full flex flex-col gap-y-7 overflow-y-auto overflow-x-hidden">
|
||||
<PageEditorTitle
|
||||
editorRef={editorRef}
|
||||
title={pageTitle}
|
||||
updateTitle={updateTitle}
|
||||
readOnly={!isContentEditable}
|
||||
/>
|
||||
<CollaborativeDocumentEditorWithRef
|
||||
editable={isContentEditable}
|
||||
id={pageId}
|
||||
fileHandler={config.fileHandler}
|
||||
handleEditorReady={handleEditorReady}
|
||||
ref={editorRef}
|
||||
containerClassName="h-full p-0 pb-64"
|
||||
displayConfig={displayConfig}
|
||||
editorClassName="pl-10"
|
||||
mentionHandler={{
|
||||
searchCallback: async (query) => {
|
||||
const res = await fetchMentions(query);
|
||||
if (!res) throw new Error("Failed in fetching mentions");
|
||||
return res;
|
||||
},
|
||||
renderComponent: (props) => <EditorMentionsRoot {...props} />,
|
||||
getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }),
|
||||
}}
|
||||
embedHandler={{
|
||||
issue: issueEmbedProps,
|
||||
}}
|
||||
realtimeConfig={realtimeConfig}
|
||||
serverHandler={serverHandler}
|
||||
user={userConfig}
|
||||
disabledExtensions={disabledExtensions}
|
||||
aiHandler={{
|
||||
menu: getAIMenu,
|
||||
}}
|
||||
/>
|
||||
<Row
|
||||
className="relative size-full flex flex-col pt-[64px] overflow-y-auto overflow-x-hidden vertical-scrollbar scrollbar-md duration-200"
|
||||
variant={ERowVariant.HUGGING}
|
||||
>
|
||||
<div id="page-content-container" className="relative w-full flex-shrink-0 space-y-4">
|
||||
{/* table of content */}
|
||||
<div className="page-summary-container absolute h-full right-0 top-[64px] z-[5]">
|
||||
<div className="sticky top-[72px]">
|
||||
<div className="group/page-toc relative px-page-x">
|
||||
<div className="cursor-pointer max-h-[50vh] overflow-hidden">
|
||||
<PageContentBrowser editorRef={editorRef?.current} showOutline />
|
||||
</div>
|
||||
<div className="absolute top-0 right-0 opacity-0 translate-x-1/2 pointer-events-none group-hover/page-toc:opacity-100 group-hover/page-toc:-translate-x-1/4 group-hover/page-toc:pointer-events-auto transition-all duration-300 w-52 max-h-[70vh] overflow-y-scroll vertical-scrollbar scrollbar-sm whitespace-nowrap bg-custom-background-90 p-4 rounded">
|
||||
<PageContentBrowser editorRef={editorRef?.current} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<PageEditorTitle
|
||||
editorRef={editorRef}
|
||||
readOnly={!isContentEditable}
|
||||
title={pageTitle}
|
||||
updateTitle={updateTitle}
|
||||
widthClassName={blockWidthClassName}
|
||||
/>
|
||||
<CollaborativeDocumentEditorWithRef
|
||||
editable={isContentEditable}
|
||||
id={pageId}
|
||||
fileHandler={config.fileHandler}
|
||||
handleEditorReady={handleEditorReady}
|
||||
ref={editorRef}
|
||||
containerClassName="h-full p-0 pb-64"
|
||||
displayConfig={displayConfig}
|
||||
mentionHandler={{
|
||||
searchCallback: async (query) => {
|
||||
const res = await fetchMentions(query);
|
||||
if (!res) throw new Error("Failed in fetching mentions");
|
||||
return res;
|
||||
},
|
||||
renderComponent: (props) => <EditorMentionsRoot {...props} />,
|
||||
getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }),
|
||||
}}
|
||||
embedHandler={{
|
||||
issue: issueEmbedProps,
|
||||
}}
|
||||
realtimeConfig={realtimeConfig}
|
||||
serverHandler={serverHandler}
|
||||
user={userConfig}
|
||||
disabledExtensions={disabledExtensions}
|
||||
aiHandler={{
|
||||
menu: getAIMenu,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={cn("hidden xl:block flex-shrink-0 duration-200", {
|
||||
"w-[10rem] lg:w-[14rem]": !isFullWidth,
|
||||
"w-[5%]": isFullWidth,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export const PageExtraOptions: React.FC<Props> = observer((props) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-end gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
{is_locked && <LockedComponent />}
|
||||
{archived_at && (
|
||||
<div className="flex-shrink-0 flex h-7 items-center gap-2 rounded-full bg-blue-500/20 px-3 py-0.5 text-xs font-medium text-blue-500">
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ import { observer } from "mobx-react";
|
|||
import { EditorRefApi } from "@plane/editor";
|
||||
// components
|
||||
import { Header, EHeaderVariant } from "@plane/ui";
|
||||
import { PageExtraOptions, PageSummaryPopover, PageToolbar } from "@/components/pages";
|
||||
// hooks
|
||||
import { usePageFilters } from "@/hooks/use-page-filters";
|
||||
import { PageExtraOptions, PageToolbar } from "@/components/pages";
|
||||
// plane web hooks
|
||||
import { EPageStoreType } from "@/plane-web/hooks/store";
|
||||
// store
|
||||
|
|
@ -13,29 +11,17 @@ import { TPageInstance } from "@/store/pages/base-page";
|
|||
type Props = {
|
||||
editorRef: EditorRefApi;
|
||||
page: TPageInstance;
|
||||
setSidePeekVisible: (sidePeekState: boolean) => void;
|
||||
sidePeekVisible: boolean;
|
||||
storeType: EPageStoreType;
|
||||
};
|
||||
|
||||
export const PageEditorMobileHeaderRoot: React.FC<Props> = observer((props) => {
|
||||
const { editorRef, page, setSidePeekVisible, sidePeekVisible, storeType } = props;
|
||||
const { editorRef, page, storeType } = props;
|
||||
// derived values
|
||||
const { isContentEditable } = page;
|
||||
// page filters
|
||||
const { isFullWidth } = usePageFilters();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header variant={EHeaderVariant.SECONDARY}>
|
||||
<div className="flex-shrink-0 my-auto">
|
||||
<PageSummaryPopover
|
||||
editorRef={editorRef}
|
||||
isFullWidth={isFullWidth}
|
||||
sidePeekVisible={sidePeekVisible}
|
||||
setSidePeekVisible={setSidePeekVisible}
|
||||
/>
|
||||
</div>
|
||||
<PageExtraOptions editorRef={editorRef} page={page} storeType={storeType} />
|
||||
</Header>
|
||||
<Header variant={EHeaderVariant.TERNARY}>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { observer } from "mobx-react";
|
||||
import { EditorRefApi } from "@plane/editor";
|
||||
// components
|
||||
import { Header, EHeaderVariant } from "@plane/ui";
|
||||
import { PageEditorMobileHeaderRoot, PageExtraOptions, PageSummaryPopover, PageToolbar } from "@/components/pages";
|
||||
import { PageEditorMobileHeaderRoot, PageExtraOptions, PageToolbar } from "@/components/pages";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
|
|
@ -16,13 +15,11 @@ type Props = {
|
|||
editorReady: boolean;
|
||||
editorRef: React.RefObject<EditorRefApi>;
|
||||
page: TPageInstance;
|
||||
setSidePeekVisible: (sidePeekState: boolean) => void;
|
||||
sidePeekVisible: boolean;
|
||||
storeType: EPageStoreType;
|
||||
};
|
||||
|
||||
export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
|
||||
const { editorReady, editorRef, page, setSidePeekVisible, sidePeekVisible, storeType } = props;
|
||||
const { editorReady, editorRef, page, storeType } = props;
|
||||
// derived values
|
||||
const { isContentEditable } = page;
|
||||
// page filters
|
||||
|
|
@ -33,39 +30,27 @@ export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
|
|||
if (!resolvedEditorRef) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header variant={EHeaderVariant.SECONDARY} showOnMobile={false}>
|
||||
<Header.LeftItem className="gap-0 w-full">
|
||||
{editorReady && (
|
||||
<div
|
||||
className={cn("flex-shrink-0 my-auto", {
|
||||
"w-40 lg:w-56": !isFullWidth,
|
||||
"w-[5%]": isFullWidth,
|
||||
})}
|
||||
>
|
||||
<PageSummaryPopover
|
||||
editorRef={editorRef.current}
|
||||
isFullWidth={isFullWidth}
|
||||
sidePeekVisible={sidePeekVisible}
|
||||
setSidePeekVisible={setSidePeekVisible}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isStickyToolbarEnabled && editorReady && isContentEditable && editorRef.current && (
|
||||
<PageToolbar editorRef={editorRef?.current} />
|
||||
)}
|
||||
</Header.LeftItem>
|
||||
<PageExtraOptions editorRef={resolvedEditorRef} page={page} storeType={storeType} />
|
||||
</Header>
|
||||
<div className="md:hidden">
|
||||
<PageEditorMobileHeaderRoot
|
||||
editorRef={resolvedEditorRef}
|
||||
page={page}
|
||||
sidePeekVisible={sidePeekVisible}
|
||||
setSidePeekVisible={setSidePeekVisible}
|
||||
storeType={storeType}
|
||||
/>
|
||||
<div id="page-header-container">
|
||||
<div
|
||||
className={cn(
|
||||
"hidden md:flex items-center relative min-h-[52px] page-header-content border-b border-custom-border-200 px-page-x transition-all duration-200 ease-in-out",
|
||||
{
|
||||
"wide-layout": isFullWidth,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div className="max-w-full w-full flex items-center justify-between">
|
||||
<div>
|
||||
{isStickyToolbarEnabled && editorReady && isContentEditable && editorRef.current && (
|
||||
<PageToolbar editorRef={editorRef?.current} />
|
||||
)}
|
||||
</div>
|
||||
<PageExtraOptions editorRef={resolvedEditorRef} page={page} storeType={storeType} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
<div className="md:hidden">
|
||||
<PageEditorMobileHeaderRoot editorRef={resolvedEditorRef} page={page} storeType={storeType} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ export const PageRoot = observer((props: TPageRootProps) => {
|
|||
// states
|
||||
const [editorReady, setEditorReady] = useState(false);
|
||||
const [hasConnectionFailed, setHasConnectionFailed] = useState(false);
|
||||
const [sidePeekVisible, setSidePeekVisible] = useState(window.innerWidth >= 768);
|
||||
const [isVersionsOverlayOpen, setIsVersionsOverlayOpen] = useState(false);
|
||||
// refs
|
||||
const editorRef = useRef<EditorRefApi>(null);
|
||||
|
|
@ -104,14 +103,7 @@ export const PageRoot = observer((props: TPageRootProps) => {
|
|||
pageId={page.id ?? ""}
|
||||
restoreEnabled={isContentEditable}
|
||||
/>
|
||||
<PageEditorHeaderRoot
|
||||
editorReady={editorReady}
|
||||
editorRef={editorRef}
|
||||
page={page}
|
||||
setSidePeekVisible={(state) => setSidePeekVisible(state)}
|
||||
sidePeekVisible={sidePeekVisible}
|
||||
storeType={storeType}
|
||||
/>
|
||||
<PageEditorHeaderRoot editorReady={editorReady} editorRef={editorRef} page={page} storeType={storeType} />
|
||||
<PageEditorBody
|
||||
config={config}
|
||||
editorReady={editorReady}
|
||||
|
|
@ -120,7 +112,6 @@ export const PageRoot = observer((props: TPageRootProps) => {
|
|||
handleEditorReady={setEditorReady}
|
||||
handlers={handlers}
|
||||
page={page}
|
||||
sidePeekVisible={sidePeekVisible}
|
||||
webhookConnectionParams={webhookConnectionParams}
|
||||
workspaceSlug={workspaceSlug}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ import { OutlineHeading1, OutlineHeading2, OutlineHeading3 } from "./heading-com
|
|||
type Props = {
|
||||
editorRef: EditorRefApi | null;
|
||||
setSidePeekVisible?: (sidePeekState: boolean) => void;
|
||||
showOutline?: boolean;
|
||||
};
|
||||
|
||||
export const PageContentBrowser: React.FC<Props> = (props) => {
|
||||
const { editorRef, setSidePeekVisible } = props;
|
||||
const { editorRef, setSidePeekVisible, showOutline = false } = props;
|
||||
// states
|
||||
const [headings, setHeadings] = useState<IMarking[]>([]);
|
||||
|
||||
|
|
@ -37,24 +38,28 @@ export const PageContentBrowser: React.FC<Props> = (props) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col overflow-hidden">
|
||||
<div className="h-full flex flex-col items-start gap-y-2 overflow-y-auto mt-2">
|
||||
{headings && headings.length !== 0 ? (
|
||||
headings.map((marking) => {
|
||||
const Component = HeadingComponent[marking.level];
|
||||
if (!Component) return null;
|
||||
return (
|
||||
<Component
|
||||
key={`${marking.level}-${marking.sequence}`}
|
||||
marking={marking}
|
||||
onClick={() => handleOnClick(marking)}
|
||||
/>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<p className="mt-3 text-xs text-custom-text-400">Headings will be displayed here for navigation</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="h-full flex flex-col items-start gap-y-2 overflow-y-auto mt-2">
|
||||
{headings.map((marking) => {
|
||||
const Component = HeadingComponent[marking.level];
|
||||
if (!Component) return null;
|
||||
if (showOutline === true)
|
||||
return (
|
||||
<div
|
||||
key={`${marking.level}-${marking.sequence}`}
|
||||
className="h-0.5 bg-custom-border-400 self-end rounded-sm"
|
||||
style={{
|
||||
width: marking.level === 1 ? "20px" : marking.level === 2 ? "18px" : "14px",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<Component
|
||||
key={`${marking.level}-${marking.sequence}`}
|
||||
marking={marking}
|
||||
onClick={() => handleOnClick(marking)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,36 +1,36 @@
|
|||
// document editor
|
||||
import { IMarking } from "@plane/editor";
|
||||
// plane editor
|
||||
import type { IMarking } from "@plane/editor";
|
||||
|
||||
type HeadingProps = {
|
||||
export type THeadingComponentProps = {
|
||||
marking: IMarking;
|
||||
onClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
};
|
||||
|
||||
export const OutlineHeading1 = ({ marking, onClick }: HeadingProps) => (
|
||||
export const OutlineHeading1 = ({ marking, onClick }: THeadingComponentProps) => (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className="text-sm text-left font-medium text-custom-text-300 hover:text-custom-primary-100"
|
||||
className="text-sm text-left font-medium text-custom-text-300 hover:text-custom-primary-100 transition-colors"
|
||||
>
|
||||
{marking.text}
|
||||
</button>
|
||||
);
|
||||
|
||||
export const OutlineHeading2 = ({ marking, onClick }: HeadingProps) => (
|
||||
export const OutlineHeading2 = ({ marking, onClick }: THeadingComponentProps) => (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className="ml-2 text-xs text-left font-medium text-custom-text-300 hover:text-custom-primary-100"
|
||||
className="ml-2 text-xs text-left font-medium text-custom-text-300 hover:text-custom-primary-100 transition-colors"
|
||||
>
|
||||
{marking.text}
|
||||
</button>
|
||||
);
|
||||
|
||||
export const OutlineHeading3 = ({ marking, onClick }: HeadingProps) => (
|
||||
export const OutlineHeading3 = ({ marking, onClick }: THeadingComponentProps) => (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className="ml-4 text-xs text-left font-medium text-custom-text-300 hover:text-custom-primary-100"
|
||||
className="ml-4 text-xs text-left font-medium text-custom-text-300 hover:text-custom-primary-100 transition-colors"
|
||||
>
|
||||
{marking.text}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,2 +1 @@
|
|||
export * from "./content-browser";
|
||||
export * from "./popover";
|
||||
|
|
|
|||
|
|
@ -1,74 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import { usePopper } from "react-popper";
|
||||
import { List } from "lucide-react";
|
||||
// document editor
|
||||
import { EditorRefApi } from "@plane/editor";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// components
|
||||
import { PageContentBrowser } from "./content-browser";
|
||||
|
||||
type Props = {
|
||||
editorRef: EditorRefApi | null;
|
||||
isFullWidth: boolean;
|
||||
sidePeekVisible: boolean;
|
||||
setSidePeekVisible: (sidePeekState: boolean) => void;
|
||||
};
|
||||
|
||||
export const PageSummaryPopover: React.FC<Props> = (props) => {
|
||||
const { editorRef, sidePeekVisible, setSidePeekVisible } = props;
|
||||
// refs
|
||||
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||
// popper-js
|
||||
const { styles: summaryPopoverStyles, attributes: summaryPopoverAttributes } = usePopper(
|
||||
referenceElement,
|
||||
popperElement,
|
||||
{
|
||||
placement: "bottom-start",
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="group/summary-popover w-min whitespace-nowrap">
|
||||
<button
|
||||
type="button"
|
||||
ref={setReferenceElement}
|
||||
className={`grid h-7 w-7 place-items-center rounded ${
|
||||
sidePeekVisible ? "bg-custom-primary-100/20 text-custom-primary-100" : "text-custom-text-300"
|
||||
}`}
|
||||
onClick={() => setSidePeekVisible(!sidePeekVisible)}
|
||||
>
|
||||
<List className="h-4 w-4" />
|
||||
</button>
|
||||
<div
|
||||
className={cn("block md:hidden", {
|
||||
// "md:hidden": !isFullWidth,
|
||||
})}
|
||||
>
|
||||
{sidePeekVisible && (
|
||||
<div
|
||||
className="z-10 max-h-80 w-64 overflow-y-auto rounded border-[0.5px] border-custom-border-200 bg-custom-background-100 p-3 shadow-custom-shadow-rg"
|
||||
ref={setPopperElement}
|
||||
style={summaryPopoverStyles.popper}
|
||||
{...summaryPopoverAttributes.popper}
|
||||
>
|
||||
<PageContentBrowser setSidePeekVisible={setSidePeekVisible} editorRef={editorRef} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="hidden md:block">
|
||||
{!sidePeekVisible && (
|
||||
<div
|
||||
className="z-10 hidden max-h-80 w-64 overflow-y-auto rounded border-[0.5px] border-custom-border-200 bg-custom-background-100 p-3 shadow-custom-shadow-rg group-hover/summary-popover:block"
|
||||
ref={setPopperElement}
|
||||
style={summaryPopoverStyles.popper}
|
||||
{...summaryPopoverAttributes.popper}
|
||||
>
|
||||
<PageContentBrowser editorRef={editorRef} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -17,26 +17,28 @@ type Props = {
|
|||
readOnly: boolean;
|
||||
title: string | undefined;
|
||||
updateTitle: (title: string) => void;
|
||||
widthClassName: string;
|
||||
};
|
||||
|
||||
export const PageEditorTitle: React.FC<Props> = observer((props) => {
|
||||
const { editorRef, readOnly, title, updateTitle } = props;
|
||||
const { editorRef, readOnly, title, updateTitle, widthClassName } = props;
|
||||
// states
|
||||
const [isLengthVisible, setIsLengthVisible] = useState(false);
|
||||
// page filters
|
||||
const { fontSize } = usePageFilters();
|
||||
// ui
|
||||
const titleClassName = cn("bg-transparent tracking-[-2%] font-bold", {
|
||||
const titleFontClassName = cn("tracking-[-2%] font-bold", {
|
||||
"text-[1.6rem] leading-[1.9rem]": fontSize === "small-font",
|
||||
"text-[2rem] leading-[2.375rem]": fontSize === "large-font",
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="relative w-full flex-shrink-0 md:pl-5 px-4">
|
||||
<div className="relative w-full flex-shrink-0 py-3 page-title-container">
|
||||
{readOnly ? (
|
||||
<h6
|
||||
className={cn(
|
||||
titleClassName,
|
||||
titleFontClassName,
|
||||
widthClassName,
|
||||
{
|
||||
"text-custom-text-400": !title,
|
||||
},
|
||||
|
|
@ -46,9 +48,9 @@ export const PageEditorTitle: React.FC<Props> = observer((props) => {
|
|||
{getPageName(title)}
|
||||
</h6>
|
||||
) : (
|
||||
<>
|
||||
<div className={cn("relative", widthClassName)}>
|
||||
<TextArea
|
||||
className={cn(titleClassName, "w-full outline-none p-0 border-none resize-none rounded-none")}
|
||||
className={cn(titleFontClassName, "block w-full border-none outline-none p-0 resize-none rounded-none")}
|
||||
placeholder="Untitled"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
|
|
@ -65,7 +67,7 @@ export const PageEditorTitle: React.FC<Props> = observer((props) => {
|
|||
/>
|
||||
<div
|
||||
className={cn(
|
||||
"pointer-events-none absolute bottom-1 right-1 z-[2] rounded bg-custom-background-100 p-0.5 text-xs text-custom-text-200 opacity-0 transition-opacity",
|
||||
"pointer-events-none absolute bottom-1 right-1 z-[2] font-normal rounded bg-custom-background-100 p-0.5 text-xs text-custom-text-200 opacity-0 transition-opacity",
|
||||
{
|
||||
"opacity-100": isLengthVisible,
|
||||
}
|
||||
|
|
@ -80,7 +82,7 @@ export const PageEditorTitle: React.FC<Props> = observer((props) => {
|
|||
</span>
|
||||
/255
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,19 +1,20 @@
|
|||
"use client";
|
||||
|
||||
// ui
|
||||
// plane imports
|
||||
import { Loader } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
|
||||
export const PageContentLoader = () => (
|
||||
<div className="relative w-full h-full flex flex-col">
|
||||
{/* header */}
|
||||
<div className="px-16 flex-shrink-0 relative flex items-center justify-between h-12 border-b border-custom-border-100">
|
||||
{/* left options */}
|
||||
<Loader className="flex-shrink-0 w-[280px]">
|
||||
<Loader.Item width="26px" height="26px" />
|
||||
</Loader>
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
{/* editor options */}
|
||||
<div className="w-full relative flex items-center divide-x divide-custom-border-100">
|
||||
export const PageContentLoader = (props: Props) => {
|
||||
const { className } = props;
|
||||
|
||||
return (
|
||||
<div className={cn("relative size-full flex flex-col", className)}>
|
||||
{/* header */}
|
||||
<div className="flex-shrink-0 w-full h-12 border-b border-custom-border-100 relative flex items-center divide-x divide-custom-border-100">
|
||||
<Loader className="relative flex items-center gap-1 pr-2">
|
||||
<Loader.Item width="26px" height="26px" />
|
||||
<Loader.Item width="26px" height="26px" />
|
||||
|
|
@ -37,56 +38,48 @@ export const PageContentLoader = () => (
|
|||
</Loader>
|
||||
</div>
|
||||
|
||||
{/* right options */}
|
||||
<Loader className="w-full relative flex justify-end items-center gap-1">
|
||||
<Loader.Item width="60px" height="26px" />
|
||||
<Loader.Item width="40px" height="26px" />
|
||||
<Loader.Item width="26px" height="26px" />
|
||||
<Loader.Item width="26px" height="26px" />
|
||||
</Loader>
|
||||
</div>
|
||||
|
||||
{/* content */}
|
||||
<div className="px-16 w-full h-full overflow-hidden relative flex">
|
||||
{/* editor loader */}
|
||||
<div className="w-full h-full py-5">
|
||||
<Loader className="relative space-y-4">
|
||||
<Loader.Item width="50%" height="36px" />
|
||||
<div className="space-y-2">
|
||||
<div className="py-2">
|
||||
<Loader.Item width="100%" height="36px" />
|
||||
</div>
|
||||
<Loader.Item width="80%" height="22px" />
|
||||
<div className="relative flex items-center gap-2">
|
||||
<Loader.Item width="30px" height="30px" />
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
</div>
|
||||
<div className="py-2">
|
||||
<Loader.Item width="60%" height="36px" />
|
||||
</div>
|
||||
<Loader.Item width="70%" height="22px" />
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
<div className="relative flex items-center gap-2">
|
||||
<Loader.Item width="30px" height="30px" />
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
</div>
|
||||
<div className="py-2">
|
||||
<Loader.Item width="50%" height="30px" />
|
||||
</div>
|
||||
<Loader.Item width="100%" height="22px" />
|
||||
<div className="py-2">
|
||||
<Loader.Item width="30%" height="30px" />
|
||||
</div>
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
<div className="relative flex items-center gap-2">
|
||||
{/* content */}
|
||||
<div className="size-full pt-[64px] overflow-hidden relative flex">
|
||||
{/* editor loader */}
|
||||
<div className="size-full py-5">
|
||||
<Loader className="relative space-y-4">
|
||||
<Loader.Item width="50%" height="36px" />
|
||||
<div className="space-y-2">
|
||||
<div className="py-2">
|
||||
<Loader.Item width="100%" height="36px" />
|
||||
</div>
|
||||
<Loader.Item width="80%" height="22px" />
|
||||
<div className="relative flex items-center gap-2">
|
||||
<Loader.Item width="30px" height="30px" />
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
</div>
|
||||
<div className="py-2">
|
||||
<Loader.Item width="60%" height="36px" />
|
||||
</div>
|
||||
<Loader.Item width="70%" height="22px" />
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
<div className="relative flex items-center gap-2">
|
||||
<Loader.Item width="30px" height="30px" />
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
</div>
|
||||
<div className="py-2">
|
||||
<Loader.Item width="50%" height="30px" />
|
||||
</div>
|
||||
<Loader.Item width="100%" height="22px" />
|
||||
<div className="py-2">
|
||||
<Loader.Item width="30%" height="30px" />
|
||||
</div>
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
<div className="relative flex items-center gap-2">
|
||||
<div className="py-2">
|
||||
<Loader.Item width="30px" height="30px" />
|
||||
</div>
|
||||
<Loader.Item width="30%" height="22px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Loader>
|
||||
</Loader>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,19 +1,15 @@
|
|||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// plane editor
|
||||
// plane imports
|
||||
import { DocumentReadOnlyEditorWithRef, TDisplayConfig } from "@plane/editor";
|
||||
// plane types
|
||||
import { TPageVersion } from "@plane/types";
|
||||
// plane ui
|
||||
import { Loader } from "@plane/ui";
|
||||
// components
|
||||
import { EditorMentionsRoot } from "@/components/editor";
|
||||
// hooks
|
||||
import { useEditorConfig } from "@/hooks/editor";
|
||||
import { useWorkspace } from "@/hooks/store";
|
||||
import { useMember, useWorkspace } from "@/hooks/store";
|
||||
import { usePageFilters } from "@/hooks/use-page-filters";
|
||||
// store hooks
|
||||
import { useMember } from "@/hooks/store";
|
||||
// plane web hooks
|
||||
import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging";
|
||||
import { useIssueEmbed } from "@/plane-web/hooks/use-issue-embed";
|
||||
|
|
@ -50,6 +46,7 @@ export const PagesVersionEditor: React.FC<TVersionEditorProps> = observer((props
|
|||
const displayConfig: TDisplayConfig = {
|
||||
fontSize,
|
||||
fontStyle,
|
||||
wideLayout: true,
|
||||
};
|
||||
|
||||
if (!isCurrentVersionActive && !versionDetails)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue