From 04c8c6c52932df856a1389008c3de05339cf9ec5 Mon Sep 17 00:00:00 2001 From: Basile Parent Date: Tue, 23 Jul 2024 20:04:00 +0200 Subject: [PATCH 1/8] Refacto MultipleChoiceToggleGroup to match RadixUI specifications --- .../components/BoldItalicUnderlineToggles.tsx | 214 +++++----- src/plugins/toolbar/components/CodeToggle.tsx | 42 +- src/plugins/toolbar/components/UndoRedo.tsx | 111 ++--- src/plugins/toolbar/primitives/toolbar.tsx | 400 +++++++++--------- 4 files changed, 414 insertions(+), 353 deletions(-) diff --git a/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx b/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx index 4b70c1ac..73c01246 100644 --- a/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx +++ b/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx @@ -1,124 +1,144 @@ -import { applyFormat$, currentFormat$, iconComponentFor$, useTranslation } from '../../core' -import { useCellValues, usePublisher } from '@mdxeditor/gurx' -import React from 'react' -import { FORMAT, IS_BOLD, IS_ITALIC, IS_STRIKETHROUGH, IS_SUBSCRIPT, IS_SUPERSCRIPT, IS_UNDERLINE } from '../../../FormatConstants' -import { ToggleSingleGroupWithItem } from '.././primitives/toolbar' -import { TextFormatType } from 'lexical' +import {applyFormat$, currentFormat$, iconComponentFor$, useTranslation} from '../../core' +import {useCellValues, usePublisher} from '@mdxeditor/gurx' +import React, {useEffect, useState} from 'react' +import { + FORMAT, + IS_BOLD, + IS_ITALIC, + IS_STRIKETHROUGH, + IS_SUBSCRIPT, + IS_SUPERSCRIPT, + IS_UNDERLINE +} from '../../../FormatConstants' +import {MultipleChoiceToggleGroup, ToggleSingleGroupWithItem} from '.././primitives/toolbar' +import {TextFormatType} from 'lexical' import styles from '../../../styles/ui.module.css' -import { IconKey } from '../../../defaultSvgIcons' +import {IconKey} from '../../../defaultSvgIcons' interface FormatButtonProps { - format: FORMAT - addTitle: string - removeTitle: string - icon: IconKey - formatName: TextFormatType + format: FORMAT + addTitle: string + removeTitle: string + icon: IconKey + formatName: TextFormatType } -const FormatButton: React.FC = ({ format, addTitle, removeTitle, icon, formatName }) => { - const [currentFormat, iconComponentFor] = useCellValues(currentFormat$, iconComponentFor$) - const applyFormat = usePublisher(applyFormat$) - const active = (currentFormat & format) !== 0 +const FormatButton: React.FC = ({format, addTitle, removeTitle, icon, formatName}) => { + const [currentFormat, iconComponentFor] = useCellValues(currentFormat$, iconComponentFor$) + const applyFormat = usePublisher(applyFormat$) + const active = (currentFormat & format) !== 0 - return ( - { - applyFormat(formatName) - }} - > - {iconComponentFor(icon)} - - ) + return ( + { + applyFormat(formatName) + }} + > + {iconComponentFor(icon)} + + ) } export interface BoldItalicUnderlineTogglesProps { - options?: ('Bold' | 'Italic' | 'Underline')[] + options?: ('Bold' | 'Italic' | 'Underline')[] } /** * A toolbar component that lets the user toggle bold, italic and underline formatting. * @group Toolbar Components */ -export const BoldItalicUnderlineToggles: React.FC = ({ options }) => { - const t = useTranslation() +export const BoldItalicUnderlineToggles: React.FC = ({options}) => { + const t = useTranslation() + const [currentFormat, iconComponentFor] = useCellValues(currentFormat$, iconComponentFor$) + const applyFormat = usePublisher(applyFormat$) - const showAllButtons = typeof options === 'undefined' - return ( -
- {showAllButtons || options.includes('Bold') ? ( - - ) : null} - {showAllButtons || options.includes('Italic') ? ( - - ) : null} - {showAllButtons || options.includes('Underline') ? ( - - ) : null} -
- ) + const showAllButtons = typeof options === 'undefined' + const isBold = (currentFormat & IS_BOLD) !== 0 + const isItalic = (currentFormat & IS_ITALIC) !== 0 + const isUnderline = (currentFormat & IS_UNDERLINE) !== 0 + + const [appliedFormats, setAppliedFormats] = useState([]) + useEffect(() => { + setAppliedFormats( + [ + isBold ? 'bold' : null, + isItalic ? 'italic' : null, + isUnderline ? 'underline' : null + ].filter((f) => !!f) as TextFormatType[] + ) + }, [currentFormat]) + const handleApplyFormatDiff = (diff: string[]) => (diff as TextFormatType[]).forEach(applyFormat) + + return ( +
+ +
+ ) } export interface StrikeThroughSupSubTogglesProps { - options?: ('Strikethrough' | 'Sub' | 'Sup')[] + options?: ('Strikethrough' | 'Sub' | 'Sup')[] } /** * A toolbar component that lets the user toggle strikeThrough, superscript and subscript formatting. * @group Toolbar Components */ -export const StrikeThroughSupSubToggles: React.FC = ({ options }) => { - const t = useTranslation() - const showAllButtons = typeof options === 'undefined' +export const StrikeThroughSupSubToggles: React.FC = ({options}) => { + const t = useTranslation() + const showAllButtons = typeof options === 'undefined' - return ( -
- {showAllButtons || options.includes('Strikethrough') ? ( - - ) : null} - {showAllButtons || options.includes('Sup') ? ( - - ) : null} - {showAllButtons || options.includes('Sub') ? ( - - ) : null} -
- ) + return ( +
+ {showAllButtons || options.includes('Strikethrough') ? ( + + ) : null} + {showAllButtons || options.includes('Sup') ? ( + + ) : null} + {showAllButtons || options.includes('Sub') ? ( + + ) : null} +
+ ) } diff --git a/src/plugins/toolbar/components/CodeToggle.tsx b/src/plugins/toolbar/components/CodeToggle.tsx index 643f813f..962d48c5 100644 --- a/src/plugins/toolbar/components/CodeToggle.tsx +++ b/src/plugins/toolbar/components/CodeToggle.tsx @@ -1,26 +1,38 @@ -import React from 'react' -import { IS_CODE } from '../../../FormatConstants' -import { applyFormat$, currentFormat$, iconComponentFor$, useTranslation } from '../../core' -import { MultipleChoiceToggleGroup } from '.././primitives/toolbar' -import { useCellValues, usePublisher } from '@mdxeditor/gurx' +import React, {useEffect, useState} from 'react' +import {IS_CODE} from '../../../FormatConstants' +import {applyFormat$, currentFormat$, iconComponentFor$, useTranslation} from '../../core' +import {MultipleChoiceToggleGroup} from '.././primitives/toolbar' +import {useCellValues, usePublisher} from '@mdxeditor/gurx' +import {TextFormatType} from "lexical"; +1 /** * A toolbar component that lets the user toggle code formatting. * Use for inline `code` elements (like variables, methods, etc). * @group Toolbar Components */ export const CodeToggle: React.FC = () => { - const [currentFormat, iconComponentFor] = useCellValues(currentFormat$, iconComponentFor$) - const applyFormat = usePublisher(applyFormat$) - const t = useTranslation() + const [currentFormat, iconComponentFor] = useCellValues(currentFormat$, iconComponentFor$) + const applyFormat = usePublisher(applyFormat$) + const t = useTranslation() - const codeIsOn = (currentFormat & IS_CODE) !== 0 + const codeIsOn = (currentFormat & IS_CODE) !== 0 - const title = codeIsOn ? t('toolbar.removeInlineCode', 'Remove code format') : t('toolbar.inlineCode', 'Inline code format') + const title = codeIsOn ? t('toolbar.removeInlineCode', 'Remove code format') : t('toolbar.inlineCode', 'Inline code format') - return ( - - ) + const [appliedFormats, setAppliedFormats] = useState([]) + useEffect(() => { + setAppliedFormats([codeIsOn ? 'code' : null].filter((f) => !!f) as string[]) + }, [currentFormat]) + + const handleApplyFormatDiff = (diff: string[]) => (diff as TextFormatType[]).forEach(applyFormat) + + return ( + + ) } diff --git a/src/plugins/toolbar/components/UndoRedo.tsx b/src/plugins/toolbar/components/UndoRedo.tsx index db3fca2e..741018d4 100644 --- a/src/plugins/toolbar/components/UndoRedo.tsx +++ b/src/plugins/toolbar/components/UndoRedo.tsx @@ -1,62 +1,71 @@ -import { mergeRegister } from '@lexical/utils' -import { useCellValues } from '@mdxeditor/gurx' -import { CAN_REDO_COMMAND, CAN_UNDO_COMMAND, COMMAND_PRIORITY_CRITICAL, REDO_COMMAND, UNDO_COMMAND } from 'lexical' +import {mergeRegister} from '@lexical/utils' +import {useCellValues} from '@mdxeditor/gurx' +import {CAN_REDO_COMMAND, CAN_UNDO_COMMAND, COMMAND_PRIORITY_CRITICAL, REDO_COMMAND, UNDO_COMMAND} from 'lexical' import React from 'react' -import { IS_APPLE } from '../../../utils/detectMac' -import { activeEditor$, iconComponentFor$, useTranslation } from '../../core' -import { MultipleChoiceToggleGroup } from '.././primitives/toolbar' +import {IS_APPLE} from '../../../utils/detectMac' +import {activeEditor$, iconComponentFor$, useTranslation} from '../../core' +import {MultipleChoiceToggleGroup} from '.././primitives/toolbar' /** * A toolbar component that lets the user undo and redo changes in the editor. * @group Toolbar Components */ export const UndoRedo: React.FC = () => { - const [iconComponentFor, activeEditor] = useCellValues(iconComponentFor$, activeEditor$) - const [canUndo, setCanUndo] = React.useState(false) - const [canRedo, setCanRedo] = React.useState(false) - const t = useTranslation() + const [iconComponentFor, activeEditor] = useCellValues(iconComponentFor$, activeEditor$) + const [canUndo, setCanUndo] = React.useState(false) + const [canRedo, setCanRedo] = React.useState(false) + const t = useTranslation() - React.useEffect(() => { - if (activeEditor) { - return mergeRegister( - activeEditor.registerCommand( - CAN_UNDO_COMMAND, - (payload) => { - setCanUndo(payload) - return false - }, - COMMAND_PRIORITY_CRITICAL - ), - activeEditor.registerCommand( - CAN_REDO_COMMAND, - (payload) => { - setCanRedo(payload) - return false - }, - COMMAND_PRIORITY_CRITICAL - ) - ) - } - }, [activeEditor]) + React.useEffect(() => { + if (activeEditor) { + return mergeRegister( + activeEditor.registerCommand( + CAN_UNDO_COMMAND, + (payload) => { + setCanUndo(payload) + return false + }, + COMMAND_PRIORITY_CRITICAL + ), + activeEditor.registerCommand( + CAN_REDO_COMMAND, + (payload) => { + setCanRedo(payload) + return false + }, + COMMAND_PRIORITY_CRITICAL + ) + ) + } + }, [activeEditor]) - return ( - activeEditor?.dispatchCommand(UNDO_COMMAND, undefined) - }, - { - title: t('toolbar.redo', 'Redo {{shortcut}}', { shortcut: IS_APPLE ? '⌘Y' : 'Ctrl+Y' }), - disabled: !canRedo, - contents: iconComponentFor('redo'), - active: false, - onChange: () => activeEditor?.dispatchCommand(REDO_COMMAND, undefined) + const handleValueChange = (newValue: string[]) => { + const action = newValue[0] + if (action === 'undo') { + activeEditor?.dispatchCommand(UNDO_COMMAND, undefined) + } else { + activeEditor?.dispatchCommand(REDO_COMMAND, undefined) } - ]} - /> - ) + } + + return ( + + ) } diff --git a/src/plugins/toolbar/primitives/toolbar.tsx b/src/plugins/toolbar/primitives/toolbar.tsx index 6916363a..c9eb29b0 100644 --- a/src/plugins/toolbar/primitives/toolbar.tsx +++ b/src/plugins/toolbar/primitives/toolbar.tsx @@ -3,10 +3,11 @@ import * as RadixToolbar from '@radix-ui/react-toolbar' import classNames from 'classnames' import React from 'react' import styles from '../../../styles/ui.module.css' -import { TooltipWrap } from './TooltipWrap' -import { SelectButtonTrigger, SelectContent, SelectItem } from './select' -import { EditorInFocus, editorInFocus$, readOnly$, useTranslation } from '../../core' -import { useCellValue } from '@mdxeditor/gurx' +import {TooltipWrap} from './TooltipWrap' +import {SelectButtonTrigger, SelectContent, SelectItem} from './select' +import {EditorInFocus, editorInFocus$, readOnly$, useTranslation} from '../../core' +import {useCellValue} from '@mdxeditor/gurx' +import {TextFormatType} from "lexical"; // // function decorate

