From 85f8fe9247549dd9f8af79afeb2dc8fb8c1eec36 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Fri, 9 Aug 2024 19:22:47 +0530 Subject: [PATCH] [WEB-2045] dev: editor variable font sizes and styles support (#5340) * chore: added variable font size and font style support * chore: remove font style switcher * chore: update typography --- .../components/editors/document/editor.tsx | 22 ++- .../editors/document/page-renderer.tsx | 12 +- .../editors/document/read-only-editor.tsx | 22 ++- .../components/editors/editor-container.tsx | 11 +- .../components/editors/editor-wrapper.tsx | 10 +- .../editors/read-only-editor-wrapper.tsx | 21 ++- packages/editor/src/core/constants/config.ts | 7 + .../extensions/code/code-block-node-view.tsx | 2 +- .../src/core/hooks/use-document-editor.ts | 4 +- packages/editor/src/core/hooks/use-editor.ts | 77 ++++------ .../src/core/hooks/use-read-only-editor.ts | 4 +- packages/editor/src/core/props/props.tsx | 10 +- packages/editor/src/core/props/read-only.tsx | 22 +-- packages/editor/src/core/types/config.ts | 17 +++ packages/editor/src/core/types/editor.ts | 6 +- packages/editor/src/core/types/index.ts | 1 + packages/editor/src/index.ts | 2 +- packages/editor/src/styles/editor.css | 132 ++++++++++++------ packages/editor/src/styles/table.css | 5 - packages/ui/src/icons/index.ts | 3 + packages/ui/src/icons/monospace-icon.tsx | 16 +++ packages/ui/src/icons/sans-serif-icon.tsx | 16 +++ packages/ui/src/icons/serif-icon.tsx | 16 +++ .../components/pages/editor/editor-body.tsx | 11 +- .../pages/editor/header/options-dropdown.tsx | 1 + web/core/components/pages/editor/title.tsx | 27 ++-- web/core/constants/editor.ts | 26 +++- web/core/hooks/use-page-filters.ts | 61 +++++++- 28 files changed, 419 insertions(+), 145 deletions(-) create mode 100644 packages/editor/src/core/constants/config.ts create mode 100644 packages/editor/src/core/types/config.ts create mode 100644 packages/ui/src/icons/monospace-icon.tsx create mode 100644 packages/ui/src/icons/sans-serif-icon.tsx create mode 100644 packages/ui/src/icons/serif-icon.tsx diff --git a/packages/editor/src/core/components/editors/document/editor.tsx b/packages/editor/src/core/components/editors/document/editor.tsx index 2ec49acc0..968bc4572 100644 --- a/packages/editor/src/core/components/editors/document/editor.tsx +++ b/packages/editor/src/core/components/editors/document/editor.tsx @@ -1,19 +1,28 @@ import React from "react"; // components import { PageRenderer } from "@/components/editors"; +// constants +import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config"; // helpers import { getEditorClassNames } from "@/helpers/common"; // hooks import { useDocumentEditor } from "@/hooks/use-document-editor"; -import { TFileHandler } from "@/hooks/use-editor"; // plane editor types import { TEmbedConfig } from "@/plane-editor/types"; // types -import { EditorRefApi, IMentionHighlight, IMentionSuggestion, TExtensions } from "@/types"; +import { + EditorRefApi, + IMentionHighlight, + IMentionSuggestion, + TDisplayConfig, + TExtensions, + TFileHandler, +} from "@/types"; interface IDocumentEditor { containerClassName?: string; disabledExtensions?: TExtensions[]; + displayConfig?: TDisplayConfig; editorClassName?: string; embedHandler: TEmbedConfig; fileHandler: TFileHandler; @@ -34,6 +43,7 @@ const DocumentEditor = (props: IDocumentEditor) => { const { containerClassName, disabledExtensions, + displayConfig = DEFAULT_DISPLAY_CONFIG, editorClassName = "", embedHandler, fileHandler, @@ -72,7 +82,13 @@ const DocumentEditor = (props: IDocumentEditor) => { if (!editor || !isIndexedDbSynced) return null; return ( - + ); }; 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 2de5f6c57..66cde240e 100644 --- a/packages/editor/src/core/components/editors/document/page-renderer.tsx +++ b/packages/editor/src/core/components/editors/document/page-renderer.tsx @@ -16,8 +16,11 @@ import { Editor, ReactRenderer } from "@tiptap/react"; import { EditorContainer, EditorContentWrapper } from "@/components/editors"; import { LinkView, LinkViewProps } from "@/components/links"; import { BlockMenu } from "@/components/menus"; +// types +import { TDisplayConfig } from "@/types"; type IPageRenderer = { + displayConfig: TDisplayConfig; editor: Editor; editorContainerClassName: string; id: string; @@ -25,7 +28,7 @@ type IPageRenderer = { }; export const PageRenderer = (props: IPageRenderer) => { - const { editor, editorContainerClassName, id, tabIndex } = props; + const { displayConfig, editor, editorContainerClassName, id, tabIndex } = props; // states const [linkViewProps, setLinkViewProps] = useState(); const [isOpen, setIsOpen] = useState(false); @@ -128,7 +131,12 @@ export const PageRenderer = (props: IPageRenderer) => { return ( <>
- + {editor.isEditable && } diff --git a/packages/editor/src/core/components/editors/document/read-only-editor.tsx b/packages/editor/src/core/components/editors/document/read-only-editor.tsx index b259574e4..d7e56f50a 100644 --- a/packages/editor/src/core/components/editors/document/read-only-editor.tsx +++ b/packages/editor/src/core/components/editors/document/read-only-editor.tsx @@ -1,6 +1,8 @@ import { forwardRef, MutableRefObject } from "react"; // components import { PageRenderer } from "@/components/editors"; +// constants +import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config"; // extensions import { IssueWidget } from "@/extensions"; // helpers @@ -10,12 +12,13 @@ import { useReadOnlyEditor } from "@/hooks/use-read-only-editor"; // plane web types import { TEmbedConfig } from "@/plane-editor/types"; // types -import { EditorReadOnlyRefApi, IMentionHighlight } from "@/types"; +import { EditorReadOnlyRefApi, IMentionHighlight, TDisplayConfig } from "@/types"; interface IDocumentReadOnlyEditor { id: string; initialValue: string; containerClassName: string; + displayConfig?: TDisplayConfig; editorClassName?: string; embedHandler: TEmbedConfig; tabIndex?: number; @@ -29,6 +32,7 @@ interface IDocumentReadOnlyEditor { const DocumentReadOnlyEditor = (props: IDocumentReadOnlyEditor) => { const { containerClassName, + displayConfig = DEFAULT_DISPLAY_CONFIG, editorClassName = "", embedHandler, id, @@ -39,17 +43,17 @@ const DocumentReadOnlyEditor = (props: IDocumentReadOnlyEditor) => { mentionHandler, } = props; const editor = useReadOnlyEditor({ - initialValue, editorClassName, - mentionHandler, - forwardedRef, - handleEditorReady, extensions: [ embedHandler?.issue && IssueWidget({ widgetCallback: embedHandler?.issue.widgetCallback, }), ], + forwardedRef, + handleEditorReady, + initialValue, + mentionHandler, }); if (!editor) { @@ -61,7 +65,13 @@ const DocumentReadOnlyEditor = (props: IDocumentReadOnlyEditor) => { }); return ( - + ); }; diff --git a/packages/editor/src/core/components/editors/editor-container.tsx b/packages/editor/src/core/components/editors/editor-container.tsx index 5102cf9ac..2e6ad5250 100644 --- a/packages/editor/src/core/components/editors/editor-container.tsx +++ b/packages/editor/src/core/components/editors/editor-container.tsx @@ -1,17 +1,22 @@ import { FC, ReactNode } from "react"; import { Editor } from "@tiptap/react"; +// constants +import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config"; // helpers import { cn } from "@/helpers/common"; +// types +import { TDisplayConfig } from "@/types"; interface EditorContainerProps { children: ReactNode; + displayConfig: TDisplayConfig; editor: Editor | null; editorContainerClassName: string; id: string; } export const EditorContainer: FC = (props) => { - const { children, editor, editorContainerClassName, id } = props; + const { children, displayConfig, editor, editorContainerClassName, id } = props; const handleContainerClick = () => { if (!editor) return; @@ -65,10 +70,12 @@ export const EditorContainer: FC = (props) => { onClick={handleContainerClick} onMouseLeave={handleContainerMouseLeave} className={cn( - "cursor-text relative", + "editor-container cursor-text relative", { "active-editor": editor?.isFocused && editor?.isEditable, }, + displayConfig.fontSize ?? DEFAULT_DISPLAY_CONFIG.fontSize, + displayConfig.fontStyle ?? DEFAULT_DISPLAY_CONFIG.fontStyle, editorContainerClassName )} > diff --git a/packages/editor/src/core/components/editors/editor-wrapper.tsx b/packages/editor/src/core/components/editors/editor-wrapper.tsx index 9d2d6b2a7..3e00dc2af 100644 --- a/packages/editor/src/core/components/editors/editor-wrapper.tsx +++ b/packages/editor/src/core/components/editors/editor-wrapper.tsx @@ -1,6 +1,8 @@ import { Editor, Extension } from "@tiptap/core"; // components import { EditorContainer } from "@/components/editors"; +// constants +import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config"; // hooks import { getEditorClassNames } from "@/helpers/common"; import { useEditor } from "@/hooks/use-editor"; @@ -17,6 +19,7 @@ export const EditorWrapper: React.FC = (props) => { const { children, containerClassName, + displayConfig = DEFAULT_DISPLAY_CONFIG, editorClassName = "", extensions, id, @@ -54,7 +57,12 @@ export const EditorWrapper: React.FC = (props) => { if (!editor) return null; return ( - + {children?.(editor)}
diff --git a/packages/editor/src/core/components/editors/read-only-editor-wrapper.tsx b/packages/editor/src/core/components/editors/read-only-editor-wrapper.tsx index d75a5d530..fc0911bee 100644 --- a/packages/editor/src/core/components/editors/read-only-editor-wrapper.tsx +++ b/packages/editor/src/core/components/editors/read-only-editor-wrapper.tsx @@ -1,5 +1,7 @@ // components import { EditorContainer, EditorContentWrapper } from "@/components/editors"; +// constants +import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config"; // helpers import { getEditorClassNames } from "@/helpers/common"; // hooks @@ -8,12 +10,20 @@ import { useReadOnlyEditor } from "@/hooks/use-read-only-editor"; import { IReadOnlyEditorProps } from "@/types"; export const ReadOnlyEditorWrapper = (props: IReadOnlyEditorProps) => { - const { containerClassName, editorClassName = "", id, initialValue, forwardedRef, mentionHandler } = props; + const { + containerClassName, + displayConfig = DEFAULT_DISPLAY_CONFIG, + editorClassName = "", + id, + initialValue, + forwardedRef, + mentionHandler, + } = props; const editor = useReadOnlyEditor({ - initialValue, editorClassName, forwardedRef, + initialValue, mentionHandler, }); @@ -24,7 +34,12 @@ export const ReadOnlyEditorWrapper = (props: IReadOnlyEditorProps) => { if (!editor) return null; return ( - +
diff --git a/packages/editor/src/core/constants/config.ts b/packages/editor/src/core/constants/config.ts new file mode 100644 index 000000000..5a9577044 --- /dev/null +++ b/packages/editor/src/core/constants/config.ts @@ -0,0 +1,7 @@ +// types +import { TDisplayConfig } from "@/types"; + +export const DEFAULT_DISPLAY_CONFIG: TDisplayConfig = { + fontSize: "large-font", + fontStyle: "sans-serif", +}; diff --git a/packages/editor/src/core/extensions/code/code-block-node-view.tsx b/packages/editor/src/core/extensions/code/code-block-node-view.tsx index b383b4c81..8dbdb044f 100644 --- a/packages/editor/src/core/extensions/code/code-block-node-view.tsx +++ b/packages/editor/src/core/extensions/code/code-block-node-view.tsx @@ -56,7 +56,7 @@ export const CodeBlockComponent: React.FC = ({ node })
-        
+        
       
); diff --git a/packages/editor/src/core/hooks/use-document-editor.ts b/packages/editor/src/core/hooks/use-document-editor.ts index 21b224b9d..e21b7f1b8 100644 --- a/packages/editor/src/core/hooks/use-document-editor.ts +++ b/packages/editor/src/core/hooks/use-document-editor.ts @@ -5,7 +5,7 @@ import * as Y from "yjs"; // extensions import { IssueWidget, SideMenuExtension } from "@/extensions"; // hooks -import { TFileHandler, useEditor } from "@/hooks/use-editor"; +import { useEditor } from "@/hooks/use-editor"; // plane editor extensions import { DocumentEditorAdditionalExtensions } from "@/plane-editor/extensions"; // plane editor provider @@ -13,7 +13,7 @@ import { CollaborationProvider } from "@/plane-editor/providers"; // plane editor types import { TEmbedConfig } from "@/plane-editor/types"; // types -import { EditorRefApi, IMentionHighlight, IMentionSuggestion, TExtensions } from "@/types"; +import { EditorRefApi, IMentionHighlight, IMentionSuggestion, TExtensions, TFileHandler } from "@/types"; type DocumentEditorProps = { disabledExtensions?: TExtensions[]; diff --git a/packages/editor/src/core/hooks/use-editor.ts b/packages/editor/src/core/hooks/use-editor.ts index 41d962af0..01718fbe7 100644 --- a/packages/editor/src/core/hooks/use-editor.ts +++ b/packages/editor/src/core/hooks/use-editor.ts @@ -1,7 +1,7 @@ import { useImperativeHandle, useRef, MutableRefObject, useState, useEffect } from "react"; import { Selection } from "@tiptap/pm/state"; import { EditorProps } from "@tiptap/pm/view"; -import { useEditor as useCustomEditor, Editor } from "@tiptap/react"; +import { useEditor as useTiptapEditor, Editor } from "@tiptap/react"; // components import { getEditorMenuItems } from "@/components/menus"; // extensions @@ -14,22 +14,7 @@ import { CollaborationProvider } from "@/plane-editor/providers"; // props import { CoreEditorProps } from "@/props"; // types -import { - DeleteImage, - EditorRefApi, - IMentionHighlight, - IMentionSuggestion, - RestoreImage, - TEditorCommands, - UploadImage, -} from "@/types"; - -export type TFileHandler = { - cancel: () => void; - delete: DeleteImage; - upload: UploadImage; - restore: RestoreImage; -}; +import { EditorRefApi, IMentionHighlight, IMentionSuggestion, TEditorCommands, TFileHandler } from "@/types"; export interface CustomEditorProps { editorClassName: string; @@ -54,26 +39,30 @@ export interface CustomEditorProps { value?: string | null | undefined; } -export const useEditor = ({ - editorClassName, - editorProps = {}, - enableHistory, - extensions = [], - fileHandler, - forwardedRef, - handleEditorReady, - id = "", - initialValue, - mentionHandler, - onChange, - placeholder, - provider, - tabIndex, - value, -}: CustomEditorProps) => { - const editor = useCustomEditor({ +export const useEditor = (props: CustomEditorProps) => { + const { + editorClassName, + editorProps = {}, + enableHistory, + extensions = [], + fileHandler, + forwardedRef, + handleEditorReady, + id = "", + initialValue, + mentionHandler, + onChange, + placeholder, + provider, + tabIndex, + value, + } = props; + + const editor = useTiptapEditor({ editorProps: { - ...CoreEditorProps(editorClassName), + ...CoreEditorProps({ + editorClassName, + }), ...editorProps, }, extensions: [ @@ -95,18 +84,10 @@ export const useEditor = ({ ...extensions, ], content: typeof initialValue === "string" && initialValue.trim() !== "" ? initialValue : "

