[PE-155] chore: floating toolbar for pages (#6482)

* chore: add floating toolbar to pages

* fix: locked page toolbar
This commit is contained in:
Aaryan Khandelwal 2025-01-28 20:21:09 +05:30 committed by GitHub
parent 421839ec51
commit b698f44500
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 67 additions and 10 deletions

View file

@ -16,6 +16,7 @@ const CollaborativeDocumentEditor = (props: ICollaborativeDocumentEditor) => {
const {
onTransaction,
aiHandler,
bubbleMenuEnabled = true,
containerClassName,
disabledExtensions,
displayConfig = DEFAULT_DISPLAY_CONFIG,
@ -75,8 +76,9 @@ const CollaborativeDocumentEditor = (props: ICollaborativeDocumentEditor) => {
return (
<PageRenderer
displayConfig={displayConfig}
aiHandler={aiHandler}
bubbleMenuEnabled={bubbleMenuEnabled}
displayConfig={displayConfig}
editor={editor}
editorContainerClassName={editorContainerClassNames}
id={id}

View file

@ -15,12 +15,13 @@ import { Editor, ReactRenderer } from "@tiptap/react";
// components
import { EditorContainer, EditorContentWrapper } from "@/components/editors";
import { LinkView, LinkViewProps } from "@/components/links";
import { AIFeaturesMenu, BlockMenu } from "@/components/menus";
import { AIFeaturesMenu, BlockMenu, EditorBubbleMenu } from "@/components/menus";
// types
import { TAIHandler, TDisplayConfig } from "@/types";
type IPageRenderer = {
aiHandler?: TAIHandler;
bubbleMenuEnabled: boolean;
displayConfig: TDisplayConfig;
editor: Editor;
editorContainerClassName: string;
@ -29,7 +30,7 @@ type IPageRenderer = {
};
export const PageRenderer = (props: IPageRenderer) => {
const { aiHandler, displayConfig, editor, editorContainerClassName, id, tabIndex } = props;
const { aiHandler, bubbleMenuEnabled, displayConfig, editor, editorContainerClassName, id, tabIndex } = props;
// states
const [linkViewProps, setLinkViewProps] = useState<LinkViewProps>();
const [isOpen, setIsOpen] = useState(false);
@ -141,6 +142,7 @@ export const PageRenderer = (props: IPageRenderer) => {
<EditorContentWrapper editor={editor} id={id} tabIndex={tabIndex} />
{editor.isEditable && (
<div>
{bubbleMenuEnabled && <EditorBubbleMenu editor={editor} />}
<BlockMenu editor={editor} />
<AIFeaturesMenu menu={aiHandler?.menu} />
</div>

View file

@ -69,6 +69,7 @@ const DocumentReadOnlyEditor = (props: IDocumentReadOnlyEditor) => {
return (
<PageRenderer
bubbleMenuEnabled={false}
displayConfig={displayConfig}
editor={editor}
editorContainerClassName={editorContainerClassName}

View file

@ -138,8 +138,9 @@ export interface IRichTextEditor extends IEditorProps {
export interface ICollaborativeDocumentEditor
extends Omit<IEditorProps, "initialValue" | "onChange" | "onEnterKeyPress" | "value"> {
editable: boolean;
aiHandler?: TAIHandler;
bubbleMenuEnabled?: boolean;
editable: boolean;
embedHandler: TEmbedConfig;
handleEditorReady?: (value: boolean) => void;
id: string;

View file

@ -36,6 +36,7 @@ import { TPageInstance } from "@/store/pages/base-page";
export type TPageActions =
| "full-screen"
| "sticky-toolbar"
| "copy-markdown"
| "toggle-lock"
| "toggle-access"

View file

@ -30,9 +30,9 @@ export const PageOptionsDropdown: React.FC<Props> = observer((props) => {
// router
const router = useRouter();
// store values
const { name } = page;
const { name, isContentEditable } = page;
// page filters
const { isFullWidth, handleFullWidth } = usePageFilters();
const { isFullWidth, handleFullWidth, isStickyToolbarEnabled, handleStickyToolbar } = usePageFilters();
// update query params
const { updateQueryParams } = useQueryParams();
// menu items list
@ -49,6 +49,18 @@ export const PageOptionsDropdown: React.FC<Props> = observer((props) => {
),
className: "flex items-center justify-between gap-2",
},
{
key: "sticky-toolbar",
action: () => handleStickyToolbar(!isStickyToolbarEnabled),
customContent: (
<>
Sticky toolbar
<ToggleSwitch value={isStickyToolbarEnabled} onChange={() => {}} />
</>
),
className: "flex items-center justify-between gap-2",
shouldRender: isContentEditable,
},
{
key: "copy-markdown",
action: () => {
@ -86,7 +98,16 @@ export const PageOptionsDropdown: React.FC<Props> = observer((props) => {
shouldRender: true,
},
],
[editorRef, handleFullWidth, isFullWidth, router, updateQueryParams]
[
editorRef,
handleFullWidth,
handleStickyToolbar,
isContentEditable,
isFullWidth,
isStickyToolbarEnabled,
router,
updateQueryParams,
]
);
return (
@ -102,6 +123,7 @@ export const PageOptionsDropdown: React.FC<Props> = observer((props) => {
extraOptions={EXTRA_MENU_OPTIONS}
optionsOrder={[
"full-screen",
"sticky-toolbar",
"copy-link",
"make-a-copy",
"move",

View file

@ -23,7 +23,7 @@ export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
// derived values
const { isContentEditable } = page;
// page filters
const { isFullWidth } = usePageFilters();
const { isFullWidth, isStickyToolbarEnabled } = usePageFilters();
// derived values
const resolvedEditorRef = editorRef.current;
@ -48,7 +48,9 @@ export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
/>
</div>
)}
{editorReady && isContentEditable && editorRef.current && <PageToolbar editorRef={editorRef?.current} />}
{isStickyToolbarEnabled && editorReady && isContentEditable && editorRef.current && (
<PageToolbar editorRef={editorRef?.current} />
)}
</Header.LeftItem>
<PageExtraOptions editorRef={resolvedEditorRef} page={page} />
</Header>

View file

@ -8,12 +8,14 @@ export type TPagesPersonalizationConfig = {
full_width: boolean;
font_size: TEditorFontSize;
font_style: TEditorFontStyle;
sticky_toolbar: boolean;
};
const DEFAULT_PERSONALIZATION_VALUES: TPagesPersonalizationConfig = {
full_width: false,
font_size: "large-font",
font_style: "sans-serif",
sticky_toolbar: true,
};
export const usePageFilters = () => {
@ -23,7 +25,17 @@ export const usePageFilters = () => {
DEFAULT_PERSONALIZATION_VALUES
);
// stored values
const isFullWidth = useMemo(() => !!pagesConfig?.full_width, [pagesConfig?.full_width]);
const isFullWidth = useMemo(
() => (pagesConfig?.full_width === undefined ? DEFAULT_PERSONALIZATION_VALUES.full_width : pagesConfig?.full_width),
[pagesConfig?.full_width]
);
const isStickyToolbarEnabled = useMemo(
() =>
pagesConfig?.sticky_toolbar === undefined
? DEFAULT_PERSONALIZATION_VALUES.sticky_toolbar
: pagesConfig?.sticky_toolbar,
[pagesConfig?.sticky_toolbar]
);
const fontSize = useMemo(
() => pagesConfig?.font_size ?? DEFAULT_PERSONALIZATION_VALUES.font_size,
[pagesConfig?.font_size]
@ -78,6 +90,18 @@ export const usePageFilters = () => {
},
[handleUpdateConfig]
);
/**
* @description action to update full_width value
* @param {boolean} value
*/
const handleStickyToolbar = useCallback(
(value: boolean) => {
handleUpdateConfig({
sticky_toolbar: value,
});
},
[handleUpdateConfig]
);
return {
fontSize,
@ -86,5 +110,7 @@ export const usePageFilters = () => {
handleFontStyle,
isFullWidth,
handleFullWidth,
isStickyToolbarEnabled,
handleStickyToolbar,
};
};