(Component: React.ComponentType

, decoratedProps: P) { @@ -18,44 +19,44 @@ import { useCellValue } from '@mdxeditor/gurx' // function decorateWithRef

( - Component: React.ForwardRefExoticComponent

, - decoratedProps: Partial> & { 'data-toolbar-item'?: boolean } + Component: React.ForwardRefExoticComponent

, + decoratedProps: Partial> & { 'data-toolbar-item'?: boolean } ) { - return React.forwardRef((props: P, ref) => { - const className = classNames(decoratedProps.className, props.className) - return - }) + return React.forwardRef((props: P, ref) => { + const className = classNames(decoratedProps.className, props.className) + return + }) } function addTooltipToChildren>(Component: C) { - return ({ title, children, ...props }: React.ComponentProps & { title: string }) => { - return ( - - {children} - - ) - } + return ({title, children, ...props}: React.ComponentProps & { title: string }) => { + return ( + + {children} + + ) + } } /** * @internal */ -export const Root: React.FC<{ readOnly: boolean; children: React.ReactNode }> = ({ readOnly, children }) => { - return ( - - {children} - - ) +export const Root: React.FC<{ readOnly: boolean; children: React.ReactNode }> = ({readOnly, children}) => { + return ( + + {children} + + ) } /** * A toolbar button primitive. * @group Toolbar Primitives */ -export const Button = decorateWithRef(RadixToolbar.Button, { className: styles.toolbarButton, 'data-toolbar-item': true }) +export const Button = decorateWithRef(RadixToolbar.Button, {className: styles.toolbarButton, 'data-toolbar-item': true}) /** * A toolbar button with tooltip primitive. @@ -67,113 +68,132 @@ export const ButtonWithTooltip = addTooltipToChildren(Button) * @internal */ export const ToolbarToggleItem = decorateWithRef(RadixToolbar.ToggleItem, { - className: styles.toolbarToggleItem, - 'data-toolbar-item': true + className: styles.toolbarToggleItem, + 'data-toolbar-item': true }) /** * @internal */ export const SingleToggleGroup = decorateWithRef(RadixToolbar.ToggleGroup, { - type: 'single', - className: styles.toolbarToggleSingleGroup + type: 'single', + className: styles.toolbarToggleSingleGroup }) /** * @internal */ export const ToggleSingleGroupWithItem = React.forwardRef< - HTMLDivElement, - Omit & { on: boolean; title: string; disabled?: boolean } ->(({ on, title, children, disabled, ...props }, forwardedRef) => { - return ( - - - {children} - - - ) + HTMLDivElement, + Omit & { on: boolean; title: string; disabled?: boolean } +>(({on, title, children, disabled, ...props}, forwardedRef) => { + return ( + + + {children} + + + ) }) /** * A toolbar primitive that allows you to build an UI with multiple non-exclusive toggle groups, like the bold/italic/underline toggle. * @group Toolbar Primitives */ -export const MultipleChoiceToggleGroup: React.FC<{ - items: { - title: string - contents: React.ReactNode - active: boolean - onChange: (active: boolean) => void - disabled?: boolean - }[] -}> = ({ items }) => { - return ( -

- {items.map((item, index) => ( - { - item.onChange(v === 'on') - }} - disabled={item.disabled} - > - {item.contents} - - ))} -
- ) +export const MultipleChoiceToggleGroup: React.FC< + Omit & { + onValueChangeDiff?: (diff: string[]) => void + items: { + title: string + contents: React.ReactNode + value: string + disabled?: boolean + }[] +}> = ({items, value, onValueChange, onValueChangeDiff, ...otherProps}) => { + + const handleValueChange = (newValue: string[]) => { + if (onValueChangeDiff) { + const diff = [ + ...newValue.filter((format) => !(value || []).includes(format)), + ...(value || []).filter((existingFormat) => !newValue.includes(existingFormat)), + ] + onValueChangeDiff(diff) + } + + onValueChange?.(newValue) + } + + return ( +
+ + {items.map((item, index) => ( + + {item.contents} + + ))} + +
+ ) } + /** * A toolbar primitive that allows you to build an UI with multiple exclusive toggle groups, like the list type toggle. * @group Toolbar Primitives */ export const SingleChoiceToggleGroup = ({ - value, - onChange, - className, - items -}: { - items: { - title: string - value: T - contents: React.ReactNode - }[] - onChange: (value: T | '') => void - value: T | '' - className?: string + value, + onChange, + className, + items + }: { + items: { + title: string + value: T + contents: React.ReactNode + }[] + onChange: (value: T | '') => void + value: T | '' + className?: string }) => { - const t = useTranslation() + const t = useTranslation() - return ( -
- { - e.preventDefault() - }} - > - {items.map((item, index) => ( - - {item.contents} - - ))} - -
- ) + return ( +
+ { + e.preventDefault() + }} + > + {items.map((item, index) => ( + + {item.contents} + + ))} + +
+ ) } /** @@ -181,61 +201,61 @@ export const SingleChoiceToggleGroup = ({ * @group Toolbar Primitives */ export const ButtonOrDropdownButton = (props: { - /** - * The contents of the button - usually an icon. - */ - children: React.ReactNode - /** - * The title used for the tooltip. - */ - title: string - /** - * The function to execute when the button is clicked or an item is chosen from the dropdown. - * If there is only one item in the dropdown, the value will be an empty string. - */ - onChoose: (value: T) => void - /** - * The items to show in the dropdown. - */ - items: { /** - * The value to pass to the `onChoose` function when this item is chosen. + * The contents of the button - usually an icon. + */ + children: React.ReactNode + /** + * The title used for the tooltip. + */ + title: string + /** + * The function to execute when the button is clicked or an item is chosen from the dropdown. + * If there is only one item in the dropdown, the value will be an empty string. */ - value: T + onChoose: (value: T) => void /** - * The label to show in the dropdown. + * The items to show in the dropdown. */ - label: string | JSX.Element - }[] + items: { + /** + * The value to pass to the `onChoose` function when this item is chosen. + */ + value: T + /** + * The label to show in the dropdown. + */ + label: string | JSX.Element + }[] }) => { - const readOnly = useCellValue(readOnly$) - return ( - <> - {props.items.length === 1 ? ( - { - props.onChoose('' as T) - }} - disabled={readOnly} - > - {props.children} - - ) : ( - - {props.children} + const readOnly = useCellValue(readOnly$) + return ( + <> + {props.items.length === 1 ? ( + { + props.onChoose('' as T) + }} + disabled={readOnly} + > + {props.children} + + ) : ( + + {props.children} - - {props.items.map((item, index) => ( - - {item.label} - - ))} - - - )} - - ) + + {props.items.map((item, index) => ( + + {item.label} + + ))} + + + )} + + ) } /** @@ -243,14 +263,14 @@ export const ButtonOrDropdownButton = (props: { * @group Toolbar Primitives */ export interface ConditionalContentsOption { - /** - * A function that returns `true` if the option should be displayed for the current editor in focus. - */ - when: (rootNode: EditorInFocus | null) => boolean - /** - * The contents to display if the `when` function returns `true`. - */ - contents: () => React.ReactNode + /** + * A function that returns `true` if the option should be displayed for the current editor in focus. + */ + when: (rootNode: EditorInFocus | null) => boolean + /** + * The contents to display if the `when` function returns `true`. + */ + contents: () => React.ReactNode } /** @@ -258,14 +278,14 @@ export interface ConditionalContentsOption { * @group Toolbar Primitives */ export interface FallbackOption { - /** - * The contents to display - */ - fallback: () => React.ReactNode + /** + * The contents to display + */ + fallback: () => React.ReactNode } function isConditionalContentsOption(option: ConditionalContentsOption | FallbackOption): option is ConditionalContentsOption { - return Object.hasOwn(option, 'when') + return Object.hasOwn(option, 'when') } /** @@ -292,28 +312,28 @@ function isConditionalContentsOption(option: ConditionalContentsOption | Fallbac * @group Toolbar Primitives */ export const ConditionalContents: React.FC<{ - /** - * A set of options that define the contents to show based on the editor that is in focus. - * Can be either a {@link ConditionalContentsOption} or a {@link FallbackOption}. - * See the {@link ConditionalContents} documentation for an example. - */ - options: (ConditionalContentsOption | FallbackOption)[] -}> = ({ options }) => { - const editorInFocus = useCellValue(editorInFocus$) - const contents = React.useMemo(() => { - const option = options.find((option) => { - if (isConditionalContentsOption(option)) { - if (option.when(editorInFocus)) { - return true - } - } else { - return true - } - }) - return option ? (isConditionalContentsOption(option) ? option.contents() : option.fallback()) : null - }, [options, editorInFocus]) + /** + * A set of options that define the contents to show based on the editor that is in focus. + * Can be either a {@link ConditionalContentsOption} or a {@link FallbackOption}. + * See the {@link ConditionalContents} documentation for an example. + */ + options: (ConditionalContentsOption | FallbackOption)[] +}> = ({options}) => { + const editorInFocus = useCellValue(editorInFocus$) + const contents = React.useMemo(() => { + const option = options.find((option) => { + if (isConditionalContentsOption(option)) { + if (option.when(editorInFocus)) { + return true + } + } else { + return true + } + }) + return option ? (isConditionalContentsOption(option) ? option.contents() : option.fallback()) : null + }, [options, editorInFocus]) - return
{contents}
+ return
{contents}
} /** From bf8ae27a1b7c2b4eb278c4187f114d5c57e803e3 Mon Sep 17 00:00:00 2001 From: Basile Parent Date: Tue, 23 Jul 2024 20:53:00 +0200 Subject: [PATCH 2/8] Group sup/sub formatting inside the same group --- locales/en/translation.json | 1 + .../components/BoldItalicUnderlineToggles.tsx | 100 ++++++++++++------ .../toolbar/components/ListsToggle.tsx | 48 +++++---- src/plugins/toolbar/primitives/toolbar.tsx | 4 +- 4 files changed, 100 insertions(+), 53 deletions(-) diff --git a/locales/en/translation.json b/locales/en/translation.json index 0f422f44..3f048b81 100644 --- a/locales/en/translation.json +++ b/locales/en/translation.json @@ -59,6 +59,7 @@ "placeholder": "Block type" }, "toggleGroup":"toggle group", + "supSubRadio": "Superscript / subscript formatting", "removeBold": "Remove bold", "bold": "Bold", "removeItalic": "Remove italic", diff --git a/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx b/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx index 73c01246..17a696b1 100644 --- a/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx +++ b/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx @@ -10,7 +10,7 @@ import { IS_SUPERSCRIPT, IS_UNDERLINE } from '../../../FormatConstants' -import {MultipleChoiceToggleGroup, ToggleSingleGroupWithItem} from '.././primitives/toolbar' +import {MultipleChoiceToggleGroup, SingleChoiceToggleGroup, ToggleSingleGroupWithItem} from '.././primitives/toolbar' import {TextFormatType} from 'lexical' import styles from '../../../styles/ui.module.css' import {IconKey} from '../../../defaultSvgIcons' @@ -102,43 +102,83 @@ export interface StrikeThroughSupSubTogglesProps { options?: ('Strikethrough' | 'Sub' | 'Sup')[] } +const SupSubRadioItems = { + Sup: { + icon: 'superscript', + formatName: 'superscript' as TextFormatType, + titleFormatKey: 'toolbar.superscript', + titleFormatDefaultTranslation: 'Superscript', + titleRemoveFormatKey: 'toolbar.removeSuperscript', + titleRemoveFormatDefaultTranslation: 'Remove superscript', + }, + Sub: { + icon: 'subscript', + formatName: 'subscript' as TextFormatType, + titleFormatKey: 'toolbar.subscript', + titleFormatDefaultTranslation: 'Subscript', + titleRemoveFormatKey: 'toolbar.removeSubscript', + titleRemoveFormatDefaultTranslation: 'Remove subscript', + } +} +const isSupSubRadioItemsKey = (key: string): key is keyof typeof SupSubRadioItems => Object.keys(SupSubRadioItems).includes(key) /** * A toolbar component that lets the user toggle strikeThrough, superscript and subscript formatting. * @group Toolbar Components */ export const StrikeThroughSupSubToggles: React.FC = ({options}) => { const t = useTranslation() + const [currentFormat, iconComponentFor] = useCellValues(currentFormat$, iconComponentFor$) + const applyFormat = usePublisher(applyFormat$) + + const isSup = (currentFormat & IS_SUPERSCRIPT) !== 0 + const isSub = (currentFormat & IS_SUBSCRIPT) !== 0 + + const [radioValue, setRadioValue] = useState('') + const radioItems = (options || Object.keys(SupSubRadioItems)) + .filter(isSupSubRadioItemsKey) + .map((type) => { + const item = SupSubRadioItems[type] + return ({ + value: type, + title: radioValue !== type ? t(item.titleFormatKey, item.titleFormatDefaultTranslation) : t(item.titleRemoveFormatKey, item.titleRemoveFormatDefaultTranslation), + contents: iconComponentFor(item.icon as IconKey) + }); + }) + useEffect(() => { + if (isSup) { + setRadioValue("Sup") + } else if (isSub) { + setRadioValue("Sub") + } else { + setRadioValue('') + } + }, [currentFormat]) + + const handleRadioItemChange = (newValue: keyof typeof SupSubRadioItems | '') => { + if (newValue) { + applyFormat(SupSubRadioItems[newValue].formatName) + } else if (radioValue) { + applyFormat(SupSubRadioItems[radioValue].formatName) + } + setRadioValue(newValue) + } + const showAllButtons = typeof options === 'undefined' return ( -
- {showAllButtons || options.includes('Strikethrough') ? ( - - ) : null} - {showAllButtons || options.includes('Sup') ? ( - - ) : null} - {showAllButtons || options.includes('Sub') ? ( - - ) : null} -
+ <> +
+ {showAllButtons || options.includes('Strikethrough') ? ( + + ) : null} +
+ + ) } diff --git a/src/plugins/toolbar/components/ListsToggle.tsx b/src/plugins/toolbar/components/ListsToggle.tsx index efdcc1f0..4a18537e 100644 --- a/src/plugins/toolbar/components/ListsToggle.tsx +++ b/src/plugins/toolbar/components/ListsToggle.tsx @@ -1,13 +1,13 @@ import React from 'react' -import { applyListType$, currentListType$ } from '../../lists' -import { SingleChoiceToggleGroup } from '.././primitives/toolbar' -import { useCellValues, usePublisher } from '@mdxeditor/gurx' -import { iconComponentFor$, useTranslation } from '../../core' +import {applyListType$, currentListType$} from '../../lists' +import {SingleChoiceToggleGroup} from '.././primitives/toolbar' +import {useCellValues, usePublisher} from '@mdxeditor/gurx' +import {iconComponentFor$, useTranslation} from '../../core' const ICON_NAME_MAP = { - bullet: 'format_list_bulleted', - number: 'format_list_numbered', - check: 'format_list_checked' + bullet: 'format_list_bulleted', + number: 'format_list_numbered', + check: 'format_list_checked' } as const /** @@ -17,22 +17,26 @@ const ICON_NAME_MAP = { * @group Toolbar Components * @param options - The list types that the user can toggle between. Defaults to `['bullet', 'number', 'check']`. */ -export const ListsToggle: React.FC<{ options?: ('bullet' | 'number' | 'check')[] }> = ({ options = ['bullet', 'number', 'check'] }) => { - const [currentListType, iconComponentFor] = useCellValues(currentListType$, iconComponentFor$) - const applyListType = usePublisher(applyListType$) - const t = useTranslation() +export const ListsToggle: React.FC<{ + options?: ('bullet' | 'number' | 'check')[] +}> = ({options = ['bullet', 'number', 'check']}) => { + const [currentListType, iconComponentFor] = useCellValues(currentListType$, iconComponentFor$) + const applyListType = usePublisher(applyListType$) + const t = useTranslation() - const LIST_TITLE_MAP = { - bullet: t('toolbar.bulletedList', 'Bulleted list'), - number: t('toolbar.numberedList', 'Numbered list'), - check: t('toolbar.checkList', 'Check list') - } as const + const LIST_TITLE_MAP = { + bullet: t('toolbar.bulletedList', 'Bulleted list'), + number: t('toolbar.numberedList', 'Numbered list'), + check: t('toolbar.checkList', 'Check list') + } as const - const items = options.map((type) => ({ - value: type, - title: LIST_TITLE_MAP[type], - contents: iconComponentFor(ICON_NAME_MAP[type]) - })) + const items = options.map((type) => ({ + value: type, + title: LIST_TITLE_MAP[type], + contents: iconComponentFor(ICON_NAME_MAP[type]) + })) - return + return } diff --git a/src/plugins/toolbar/primitives/toolbar.tsx b/src/plugins/toolbar/primitives/toolbar.tsx index c9eb29b0..775f1b44 100644 --- a/src/plugins/toolbar/primitives/toolbar.tsx +++ b/src/plugins/toolbar/primitives/toolbar.tsx @@ -161,6 +161,7 @@ export const SingleChoiceToggleGroup = ({ value, onChange, className, + "aria-label": ariaLabel, items }: { items: { @@ -168,6 +169,7 @@ export const SingleChoiceToggleGroup = ({ value: T contents: React.ReactNode }[] + "aria-label": string onChange: (value: T | '') => void value: T | '' className?: string @@ -177,7 +179,7 @@ export const SingleChoiceToggleGroup = ({ return (
Date: Tue, 23 Jul 2024 20:55:26 +0200 Subject: [PATCH 3/8] Fix aria-label --- src/plugins/toolbar/components/DiffSourceToggleWrapper.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/toolbar/components/DiffSourceToggleWrapper.tsx b/src/plugins/toolbar/components/DiffSourceToggleWrapper.tsx index 2b06186e..6ec56e10 100644 --- a/src/plugins/toolbar/components/DiffSourceToggleWrapper.tsx +++ b/src/plugins/toolbar/components/DiffSourceToggleWrapper.tsx @@ -56,6 +56,7 @@ export const DiffSourceToggleWrapper: React.FC<{ children: React.ReactNode; opti
Date: Wed, 24 Jul 2024 14:51:35 +0200 Subject: [PATCH 4/8] Formatting --- package-lock.json | 25 +- .../components/BoldItalicUnderlineToggles.tsx | 301 +++++++++--------- src/plugins/toolbar/components/CodeToggle.tsx | 49 ++- .../toolbar/components/ListsToggle.tsx | 55 ++-- src/plugins/toolbar/components/UndoRedo.tsx | 118 +++---- 5 files changed, 268 insertions(+), 280 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5d969651..6c5f7d7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18242,31 +18242,8 @@ "optional": true }, "node_modules/npm/node_modules/semver": { - "version": "7.6.0", "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } + "inBundle": true }, "node_modules/npm/node_modules/set-blocking": { "version": "2.0.0", diff --git a/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx b/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx index 17a696b1..da4e92fa 100644 --- a/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx +++ b/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx @@ -1,184 +1,191 @@ -import {applyFormat$, currentFormat$, iconComponentFor$, useTranslation} from '../../core' -import {useCellValues, usePublisher} from '@mdxeditor/gurx' -import React, {useEffect, useState} from 'react' -import { - FORMAT, - IS_BOLD, - IS_ITALIC, - IS_STRIKETHROUGH, - IS_SUBSCRIPT, - IS_SUPERSCRIPT, - IS_UNDERLINE -} from '../../../FormatConstants' -import {MultipleChoiceToggleGroup, SingleChoiceToggleGroup, ToggleSingleGroupWithItem} from '.././primitives/toolbar' -import {TextFormatType} from 'lexical' +import { applyFormat$, currentFormat$, iconComponentFor$, useTranslation } from '../../core' +import { useCellValues, usePublisher } from '@mdxeditor/gurx' +import React from 'react' +import { FORMAT, IS_BOLD, IS_ITALIC, IS_STRIKETHROUGH, IS_SUBSCRIPT, IS_SUPERSCRIPT, IS_UNDERLINE } from '../../../FormatConstants' +import { ToggleSingleGroupWithItem } from '.././primitives/toolbar' +import { TextFormatType } from 'lexical' import styles from '../../../styles/ui.module.css' -import {IconKey} from '../../../defaultSvgIcons' +import { IconKey } from '../../../defaultSvgIcons' interface FormatButtonProps { - format: FORMAT - addTitle: string - removeTitle: string - icon: IconKey - formatName: TextFormatType + format: FORMAT + addTitle: string + removeTitle: string + icon: IconKey + formatName: TextFormatType } -const FormatButton: React.FC = ({format, addTitle, removeTitle, icon, formatName}) => { - const [currentFormat, iconComponentFor] = useCellValues(currentFormat$, iconComponentFor$) - const applyFormat = usePublisher(applyFormat$) - const active = (currentFormat & format) !== 0 +const FormatButton: React.FC = ({ format, addTitle, removeTitle, icon, formatName }) => { + const [currentFormat, iconComponentFor] = useCellValues(currentFormat$, iconComponentFor$) + const applyFormat = usePublisher(applyFormat$) + const active = (currentFormat & format) !== 0 - return ( - { - applyFormat(formatName) - }} - > - {iconComponentFor(icon)} - - ) + return ( + { + applyFormat(formatName) + }} + > + {iconComponentFor(icon)} + + ) } export interface BoldItalicUnderlineTogglesProps { - options?: ('Bold' | 'Italic' | 'Underline')[] + options?: ('Bold' | 'Italic' | 'Underline')[] } /** * A toolbar component that lets the user toggle bold, italic and underline formatting. * @group Toolbar Components */ -export const BoldItalicUnderlineToggles: React.FC = ({options}) => { - const t = useTranslation() - const [currentFormat, iconComponentFor] = useCellValues(currentFormat$, iconComponentFor$) - const applyFormat = usePublisher(applyFormat$) - - const showAllButtons = typeof options === 'undefined' - const isBold = (currentFormat & IS_BOLD) !== 0 - const isItalic = (currentFormat & IS_ITALIC) !== 0 - const isUnderline = (currentFormat & IS_UNDERLINE) !== 0 +export const BoldItalicUnderlineToggles: React.FC = ({ options }) => { + const t = useTranslation() + const [currentFormat, iconComponentFor] = useCellValues(currentFormat$, iconComponentFor$) + const applyFormat = usePublisher(applyFormat$) - const [appliedFormats, setAppliedFormats] = useState([]) - useEffect(() => { - setAppliedFormats( - [ - isBold ? 'bold' : null, - isItalic ? 'italic' : null, - isUnderline ? 'underline' : null - ].filter((f) => !!f) as TextFormatType[] - ) - }, [currentFormat]) - const handleApplyFormatDiff = (diff: string[]) => (diff as TextFormatType[]).forEach(applyFormat) + const showAllButtons = typeof options === 'undefined' + const isBold = (currentFormat & IS_BOLD) !== 0 + const isItalic = (currentFormat & IS_ITALIC) !== 0 + const isUnderline = (currentFormat & IS_UNDERLINE) !== 0 - return ( -
- -
+ const [appliedFormats, setAppliedFormats] = useState([]) + useEffect(() => { + setAppliedFormats( + [isBold ? 'bold' : null, isItalic ? 'italic' : null, isUnderline ? 'underline' : null].filter((f) => !!f) as TextFormatType[] ) + }, [currentFormat]) + const handleApplyFormatDiff = (diff: string[]) => (diff as TextFormatType[]).forEach(applyFormat) + + return ( +
+ +
+ ) } export interface StrikeThroughSupSubTogglesProps { - options?: ('Strikethrough' | 'Sub' | 'Sup')[] + options?: ('Strikethrough' | 'Sub' | 'Sup')[] } const SupSubRadioItems = { - Sup: { - icon: 'superscript', - formatName: 'superscript' as TextFormatType, - titleFormatKey: 'toolbar.superscript', - titleFormatDefaultTranslation: 'Superscript', - titleRemoveFormatKey: 'toolbar.removeSuperscript', - titleRemoveFormatDefaultTranslation: 'Remove superscript', - }, - Sub: { - icon: 'subscript', - formatName: 'subscript' as TextFormatType, - titleFormatKey: 'toolbar.subscript', - titleFormatDefaultTranslation: 'Subscript', - titleRemoveFormatKey: 'toolbar.removeSubscript', - titleRemoveFormatDefaultTranslation: 'Remove subscript', - } + Sup: { + icon: 'superscript', + formatName: 'superscript' as TextFormatType, + titleFormatKey: 'toolbar.superscript', + titleFormatDefaultTranslation: 'Superscript', + titleRemoveFormatKey: 'toolbar.removeSuperscript', + titleRemoveFormatDefaultTranslation: 'Remove superscript' + }, + Sub: { + icon: 'subscript', + formatName: 'subscript' as TextFormatType, + titleFormatKey: 'toolbar.subscript', + titleFormatDefaultTranslation: 'Subscript', + titleRemoveFormatKey: 'toolbar.removeSubscript', + titleRemoveFormatDefaultTranslation: 'Remove subscript' + } } const isSupSubRadioItemsKey = (key: string): key is keyof typeof SupSubRadioItems => Object.keys(SupSubRadioItems).includes(key) /** * A toolbar component that lets the user toggle strikeThrough, superscript and subscript formatting. * @group Toolbar Components */ -export const StrikeThroughSupSubToggles: React.FC = ({options}) => { - const t = useTranslation() - const [currentFormat, iconComponentFor] = useCellValues(currentFormat$, iconComponentFor$) - const applyFormat = usePublisher(applyFormat$) +export const StrikeThroughSupSubToggles: React.FC = ({ options }) => { + const t = useTranslation() + const [currentFormat, iconComponentFor] = useCellValues(currentFormat$, iconComponentFor$) + const applyFormat = usePublisher(applyFormat$) - const isSup = (currentFormat & IS_SUPERSCRIPT) !== 0 - const isSub = (currentFormat & IS_SUBSCRIPT) !== 0 + const isSup = (currentFormat & IS_SUPERSCRIPT) !== 0 + const isSub = (currentFormat & IS_SUBSCRIPT) !== 0 - const [radioValue, setRadioValue] = useState('') - const radioItems = (options || Object.keys(SupSubRadioItems)) - .filter(isSupSubRadioItemsKey) - .map((type) => { - const item = SupSubRadioItems[type] - return ({ - value: type, - title: radioValue !== type ? t(item.titleFormatKey, item.titleFormatDefaultTranslation) : t(item.titleRemoveFormatKey, item.titleRemoveFormatDefaultTranslation), - contents: iconComponentFor(item.icon as IconKey) - }); - }) - useEffect(() => { - if (isSup) { - setRadioValue("Sup") - } else if (isSub) { - setRadioValue("Sub") - } else { - setRadioValue('') - } - }, [currentFormat]) + const [radioValue, setRadioValue] = useState('') + const radioItems = (options || Object.keys(SupSubRadioItems)).filter(isSupSubRadioItemsKey).map((type) => { + const item = SupSubRadioItems[type] + return { + value: type, + title: + radioValue !== type + ? t(item.titleFormatKey, item.titleFormatDefaultTranslation) + : t(item.titleRemoveFormatKey, item.titleRemoveFormatDefaultTranslation), + contents: iconComponentFor(item.icon as IconKey) + } + }) + useEffect(() => { + if (isSup) { + setRadioValue('Sup') + } else if (isSub) { + setRadioValue('Sub') + } else { + setRadioValue('') + } + }, [currentFormat]) - const handleRadioItemChange = (newValue: keyof typeof SupSubRadioItems | '') => { - if (newValue) { - applyFormat(SupSubRadioItems[newValue].formatName) - } else if (radioValue) { - applyFormat(SupSubRadioItems[radioValue].formatName) - } - setRadioValue(newValue) + const handleRadioItemChange = (newValue: keyof typeof SupSubRadioItems | '') => { + if (newValue) { + applyFormat(SupSubRadioItems[newValue].formatName) + } else if (radioValue) { + applyFormat(SupSubRadioItems[radioValue].formatName) } + setRadioValue(newValue) + } - const showAllButtons = typeof options === 'undefined' + const showAllButtons = typeof options === 'undefined' - return ( - <> -
- {showAllButtons || options.includes('Strikethrough') ? ( - - ) : null} -
- - - ) + return ( + <> +
+ {showAllButtons || options.includes('Strikethrough') ? ( + + ) : null} +
+ + + ) } diff --git a/src/plugins/toolbar/components/CodeToggle.tsx b/src/plugins/toolbar/components/CodeToggle.tsx index 962d48c5..7f195739 100644 --- a/src/plugins/toolbar/components/CodeToggle.tsx +++ b/src/plugins/toolbar/components/CodeToggle.tsx @@ -1,38 +1,37 @@ -import React, {useEffect, useState} from 'react' -import {IS_CODE} from '../../../FormatConstants' -import {applyFormat$, currentFormat$, iconComponentFor$, useTranslation} from '../../core' -import {MultipleChoiceToggleGroup} from '.././primitives/toolbar' -import {useCellValues, usePublisher} from '@mdxeditor/gurx' -import {TextFormatType} from "lexical"; +import React from 'react' +import { IS_CODE } from '../../../FormatConstants' +import { applyFormat$, currentFormat$, iconComponentFor$, useTranslation } from '../../core' +import { MultipleChoiceToggleGroup } from '.././primitives/toolbar' +import { useCellValues, usePublisher } from '@mdxeditor/gurx' +import { TextFormatType } from 'lexical' -1 /** * A toolbar component that lets the user toggle code formatting. * Use for inline `code` elements (like variables, methods, etc). * @group Toolbar Components */ export const CodeToggle: React.FC = () => { - const [currentFormat, iconComponentFor] = useCellValues(currentFormat$, iconComponentFor$) - const applyFormat = usePublisher(applyFormat$) - const t = useTranslation() + const [currentFormat, iconComponentFor] = useCellValues(currentFormat$, iconComponentFor$) + const applyFormat = usePublisher(applyFormat$) + const t = useTranslation() - const codeIsOn = (currentFormat & IS_CODE) !== 0 + const codeIsOn = (currentFormat & IS_CODE) !== 0 - const title = codeIsOn ? t('toolbar.removeInlineCode', 'Remove code format') : t('toolbar.inlineCode', 'Inline code format') + const title = codeIsOn ? t('toolbar.removeInlineCode', 'Remove code format') : t('toolbar.inlineCode', 'Inline code format') - const [appliedFormats, setAppliedFormats] = useState([]) - useEffect(() => { - setAppliedFormats([codeIsOn ? 'code' : null].filter((f) => !!f) as string[]) - }, [currentFormat]) + const [appliedFormats, setAppliedFormats] = useState([]) + useEffect(() => { + setAppliedFormats([codeIsOn ? 'code' : null].filter((f) => !!f) as string[]) + }, [currentFormat]) - const handleApplyFormatDiff = (diff: string[]) => (diff as TextFormatType[]).forEach(applyFormat) + const handleApplyFormatDiff = (diff: string[]) => (diff as TextFormatType[]).forEach(applyFormat) - return ( - - ) + return ( + + ) } diff --git a/src/plugins/toolbar/components/ListsToggle.tsx b/src/plugins/toolbar/components/ListsToggle.tsx index 4a18537e..08449385 100644 --- a/src/plugins/toolbar/components/ListsToggle.tsx +++ b/src/plugins/toolbar/components/ListsToggle.tsx @@ -1,13 +1,13 @@ import React from 'react' -import {applyListType$, currentListType$} from '../../lists' -import {SingleChoiceToggleGroup} from '.././primitives/toolbar' -import {useCellValues, usePublisher} from '@mdxeditor/gurx' -import {iconComponentFor$, useTranslation} from '../../core' +import { applyListType$, currentListType$ } from '../../lists' +import { SingleChoiceToggleGroup } from '.././primitives/toolbar' +import { useCellValues, usePublisher } from '@mdxeditor/gurx' +import { iconComponentFor$, useTranslation } from '../../core' const ICON_NAME_MAP = { - bullet: 'format_list_bulleted', - number: 'format_list_numbered', - check: 'format_list_checked' + bullet: 'format_list_bulleted', + number: 'format_list_numbered', + check: 'format_list_checked' } as const /** @@ -18,25 +18,30 @@ const ICON_NAME_MAP = { * @param options - The list types that the user can toggle between. Defaults to `['bullet', 'number', 'check']`. */ export const ListsToggle: React.FC<{ - options?: ('bullet' | 'number' | 'check')[] -}> = ({options = ['bullet', 'number', 'check']}) => { - const [currentListType, iconComponentFor] = useCellValues(currentListType$, iconComponentFor$) - const applyListType = usePublisher(applyListType$) - const t = useTranslation() + options?: ('bullet' | 'number' | 'check')[] +}> = ({ options = ['bullet', 'number', 'check'] }) => { + const [currentListType, iconComponentFor] = useCellValues(currentListType$, iconComponentFor$) + const applyListType = usePublisher(applyListType$) + const t = useTranslation() - const LIST_TITLE_MAP = { - bullet: t('toolbar.bulletedList', 'Bulleted list'), - number: t('toolbar.numberedList', 'Numbered list'), - check: t('toolbar.checkList', 'Check list') - } as const + const LIST_TITLE_MAP = { + bullet: t('toolbar.bulletedList', 'Bulleted list'), + number: t('toolbar.numberedList', 'Numbered list'), + check: t('toolbar.checkList', 'Check list') + } as const - const items = options.map((type) => ({ - value: type, - title: LIST_TITLE_MAP[type], - contents: iconComponentFor(ICON_NAME_MAP[type]) - })) + const items = options.map((type) => ({ + value: type, + title: LIST_TITLE_MAP[type], + contents: iconComponentFor(ICON_NAME_MAP[type]) + })) - return + return ( + + ) } diff --git a/src/plugins/toolbar/components/UndoRedo.tsx b/src/plugins/toolbar/components/UndoRedo.tsx index 741018d4..8d113697 100644 --- a/src/plugins/toolbar/components/UndoRedo.tsx +++ b/src/plugins/toolbar/components/UndoRedo.tsx @@ -1,71 +1,71 @@ -import {mergeRegister} from '@lexical/utils' -import {useCellValues} from '@mdxeditor/gurx' -import {CAN_REDO_COMMAND, CAN_UNDO_COMMAND, COMMAND_PRIORITY_CRITICAL, REDO_COMMAND, UNDO_COMMAND} from 'lexical' +import { mergeRegister } from '@lexical/utils' +import { useCellValues } from '@mdxeditor/gurx' +import { CAN_REDO_COMMAND, CAN_UNDO_COMMAND, COMMAND_PRIORITY_CRITICAL, REDO_COMMAND, UNDO_COMMAND } from 'lexical' import React from 'react' -import {IS_APPLE} from '../../../utils/detectMac' -import {activeEditor$, iconComponentFor$, useTranslation} from '../../core' -import {MultipleChoiceToggleGroup} from '.././primitives/toolbar' +import { IS_APPLE } from '../../../utils/detectMac' +import { activeEditor$, iconComponentFor$, useTranslation } from '../../core' +import { MultipleChoiceToggleGroup } from '.././primitives/toolbar' /** * A toolbar component that lets the user undo and redo changes in the editor. * @group Toolbar Components */ export const UndoRedo: React.FC = () => { - const [iconComponentFor, activeEditor] = useCellValues(iconComponentFor$, activeEditor$) - const [canUndo, setCanUndo] = React.useState(false) - const [canRedo, setCanRedo] = React.useState(false) - const t = useTranslation() + const [iconComponentFor, activeEditor] = useCellValues(iconComponentFor$, activeEditor$) + const [canUndo, setCanUndo] = React.useState(false) + const [canRedo, setCanRedo] = React.useState(false) + const t = useTranslation() - React.useEffect(() => { - if (activeEditor) { - return mergeRegister( - activeEditor.registerCommand( - CAN_UNDO_COMMAND, - (payload) => { - setCanUndo(payload) - return false - }, - COMMAND_PRIORITY_CRITICAL - ), - activeEditor.registerCommand( - CAN_REDO_COMMAND, - (payload) => { - setCanRedo(payload) - return false - }, - COMMAND_PRIORITY_CRITICAL - ) - ) - } - }, [activeEditor]) + React.useEffect(() => { + if (activeEditor) { + return mergeRegister( + activeEditor.registerCommand( + CAN_UNDO_COMMAND, + (payload) => { + setCanUndo(payload) + return false + }, + COMMAND_PRIORITY_CRITICAL + ), + activeEditor.registerCommand( + CAN_REDO_COMMAND, + (payload) => { + setCanRedo(payload) + return false + }, + COMMAND_PRIORITY_CRITICAL + ) + ) + } + }, [activeEditor]) - const handleValueChange = (newValue: string[]) => { - const action = newValue[0] - if (action === 'undo') { - activeEditor?.dispatchCommand(UNDO_COMMAND, undefined) - } else { - activeEditor?.dispatchCommand(REDO_COMMAND, undefined) - } + const handleValueChange = (newValue: string[]) => { + const action = newValue[0] + if (action === 'undo') { + activeEditor?.dispatchCommand(UNDO_COMMAND, undefined) + } else { + activeEditor?.dispatchCommand(REDO_COMMAND, undefined) } + } - return ( - - ) + return ( + + ) } From 8c5c603bdb4a7b3e28df6c275c83aa7901f226fe Mon Sep 17 00:00:00 2001 From: Basile Parent Date: Wed, 24 Jul 2024 14:55:59 +0200 Subject: [PATCH 5/8] Formatting --- package-lock.json | 25 +- src/plugins/toolbar/primitives/toolbar.tsx | 407 ++++++++++----------- 2 files changed, 225 insertions(+), 207 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6c5f7d7a..5d969651 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18242,8 +18242,31 @@ "optional": true }, "node_modules/npm/node_modules/semver": { + "version": "7.6.0", "dev": true, - "inBundle": true + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } }, "node_modules/npm/node_modules/set-blocking": { "version": "2.0.0", diff --git a/src/plugins/toolbar/primitives/toolbar.tsx b/src/plugins/toolbar/primitives/toolbar.tsx index 775f1b44..3a46818e 100644 --- a/src/plugins/toolbar/primitives/toolbar.tsx +++ b/src/plugins/toolbar/primitives/toolbar.tsx @@ -3,11 +3,11 @@ import * as RadixToolbar from '@radix-ui/react-toolbar' import classNames from 'classnames' import React from 'react' import styles from '../../../styles/ui.module.css' -import {TooltipWrap} from './TooltipWrap' -import {SelectButtonTrigger, SelectContent, SelectItem} from './select' -import {EditorInFocus, editorInFocus$, readOnly$, useTranslation} from '../../core' -import {useCellValue} from '@mdxeditor/gurx' -import {TextFormatType} from "lexical"; +import { TooltipWrap } from './TooltipWrap' +import { SelectButtonTrigger, SelectContent, SelectItem } from './select' +import { EditorInFocus, editorInFocus$, readOnly$, useTranslation } from '../../core' +import { useCellValue } from '@mdxeditor/gurx' +import { TextFormatType } from 'lexical' // // function decorate

(Component: React.ComponentType

, decoratedProps: P) { @@ -19,44 +19,44 @@ import {TextFormatType} from "lexical"; // function decorateWithRef

( - Component: React.ForwardRefExoticComponent

, - decoratedProps: Partial> & { 'data-toolbar-item'?: boolean } + Component: React.ForwardRefExoticComponent

, + decoratedProps: Partial> & { 'data-toolbar-item'?: boolean } ) { - return React.forwardRef((props: P, ref) => { - const className = classNames(decoratedProps.className, props.className) - return - }) + return React.forwardRef((props: P, ref) => { + const className = classNames(decoratedProps.className, props.className) + return + }) } function addTooltipToChildren>(Component: C) { - return ({title, children, ...props}: React.ComponentProps & { title: string }) => { - return ( - - {children} - - ) - } + return ({ title, children, ...props }: React.ComponentProps & { title: string }) => { + return ( + + {children} + + ) + } } /** * @internal */ -export const Root: React.FC<{ readOnly: boolean; children: React.ReactNode }> = ({readOnly, children}) => { - return ( - - {children} - - ) +export const Root: React.FC<{ readOnly: boolean; children: React.ReactNode }> = ({ readOnly, children }) => { + return ( + + {children} + + ) } /** * A toolbar button primitive. * @group Toolbar Primitives */ -export const Button = decorateWithRef(RadixToolbar.Button, {className: styles.toolbarButton, 'data-toolbar-item': true}) +export const Button = decorateWithRef(RadixToolbar.Button, { className: styles.toolbarButton, 'data-toolbar-item': true }) /** * A toolbar button with tooltip primitive. @@ -68,38 +68,38 @@ export const ButtonWithTooltip = addTooltipToChildren(Button) * @internal */ export const ToolbarToggleItem = decorateWithRef(RadixToolbar.ToggleItem, { - className: styles.toolbarToggleItem, - 'data-toolbar-item': true + className: styles.toolbarToggleItem, + 'data-toolbar-item': true }) /** * @internal */ export const SingleToggleGroup = decorateWithRef(RadixToolbar.ToggleGroup, { - type: 'single', - className: styles.toolbarToggleSingleGroup + type: 'single', + className: styles.toolbarToggleSingleGroup }) /** * @internal */ export const ToggleSingleGroupWithItem = React.forwardRef< - HTMLDivElement, - Omit & { on: boolean; title: string; disabled?: boolean } ->(({on, title, children, disabled, ...props}, forwardedRef) => { - return ( - - - {children} - - - ) + HTMLDivElement, + Omit & { on: boolean; title: string; disabled?: boolean } +>(({ on, title, children, disabled, ...props }, forwardedRef) => { + return ( + + + {children} + + + ) }) /** @@ -107,95 +107,90 @@ export const ToggleSingleGroupWithItem = React.forwardRef< * @group Toolbar Primitives */ export const MultipleChoiceToggleGroup: React.FC< - Omit & { + Omit & { onValueChangeDiff?: (diff: string[]) => void items: { - title: string - contents: React.ReactNode - value: string - disabled?: boolean + title: string + contents: React.ReactNode + value: string + disabled?: boolean }[] -}> = ({items, value, onValueChange, onValueChangeDiff, ...otherProps}) => { - - const handleValueChange = (newValue: string[]) => { - if (onValueChangeDiff) { - const diff = [ - ...newValue.filter((format) => !(value || []).includes(format)), - ...(value || []).filter((existingFormat) => !newValue.includes(existingFormat)), - ] - onValueChangeDiff(diff) - } - - onValueChange?.(newValue) + } +> = ({ items, value, onValueChange, onValueChangeDiff, ...otherProps }) => { + const handleValueChange = (newValue: string[]) => { + if (onValueChangeDiff) { + const diff = [ + ...newValue.filter((format) => !(value || []).includes(format)), + ...(value || []).filter((existingFormat) => !newValue.includes(existingFormat)) + ] + onValueChangeDiff(diff) } - return ( -

- - {items.map((item, index) => ( - - {item.contents} - - ))} - -
- ) -} + onValueChange?.(newValue) + } + return ( +
+ + {items.map((item, index) => ( + + {item.contents} + + ))} + +
+ ) +} /** * A toolbar primitive that allows you to build an UI with multiple exclusive toggle groups, like the list type toggle. * @group Toolbar Primitives */ export const SingleChoiceToggleGroup = ({ - value, - onChange, - className, - "aria-label": ariaLabel, - items - }: { - items: { - title: string - value: T - contents: React.ReactNode - }[] - "aria-label": string - onChange: (value: T | '') => void - value: T | '' - className?: string + value, + onChange, + className, + 'aria-label': ariaLabel, + items +}: { + items: { + title: string + value: T + contents: React.ReactNode + }[] + 'aria-label': string + onChange: (value: T | '') => void + value: T | '' + className?: string }) => { - const t = useTranslation() + const t = useTranslation() - return ( -
- { - e.preventDefault() - }} - > - {items.map((item, index) => ( - - {item.contents} - - ))} - -
- ) + return ( +
+ { + e.preventDefault() + }} + > + {items.map((item, index) => ( + + {item.contents} + + ))} + +
+ ) } /** @@ -203,61 +198,61 @@ export const SingleChoiceToggleGroup = ({ * @group Toolbar Primitives */ export const ButtonOrDropdownButton = (props: { + /** + * The contents of the button - usually an icon. + */ + children: React.ReactNode + /** + * The title used for the tooltip. + */ + title: string + /** + * The function to execute when the button is clicked or an item is chosen from the dropdown. + * If there is only one item in the dropdown, the value will be an empty string. + */ + onChoose: (value: T) => void + /** + * The items to show in the dropdown. + */ + items: { /** - * The contents of the button - usually an icon. - */ - children: React.ReactNode - /** - * The title used for the tooltip. - */ - title: string - /** - * The function to execute when the button is clicked or an item is chosen from the dropdown. - * If there is only one item in the dropdown, the value will be an empty string. + * The value to pass to the `onChoose` function when this item is chosen. */ - onChoose: (value: T) => void + value: T /** - * The items to show in the dropdown. + * The label to show in the dropdown. */ - items: { - /** - * The value to pass to the `onChoose` function when this item is chosen. - */ - value: T - /** - * The label to show in the dropdown. - */ - label: string | JSX.Element - }[] + label: string | JSX.Element + }[] }) => { - const readOnly = useCellValue(readOnly$) - return ( - <> - {props.items.length === 1 ? ( - { - props.onChoose('' as T) - }} - disabled={readOnly} - > - {props.children} - - ) : ( - - {props.children} + const readOnly = useCellValue(readOnly$) + return ( + <> + {props.items.length === 1 ? ( + { + props.onChoose('' as T) + }} + disabled={readOnly} + > + {props.children} + + ) : ( + + {props.children} - - {props.items.map((item, index) => ( - - {item.label} - - ))} - - - )} - - ) + + {props.items.map((item, index) => ( + + {item.label} + + ))} + + + )} + + ) } /** @@ -265,14 +260,14 @@ export const ButtonOrDropdownButton = (props: { * @group Toolbar Primitives */ export interface ConditionalContentsOption { - /** - * A function that returns `true` if the option should be displayed for the current editor in focus. - */ - when: (rootNode: EditorInFocus | null) => boolean - /** - * The contents to display if the `when` function returns `true`. - */ - contents: () => React.ReactNode + /** + * A function that returns `true` if the option should be displayed for the current editor in focus. + */ + when: (rootNode: EditorInFocus | null) => boolean + /** + * The contents to display if the `when` function returns `true`. + */ + contents: () => React.ReactNode } /** @@ -280,14 +275,14 @@ export interface ConditionalContentsOption { * @group Toolbar Primitives */ export interface FallbackOption { - /** - * The contents to display - */ - fallback: () => React.ReactNode + /** + * The contents to display + */ + fallback: () => React.ReactNode } function isConditionalContentsOption(option: ConditionalContentsOption | FallbackOption): option is ConditionalContentsOption { - return Object.hasOwn(option, 'when') + return Object.hasOwn(option, 'when') } /** @@ -314,28 +309,28 @@ function isConditionalContentsOption(option: ConditionalContentsOption | Fallbac * @group Toolbar Primitives */ export const ConditionalContents: React.FC<{ - /** - * A set of options that define the contents to show based on the editor that is in focus. - * Can be either a {@link ConditionalContentsOption} or a {@link FallbackOption}. - * See the {@link ConditionalContents} documentation for an example. - */ - options: (ConditionalContentsOption | FallbackOption)[] -}> = ({options}) => { - const editorInFocus = useCellValue(editorInFocus$) - const contents = React.useMemo(() => { - const option = options.find((option) => { - if (isConditionalContentsOption(option)) { - if (option.when(editorInFocus)) { - return true - } - } else { - return true - } - }) - return option ? (isConditionalContentsOption(option) ? option.contents() : option.fallback()) : null - }, [options, editorInFocus]) + /** + * A set of options that define the contents to show based on the editor that is in focus. + * Can be either a {@link ConditionalContentsOption} or a {@link FallbackOption}. + * See the {@link ConditionalContents} documentation for an example. + */ + options: (ConditionalContentsOption | FallbackOption)[] +}> = ({ options }) => { + const editorInFocus = useCellValue(editorInFocus$) + const contents = React.useMemo(() => { + const option = options.find((option) => { + if (isConditionalContentsOption(option)) { + if (option.when(editorInFocus)) { + return true + } + } else { + return true + } + }) + return option ? (isConditionalContentsOption(option) ? option.contents() : option.fallback()) : null + }, [options, editorInFocus]) - return
{contents}
+ return
{contents}
} /** From dea2a224cca5de6a35517af2e7ea04cb7ea54b3c Mon Sep 17 00:00:00 2001 From: Basile Parent Date: Fri, 26 Jul 2024 09:25:33 +0200 Subject: [PATCH 6/8] Fix type errors --- .../toolbar/components/BoldItalicUnderlineToggles.tsx | 10 ++++++---- src/plugins/toolbar/components/CodeToggle.tsx | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx b/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx index da4e92fa..a1fd64e3 100644 --- a/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx +++ b/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx @@ -1,8 +1,8 @@ import { applyFormat$, currentFormat$, iconComponentFor$, useTranslation } from '../../core' import { useCellValues, usePublisher } from '@mdxeditor/gurx' -import React from 'react' +import React, { useEffect, useState } from 'react' import { FORMAT, IS_BOLD, IS_ITALIC, IS_STRIKETHROUGH, IS_SUBSCRIPT, IS_SUPERSCRIPT, IS_UNDERLINE } from '../../../FormatConstants' -import { ToggleSingleGroupWithItem } from '.././primitives/toolbar' +import { MultipleChoiceToggleGroup, SingleChoiceToggleGroup, ToggleSingleGroupWithItem } from '.././primitives/toolbar' import { TextFormatType } from 'lexical' import styles from '../../../styles/ui.module.css' import { IconKey } from '../../../defaultSvgIcons' @@ -57,7 +57,9 @@ export const BoldItalicUnderlineToggles: React.FC !!f) as TextFormatType[] ) }, [currentFormat]) - const handleApplyFormatDiff = (diff: string[]) => (diff as TextFormatType[]).forEach(applyFormat) + const handleApplyFormatDiff = (diff: string[]) => { + ;(diff as TextFormatType[]).forEach(applyFormat) + } return (
@@ -135,7 +137,7 @@ export const StrikeThroughSupSubToggles: React.FC('') - const radioItems = (options || Object.keys(SupSubRadioItems)).filter(isSupSubRadioItemsKey).map((type) => { + const radioItems = (options ?? Object.keys(SupSubRadioItems)).filter(isSupSubRadioItemsKey).map((type) => { const item = SupSubRadioItems[type] return { value: type, diff --git a/src/plugins/toolbar/components/CodeToggle.tsx b/src/plugins/toolbar/components/CodeToggle.tsx index 7f195739..206208f7 100644 --- a/src/plugins/toolbar/components/CodeToggle.tsx +++ b/src/plugins/toolbar/components/CodeToggle.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useEffect, useState } from 'react' import { IS_CODE } from '../../../FormatConstants' import { applyFormat$, currentFormat$, iconComponentFor$, useTranslation } from '../../core' import { MultipleChoiceToggleGroup } from '.././primitives/toolbar' @@ -24,7 +24,9 @@ export const CodeToggle: React.FC = () => { setAppliedFormats([codeIsOn ? 'code' : null].filter((f) => !!f) as string[]) }, [currentFormat]) - const handleApplyFormatDiff = (diff: string[]) => (diff as TextFormatType[]).forEach(applyFormat) + const handleApplyFormatDiff = (diff: string[]) => { + ;(diff as TextFormatType[]).forEach(applyFormat) + } return ( Date: Fri, 26 Jul 2024 09:54:31 +0200 Subject: [PATCH 7/8] Fix spacing differences --- .../components/BoldItalicUnderlineToggles.tsx | 1 + src/plugins/toolbar/components/CodeToggle.tsx | 15 ++-- .../components/DiffSourceToggleWrapper.tsx | 38 ++++++---- .../toolbar/components/ListsToggle.tsx | 15 ++-- src/plugins/toolbar/components/UndoRedo.tsx | 39 +++++----- src/plugins/toolbar/primitives/toolbar.tsx | 71 +++++++++---------- src/styles/ui.module.css | 4 ++ 7 files changed, 102 insertions(+), 81 deletions(-) diff --git a/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx b/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx index a1fd64e3..244c130c 100644 --- a/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx +++ b/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx @@ -184,6 +184,7 @@ export const StrikeThroughSupSubToggles: React.FC { } return ( - +
+ +
) } diff --git a/src/plugins/toolbar/components/DiffSourceToggleWrapper.tsx b/src/plugins/toolbar/components/DiffSourceToggleWrapper.tsx index 6ec56e10..169fa52d 100644 --- a/src/plugins/toolbar/components/DiffSourceToggleWrapper.tsx +++ b/src/plugins/toolbar/components/DiffSourceToggleWrapper.tsx @@ -35,13 +35,25 @@ export const DiffSourceToggleWrapper: React.FC<{ children: React.ReactNode; opti }[] = [] if (options.includes('rich-text')) { - toggleGroupItems.push({ title: t('toolbar.richText', 'Rich text'), contents: iconComponentFor('rich_text'), value: 'rich-text' }) + toggleGroupItems.push({ + title: t('toolbar.richText', 'Rich text'), + contents: iconComponentFor('rich_text'), + value: 'rich-text' + }) } if (options.includes('diff')) { - toggleGroupItems.push({ title: t('toolbar.diffMode', 'Diff mode'), contents: iconComponentFor('difference'), value: 'diff' }) + toggleGroupItems.push({ + title: t('toolbar.diffMode', 'Diff mode'), + contents: iconComponentFor('difference'), + value: 'diff' + }) } if (options.includes('source')) { - toggleGroupItems.push({ title: t('toolbar.source', 'Source mode'), contents: iconComponentFor('markdown'), value: 'source' }) + toggleGroupItems.push({ + title: t('toolbar.source', 'Source mode'), + contents: iconComponentFor('markdown'), + value: 'source' + }) } return ( @@ -55,15 +67,17 @@ export const DiffSourceToggleWrapper: React.FC<{ children: React.ReactNode; opti )}
- { - changeViewMode(value === '' ? 'rich-text' : value) - }} - /> +
+ { + changeViewMode(value === '' ? 'rich-text' : value) + }} + /> +
) diff --git a/src/plugins/toolbar/components/ListsToggle.tsx b/src/plugins/toolbar/components/ListsToggle.tsx index 08449385..741fac84 100644 --- a/src/plugins/toolbar/components/ListsToggle.tsx +++ b/src/plugins/toolbar/components/ListsToggle.tsx @@ -3,6 +3,7 @@ import { applyListType$, currentListType$ } from '../../lists' import { SingleChoiceToggleGroup } from '.././primitives/toolbar' import { useCellValues, usePublisher } from '@mdxeditor/gurx' import { iconComponentFor$, useTranslation } from '../../core' +import styles from '@/styles/ui.module.css' const ICON_NAME_MAP = { bullet: 'format_list_bulleted', @@ -37,11 +38,13 @@ export const ListsToggle: React.FC<{ })) return ( - +
+ +
) } diff --git a/src/plugins/toolbar/components/UndoRedo.tsx b/src/plugins/toolbar/components/UndoRedo.tsx index 8d113697..ef96102d 100644 --- a/src/plugins/toolbar/components/UndoRedo.tsx +++ b/src/plugins/toolbar/components/UndoRedo.tsx @@ -5,6 +5,7 @@ import React from 'react' import { IS_APPLE } from '../../../utils/detectMac' import { activeEditor$, iconComponentFor$, useTranslation } from '../../core' import { MultipleChoiceToggleGroup } from '.././primitives/toolbar' +import styles from '@/styles/ui.module.css' /** * A toolbar component that lets the user undo and redo changes in the editor. @@ -49,23 +50,25 @@ export const UndoRedo: React.FC = () => { } return ( - +
+ +
) } diff --git a/src/plugins/toolbar/primitives/toolbar.tsx b/src/plugins/toolbar/primitives/toolbar.tsx index 3a46818e..a5bec5d4 100644 --- a/src/plugins/toolbar/primitives/toolbar.tsx +++ b/src/plugins/toolbar/primitives/toolbar.tsx @@ -5,9 +5,8 @@ import React from 'react' import styles from '../../../styles/ui.module.css' import { TooltipWrap } from './TooltipWrap' import { SelectButtonTrigger, SelectContent, SelectItem } from './select' -import { EditorInFocus, editorInFocus$, readOnly$, useTranslation } from '../../core' +import { EditorInFocus, editorInFocus$, readOnly$ } from '../../core' import { useCellValue } from '@mdxeditor/gurx' -import { TextFormatType } from 'lexical' // // function decorate

(Component: React.ComponentType

, decoratedProps: P) { @@ -120,8 +119,8 @@ export const MultipleChoiceToggleGroup: React.FC< const handleValueChange = (newValue: string[]) => { if (onValueChangeDiff) { const diff = [ - ...newValue.filter((format) => !(value || []).includes(format)), - ...(value || []).filter((existingFormat) => !newValue.includes(existingFormat)) + ...newValue.filter((format) => !(value ?? []).includes(format)), + ...(value ?? []).filter((existingFormat) => !newValue.includes(existingFormat)) ] onValueChangeDiff(diff) } @@ -130,21 +129,19 @@ export const MultipleChoiceToggleGroup: React.FC< } return ( -

- - {items.map((item, index) => ( - - {item.contents} - - ))} - -
+ + {items.map((item, index) => ( + + {item.contents} + + ))} + ) } @@ -169,27 +166,23 @@ export const SingleChoiceToggleGroup = ({ value: T | '' className?: string }) => { - const t = useTranslation() - return ( -
- { - e.preventDefault() - }} - > - {items.map((item, index) => ( - - {item.contents} - - ))} - -
+ { + e.preventDefault() + }} + > + {items.map((item, index) => ( + + {item.contents} + + ))} + ) } diff --git a/src/styles/ui.module.css b/src/styles/ui.module.css index 457e30ce..c47566e4 100644 --- a/src/styles/ui.module.css +++ b/src/styles/ui.module.css @@ -217,6 +217,10 @@ .toolbarGroupOfGroups { display: flex; margin: 0 var(--spacing-1); + + &.noMargin { + margin: 0; + } } .toolbarToggleSingleGroup:first-of-type .toolbarToggleItem:only-child, From 635c8fbcec8e6b7a19ef25b050067d9b1c640512 Mon Sep 17 00:00:00 2001 From: Basile Parent Date: Fri, 26 Jul 2024 10:06:14 +0200 Subject: [PATCH 8/8] Cleanup --- src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx | 1 - src/styles/ui.module.css | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx b/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx index 244c130c..a1fd64e3 100644 --- a/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx +++ b/src/plugins/toolbar/components/BoldItalicUnderlineToggles.tsx @@ -184,7 +184,6 @@ export const StrikeThroughSupSubToggles: React.FC