", - onCreate: async () => { - handleEditorReady?.(true); - }, - onTransaction: async ({ editor }) => { - setSavedSelection(editor.state.selection); - }, - onUpdate: async ({ editor }) => { - onChange?.(editor.getJSON(), editor.getHTML()); - }, - onDestroy: async () => { - handleEditorReady?.(false); - }, + onCreate: () => handleEditorReady?.(true), + onTransaction: ({ editor }) => setSavedSelection(editor.state.selection), + onUpdate: ({ editor }) => onChange?.(editor.getJSON(), editor.getHTML()), + onDestroy: () => handleEditorReady?.(false), }); const editorRef: MutableRefObject = useRef(null); diff --git a/packages/editor/src/core/hooks/use-read-only-editor.ts b/packages/editor/src/core/hooks/use-read-only-editor.ts index fcaf0c6dd..0a737873e 100644 --- a/packages/editor/src/core/hooks/use-read-only-editor.ts +++ b/packages/editor/src/core/hooks/use-read-only-editor.ts @@ -35,7 +35,9 @@ export const useReadOnlyEditor = ({ editable: false, content: typeof initialValue === "string" && initialValue.trim() !== "" ? initialValue : "

", editorProps: { - ...CoreReadOnlyEditorProps(editorClassName), + ...CoreReadOnlyEditorProps({ + editorClassName, + }), ...editorProps, }, onCreate: async () => { diff --git a/packages/editor/src/core/props/props.tsx b/packages/editor/src/core/props/props.tsx index 11e829162..4bda3e51a 100644 --- a/packages/editor/src/core/props/props.tsx +++ b/packages/editor/src/core/props/props.tsx @@ -2,7 +2,13 @@ import { EditorProps } from "@tiptap/pm/view"; // helpers import { cn } from "@/helpers/common"; -export function CoreEditorProps(editorClassName: string): EditorProps { +export type TCoreEditorProps = { + editorClassName: string; +}; + +export const CoreEditorProps = (props: TCoreEditorProps): EditorProps => { + const { editorClassName } = props; + return { attributes: { class: cn( @@ -25,4 +31,4 @@ export function CoreEditorProps(editorClassName: string): EditorProps { return html.replace(//g, ""); }, }; -} +}; diff --git a/packages/editor/src/core/props/read-only.tsx b/packages/editor/src/core/props/read-only.tsx index ea583938f..aaa635a50 100644 --- a/packages/editor/src/core/props/read-only.tsx +++ b/packages/editor/src/core/props/read-only.tsx @@ -1,12 +1,18 @@ import { EditorProps } from "@tiptap/pm/view"; // helpers import { cn } from "@/helpers/common"; +// props +import { TCoreEditorProps } from "@/props"; -export const CoreReadOnlyEditorProps = (editorClassName: string): EditorProps => ({ - attributes: { - class: cn( - "prose prose-brand max-w-full prose-headings:font-display font-default focus:outline-none", - editorClassName - ), - }, -}); +export const CoreReadOnlyEditorProps = (props: TCoreEditorProps): EditorProps => { + const { editorClassName } = props; + + return { + attributes: { + class: cn( + "prose prose-brand max-w-full prose-headings:font-display font-default focus:outline-none", + editorClassName + ), + }, + }; +}; diff --git a/packages/editor/src/core/types/config.ts b/packages/editor/src/core/types/config.ts new file mode 100644 index 000000000..93d612e59 --- /dev/null +++ b/packages/editor/src/core/types/config.ts @@ -0,0 +1,17 @@ +import { DeleteImage, RestoreImage, UploadImage } from "@/types"; + +export type TFileHandler = { + cancel: () => void; + delete: DeleteImage; + upload: UploadImage; + restore: RestoreImage; +}; + +export type TEditorFontStyle = "sans-serif" | "serif" | "monospace"; + +export type TEditorFontSize = "small-font" | "large-font"; + +export type TDisplayConfig = { + fontStyle?: TEditorFontStyle; + fontSize?: TEditorFontSize; +}; diff --git a/packages/editor/src/core/types/editor.ts b/packages/editor/src/core/types/editor.ts index 695082c11..f817969c3 100644 --- a/packages/editor/src/core/types/editor.ts +++ b/packages/editor/src/core/types/editor.ts @@ -1,9 +1,7 @@ // helpers import { IMarking } from "@/helpers/scroll-to-node"; -// hooks -import { TFileHandler } from "@/hooks/use-editor"; // types -import { IMentionHighlight, IMentionSuggestion, TEditorCommands } from "@/types"; +import { IMentionHighlight, IMentionSuggestion, TDisplayConfig, TEditorCommands, TFileHandler } from "@/types"; export type EditorReadOnlyRefApi = { getMarkDown: () => string; @@ -26,6 +24,7 @@ export interface EditorRefApi extends EditorReadOnlyRefApi { export interface IEditorProps { containerClassName?: string; + displayConfig?: TDisplayConfig; editorClassName?: string; fileHandler: TFileHandler; forwardedRef?: React.MutableRefObject; @@ -50,6 +49,7 @@ export interface IRichTextEditor extends IEditorProps { export interface IReadOnlyEditorProps { containerClassName?: string; + displayConfig?: TDisplayConfig; editorClassName?: string; forwardedRef?: React.MutableRefObject; id: string; diff --git a/packages/editor/src/core/types/index.ts b/packages/editor/src/core/types/index.ts index 891a86286..7190cfb51 100644 --- a/packages/editor/src/core/types/index.ts +++ b/packages/editor/src/core/types/index.ts @@ -1,3 +1,4 @@ +export * from "./config"; export * from "./editor"; export * from "./embed"; export * from "./extensions"; diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index 828fab021..4a317e41c 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -34,5 +34,5 @@ export { type IMarking, useEditorMarkings } from "@/hooks/use-editor-markings"; export { useReadOnlyEditor } from "@/hooks/use-read-only-editor"; // types -export type { CustomEditorProps, TFileHandler } from "@/hooks/use-editor"; +export type { CustomEditorProps } from "@/hooks/use-editor"; export * from "@/types"; diff --git a/packages/editor/src/styles/editor.css b/packages/editor/src/styles/editor.css index 28fb2dd11..2ec35dc25 100644 --- a/packages/editor/src/styles/editor.css +++ b/packages/editor/src/styles/editor.css @@ -1,12 +1,82 @@ +.editor-container { + &.large-font { + --font-size-h1: 1.75rem; + --font-size-h2: 1.5rem; + --font-size-h3: 1.375rem; + --font-size-h4: 1.25rem; + --font-size-h5: 1.125rem; + --font-size-h6: 1rem; + --font-size-regular: 1rem; + --font-size-list: var(--font-size-regular); + --font-size-code: var(--font-size-regular); + + --line-height-h1: 2.25rem; + --line-height-h2: 2rem; + --line-height-h3: 1.75rem; + --line-height-h4: 1.5rem; + --line-height-h5: 1.5rem; + --line-height-h6: 1.5rem; + --line-height-regular: 1.5rem; + --line-height-list: var(--line-height-regular); + --line-height-code: var(--line-height-regular); + } + + &.small-font { + --font-size-h1: 1.4rem; + --font-size-h2: 1.2rem; + --font-size-h3: 1.1rem; + --font-size-h4: 1rem; + --font-size-h5: 0.9rem; + --font-size-h6: 0.8rem; + --font-size-regular: 0.8rem; + --font-size-list: var(--font-size-regular); + --font-size-code: var(--font-size-regular); + + --line-height-h1: 1.8rem; + --line-height-h2: 1.6rem; + --line-height-h3: 1.4rem; + --line-height-h4: 1.2rem; + --line-height-h5: 1.2rem; + --line-height-h6: 1.2rem; + --line-height-regular: 1.2rem; + --line-height-list: var(--line-height-regular); + --line-height-code: var(--line-height-regular); + } + + &.sans-serif { + --font-style: sans-serif; + } + + &.serif { + --font-style: serif; + } + + &.monospace { + --font-style: monospace; + } +} + .ProseMirror { - --font-size-h1: 1.5rem; - --font-size-h2: 1.3125rem; - --font-size-h3: 1.125rem; - --font-size-h4: 0.9375rem; - --font-size-h5: 0.8125rem; - --font-size-h6: 0.75rem; - --font-size-regular: 0.9375rem; - --font-size-list: var(--font-size-regular); + position: relative; + word-wrap: break-word; + white-space: pre-wrap; + -moz-tab-size: 4; + tab-size: 4; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; + outline: none; + cursor: text; + font-family: var(--font-style); + font-size: var(--font-size-regular); + line-height: 1.2; + color: inherit; + -moz-box-sizing: border-box; + box-sizing: border-box; + appearance: textfield; + -webkit-appearance: textfield; + -moz-appearance: textfield; } .ProseMirror p.is-editor-empty:first-child::before { @@ -179,29 +249,6 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { max-width: 400px !important; } -.ProseMirror { - position: relative; - word-wrap: break-word; - white-space: pre-wrap; - -moz-tab-size: 4; - tab-size: 4; - -webkit-user-select: text; - -moz-user-select: text; - -ms-user-select: text; - user-select: text; - outline: none; - cursor: text; - line-height: 1.2; - font-family: inherit; - font-size: var(--font-size-regular); - color: inherit; - -moz-box-sizing: border-box; - box-sizing: border-box; - appearance: textfield; - -webkit-appearance: textfield; - -moz-appearance: textfield; -} - .fade-in { opacity: 1; transition: opacity 0.3s ease-in; @@ -248,6 +295,7 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { opacity: 0; } +/* code block, inline code */ .ProseMirror pre { font-family: JetBrainsMono, monospace; tab-size: 2; @@ -256,10 +304,14 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { .ProseMirror pre code { background: none; color: inherit; - font-size: 0.8rem; padding: 0; } +.ProseMirror code { + font-size: var(--font-size-code); +} +/* end code block, inline code */ + div[data-type="horizontalRule"] { line-height: 0; padding: 0.25rem 0; @@ -342,48 +394,48 @@ ul[data-type="taskList"] ul[data-type="taskList"] { margin-top: 2rem; margin-bottom: 4px; font-size: var(--font-size-h1); + line-height: var(--line-height-h1); font-weight: 600; - line-height: 1.3; } .prose :where(h2):not(:where([class~="not-prose"], [class~="not-prose"] *)) { margin-top: 1.4rem; margin-bottom: 1px; font-size: var(--font-size-h2); + line-height: var(--line-height-h2); font-weight: 600; - line-height: 1.3; } .prose :where(h3):not(:where([class~="not-prose"], [class~="not-prose"] *)) { margin-top: 1rem; margin-bottom: 1px; font-size: var(--font-size-h3); + line-height: var(--line-height-h3); font-weight: 600; - line-height: 1.3; } .prose :where(h4):not(:where([class~="not-prose"], [class~="not-prose"] *)) { margin-top: 1rem; margin-bottom: 1px; font-size: var(--font-size-h4); + line-height: var(--line-height-h4); font-weight: 600; - line-height: 1.5; } .prose :where(h5):not(:where([class~="not-prose"], [class~="not-prose"] *)) { margin-top: 1rem; margin-bottom: 1px; font-size: var(--font-size-h5); + line-height: var(--line-height-h5); font-weight: 600; - line-height: 1.5; } .prose :where(h6):not(:where([class~="not-prose"], [class~="not-prose"] *)) { margin-top: 1rem; margin-bottom: 1px; font-size: var(--font-size-h6); + line-height: var(--line-height-h6); font-weight: 600; - line-height: 1.5; } .prose :where(p):not(:where([class~="not-prose"], [class~="not-prose"] *)) { @@ -391,13 +443,13 @@ ul[data-type="taskList"] ul[data-type="taskList"] { margin-bottom: 1px; padding: 3px 0; font-size: var(--font-size-regular); - line-height: 1.5; + line-height: var(--line-height-regular); } .prose :where(ol):not(:where([class~="not-prose"], [class~="not-prose"] *)) li p, .prose :where(ul):not(:where([class~="not-prose"], [class~="not-prose"] *)) li p { font-size: var(--font-size-list); - line-height: 1.5; + line-height: var(--line-height-list); } .prose :where(.prose > :first-child):not(:where([class~="not-prose"], [class~="not-prose"] *)) { diff --git a/packages/editor/src/styles/table.css b/packages/editor/src/styles/table.css index 6b45abcf5..2a0140a2b 100644 --- a/packages/editor/src/styles/table.css +++ b/packages/editor/src/styles/table.css @@ -12,10 +12,6 @@ width: 100%; } -.table-wrapper table p { - font-size: 14px; -} - .table-wrapper table td, .table-wrapper table th { min-width: 1em; @@ -115,4 +111,3 @@ opacity: 0; pointer-events: none; } - diff --git a/packages/ui/src/icons/index.ts b/packages/ui/src/icons/index.ts index a87d19146..660768845 100644 --- a/packages/ui/src/icons/index.ts +++ b/packages/ui/src/icons/index.ts @@ -15,9 +15,12 @@ export * from "./github-icon"; export * from "./gitlab-icon"; export * from "./layer-stack"; export * from "./layers-icon"; +export * from "./monospace-icon"; export * from "./photo-filter-icon"; export * from "./priority-icon"; export * from "./related-icon"; +export * from "./sans-serif-icon"; +export * from "./serif-icon"; export * from "./side-panel-icon"; export * from "./transfer-icon"; export * from "./info-icon"; diff --git a/packages/ui/src/icons/monospace-icon.tsx b/packages/ui/src/icons/monospace-icon.tsx new file mode 100644 index 000000000..8eb64f01c --- /dev/null +++ b/packages/ui/src/icons/monospace-icon.tsx @@ -0,0 +1,16 @@ +import * as React from "react"; + +import { ISvgIcons } from "./type"; + +export const MonospaceIcon: React.FC = ({ className = "text-current", ...rest }) => ( + + + + +); diff --git a/packages/ui/src/icons/sans-serif-icon.tsx b/packages/ui/src/icons/sans-serif-icon.tsx new file mode 100644 index 000000000..2260130a7 --- /dev/null +++ b/packages/ui/src/icons/sans-serif-icon.tsx @@ -0,0 +1,16 @@ +import * as React from "react"; + +import { ISvgIcons } from "./type"; + +export const SansSerifIcon: React.FC = ({ className = "text-current", ...rest }) => ( + + + + +); diff --git a/packages/ui/src/icons/serif-icon.tsx b/packages/ui/src/icons/serif-icon.tsx new file mode 100644 index 000000000..fab5c12d3 --- /dev/null +++ b/packages/ui/src/icons/serif-icon.tsx @@ -0,0 +1,16 @@ +import * as React from "react"; + +import { ISvgIcons } from "./type"; + +export const SerifIcon: React.FC = ({ className = "text-current", ...rest }) => ( + + + + +); diff --git a/web/core/components/pages/editor/editor-body.tsx b/web/core/components/pages/editor/editor-body.tsx index 4b4c80a3a..64628622d 100644 --- a/web/core/components/pages/editor/editor-body.tsx +++ b/web/core/components/pages/editor/editor-body.tsx @@ -8,6 +8,7 @@ import { EditorReadOnlyRefApi, EditorRefApi, IMarking, + TDisplayConfig, } from "@plane/editor"; // types import { IUserLite } from "@plane/types"; @@ -82,12 +83,16 @@ export const PageEditorBody: React.FC = observer((props) => { }); // editor flaggings const { documentEditor } = useEditorFlagging(); - // page filters - const { isFullWidth } = usePageFilters(); + const { fontSize, fontStyle, isFullWidth } = usePageFilters(); // issue-embed const { issueEmbedProps } = useIssueEmbed(workspaceSlug?.toString() ?? "", projectId?.toString() ?? ""); + const displayConfig: TDisplayConfig = { + fontSize, + fontStyle, + }; + useEffect(() => { updateMarkings(pageDescription ?? "

