chore: consolidate super toolbar items and remove need for scroll [skip e2e]

This commit is contained in:
Aman Harwara
2023-10-25 11:14:34 +05:30
parent 04d95cc264
commit f6bfe9e868
9 changed files with 329 additions and 166 deletions

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path fill="currentColor" fill-rule="evenodd" d="M5 11.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zM3.854 2.146a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 1 1 .708-.708L2 3.293l1.146-1.147a.5.5 0 0 1 .708 0zm0 4a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 1 1 .708-.708L2 7.293l1.146-1.147a.5.5 0 0 1 .708 0zm0 4a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 0 1 .708-.708l.146.147l1.146-1.147a.5.5 0 0 1 .708 0z" />
</svg>

After

Width:  |  Height:  |  Size: 660 B

View File

@@ -35,6 +35,7 @@ import CheckBoldIcon from './ic-check-bold.svg'
import CheckCircleFilledIcon from './ic-check-circle-filled.svg' import CheckCircleFilledIcon from './ic-check-circle-filled.svg'
import CheckCircleIcon from './ic-check-circle.svg' import CheckCircleIcon from './ic-check-circle.svg'
import CheckIcon from './ic-check.svg' import CheckIcon from './ic-check.svg'
import CheckListIcon from './ic-list-check.svg'
import ChevronDownIcon from './ic-chevron-down.svg' import ChevronDownIcon from './ic-chevron-down.svg'
import ChevronLeftIcon from './ic-chevron-left.svg' import ChevronLeftIcon from './ic-chevron-left.svg'
import ChevronRightIcon from './ic-chevron-right.svg' import ChevronRightIcon from './ic-chevron-right.svg'
@@ -254,6 +255,7 @@ export {
CheckCircleFilledIcon, CheckCircleFilledIcon,
CheckCircleIcon, CheckCircleIcon,
CheckIcon, CheckIcon,
CheckListIcon,
ChevronDownIcon, ChevronDownIcon,
ChevronLeftIcon, ChevronLeftIcon,
ChevronRightIcon, ChevronRightIcon,

View File

@@ -49,6 +49,7 @@ export const IconNameToSvgMapping = {
'link-off': icons.LinkOffIcon, 'link-off': icons.LinkOffIcon,
'list-bulleted': icons.ListBulleted, 'list-bulleted': icons.ListBulleted,
'list-numbered': icons.ListNumbered, 'list-numbered': icons.ListNumbered,
'list-check': icons.CheckListIcon,
'lock-filled': icons.LockFilledIcon, 'lock-filled': icons.LockFilledIcon,
'menu-arrow-down-alt': icons.MenuArrowDownAlt, 'menu-arrow-down-alt': icons.MenuArrowDownAlt,
'menu-arrow-down': icons.MenuArrowDownIcon, 'menu-arrow-down': icons.MenuArrowDownIcon,

View File

@@ -1,8 +1,7 @@
import { import {
CSSProperties, ComponentPropsWithoutRef,
forwardRef, forwardRef,
KeyboardEventHandler, KeyboardEventHandler,
ReactNode,
useCallback, useCallback,
useImperativeHandle, useImperativeHandle,
useRef, useRef,
@@ -12,15 +11,11 @@ import { useListKeyboardNavigation } from '@/Hooks/useListKeyboardNavigation'
import { mergeRefs } from '@/Hooks/mergeRefs' import { mergeRefs } from '@/Hooks/mergeRefs'
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery' import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
type MenuProps = { interface MenuProps extends ComponentPropsWithoutRef<'menu'> {
className?: string
style?: CSSProperties | undefined
a11yLabel: string a11yLabel: string
children: ReactNode
closeMenu?: () => void closeMenu?: () => void
isOpen: boolean isOpen: boolean
initialFocus?: number initialFocus?: number
onKeyDown?: KeyboardEventHandler<HTMLMenuElement>
shouldAutoFocus?: boolean shouldAutoFocus?: boolean
} }
@@ -35,6 +30,7 @@ const Menu = forwardRef(
initialFocus, initialFocus,
onKeyDown, onKeyDown,
shouldAutoFocus = true, shouldAutoFocus = true,
...props
}: MenuProps, }: MenuProps,
forwardedRef, forwardedRef,
) => { ) => {
@@ -73,6 +69,7 @@ const Menu = forwardRef(
ref={mergeRefs([menuElementRef, forwardedRef])} ref={mergeRefs([menuElementRef, forwardedRef])}
style={style} style={style}
aria-label={a11yLabel} aria-label={a11yLabel}
{...props}
> >
{children} {children}
</menu> </menu>

View File

@@ -7,7 +7,7 @@ import { PlatformedKeyboardShortcut } from '@standardnotes/ui-services'
import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator' import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator'
import MenuListItem from './MenuListItem' import MenuListItem from './MenuListItem'
type MenuItemProps = { export interface MenuItemProps extends ComponentPropsWithoutRef<'button'> {
children: ReactNode children: ReactNode
onClick?: MouseEventHandler<HTMLButtonElement> onClick?: MouseEventHandler<HTMLButtonElement>
onBlur?: (event: { relatedTarget: EventTarget | null }) => void onBlur?: (event: { relatedTarget: EventTarget | null }) => void
@@ -17,7 +17,7 @@ type MenuItemProps = {
tabIndex?: number tabIndex?: number
disabled?: boolean disabled?: boolean
shortcut?: PlatformedKeyboardShortcut shortcut?: PlatformedKeyboardShortcut
} & ComponentPropsWithoutRef<'button'> }
const MenuItem = forwardRef( const MenuItem = forwardRef(
( (

View File

@@ -12,7 +12,7 @@ export const BulletedListBlock = {
export const ChecklistBlock = { export const ChecklistBlock = {
name: 'Check List', name: 'Check List',
iconName: 'check' as LexicalIconName, iconName: 'list-check' as LexicalIconName,
keywords: ['check list', 'todo list'], keywords: ['check list', 'todo list'],
onSelect: (editor: LexicalEditor) => editor.dispatchCommand(INSERT_CHECK_LIST_COMMAND, undefined), onSelect: (editor: LexicalEditor) => editor.dispatchCommand(INSERT_CHECK_LIST_COMMAND, undefined),
} }

View File

@@ -93,7 +93,7 @@ const CodeOptionsPlugin = () => {
return ( return (
<> <>
<div className="absolute right-6 top-2 rounded border border-border bg-default p-2"> <div className="absolute right-6 top-13 rounded border border-border bg-default p-2">
<Dropdown <Dropdown
label="Change code block language" label="Change code block language"
items={CODE_LANGUAGE_OPTIONS.map(([value, label]) => ({ items={CODE_LANGUAGE_OPTIONS.map(([value, label]) => ({

View File

@@ -53,7 +53,7 @@ export const SearchDialog = ({ open, closeDialog }: { open: boolean; closeDialog
return ( return (
<div <div
className="absolute right-6 top-4 z-10 flex select-none rounded border border-border bg-default" className="absolute right-6 top-13 z-10 flex select-none rounded border border-border bg-default"
ref={setElement} ref={setElement}
> >
<button <button

View File

@@ -25,7 +25,16 @@ import { mergeRegister, $findMatchingParent, $getNearestNodeOfType } from '@lexi
import { $isLinkNode, $isAutoLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link' import { $isLinkNode, $isAutoLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link'
import { $isListNode, ListNode } from '@lexical/list' import { $isListNode, ListNode } from '@lexical/list'
import { $isHeadingNode } from '@lexical/rich-text' import { $isHeadingNode } from '@lexical/rich-text'
import { ComponentPropsWithoutRef, ForwardedRef, forwardRef, useCallback, useEffect, useRef, useState } from 'react' import {
ComponentPropsWithoutRef,
ForwardedRef,
ReactNode,
forwardRef,
useCallback,
useEffect,
useRef,
useState,
} from 'react'
import { CenterAlignBlock, JustifyAlignBlock, LeftAlignBlock, RightAlignBlock } from '../Blocks/Alignment' import { CenterAlignBlock, JustifyAlignBlock, LeftAlignBlock, RightAlignBlock } from '../Blocks/Alignment'
import { BulletedListBlock, ChecklistBlock, NumberedListBlock } from '../Blocks/List' import { BulletedListBlock, ChecklistBlock, NumberedListBlock } from '../Blocks/List'
import { CodeBlock } from '../Blocks/Code' import { CodeBlock } from '../Blocks/Code'
@@ -48,9 +57,10 @@ import { $isLinkTextNode } from './ToolbarLinkTextEditor'
import Popover from '@/Components/Popover/Popover' import Popover from '@/Components/Popover/Popover'
import LexicalTableOfContents from '@lexical/react/LexicalTableOfContents' import LexicalTableOfContents from '@lexical/react/LexicalTableOfContents'
import Menu from '@/Components/Menu/Menu' import Menu from '@/Components/Menu/Menu'
import MenuItem from '@/Components/Menu/MenuItem' import MenuItem, { MenuItemProps } from '@/Components/Menu/MenuItem'
import { remToPx } from '@/Utils' import { remToPx } from '@/Utils'
import FloatingLinkEditor from './FloatingLinkEditor' import FloatingLinkEditor from './FloatingLinkEditor'
import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator'
const TOGGLE_LINK_AND_EDIT_COMMAND = createCommand<string | null>('TOGGLE_LINK_AND_EDIT_COMMAND') const TOGGLE_LINK_AND_EDIT_COMMAND = createCommand<string | null>('TOGGLE_LINK_AND_EDIT_COMMAND')
@@ -69,16 +79,32 @@ const blockTypeToBlockName = {
quote: 'Quote', quote: 'Quote',
} }
const blockTypeToIconName = {
bullet: 'list-bulleted',
check: 'list-check',
code: 'code',
h1: 'h1',
h2: 'h2',
h3: 'h3',
h4: 'h4',
h5: 'h5',
h6: 'h6',
number: 'list-numbered',
paragraph: 'paragraph',
quote: 'quote',
}
interface ToolbarButtonProps extends ComponentPropsWithoutRef<'button'> { interface ToolbarButtonProps extends ComponentPropsWithoutRef<'button'> {
name: string name: string
active?: boolean active?: boolean
iconName: string iconName?: string
children?: ReactNode
onSelect: () => void onSelect: () => void
} }
const ToolbarButton = forwardRef( const ToolbarButton = forwardRef(
( (
{ name, active, iconName, onSelect, disabled, ...props }: ToolbarButtonProps, { name, active, iconName, children, onSelect, disabled, ...props }: ToolbarButtonProps,
ref: ForwardedRef<HTMLButtonElement>, ref: ForwardedRef<HTMLButtonElement>,
) => { ) => {
const [editor] = useLexicalComposerContext() const [editor] = useLexicalComposerContext()
@@ -105,11 +131,15 @@ const ToolbarButton = forwardRef(
active && 'bg-info text-info-contrast', active && 'bg-info text-info-contrast',
)} )}
> >
<Icon {children ? (
type={iconName} children
size="custom" ) : iconName ? (
className="h-4 w-4 !text-current md:h-3.5 md:w-3.5 [&>path]:!text-current" <Icon
/> type={iconName}
size="custom"
className="h-4 w-4 !text-current md:h-3.5 md:w-3.5 [&>path]:!text-current"
/>
) : null}
</div> </div>
</ToolbarItem> </ToolbarItem>
</StyledTooltip> </StyledTooltip>
@@ -117,6 +147,24 @@ const ToolbarButton = forwardRef(
}, },
) )
interface ToolbarMenuItemProps extends Omit<MenuItemProps, 'children'> {
name: string
iconName: string
active?: boolean
}
const ToolbarMenuItem = ({ name, iconName, active, ...props }: ToolbarMenuItemProps) => {
return (
<MenuItem
className={classNames('overflow-hidden md:py-2', active ? '!bg-info text-info-contrast' : 'hover:bg-contrast')}
{...props}
>
<Icon type={iconName} className="-mt-px mr-2.5 flex-shrink-0" />
<span className="overflow-hidden text-ellipsis whitespace-nowrap">{name}</span>
</MenuItem>
)
}
const ToolbarPlugin = () => { const ToolbarPlugin = () => {
const application = useApplication() const application = useApplication()
const isMobile = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm) const isMobile = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
@@ -149,6 +197,18 @@ const ToolbarPlugin = () => {
const [isTOCOpen, setIsTOCOpen] = useState(false) const [isTOCOpen, setIsTOCOpen] = useState(false)
const tocAnchorRef = useRef<HTMLButtonElement>(null) const tocAnchorRef = useRef<HTMLButtonElement>(null)
const [isTextFormatMenuOpen, setIsTextFormatMenuOpen] = useState(false)
const textFormatAnchorRef = useRef<HTMLButtonElement>(null)
const [isTextStyleMenuOpen, setIsTextStyleMenuOpen] = useState(false)
const textStyleAnchorRef = useRef<HTMLButtonElement>(null)
const [isAlignmentMenuOpen, setIsAlignmentMenuOpen] = useState(false)
const alignmentAnchorRef = useRef<HTMLButtonElement>(null)
const [isInsertMenuOpen, setIsInsertMenuOpen] = useState(false)
const insertAnchorRef = useRef<HTMLButtonElement>(null)
const [canUndo, setCanUndo] = useState(false) const [canUndo, setCanUndo] = useState(false)
const [canRedo, setCanRedo] = useState(false) const [canRedo, setCanRedo] = useState(false)
@@ -400,7 +460,7 @@ const ToolbarPlugin = () => {
<div <div
className={classNames( className={classNames(
'bg-contrast', 'bg-contrast',
'md:w-full md:border-b md:border-border md:px-2 md:py-1 md:translucent-ui:border-[--popover-border-color] md:translucent-ui:bg-[--popover-background-color] md:translucent-ui:[backdrop-filter:var(--popover-backdrop-filter)]', 'md:w-full md:border-b md:border-border md:px-1 md:py-1 md:translucent-ui:border-[--popover-border-color] md:translucent-ui:bg-[--popover-background-color] md:translucent-ui:[backdrop-filter:var(--popover-backdrop-filter)]',
!canShowToolbar || !isEditable ? 'hidden' : '', !canShowToolbar || !isEditable ? 'hidden' : '',
)} )}
id="super-mobile-toolbar" id="super-mobile-toolbar"
@@ -420,7 +480,7 @@ const ToolbarPlugin = () => {
)} )}
<div className="flex w-full flex-shrink-0 border-t border-border md:border-0"> <div className="flex w-full flex-shrink-0 border-t border-border md:border-0">
<Toolbar <Toolbar
className="super-toolbar flex items-center gap-1 overflow-x-auto px-1" className="super-toolbar flex items-center gap-1 overflow-x-auto px-1 md:flex-wrap"
ref={toolbarRef} ref={toolbarRef}
store={toolbarStore} store={toolbarStore}
> >
@@ -431,6 +491,11 @@ const ToolbarPlugin = () => {
onSelect={() => setIsTOCOpen(!isTOCOpen)} onSelect={() => setIsTOCOpen(!isTOCOpen)}
ref={tocAnchorRef} ref={tocAnchorRef}
/> />
<ToolbarButton
name="Search"
iconName="search"
onSelect={() => application.keyboardService.triggerCommand(SUPER_TOGGLE_SEARCH)}
/>
<ToolbarButton <ToolbarButton
name="Undo" name="Undo"
iconName="undo" iconName="undo"
@@ -461,12 +526,6 @@ const ToolbarPlugin = () => {
active={isUnderline} active={isUnderline}
onSelect={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'underline')} onSelect={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'underline')}
/> />
<ToolbarButton
name="Highlight"
iconName="draw"
active={isHighlight}
onSelect={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'highlight')}
/>
<ToolbarButton <ToolbarButton
name="Link" name="Link"
iconName="link" iconName="link"
@@ -475,24 +534,6 @@ const ToolbarPlugin = () => {
editor.dispatchCommand(TOGGLE_LINK_AND_EDIT_COMMAND, '') editor.dispatchCommand(TOGGLE_LINK_AND_EDIT_COMMAND, '')
}} }}
/> />
<ToolbarButton
name="Strikethrough"
iconName="strikethrough"
active={isStrikethrough}
onSelect={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'strikethrough')}
/>
<ToolbarButton
name="Subscript"
iconName="subscript"
active={isSubscript}
onSelect={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'subscript')}
/>
<ToolbarButton
name="Superscript"
iconName="superscript"
active={isSuperscript}
onSelect={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'superscript')}
/>
<ToolbarButton <ToolbarButton
name="Inline Code" name="Inline Code"
iconName="code-tags" iconName="code-tags"
@@ -500,127 +541,45 @@ const ToolbarPlugin = () => {
onSelect={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'code')} onSelect={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'code')}
/> />
<ToolbarButton <ToolbarButton
name="Search" name="Formatting options"
iconName="search" onSelect={() => {
onSelect={() => application.keyboardService.triggerCommand(SUPER_TOGGLE_SEARCH)} setIsTextFormatMenuOpen(!isTextFormatMenuOpen)
/> }}
ref={textFormatAnchorRef}
>
<Icon type="text" size="custom" className="h-4 w-4 md:h-3.5 md:w-3.5" />
<Icon type="chevron-down" size="custom" className="ml-1 h-4 w-4 md:h-3.5 md:w-3.5" />
</ToolbarButton>
<ToolbarButton <ToolbarButton
name={ParagraphBlock.name} name="Text style"
iconName={ParagraphBlock.iconName} onSelect={() => {
active={blockType === 'paragraph'} setIsTextStyleMenuOpen(!isTextStyleMenuOpen)
onSelect={() => ParagraphBlock.onSelect(editor)} }}
/> ref={textStyleAnchorRef}
>
<Icon type={blockTypeToIconName[blockType]} size="custom" className="h-4 w-4 md:h-3.5 md:w-3.5" />
<Icon type="chevron-down" size="custom" className="ml-2 h-4 w-4 md:h-3.5 md:w-3.5" />
</ToolbarButton>
<ToolbarButton <ToolbarButton
name={H1Block.name} name="Alignment"
iconName={H1Block.iconName} onSelect={() => {
active={blockType === 'h1'} setIsAlignmentMenuOpen(!isAlignmentMenuOpen)
onSelect={() => H1Block.onSelect(editor)} }}
/> ref={alignmentAnchorRef}
>
<Icon type="align-left" size="custom" className="h-4 w-4 md:h-3.5 md:w-3.5" />
<Icon type="chevron-down" size="custom" className="ml-2 h-4 w-4 md:h-3.5 md:w-3.5" />
</ToolbarButton>
<ToolbarButton <ToolbarButton
name={H2Block.name} name="Insert"
iconName={H2Block.iconName} onSelect={() => {
active={blockType === 'h2'} setIsInsertMenuOpen(!isInsertMenuOpen)
onSelect={() => H2Block.onSelect(editor)} }}
/> ref={insertAnchorRef}
<ToolbarButton >
name={H3Block.name} <Icon type="add" size="custom" className="h-4 w-4 md:h-3.5 md:w-3.5" />
iconName={H3Block.iconName} <Icon type="chevron-down" size="custom" className="ml-2 h-4 w-4 md:h-3.5 md:w-3.5" />
active={blockType === 'h3'} </ToolbarButton>
onSelect={() => H3Block.onSelect(editor)}
/>
<ToolbarButton
name={IndentBlock.name}
iconName={IndentBlock.iconName}
onSelect={() => IndentBlock.onSelect(editor)}
/>
<ToolbarButton
name={OutdentBlock.name}
iconName={OutdentBlock.iconName}
onSelect={() => OutdentBlock.onSelect(editor)}
/>
<ToolbarButton
name={LeftAlignBlock.name}
iconName={LeftAlignBlock.iconName}
active={elementFormat === 'left'}
onSelect={() => LeftAlignBlock.onSelect(editor)}
/>
<ToolbarButton
name={CenterAlignBlock.name}
iconName={CenterAlignBlock.iconName}
active={elementFormat === 'center'}
onSelect={() => CenterAlignBlock.onSelect(editor)}
/>
<ToolbarButton
name={RightAlignBlock.name}
iconName={RightAlignBlock.iconName}
active={elementFormat === 'right'}
onSelect={() => RightAlignBlock.onSelect(editor)}
/>
<ToolbarButton
name={JustifyAlignBlock.name}
iconName={JustifyAlignBlock.iconName}
active={elementFormat === 'justify'}
onSelect={() => JustifyAlignBlock.onSelect(editor)}
/>
<ToolbarButton
name={BulletedListBlock.name}
iconName={BulletedListBlock.iconName}
active={blockType === 'bullet'}
onSelect={() => BulletedListBlock.onSelect(editor)}
/>
<ToolbarButton
name={NumberedListBlock.name}
iconName={NumberedListBlock.iconName}
active={blockType === 'number'}
onSelect={() => NumberedListBlock.onSelect(editor)}
/>
<ToolbarButton
name={ChecklistBlock.name}
iconName={ChecklistBlock.iconName}
active={blockType === 'check'}
onSelect={() => ChecklistBlock.onSelect(editor)}
/>
<ToolbarButton
name="Table"
iconName="table"
onSelect={() =>
showModal('Insert Table', (onClose) => <InsertTableDialog activeEditor={editor} onClose={onClose} />)
}
/>
<ToolbarButton
name="Image from URL"
iconName="image"
onSelect={() =>
showModal('Insert image from URL', (onClose) => <InsertRemoteImageDialog onClose={onClose} />)
}
/>
<ToolbarButton
name={CodeBlock.name}
iconName={CodeBlock.iconName}
active={blockType === 'code'}
onSelect={() => CodeBlock.onSelect(editor)}
/>
<ToolbarButton
name={QuoteBlock.name}
iconName={QuoteBlock.iconName}
active={blockType === 'quote'}
onSelect={() => QuoteBlock.onSelect(editor)}
/>
<ToolbarButton
name={DividerBlock.name}
iconName={DividerBlock.iconName}
onSelect={() => DividerBlock.onSelect(editor)}
/>
<ToolbarButton
name={CollapsibleBlock.name}
iconName={CollapsibleBlock.iconName}
onSelect={() => CollapsibleBlock.onSelect(editor)}
/>
<ToolbarButton
name={PasswordBlock.name}
iconName={PasswordBlock.iconName}
onSelect={() => PasswordBlock.onSelect(editor)}
/>
</Toolbar> </Toolbar>
{isMobile && ( {isMobile && (
<button <button
@@ -638,7 +597,7 @@ const ToolbarPlugin = () => {
anchorElement={tocAnchorRef} anchorElement={tocAnchorRef}
open={isTOCOpen} open={isTOCOpen}
togglePopover={() => setIsTOCOpen(!isTOCOpen)} togglePopover={() => setIsTOCOpen(!isTOCOpen)}
side="bottom" side={isMobile ? 'top' : 'bottom'}
align="start" align="start"
className="py-1" className="py-1"
disableMobileFullscreenTakeover disableMobileFullscreenTakeover
@@ -686,6 +645,207 @@ const ToolbarPlugin = () => {
}} }}
</LexicalTableOfContents> </LexicalTableOfContents>
</Popover> </Popover>
<Popover
title="Text formatting options"
anchorElement={textFormatAnchorRef}
open={isTextFormatMenuOpen}
togglePopover={() => setIsTextFormatMenuOpen(!isTextFormatMenuOpen)}
side={isMobile ? 'top' : 'bottom'}
align="start"
className="py-1"
disableMobileFullscreenTakeover
disableFlip
containerClassName="md:!min-w-60 md:!w-auto"
>
<Menu
a11yLabel="Text formatting options"
isOpen
className="!px-0"
onClick={() => setIsTextFormatMenuOpen(false)}
>
<ToolbarMenuItem
name="Highlight"
iconName="draw"
active={isHighlight}
onClick={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'highlight')}
/>
<ToolbarMenuItem
name="Strikethrough"
iconName="strikethrough"
active={isStrikethrough}
onClick={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'strikethrough')}
/>
<ToolbarMenuItem
name="Subscript"
iconName="subscript"
active={isSubscript}
onClick={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'subscript')}
/>
<ToolbarMenuItem
name="Superscript"
iconName="superscript"
active={isSuperscript}
onClick={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'superscript')}
/>
</Menu>
</Popover>
<Popover
title="Text style"
anchorElement={textStyleAnchorRef}
open={isTextStyleMenuOpen}
togglePopover={() => setIsTextStyleMenuOpen(!isTextStyleMenuOpen)}
side={isMobile ? 'top' : 'bottom'}
align="start"
className="py-1"
disableMobileFullscreenTakeover
disableFlip
containerClassName="md:!min-w-60 md:!w-auto"
>
<Menu a11yLabel="Text style" isOpen className="!px-0" onClick={() => setIsTextStyleMenuOpen(false)}>
<ToolbarMenuItem
name="Normal"
iconName="paragraph"
active={blockType === 'paragraph'}
onClick={() => ParagraphBlock.onSelect(editor)}
/>
<ToolbarMenuItem
name="Heading 1"
iconName="h1"
active={blockType === 'h1'}
onClick={() => H1Block.onSelect(editor)}
/>
<ToolbarMenuItem
name="Heading 2"
iconName="h2"
active={blockType === 'h2'}
onClick={() => H2Block.onSelect(editor)}
/>
<ToolbarMenuItem
name="Heading 3"
iconName="h3"
active={blockType === 'h3'}
onClick={() => H3Block.onSelect(editor)}
/>
<MenuItemSeparator />
<ToolbarMenuItem
name="Bulleted List"
iconName="list-bulleted"
active={blockType === 'bullet'}
onClick={() => BulletedListBlock.onSelect(editor)}
/>
<ToolbarMenuItem
name="Numbered List"
iconName="list-numbered"
active={blockType === 'number'}
onClick={() => NumberedListBlock.onSelect(editor)}
/>
<ToolbarMenuItem
name="Check List"
iconName="list-check"
active={blockType === 'check'}
onClick={() => ChecklistBlock.onSelect(editor)}
/>
<MenuItemSeparator />
<ToolbarMenuItem
name="Quote"
iconName="quote"
active={blockType === 'quote'}
onClick={() => QuoteBlock.onSelect(editor)}
/>
<ToolbarMenuItem
name="Code Block"
iconName="code"
active={blockType === 'code'}
onClick={() => CodeBlock.onSelect(editor)}
/>
</Menu>
</Popover>
<Popover
title="Alignment"
anchorElement={alignmentAnchorRef}
open={isAlignmentMenuOpen}
togglePopover={() => setIsAlignmentMenuOpen(!isAlignmentMenuOpen)}
side={isMobile ? 'top' : 'bottom'}
align="start"
className="py-1"
disableMobileFullscreenTakeover
disableFlip
containerClassName="md:!min-w-60 md:!w-auto"
>
<Menu a11yLabel="Alignment" isOpen className="!px-0" onClick={() => setIsAlignmentMenuOpen(false)}>
<ToolbarMenuItem
name="Left align"
iconName="align-left"
active={elementFormat === 'left'}
onClick={() => LeftAlignBlock.onSelect(editor)}
/>
<ToolbarMenuItem
name="Center align"
iconName="align-center"
active={elementFormat === 'center'}
onClick={() => CenterAlignBlock.onSelect(editor)}
/>
<ToolbarMenuItem
name="Right align"
iconName="align-right"
active={elementFormat === 'right'}
onClick={() => RightAlignBlock.onSelect(editor)}
/>
<ToolbarMenuItem
name="Justify"
iconName="align-justify"
active={elementFormat === 'justify'}
onClick={() => JustifyAlignBlock.onSelect(editor)}
/>
<MenuItemSeparator />
<ToolbarMenuItem name="Indent" iconName="indent" onClick={() => IndentBlock.onSelect(editor)} />
<ToolbarMenuItem name="Outdent" iconName="outdent" onClick={() => OutdentBlock.onSelect(editor)} />
</Menu>
</Popover>
<Popover
title="Insert"
anchorElement={insertAnchorRef}
open={isInsertMenuOpen}
togglePopover={() => setIsInsertMenuOpen(!isInsertMenuOpen)}
side={isMobile ? 'top' : 'bottom'}
align="start"
className="py-1"
disableMobileFullscreenTakeover
disableFlip
containerClassName="md:!min-w-60 md:!w-auto"
>
<Menu a11yLabel="Insert" isOpen className="!px-0" onClick={() => setIsInsertMenuOpen(false)}>
<ToolbarMenuItem
name="Table"
iconName="table"
onClick={() =>
showModal('Insert Table', (onClose) => <InsertTableDialog activeEditor={editor} onClose={onClose} />)
}
/>
<ToolbarMenuItem
name="Image from URL"
iconName="image"
onClick={() =>
showModal('Insert image from URL', (onClose) => <InsertRemoteImageDialog onClose={onClose} />)
}
/>
<ToolbarMenuItem
name={DividerBlock.name}
iconName={DividerBlock.iconName}
onClick={() => DividerBlock.onSelect(editor)}
/>
<ToolbarMenuItem
name={CollapsibleBlock.name}
iconName={CollapsibleBlock.iconName}
onClick={() => CollapsibleBlock.onSelect(editor)}
/>
<ToolbarMenuItem
name={PasswordBlock.name}
iconName={PasswordBlock.iconName}
onClick={() => PasswordBlock.onSelect(editor)}
/>
</Menu>
</Popover>
</> </>
) )
} }