chore: consolidate super toolbar items and remove need for scroll [skip e2e]
This commit is contained in:
3
packages/icons/src/Icons/ic-list-check.svg
Normal file
3
packages/icons/src/Icons/ic-list-check.svg
Normal 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 |
@@ -35,6 +35,7 @@ import CheckBoldIcon from './ic-check-bold.svg'
|
||||
import CheckCircleFilledIcon from './ic-check-circle-filled.svg'
|
||||
import CheckCircleIcon from './ic-check-circle.svg'
|
||||
import CheckIcon from './ic-check.svg'
|
||||
import CheckListIcon from './ic-list-check.svg'
|
||||
import ChevronDownIcon from './ic-chevron-down.svg'
|
||||
import ChevronLeftIcon from './ic-chevron-left.svg'
|
||||
import ChevronRightIcon from './ic-chevron-right.svg'
|
||||
@@ -254,6 +255,7 @@ export {
|
||||
CheckCircleFilledIcon,
|
||||
CheckCircleIcon,
|
||||
CheckIcon,
|
||||
CheckListIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
|
||||
@@ -49,6 +49,7 @@ export const IconNameToSvgMapping = {
|
||||
'link-off': icons.LinkOffIcon,
|
||||
'list-bulleted': icons.ListBulleted,
|
||||
'list-numbered': icons.ListNumbered,
|
||||
'list-check': icons.CheckListIcon,
|
||||
'lock-filled': icons.LockFilledIcon,
|
||||
'menu-arrow-down-alt': icons.MenuArrowDownAlt,
|
||||
'menu-arrow-down': icons.MenuArrowDownIcon,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import {
|
||||
CSSProperties,
|
||||
ComponentPropsWithoutRef,
|
||||
forwardRef,
|
||||
KeyboardEventHandler,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
@@ -12,15 +11,11 @@ import { useListKeyboardNavigation } from '@/Hooks/useListKeyboardNavigation'
|
||||
import { mergeRefs } from '@/Hooks/mergeRefs'
|
||||
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||
|
||||
type MenuProps = {
|
||||
className?: string
|
||||
style?: CSSProperties | undefined
|
||||
interface MenuProps extends ComponentPropsWithoutRef<'menu'> {
|
||||
a11yLabel: string
|
||||
children: ReactNode
|
||||
closeMenu?: () => void
|
||||
isOpen: boolean
|
||||
initialFocus?: number
|
||||
onKeyDown?: KeyboardEventHandler<HTMLMenuElement>
|
||||
shouldAutoFocus?: boolean
|
||||
}
|
||||
|
||||
@@ -35,6 +30,7 @@ const Menu = forwardRef(
|
||||
initialFocus,
|
||||
onKeyDown,
|
||||
shouldAutoFocus = true,
|
||||
...props
|
||||
}: MenuProps,
|
||||
forwardedRef,
|
||||
) => {
|
||||
@@ -73,6 +69,7 @@ const Menu = forwardRef(
|
||||
ref={mergeRefs([menuElementRef, forwardedRef])}
|
||||
style={style}
|
||||
aria-label={a11yLabel}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</menu>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { PlatformedKeyboardShortcut } from '@standardnotes/ui-services'
|
||||
import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator'
|
||||
import MenuListItem from './MenuListItem'
|
||||
|
||||
type MenuItemProps = {
|
||||
export interface MenuItemProps extends ComponentPropsWithoutRef<'button'> {
|
||||
children: ReactNode
|
||||
onClick?: MouseEventHandler<HTMLButtonElement>
|
||||
onBlur?: (event: { relatedTarget: EventTarget | null }) => void
|
||||
@@ -17,7 +17,7 @@ type MenuItemProps = {
|
||||
tabIndex?: number
|
||||
disabled?: boolean
|
||||
shortcut?: PlatformedKeyboardShortcut
|
||||
} & ComponentPropsWithoutRef<'button'>
|
||||
}
|
||||
|
||||
const MenuItem = forwardRef(
|
||||
(
|
||||
|
||||
@@ -12,7 +12,7 @@ export const BulletedListBlock = {
|
||||
|
||||
export const ChecklistBlock = {
|
||||
name: 'Check List',
|
||||
iconName: 'check' as LexicalIconName,
|
||||
iconName: 'list-check' as LexicalIconName,
|
||||
keywords: ['check list', 'todo list'],
|
||||
onSelect: (editor: LexicalEditor) => editor.dispatchCommand(INSERT_CHECK_LIST_COMMAND, undefined),
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ const CodeOptionsPlugin = () => {
|
||||
|
||||
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
|
||||
label="Change code block language"
|
||||
items={CODE_LANGUAGE_OPTIONS.map(([value, label]) => ({
|
||||
|
||||
@@ -53,7 +53,7 @@ export const SearchDialog = ({ open, closeDialog }: { open: boolean; closeDialog
|
||||
|
||||
return (
|
||||
<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}
|
||||
>
|
||||
<button
|
||||
|
||||
@@ -25,7 +25,16 @@ import { mergeRegister, $findMatchingParent, $getNearestNodeOfType } from '@lexi
|
||||
import { $isLinkNode, $isAutoLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link'
|
||||
import { $isListNode, ListNode } from '@lexical/list'
|
||||
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 { BulletedListBlock, ChecklistBlock, NumberedListBlock } from '../Blocks/List'
|
||||
import { CodeBlock } from '../Blocks/Code'
|
||||
@@ -48,9 +57,10 @@ import { $isLinkTextNode } from './ToolbarLinkTextEditor'
|
||||
import Popover from '@/Components/Popover/Popover'
|
||||
import LexicalTableOfContents from '@lexical/react/LexicalTableOfContents'
|
||||
import Menu from '@/Components/Menu/Menu'
|
||||
import MenuItem from '@/Components/Menu/MenuItem'
|
||||
import MenuItem, { MenuItemProps } from '@/Components/Menu/MenuItem'
|
||||
import { remToPx } from '@/Utils'
|
||||
import FloatingLinkEditor from './FloatingLinkEditor'
|
||||
import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator'
|
||||
|
||||
const TOGGLE_LINK_AND_EDIT_COMMAND = createCommand<string | null>('TOGGLE_LINK_AND_EDIT_COMMAND')
|
||||
|
||||
@@ -69,16 +79,32 @@ const blockTypeToBlockName = {
|
||||
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'> {
|
||||
name: string
|
||||
active?: boolean
|
||||
iconName: string
|
||||
iconName?: string
|
||||
children?: ReactNode
|
||||
onSelect: () => void
|
||||
}
|
||||
|
||||
const ToolbarButton = forwardRef(
|
||||
(
|
||||
{ name, active, iconName, onSelect, disabled, ...props }: ToolbarButtonProps,
|
||||
{ name, active, iconName, children, onSelect, disabled, ...props }: ToolbarButtonProps,
|
||||
ref: ForwardedRef<HTMLButtonElement>,
|
||||
) => {
|
||||
const [editor] = useLexicalComposerContext()
|
||||
@@ -105,11 +131,15 @@ const ToolbarButton = forwardRef(
|
||||
active && 'bg-info text-info-contrast',
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
type={iconName}
|
||||
size="custom"
|
||||
className="h-4 w-4 !text-current md:h-3.5 md:w-3.5 [&>path]:!text-current"
|
||||
/>
|
||||
{children ? (
|
||||
children
|
||||
) : iconName ? (
|
||||
<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>
|
||||
</ToolbarItem>
|
||||
</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 application = useApplication()
|
||||
const isMobile = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
|
||||
@@ -149,6 +197,18 @@ const ToolbarPlugin = () => {
|
||||
const [isTOCOpen, setIsTOCOpen] = useState(false)
|
||||
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 [canRedo, setCanRedo] = useState(false)
|
||||
|
||||
@@ -400,7 +460,7 @@ const ToolbarPlugin = () => {
|
||||
<div
|
||||
className={classNames(
|
||||
'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' : '',
|
||||
)}
|
||||
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">
|
||||
<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}
|
||||
store={toolbarStore}
|
||||
>
|
||||
@@ -431,6 +491,11 @@ const ToolbarPlugin = () => {
|
||||
onSelect={() => setIsTOCOpen(!isTOCOpen)}
|
||||
ref={tocAnchorRef}
|
||||
/>
|
||||
<ToolbarButton
|
||||
name="Search"
|
||||
iconName="search"
|
||||
onSelect={() => application.keyboardService.triggerCommand(SUPER_TOGGLE_SEARCH)}
|
||||
/>
|
||||
<ToolbarButton
|
||||
name="Undo"
|
||||
iconName="undo"
|
||||
@@ -461,12 +526,6 @@ const ToolbarPlugin = () => {
|
||||
active={isUnderline}
|
||||
onSelect={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'underline')}
|
||||
/>
|
||||
<ToolbarButton
|
||||
name="Highlight"
|
||||
iconName="draw"
|
||||
active={isHighlight}
|
||||
onSelect={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'highlight')}
|
||||
/>
|
||||
<ToolbarButton
|
||||
name="Link"
|
||||
iconName="link"
|
||||
@@ -475,24 +534,6 @@ const ToolbarPlugin = () => {
|
||||
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
|
||||
name="Inline Code"
|
||||
iconName="code-tags"
|
||||
@@ -500,127 +541,45 @@ const ToolbarPlugin = () => {
|
||||
onSelect={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'code')}
|
||||
/>
|
||||
<ToolbarButton
|
||||
name="Search"
|
||||
iconName="search"
|
||||
onSelect={() => application.keyboardService.triggerCommand(SUPER_TOGGLE_SEARCH)}
|
||||
/>
|
||||
name="Formatting options"
|
||||
onSelect={() => {
|
||||
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
|
||||
name={ParagraphBlock.name}
|
||||
iconName={ParagraphBlock.iconName}
|
||||
active={blockType === 'paragraph'}
|
||||
onSelect={() => ParagraphBlock.onSelect(editor)}
|
||||
/>
|
||||
name="Text style"
|
||||
onSelect={() => {
|
||||
setIsTextStyleMenuOpen(!isTextStyleMenuOpen)
|
||||
}}
|
||||
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
|
||||
name={H1Block.name}
|
||||
iconName={H1Block.iconName}
|
||||
active={blockType === 'h1'}
|
||||
onSelect={() => H1Block.onSelect(editor)}
|
||||
/>
|
||||
name="Alignment"
|
||||
onSelect={() => {
|
||||
setIsAlignmentMenuOpen(!isAlignmentMenuOpen)
|
||||
}}
|
||||
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
|
||||
name={H2Block.name}
|
||||
iconName={H2Block.iconName}
|
||||
active={blockType === 'h2'}
|
||||
onSelect={() => H2Block.onSelect(editor)}
|
||||
/>
|
||||
<ToolbarButton
|
||||
name={H3Block.name}
|
||||
iconName={H3Block.iconName}
|
||||
active={blockType === 'h3'}
|
||||
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)}
|
||||
/>
|
||||
name="Insert"
|
||||
onSelect={() => {
|
||||
setIsInsertMenuOpen(!isInsertMenuOpen)
|
||||
}}
|
||||
ref={insertAnchorRef}
|
||||
>
|
||||
<Icon type="add" 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>
|
||||
</Toolbar>
|
||||
{isMobile && (
|
||||
<button
|
||||
@@ -638,7 +597,7 @@ const ToolbarPlugin = () => {
|
||||
anchorElement={tocAnchorRef}
|
||||
open={isTOCOpen}
|
||||
togglePopover={() => setIsTOCOpen(!isTOCOpen)}
|
||||
side="bottom"
|
||||
side={isMobile ? 'top' : 'bottom'}
|
||||
align="start"
|
||||
className="py-1"
|
||||
disableMobileFullscreenTakeover
|
||||
@@ -686,6 +645,207 @@ const ToolbarPlugin = () => {
|
||||
}}
|
||||
</LexicalTableOfContents>
|
||||
</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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user