From 3388ddc3b935e6aaa1961783f54a6f602bb0dc79 Mon Sep 17 00:00:00 2001
From: Mikey Stengel
Date: Fri, 4 Aug 2023 21:01:31 +0200
Subject: [PATCH 1/6] WIP: Close math editor through ESC and new button
---
.../editor-ui/editor-textarea.tsx | 4 +-
src/serlo-editor/math/editor.tsx | 56 ++++++--
src/serlo-editor/math/renderer.tsx | 1 +
.../plugins/text/components/math-element.tsx | 136 +++++++++++++-----
4 files changed, 146 insertions(+), 51 deletions(-)
diff --git a/src/serlo-editor/editor-ui/editor-textarea.tsx b/src/serlo-editor/editor-ui/editor-textarea.tsx
index e9833a8584..242923b1fc 100644
--- a/src/serlo-editor/editor-ui/editor-textarea.tsx
+++ b/src/serlo-editor/editor-ui/editor-textarea.tsx
@@ -5,13 +5,14 @@ type EditorTextareaProps = TextareaHTMLAttributes & {
onMoveOutRight?(): void
onMoveOutLeft?(): void
className?: string
+ dataQa?: string
}
export const EditorTextarea = forwardRef<
HTMLTextAreaElement,
EditorTextareaProps
>(function EditorTextarea(
- { onMoveOutLeft, onMoveOutRight, className, ...props },
+ { onMoveOutLeft, onMoveOutRight, className, dataQa, ...props },
ref
) {
return (
@@ -22,6 +23,7 @@ export const EditorTextarea = forwardRef<
)}
{...props}
ref={ref}
+ data-qa={dataQa}
onKeyDown={(e) => {
if (!ref || typeof ref === 'function' || !ref.current) return
diff --git a/src/serlo-editor/math/editor.tsx b/src/serlo-editor/math/editor.tsx
index 0eb00b31b9..a7cd5556e0 100644
--- a/src/serlo-editor/math/editor.tsx
+++ b/src/serlo-editor/math/editor.tsx
@@ -3,7 +3,9 @@ import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'
import clsx from 'clsx'
import { useState, useCallback, createRef, useEffect } from 'react'
import { createPortal } from 'react-dom'
+import { useHotkeys } from 'react-hotkeys-hook'
import Modal from 'react-modal'
+import { Key } from 'ts-key-enum'
import { MathRenderer } from './renderer'
import { VisualEditor } from './visual-editor'
@@ -59,6 +61,7 @@ const MathEditorTextArea = (props: MathEditorTextAreaProps) => {
onMoveOutLeft={props.onMoveOutLeft}
value={latex}
ref={textareaRef}
+ dataQa="plugin-math-latex-editor"
/>
)
}
@@ -74,8 +77,9 @@ export interface MathEditorProps {
onEditorChange(visual: boolean): void
onInlineChange?(inline: boolean): void
onChange(state: string): void
- onMoveOutRight?(): void
- onMoveOutLeft?(): void
+
+ onMoveOutRight: (options?: { closeThroughModal?: boolean }) => void
+ onMoveOutLeft(): void
onDeleteOutRight?(): void
onDeleteOutLeft?(): void
}
@@ -89,7 +93,19 @@ export function MathEditor(props: MathEditorProps) {
const { visual, readOnly, state, disableBlock } = props
- const useVisualEditor = visual && !hasError
+ useHotkeys(
+ Key.Escape,
+ (event) => {
+ event.preventDefault()
+ // close overlay
+ props.onMoveOutRight()
+ },
+ {
+ enableOnFormTags: true,
+ }
+ )
+
+ const isVisualMode = visual && !hasError
return (
<>
@@ -173,7 +189,11 @@ export function MathEditor(props: MathEditorProps) {
return state ? (
) : (
-
+
{mathStrings.formula}
)
@@ -181,7 +201,7 @@ export function MathEditor(props: MathEditorProps) {
return (
<>
- {useVisualEditor ? (
+ {isVisualMode ? (
e.stopPropagation()}
ref={anchorRef}
@@ -214,7 +234,7 @@ export function MathEditor(props: MathEditorProps) {
px-1 py-[2px] text-base text-almost-black transition-all
hover:bg-editor-primary-200 focus:bg-editor-primary-200 focus:outline-none
`}
- value={useVisualEditor ? 'visual' : 'latex'}
+ value={isVisualMode ? 'visual' : 'latex'}
onChange={(e) => {
if (hasError) setHasError(false)
props.onEditorChange(e.target.value === 'visual')
@@ -234,7 +254,7 @@ export function MathEditor(props: MathEditorProps) {
)}
- {useVisualEditor && (
+ {isVisualMode && (
)}
- {hasError || !useVisualEditor ? renderOverlayPortal() : null}
+ {hasError || !isVisualMode ? renderOverlayPortal() : null}
>
)
}
@@ -260,16 +280,24 @@ export function MathEditor(props: MathEditorProps) {
}
function renderOverlayPortal() {
- const children = (
+ return (
-
- {hasError ? mathStrings.onlyLatex : mathStrings.latexEditorTitle}
-
- {!useVisualEditor && (
+
+
+ {hasError ? mathStrings.onlyLatex : mathStrings.latexEditorTitle}
+
+
+
+ {!isVisualMode && (
)}
)
- return children
}
}
diff --git a/src/serlo-editor/math/renderer.tsx b/src/serlo-editor/math/renderer.tsx
index 793f08c9ff..4bca2b9dad 100644
--- a/src/serlo-editor/math/renderer.tsx
+++ b/src/serlo-editor/math/renderer.tsx
@@ -32,6 +32,7 @@ export const MathRenderer = React.memo(
}
}
{...additionalContainerProps}
+ data-qa="plugin-math-renderer"
/>
)
diff --git a/src/serlo-editor/plugins/text/components/math-element.tsx b/src/serlo-editor/plugins/text/components/math-element.tsx
index a4058654b0..4337634d0d 100644
--- a/src/serlo-editor/plugins/text/components/math-element.tsx
+++ b/src/serlo-editor/plugins/text/components/math-element.tsx
@@ -1,4 +1,4 @@
-import React, { useContext, useMemo } from 'react'
+import React, { useContext, useEffect, useMemo, useState } from 'react'
import { Editor, Node, Path, Range, Transforms } from 'slate'
import {
ReactEditor,
@@ -31,6 +31,12 @@ export function MathElement({
const editor = useSlate()
const selected = useSelected()
const preferences = useContext(PreferenceContext)
+ const visualModePreferences = !!preferences.getKey(visualEditorPreferenceKey)
+ const [isVisualMode, setIsVisualMode] = useState(visualModePreferences)
+
+ useEffect(() => {
+ setIsVisualMode(visualModePreferences)
+ }, [visualModePreferences])
const isInsideListElement = useMemo(() => {
return isElementWithinList(element, editor)
@@ -43,6 +49,13 @@ export function MathElement({
Range.isCollapsed(editor.selection)
if (!shouldShowMathEditor) {
+ console.log('Should not show math editor. Rendering math formula', {
+ focused,
+ selected,
+ editorSelection: editor.selection,
+ isCollapsed: editor.selection && Range.isCollapsed(editor?.selection),
+ })
+
return (
// Slate void elements need to set attributes and contentEditable={false}
// See: https://docs.slatejs.org/api/nodes/element#rendering-void-elements
@@ -53,7 +66,43 @@ export function MathElement({
)
}
- const isVisualMode = !!preferences.getKey(visualEditorPreferenceKey)
+ const VoidWrapper = element.inline ? 'span' : 'div'
+ console.log('Rendering math editor', {
+ focused,
+ selected,
+ editorSelection: editor.selection,
+ isCollapsed: editor.selection && Range.isCollapsed(editor?.selection),
+ })
+ return (
+ // Slate void elements need to set attributes and contentEditable={false}
+ // See: https://docs.slatejs.org/api/nodes/element#rendering-void-elements
+
+ updateElement({ src })}
+ onMoveOutRight={transformOutOfElement}
+ onMoveOutLeft={() => {
+ transformOutOfElement({ reverse: true })
+ }}
+ onDeleteOutRight={() => {
+ transformOutOfElement({ shouldDelete: true })
+ }}
+ onDeleteOutLeft={() => {
+ transformOutOfElement({ shouldDelete: true, reverse: true })
+ }}
+ onEditorChange={(visual) =>
+ preferences.setKey(visualEditorPreferenceKey, visual)
+ }
+ />
+ {children}
+
+ )
function updateElement(update: Partial) {
const path = ReactEditor.findPath(editor, element)
@@ -149,50 +198,65 @@ export function MathElement({
function transformOutOfElement({
reverse = false,
shouldDelete = false,
+ closeThroughModal,
}: {
reverse?: boolean
shouldDelete?: boolean
+ closeThroughModal?: boolean
} = {}) {
const unit = 'character'
Transforms.move(editor, { unit, reverse })
-
if (shouldDelete) {
Transforms.delete(editor, { unit, reverse })
}
- ReactEditor.focus(editor)
- }
+ // if (editor.selection) {
+ // console.log('Setting selection to the end: ', { editor })
+ // // move cursor to the end of line
+ // // setTimeout(() => {
+ // const endOfNode = Editor.end(editor, editor.selection.focus.path)
+ // // const endOfNode = Editor.end(editor, editor.selection)
- const VoidWrapper = element.inline ? 'span' : 'div'
- return (
- // Slate void elements need to set attributes and contentEditable={false}
- // See: https://docs.slatejs.org/api/nodes/element#rendering-void-elements
-
- updateElement({ src })}
- onMoveOutRight={transformOutOfElement}
- onMoveOutLeft={() => {
- transformOutOfElement({ reverse: true })
- }}
- onDeleteOutRight={() => {
- transformOutOfElement({ shouldDelete: true })
- }}
- onDeleteOutLeft={() => {
- transformOutOfElement({ shouldDelete: true, reverse: true })
- }}
- onEditorChange={(visual) =>
- preferences.setKey(visualEditorPreferenceKey, visual)
- }
- />
- {children}
-
- )
+ // Transforms.setSelection(editor, { anchor: endOfNode, focus: endOfNode })
+
+ // Transforms.move(editor, {
+ // edge: 'end',
+ // unit: 'line',
+ // })
+ // // ReactEditor.focus(editor)
+
+ // // Transforms.setSelection(editor, {
+ // // anchor: endOfNode,
+ // // focus: endOfNode,
+ // // })
+ // // })
+ // // setTimeout(() => {
+ // // // Transforms.setSelection(editor, { anchor: end, focus: end })
+ // // ReactEditor.focus(editor)
+ // // })
+ // } else {
+ // console.log(
+ // 'We have no selection, simply ensure that the editor is focused.'
+ // )
+ // setTimeout(() => {
+ // })
+ // }
+
+ // When calling this function from within the 'x' button to close the
+ // popup-modal, a small timeout is needed to reset the selection.
+ // Transforms.deselect() was not needed
+ if (closeThroughModal) {
+ setTimeout(() => {
+ ReactEditor.focus(editor)
+ // move cursor to the end
+ // Transforms.move(editor, {
+ // edge: 'end',
+ // unit: 'line',
+ // })
+ })
+ } else {
+ ReactEditor.focus(editor)
+ }
+ }
}
From 8347a2d6e97c653222a4545b5f2797261d8ba2b9 Mon Sep 17 00:00:00 2001
From: Mikey Stengel
Date: Sun, 6 Aug 2023 11:30:46 +0200
Subject: [PATCH 2/6] Clean up code
---
.../plugins/text/components/math-element.tsx | 56 +------------------
1 file changed, 3 insertions(+), 53 deletions(-)
diff --git a/src/serlo-editor/plugins/text/components/math-element.tsx b/src/serlo-editor/plugins/text/components/math-element.tsx
index 4337634d0d..f6e4eac849 100644
--- a/src/serlo-editor/plugins/text/components/math-element.tsx
+++ b/src/serlo-editor/plugins/text/components/math-element.tsx
@@ -49,13 +49,6 @@ export function MathElement({
Range.isCollapsed(editor.selection)
if (!shouldShowMathEditor) {
- console.log('Should not show math editor. Rendering math formula', {
- focused,
- selected,
- editorSelection: editor.selection,
- isCollapsed: editor.selection && Range.isCollapsed(editor?.selection),
- })
-
return (
// Slate void elements need to set attributes and contentEditable={false}
// See: https://docs.slatejs.org/api/nodes/element#rendering-void-elements
@@ -67,12 +60,6 @@ export function MathElement({
}
const VoidWrapper = element.inline ? 'span' : 'div'
- console.log('Rendering math editor', {
- focused,
- selected,
- editorSelection: editor.selection,
- isCollapsed: editor.selection && Range.isCollapsed(editor?.selection),
- })
return (
// Slate void elements need to set attributes and contentEditable={false}
// See: https://docs.slatejs.org/api/nodes/element#rendering-void-elements
@@ -211,49 +198,12 @@ export function MathElement({
Transforms.delete(editor, { unit, reverse })
}
- // if (editor.selection) {
- // console.log('Setting selection to the end: ', { editor })
- // // move cursor to the end of line
- // // setTimeout(() => {
- // const endOfNode = Editor.end(editor, editor.selection.focus.path)
- // // const endOfNode = Editor.end(editor, editor.selection)
-
- // Transforms.setSelection(editor, { anchor: endOfNode, focus: endOfNode })
-
- // Transforms.move(editor, {
- // edge: 'end',
- // unit: 'line',
- // })
- // // ReactEditor.focus(editor)
-
- // // Transforms.setSelection(editor, {
- // // anchor: endOfNode,
- // // focus: endOfNode,
- // // })
- // // })
- // // setTimeout(() => {
- // // // Transforms.setSelection(editor, { anchor: end, focus: end })
- // // ReactEditor.focus(editor)
- // // })
- // } else {
- // console.log(
- // 'We have no selection, simply ensure that the editor is focused.'
- // )
- // setTimeout(() => {
- // })
- // }
-
- // When calling this function from within the 'x' button to close the
- // popup-modal, a small timeout is needed to reset the selection.
- // Transforms.deselect() was not needed
+ // When calling this function when the 'x' button of the modal is clicked,
+ // a small timeout is needed to reset the selection. Transforms.deselect()
+ // was not needed
if (closeThroughModal) {
setTimeout(() => {
ReactEditor.focus(editor)
- // move cursor to the end
- // Transforms.move(editor, {
- // edge: 'end',
- // unit: 'line',
- // })
})
} else {
ReactEditor.focus(editor)
From 5ec6b8b51b70603efc9c866d2ba79911d62724b4 Mon Sep 17 00:00:00 2001
From: Mikey Stengel
Date: Sun, 6 Aug 2023 11:38:28 +0200
Subject: [PATCH 3/6] Remove horizontal margin of EditorTextArea
---
src/serlo-editor/math/editor.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/serlo-editor/math/editor.tsx b/src/serlo-editor/math/editor.tsx
index a7cd5556e0..7dd613fefd 100644
--- a/src/serlo-editor/math/editor.tsx
+++ b/src/serlo-editor/math/editor.tsx
@@ -51,7 +51,7 @@ const MathEditorTextArea = (props: MathEditorTextAreaProps) => {
return (
Date: Mon, 7 Aug 2023 22:06:21 +0200
Subject: [PATCH 4/6] Implement feedback of Vito and Botho - Hover/focus state
& a11y - Remove unneeded code - Define extra function to close modal
---
src/serlo-editor/math/editor.tsx | 15 ++++++------
.../plugins/equations/editor/inline-math.tsx | 1 +
.../plugins/text/components/math-element.tsx | 23 ++++---------------
3 files changed, 13 insertions(+), 26 deletions(-)
diff --git a/src/serlo-editor/math/editor.tsx b/src/serlo-editor/math/editor.tsx
index b847210bad..630461344e 100644
--- a/src/serlo-editor/math/editor.tsx
+++ b/src/serlo-editor/math/editor.tsx
@@ -1,5 +1,5 @@
import { faCheckCircle, faCircle } from '@fortawesome/free-regular-svg-icons'
-import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'
+import { faQuestionCircle, faXmark } from '@fortawesome/free-solid-svg-icons'
import clsx from 'clsx'
import { useState, useCallback, createRef, useEffect } from 'react'
import { createPortal } from 'react-dom'
@@ -77,8 +77,8 @@ export interface MathEditorProps {
onEditorChange(visual: boolean): void
onInlineChange?(inline: boolean): void
onChange(state: string): void
-
- onMoveOutRight: (options?: { closeThroughModal?: boolean }) => void
+ closeMathEditorOverlay: () => void
+ onMoveOutRight: () => void
onMoveOutLeft(): void
onDeleteOutRight?(): void
onDeleteOutLeft?(): void
@@ -98,7 +98,7 @@ export function MathEditor(props: MathEditorProps) {
(event) => {
event.preventDefault()
// close overlay
- props.onMoveOutRight()
+ props.closeMathEditorOverlay()
},
{
enableOnFormTags: true,
@@ -297,11 +297,12 @@ export function MathEditor(props: MathEditorProps) {
{hasError ? mathStrings.onlyLatex : mathStrings.latexEditorTitle}
{!isVisualMode && (
diff --git a/src/serlo-editor/plugins/equations/editor/inline-math.tsx b/src/serlo-editor/plugins/equations/editor/inline-math.tsx
index ee67b29990..3e475cab09 100644
--- a/src/serlo-editor/plugins/equations/editor/inline-math.tsx
+++ b/src/serlo-editor/plugins/equations/editor/inline-math.tsx
@@ -49,6 +49,7 @@ export function InlineMath(props: InlineMathProps) {
onChange={onChange}
onMoveOutRight={onFocusNext}
onMoveOutLeft={onFocusPrevious}
+ closeMathEditorOverlay={onFocusNext}
/>
)
}
diff --git a/src/serlo-editor/plugins/text/components/math-element.tsx b/src/serlo-editor/plugins/text/components/math-element.tsx
index f6e4eac849..5cbd200a0d 100644
--- a/src/serlo-editor/plugins/text/components/math-element.tsx
+++ b/src/serlo-editor/plugins/text/components/math-element.tsx
@@ -1,4 +1,4 @@
-import React, { useContext, useEffect, useMemo, useState } from 'react'
+import React, { useContext, useMemo } from 'react'
import { Editor, Node, Path, Range, Transforms } from 'slate'
import {
ReactEditor,
@@ -31,12 +31,7 @@ export function MathElement({
const editor = useSlate()
const selected = useSelected()
const preferences = useContext(PreferenceContext)
- const visualModePreferences = !!preferences.getKey(visualEditorPreferenceKey)
- const [isVisualMode, setIsVisualMode] = useState(visualModePreferences)
-
- useEffect(() => {
- setIsVisualMode(visualModePreferences)
- }, [visualModePreferences])
+ const isVisualMode = !!preferences.getKey(visualEditorPreferenceKey)
const isInsideListElement = useMemo(() => {
return isElementWithinList(element, editor)
@@ -73,6 +68,7 @@ export function MathElement({
disableBlock={isInsideListElement}
onInlineChange={handleInlineChange}
onChange={(src) => updateElement({ src })}
+ closeMathEditorOverlay={transformOutOfElement}
onMoveOutRight={transformOutOfElement}
onMoveOutLeft={() => {
transformOutOfElement({ reverse: true })
@@ -185,11 +181,9 @@ export function MathElement({
function transformOutOfElement({
reverse = false,
shouldDelete = false,
- closeThroughModal,
}: {
reverse?: boolean
shouldDelete?: boolean
- closeThroughModal?: boolean
} = {}) {
const unit = 'character'
@@ -198,15 +192,6 @@ export function MathElement({
Transforms.delete(editor, { unit, reverse })
}
- // When calling this function when the 'x' button of the modal is clicked,
- // a small timeout is needed to reset the selection. Transforms.deselect()
- // was not needed
- if (closeThroughModal) {
- setTimeout(() => {
- ReactEditor.focus(editor)
- })
- } else {
- ReactEditor.focus(editor)
- }
+ ReactEditor.focus(editor)
}
}
From 125b8e39b839044114c6516733a0951410c99d90 Mon Sep 17 00:00:00 2001
From: Mikey Stengel
Date: Mon, 7 Aug 2023 23:37:09 +0200
Subject: [PATCH 5/6] Remove comment and let event handler call fn
---
src/serlo-editor/math/editor.tsx | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/serlo-editor/math/editor.tsx b/src/serlo-editor/math/editor.tsx
index 630461344e..32fe11279c 100644
--- a/src/serlo-editor/math/editor.tsx
+++ b/src/serlo-editor/math/editor.tsx
@@ -97,7 +97,6 @@ export function MathEditor(props: MathEditorProps) {
Key.Escape,
(event) => {
event.preventDefault()
- // close overlay
props.closeMathEditorOverlay()
},
{
@@ -297,7 +296,7 @@ export function MathEditor(props: MathEditorProps) {
{hasError ? mathStrings.onlyLatex : mathStrings.latexEditorTitle}