From 4dab6ccd35bb80418463fe135b183f7f72c9dc8c Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Wed, 26 Mar 2025 16:40:06 +0530 Subject: [PATCH 1/5] style: editor width config --- .../editors/document/page-renderer.tsx | 2 +- .../components/editors/editor-container.tsx | 1 + packages/editor/src/core/constants/config.ts | 1 + packages/editor/src/core/types/config.ts | 1 + packages/editor/src/styles/editor.css | 2 +- packages/editor/src/styles/variables.css | 129 ++++++++++++++++++ .../editor/embed/issue-embed-upgrade-card.tsx | 30 ++-- .../components/pages/editor/editor-body.tsx | 120 ++++++++-------- .../pages/editor/header/extra-options.tsx | 2 +- .../pages/editor/header/mobile-root.tsx | 18 +-- .../components/pages/editor/header/root.tsx | 59 +++----- .../components/pages/editor/page-root.tsx | 11 +- .../pages/editor/summary/content-browser.tsx | 43 +++--- .../editor/summary/heading-components.tsx | 12 +- .../components/pages/editor/summary/index.ts | 1 - .../pages/editor/summary/popover.tsx | 74 ---------- web/core/components/pages/editor/title.tsx | 20 +-- 17 files changed, 272 insertions(+), 254 deletions(-) delete mode 100644 web/core/components/pages/editor/summary/popover.tsx diff --git a/packages/editor/src/core/components/editors/document/page-renderer.tsx b/packages/editor/src/core/components/editors/document/page-renderer.tsx index a297686564d..2b4c094c59f 100644 --- a/packages/editor/src/core/components/editors/document/page-renderer.tsx +++ b/packages/editor/src/core/components/editors/document/page-renderer.tsx @@ -132,7 +132,7 @@ export const PageRenderer = (props: IPageRenderer) => { return ( <> -
+
= (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, diff --git a/packages/editor/src/core/constants/config.ts b/packages/editor/src/core/constants/config.ts index 788454f9639..ac6d63dd161 100644 --- a/packages/editor/src/core/constants/config.ts +++ b/packages/editor/src/core/constants/config.ts @@ -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"]; diff --git a/packages/editor/src/core/types/config.ts b/packages/editor/src/core/types/config.ts index 12df0aa4234..4c91fec5d10 100644 --- a/packages/editor/src/core/types/config.ts +++ b/packages/editor/src/core/types/config.ts @@ -29,4 +29,5 @@ export type TDisplayConfig = { fontStyle?: TEditorFontStyle; fontSize?: TEditorFontSize; lineSpacing?: TEditorLineSpacing; + wideLayout?: boolean; }; diff --git a/packages/editor/src/styles/editor.css b/packages/editor/src/styles/editor.css index be686a5cc77..ba910d1449e 100644 --- a/packages/editor/src/styles/editor.css +++ b/packages/editor/src/styles/editor.css @@ -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); diff --git a/packages/editor/src/styles/variables.css b/packages/editor/src/styles/variables.css index ea70fe1ab87..d596c0d218d 100644 --- a/packages/editor/src/styles/variables.css +++ b/packages/editor/src/styles/variables.css @@ -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,86 @@ } /* 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); + } +} + +#page-content-container { + container-name: page-content-container; + container-type: inline-size; +} + +.editor-container { + --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 ease-in-out; + } +} + +/* 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 { + padding-left: var(--wide-content-margin) !important; + } +} + +/* keep a static padding of 20px for wide layouts for container width <912px */ +@container page-header-container (max-width: 912px) { + .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 */ diff --git a/web/ce/components/pages/editor/embed/issue-embed-upgrade-card.tsx b/web/ce/components/pages/editor/embed/issue-embed-upgrade-card.tsx index b4f68028d84..1f698f0a748 100644 --- a/web/ce/components/pages/editor/embed/issue-embed-upgrade-card.tsx +++ b/web/ce/components/pages/editor/embed/issue-embed-upgrade-card.tsx @@ -8,27 +8,25 @@ import { cn } from "@/helpers/common.helper"; export const IssueEmbedUpgradeCard: React.FC = (props) => (
-
-
- -

- Embed and access work items in pages seamlessly, upgrade to Plane Pro now. -

-
- - Upgrade - +
+ +

+ Embed and access issues in pages seamlessly, upgrade to Plane Pro now. +

+ + Upgrade +
); diff --git a/web/core/components/pages/editor/editor-body.tsx b/web/core/components/pages/editor/editor-body.tsx index aba274affeb..c84999b1c2d 100644 --- a/web/core/components/pages/editor/editor-body.tsx +++ b/web/core/components/pages/editor/editor-body.tsx @@ -13,12 +13,12 @@ import { // plane types import { TSearchEntityRequestPayload, TSearchResponse, TWebhookConnectionQueryParams } from "@plane/types"; // plane ui -import { Row } from "@plane/ui"; +import { ERowVariant, Row } from "@plane/ui"; // 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 +48,6 @@ type Props = { handleEditorReady: Dispatch>; handlers: TEditorBodyHandlers; page: TPageInstance; - sidePeekVisible: boolean; webhookConnectionParams: TWebhookConnectionQueryParams; workspaceSlug: string; }; @@ -61,7 +60,6 @@ export const PageEditorBody: React.FC = observer((props) => { handleEditorReady, handlers, page, - sidePeekVisible, webhookConnectionParams, workspaceSlug, } = props; @@ -91,8 +89,9 @@ export const PageEditorBody: React.FC = observer((props) => { () => ({ fontSize, fontStyle, + wideLayout: isFullWidth, }), - [fontSize, fontStyle] + [fontSize, fontStyle, isFullWidth] ); const getAIMenu = useCallback( @@ -155,66 +154,59 @@ export const PageEditorBody: React.FC = observer((props) => { if (pageId === undefined || !realtimeConfig) return ; return ( -
- -
-
- - { - const res = await fetchMentions(query); - if (!res) throw new Error("Failed in fetching mentions"); - return res; - }, - renderComponent: (props) => , - getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }), - }} - embedHandler={{ - issue: issueEmbedProps, - }} - realtimeConfig={realtimeConfig} - serverHandler={serverHandler} - user={userConfig} - disabledExtensions={disabledExtensions} - aiHandler={{ - menu: getAIMenu, - }} - /> + +
+ {/* table of content */} +
+
+
+
+ +
+
+ +
+
+
+ + { + const res = await fetchMentions(query); + if (!res) throw new Error("Failed in fetching mentions"); + return res; + }, + renderComponent: (props) => , + getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }), + }} + embedHandler={{ + issue: issueEmbedProps, + }} + realtimeConfig={realtimeConfig} + serverHandler={serverHandler} + user={userConfig} + disabledExtensions={disabledExtensions} + aiHandler={{ + menu: getAIMenu, + }} + />
-
-
+
); }); diff --git a/web/core/components/pages/editor/header/extra-options.tsx b/web/core/components/pages/editor/header/extra-options.tsx index 0b81d14522e..d9198b84f13 100644 --- a/web/core/components/pages/editor/header/extra-options.tsx +++ b/web/core/components/pages/editor/header/extra-options.tsx @@ -69,7 +69,7 @@ export const PageExtraOptions: React.FC = observer((props) => { }; return ( -
+
{is_locked && } {archived_at && (
diff --git a/web/core/components/pages/editor/header/mobile-root.tsx b/web/core/components/pages/editor/header/mobile-root.tsx index 22f25e2541f..a3cde27f4ce 100644 --- a/web/core/components/pages/editor/header/mobile-root.tsx +++ b/web/core/components/pages/editor/header/mobile-root.tsx @@ -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 = 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 ( <>
-
- -
diff --git a/web/core/components/pages/editor/header/root.tsx b/web/core/components/pages/editor/header/root.tsx index 552ba60097c..212e9223bf1 100644 --- a/web/core/components/pages/editor/header/root.tsx +++ b/web/core/components/pages/editor/header/root.tsx @@ -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; page: TPageInstance; - setSidePeekVisible: (sidePeekState: boolean) => void; - sidePeekVisible: boolean; storeType: EPageStoreType; }; export const PageEditorHeaderRoot: React.FC = 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 = observer((props) => { if (!resolvedEditorRef) return null; return ( - <> -
- - {editorReady && ( -
- -
- )} - {isStickyToolbarEnabled && editorReady && isContentEditable && editorRef.current && ( - - )} -
- -
+
+
+
+
+ {isStickyToolbarEnabled && editorReady && isContentEditable && editorRef.current && ( + + )} +
+ +
+
- +
- +
); }); diff --git a/web/core/components/pages/editor/page-root.tsx b/web/core/components/pages/editor/page-root.tsx index c80a52d4349..8949d1556e8 100644 --- a/web/core/components/pages/editor/page-root.tsx +++ b/web/core/components/pages/editor/page-root.tsx @@ -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(null); @@ -104,14 +103,7 @@ export const PageRoot = observer((props: TPageRootProps) => { pageId={page.id ?? ""} restoreEnabled={isContentEditable} /> - setSidePeekVisible(state)} - sidePeekVisible={sidePeekVisible} - storeType={storeType} - /> + { handleEditorReady={setEditorReady} handlers={handlers} page={page} - sidePeekVisible={sidePeekVisible} webhookConnectionParams={webhookConnectionParams} workspaceSlug={workspaceSlug} /> diff --git a/web/core/components/pages/editor/summary/content-browser.tsx b/web/core/components/pages/editor/summary/content-browser.tsx index 16d818aaeb7..98ab8b18c39 100644 --- a/web/core/components/pages/editor/summary/content-browser.tsx +++ b/web/core/components/pages/editor/summary/content-browser.tsx @@ -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) => { - const { editorRef, setSidePeekVisible } = props; + const { editorRef, setSidePeekVisible, showOutline = false } = props; // states const [headings, setHeadings] = useState([]); @@ -37,24 +38,28 @@ export const PageContentBrowser: React.FC = (props) => { }; return ( -
-
- {headings && headings.length !== 0 ? ( - headings.map((marking) => { - const Component = HeadingComponent[marking.level]; - if (!Component) return null; - return ( - handleOnClick(marking)} - /> - ); - }) - ) : ( -

Headings will be displayed here for navigation

- )} -
+
+ {headings.map((marking) => { + const Component = HeadingComponent[marking.level]; + if (!Component) return null; + if (showOutline === true) + return ( +
+ ); + return ( + handleOnClick(marking)} + /> + ); + })}
); }; diff --git a/web/core/components/pages/editor/summary/heading-components.tsx b/web/core/components/pages/editor/summary/heading-components.tsx index 5ed1752de02..632d1d94982 100644 --- a/web/core/components/pages/editor/summary/heading-components.tsx +++ b/web/core/components/pages/editor/summary/heading-components.tsx @@ -1,12 +1,12 @@ -// 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) => void; }; -export const OutlineHeading1 = ({ marking, onClick }: HeadingProps) => ( +export const OutlineHeading1 = ({ marking, onClick }: THeadingComponentProps) => ( ); -export const OutlineHeading2 = ({ marking, onClick }: HeadingProps) => ( +export const OutlineHeading2 = ({ marking, onClick }: THeadingComponentProps) => ( ); -export const OutlineHeading3 = ({ marking, onClick }: HeadingProps) => ( +export const OutlineHeading3 = ({ marking, onClick }: THeadingComponentProps) => ( -
- {sidePeekVisible && ( -
- -
- )} -
-
- {!sidePeekVisible && ( -
- -
- )} -
-
- ); -}; diff --git a/web/core/components/pages/editor/title.tsx b/web/core/components/pages/editor/title.tsx index 9e85c33d3bc..5ad5d927379 100644 --- a/web/core/components/pages/editor/title.tsx +++ b/web/core/components/pages/editor/title.tsx @@ -24,19 +24,23 @@ export const PageEditorTitle: React.FC = observer((props) => { // states const [isLengthVisible, setIsLengthVisible] = useState(false); // page filters - const { fontSize } = usePageFilters(); + const { fontSize, isFullWidth } = 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", }); + const titleWidthClassName = cn("block bg-transparent w-full max-w-[720px] mx-auto transition-all duration-300", { + "max-w-[1152px]": isFullWidth, + }); return ( -
+
{readOnly ? (
= observer((props) => { {getPageName(title)}
) : ( - <> +