diff --git a/apps/web/src/data/en/index.ts b/apps/web/src/data/en/index.ts index 55bde91db2..dc2a9d855e 100644 --- a/apps/web/src/data/en/index.ts +++ b/apps/web/src/data/en/index.ts @@ -852,6 +852,8 @@ export const loggedInData = { mathFormula: 'Math formula (%ctrlOrCmd% + M)', code: 'Code (%ctrlOrCmd% + ⇧ + `)', blank: 'Blank', + createBlank: 'Create Blank', + removeBlank: 'Remove Blank', bold: 'Bold (%ctrlOrCmd% + B)', italic: 'Italic (%ctrlOrCmd% + I)', noItemsFound: 'No items found', @@ -903,7 +905,7 @@ export const loggedInData = { }, textAreaExercise: { title: 'Text Box Exercise', - description: 'A big text box for long answers. No feedback.' + description: 'A big text box for long answers. No feedback.', }, scMcExercise: { title: 'SC/MC Exercise', diff --git a/packages/editor/src/editor-ui/plugin-toolbar/text-controls/hooks/use-formatting-options.tsx b/packages/editor/src/editor-ui/plugin-toolbar/text-controls/hooks/use-formatting-options.tsx index 905db0d25e..28d1c2d60e 100644 --- a/packages/editor/src/editor-ui/plugin-toolbar/text-controls/hooks/use-formatting-options.tsx +++ b/packages/editor/src/editor-ui/plugin-toolbar/text-controls/hooks/use-formatting-options.tsx @@ -16,7 +16,6 @@ import { faListOl, faListUl, faSquareRootVariable, - faXmark, } from '@fortawesome/free-solid-svg-icons' import { onKeyDown as slateListsOnKeyDown } from '@prezly/slate-lists' import { FaIcon } from '@serlo/frontend/src/components/fa-icon' @@ -227,12 +226,27 @@ function createToolbarControls( ctrlKey: string ): ControlButton[] { const allFormattingOptions = [ + // Blank (For Fill in the Blank Exercises) + { + name: TextEditorFormattingOption.textBlank, + title: textStrings.createBlank, + activeTitle: textStrings.removeBlank, + isActive: isBlankActive, + onClick: toggleBlank, + group: 'blank', + renderIcon: () => ( + + {textStrings.blank} + + ), + }, // Bold { name: TextEditorFormattingOption.richTextBold, title: textStrings.bold, isActive: isBoldActive, onClick: toggleBoldMark, + group: 'default', renderIcon: () => , }, // Italic @@ -241,6 +255,7 @@ function createToolbarControls( title: textStrings.italic, isActive: isItalicActive, onClick: toggleItalicMark, + group: 'default', renderIcon: () => , }, // Link @@ -249,6 +264,7 @@ function createToolbarControls( title: textStrings.link, isActive: isLinkActive, onClick: toggleLink, + group: 'default', renderIcon: () => , }, // Headings @@ -257,8 +273,8 @@ function createToolbarControls( title: textStrings.headings, closeMenuTitle: textStrings.closeSubMenu, isActive: isAnyHeadingActive, + group: 'default', renderIcon: () => , - renderCloseMenuIcon: () => , subMenuButtons: ([1, 2, 3] as const).map((level) => ({ name: TextEditorFormattingOption.headings, title: `${textStrings.heading} ${level}`, @@ -275,12 +291,12 @@ function createToolbarControls( title: textStrings.colors, closeMenuTitle: textStrings.closeSubMenu, isActive: () => false, + group: 'default', renderIcon: (editor: SlateEditor) => { const colorIndex = getColorIndex(editor) const color = colorIndex ? textColors[colorIndex].value : 'black' return }, - renderCloseMenuIcon: () => , subMenuButtons: [ { name: TextEditorFormattingOption.colors, @@ -315,6 +331,7 @@ function createToolbarControls( title: textStrings.orderedList, isActive: isSelectionWithinOrderedList, onClick: toggleOrderedList, + group: 'lists', renderIcon: () => , }, // Unordered list @@ -323,6 +340,7 @@ function createToolbarControls( title: textStrings.unorderedList, isActive: isSelectionWithinUnorderedList, onClick: toggleUnorderedList, + group: 'lists', renderIcon: () => , }, // Math @@ -331,6 +349,7 @@ function createToolbarControls( title: textStrings.mathFormula, isActive: isMathActive, onClick: toggleMath, + group: 'default', renderIcon: () => ( ), @@ -341,20 +360,9 @@ function createToolbarControls( title: textStrings.code, isActive: isCodeActive, onClick: toggleCode, + group: 'default', renderIcon: () => , }, - // Blank (For Fill in the Blank Exercises) - { - name: TextEditorFormattingOption.textBlank, - title: textStrings.blank, - isActive: isBlankActive, - onClick: toggleBlank, - renderIcon: () => ( - - _ - - ), - }, ].map((option) => { return { ...option, @@ -362,7 +370,7 @@ function createToolbarControls( } }) - return allFormattingOptions.filter((option) => - formattingOptions.includes(TextEditorFormattingOption[option.name]) - ) + return allFormattingOptions.filter(({ name }) => + formattingOptions.includes(TextEditorFormattingOption[name]) + ) as ControlButton[] } diff --git a/packages/editor/src/editor-ui/plugin-toolbar/text-controls/plugin-toolbar-text-control-button.tsx b/packages/editor/src/editor-ui/plugin-toolbar/text-controls/plugin-toolbar-text-control-button.tsx index 619ac555df..9c40123b66 100644 --- a/packages/editor/src/editor-ui/plugin-toolbar/text-controls/plugin-toolbar-text-control-button.tsx +++ b/packages/editor/src/editor-ui/plugin-toolbar/text-controls/plugin-toolbar-text-control-button.tsx @@ -26,7 +26,7 @@ export function PluginToolbarTextControlButton({ active ? 'bg-editor-primary-200 text-almost-black shadow-menu hover:text-black' : '#b6b6b6 hover:text-editor-primary', - 'b-0 m-1 h-6 w-6 cursor-pointer rounded p-0 outline-none', + 'b-0 m-1 h-6 w-auto min-w-[1.5rem] cursor-pointer rounded p-0 outline-none', 'serlo-tooltip-trigger' )} data-qa={`plugin-toolbar-button-${ diff --git a/packages/editor/src/editor-ui/plugin-toolbar/text-controls/plugin-toolbar-text-controls.tsx b/packages/editor/src/editor-ui/plugin-toolbar/text-controls/plugin-toolbar-text-controls.tsx index 2f502208af..94d1b9bc98 100644 --- a/packages/editor/src/editor-ui/plugin-toolbar/text-controls/plugin-toolbar-text-controls.tsx +++ b/packages/editor/src/editor-ui/plugin-toolbar/text-controls/plugin-toolbar-text-controls.tsx @@ -1,8 +1,10 @@ -import { useState } from 'react' +import { faXmark } from '@fortawesome/free-solid-svg-icons' +import { Fragment, useState } from 'react' import { Editor as SlateEditor } from 'slate' import { PluginToolbarTextControlButton } from './plugin-toolbar-text-control-button' import type { NestedControlButton, ControlButton } from './types' +import { FaIcon } from '@/components/fa-icon' export interface PluginToolbarTextControlsProps { controls: ControlButton[] @@ -29,6 +31,7 @@ export function PluginToolbarTextControls({ const mathActive = controls.find(isMath)?.isActive(editor) const blankActive = controls.find(isBlank)?.isActive(editor) + const isSpecialMode = mathActive || blankActive if (typeof subMenu !== 'number') { return ( @@ -36,21 +39,33 @@ export function PluginToolbarTextControls({ {controls.map((control, index) => { if (mathActive && !isMath(control)) return null if (blankActive && !isBlank(control)) return null + + const next = controls.at(index + 1) + const showSeparator = + !isSpecialMode && !!next && next.group !== control.group + return ( - { - event.preventDefault() - event.stopPropagation() - isNestedControlButton(control) - ? setSubMenu(index) - : control.onClick(editor) - }} - key={index} - > - {control.renderIcon(editor)} - + + { + event.preventDefault() + event.stopPropagation() + isNestedControlButton(control) + ? setSubMenu(index) + : control.onClick(editor) + }} + > + {control.renderIcon(editor)} + + {showSeparator ? | : null} + ) })} @@ -66,7 +81,7 @@ export function PluginToolbarTextControls({ return false }, renderIcon() { - return activeControl.renderCloseMenuIcon() + return }, onClick() { setSubMenu(undefined) @@ -77,20 +92,22 @@ export function PluginToolbarTextControls({ return ( <> - {subMenuControls.map((control, index) => ( - { - event.preventDefault() - control.onClick(editor) - setSubMenu(undefined) - }} - key={index} - > - {control.renderIcon(editor)} - - ))} + {subMenuControls.map((control, index) => { + return ( + { + event.preventDefault() + control.onClick(editor) + setSubMenu(undefined) + }} + key={index} + > + {control.renderIcon(editor)} + + ) + })} ) } diff --git a/packages/editor/src/editor-ui/plugin-toolbar/text-controls/types.ts b/packages/editor/src/editor-ui/plugin-toolbar/text-controls/types.ts index 0fac6700f8..0eaf187154 100644 --- a/packages/editor/src/editor-ui/plugin-toolbar/text-controls/types.ts +++ b/packages/editor/src/editor-ui/plugin-toolbar/text-controls/types.ts @@ -11,6 +11,7 @@ export enum TextEditorFormattingOption { richTextBold = 'richTextBold', richTextItalic = 'richTextItalic', textBlank = 'textBlank', + separator = 'separator', } export type ControlButton = ActionControlButton | NestedControlButton @@ -18,16 +19,19 @@ export type ControlButton = ActionControlButton | NestedControlButton interface ActionControlButton { name: TextEditorFormattingOption title: string + activeTitle?: string isActive(editor: SlateEditor): boolean + group: 'blank' | 'default' onClick(editor: SlateEditor): void renderIcon(editor: SlateEditor): React.ReactNode } export interface NestedControlButton { + name: TextEditorFormattingOption title: string closeMenuTitle: string subMenuButtons: ActionControlButton[] isActive(editor: SlateEditor): boolean + group: undefined renderIcon(editor: SlateEditor): React.ReactNode - renderCloseMenuIcon(): React.ReactNode }