"); }, [pageDescription, updateMarkings]); @@ -139,6 +144,7 @@ export const PageEditorBody: React.FC = observer((props) => { value={pageDescriptionYJS} ref={editorRef} containerClassName="p-0 pb-64" + displayConfig={displayConfig} editorClassName="pl-10" onChange={handleDescriptionChange} mentionHandler={{ @@ -157,6 +163,7 @@ export const PageEditorBody: React.FC = observer((props) => { initialValue={pageDescription ?? "

"} handleEditorReady={handleReadOnlyEditorReady} containerClassName="p-0 pb-64 border-none" + displayConfig={displayConfig} editorClassName="pl-10" mentionHandler={{ highlights: mentionHighlights, diff --git a/web/core/components/pages/editor/header/options-dropdown.tsx b/web/core/components/pages/editor/header/options-dropdown.tsx index af2f2e6af..016ca8eb5 100644 --- a/web/core/components/pages/editor/header/options-dropdown.tsx +++ b/web/core/components/pages/editor/header/options-dropdown.tsx @@ -40,6 +40,7 @@ export const PageOptionsDropdown: React.FC = observer((props) => { const { workspaceSlug, projectId } = useParams(); // page filters const { isFullWidth, handleFullWidth } = usePageFilters(); + const handleArchivePage = async () => await archive().catch(() => setToast({ diff --git a/web/core/components/pages/editor/title.tsx b/web/core/components/pages/editor/title.tsx index 59502ba81..56d2f18f5 100644 --- a/web/core/components/pages/editor/title.tsx +++ b/web/core/components/pages/editor/title.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { CSSProperties, useState } from "react"; import { observer } from "mobx-react"; // editor import { EditorRefApi } from "@plane/editor"; @@ -8,6 +8,8 @@ import { EditorRefApi } from "@plane/editor"; import { TextArea } from "@plane/ui"; // helpers import { cn } from "@/helpers/common.helper"; +// hooks +import { usePageFilters } from "@/hooks/use-page-filters"; type Props = { editorRef: React.RefObject; @@ -20,25 +22,28 @@ export const PageEditorTitle: React.FC = observer((props) => { const { editorRef, readOnly, title, updateTitle } = props; // states const [isLengthVisible, setIsLengthVisible] = useState(false); + // page filters + const { fontSize, fontStyle } = usePageFilters(); + // ui + const titleClassName = cn("bg-transparent tracking-[-2%] font-semibold", { + "text-[1.6rem] leading-[1.8rem]": fontSize === "small-font", + "text-[2rem] leading-[2.25rem]": fontSize === "large-font", + }); + const titleStyle: CSSProperties = { + fontFamily: fontStyle, + }; return ( <> {readOnly ? ( -
+
{title}
) : ( <>