Skip to content

Commit

Permalink
Merge pull request #2697 from serlo/fix/close-math-editor
Browse files Browse the repository at this point in the history
Close math editor through ESC and new button
  • Loading branch information
CodingDive authored Aug 8, 2023
2 parents d22dd49 + 233aea6 commit 9347c96
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 51 deletions.
1 change: 1 addition & 0 deletions src/data/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@ export const loggedInData = {
eG: 'e.g.',
functions: 'Functions',
displayAsBlock: 'Display as block',
closeMathFormulaEditor: "Close math formula editor",
},
},
video: {
Expand Down
4 changes: 3 additions & 1 deletion src/serlo-editor/editor-ui/editor-textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ type EditorTextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement> & {
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 (
Expand All @@ -22,6 +23,7 @@ export const EditorTextarea = forwardRef<
)}
{...props}
ref={ref}
data-qa={dataQa}
onKeyDown={(e) => {
if (!ref || typeof ref === 'function' || !ref.current) return

Expand Down
60 changes: 44 additions & 16 deletions src/serlo-editor/math/editor.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
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'
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'
Expand Down Expand Up @@ -49,7 +51,7 @@ const MathEditorTextArea = (props: MathEditorTextAreaProps) => {
return (
<EditorTextarea
className={tw`
!m-0.5 h-24 !w-[80vw] !max-w-[600px] rounded-md !border-2
mx-0 my-1 h-24 !w-[80vw] !max-w-[600px] rounded-md !border-2
border-transparent text-black !shadow-none focus:border-editor-primary
`}
onChange={parentOnChange}
Expand All @@ -59,6 +61,7 @@ const MathEditorTextArea = (props: MathEditorTextAreaProps) => {
onMoveOutLeft={props.onMoveOutLeft}
value={latex}
ref={textareaRef}
dataQa="plugin-math-latex-editor"
/>
)
}
Expand All @@ -74,8 +77,9 @@ export interface MathEditorProps {
onEditorChange(visual: boolean): void
onInlineChange?(inline: boolean): void
onChange(state: string): void
onMoveOutRight?(): void
onMoveOutLeft?(): void
closeMathEditorOverlay: () => void
onMoveOutRight: () => void
onMoveOutLeft(): void
onDeleteOutRight?(): void
onDeleteOutLeft?(): void
}
Expand All @@ -89,7 +93,18 @@ export function MathEditor(props: MathEditorProps) {

const { visual, readOnly, state, disableBlock } = props

const useVisualEditor = visual && !hasError
useHotkeys(
Key.Escape,
(event) => {
event.preventDefault()
props.closeMathEditorOverlay()
},
{
enableOnFormTags: true,
}
)

const isVisualMode = visual && !hasError

return (
<>
Expand Down Expand Up @@ -173,15 +188,19 @@ export function MathEditor(props: MathEditorProps) {
return state ? (
<MathRenderer {...props} />
) : (
<span className="bg-gray-300" {...props.additionalContainerProps}>
<span
className="bg-gray-300"
{...props.additionalContainerProps}
data-qa="plugin-math-renderer"
>
{mathStrings.formula}
</span>
)
}

return (
<>
{useVisualEditor ? (
{isVisualMode ? (
<div
onClick={(e) => e.stopPropagation()}
ref={anchorRef}
Expand Down Expand Up @@ -221,7 +240,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')
Expand All @@ -241,7 +260,7 @@ export function MathEditor(props: MathEditorProps) {
<FaIcon icon={props.inline ? faCircle : faCheckCircle} />
</button>
)}
{useVisualEditor && (
{isVisualMode && (
<button
onMouseDown={() => setHelpOpen(true)}
className="mx-2 text-almost-black hover:text-editor-primary"
Expand All @@ -252,7 +271,7 @@ export function MathEditor(props: MathEditorProps) {
</div>
)}

{hasError || !useVisualEditor ? renderOverlayPortal() : null}
{hasError || !isVisualMode ? renderOverlayPortal() : null}
</>
)
}
Expand All @@ -267,19 +286,28 @@ export function MathEditor(props: MathEditorProps) {
}

function renderOverlayPortal() {
const children = (
return (
<div
className="fixed bottom-0 z-50 rounded-t-xl bg-editor-primary-100 p-3 shadow-menu"
onClick={(e) => e.stopPropagation()} // double/triple clicks close overlay otherwise (#2700)
>
<p className="mr-0.5 mt-1 text-right text-sm font-bold text-gray-600">
{hasError ? mathStrings.onlyLatex : mathStrings.latexEditorTitle}
</p>
{!useVisualEditor && (
<div className="flex items-center justify-between">
<p className="mr-0.5 mt-1 text-right text-sm font-bold text-gray-600">
{hasError ? mathStrings.onlyLatex : mathStrings.latexEditorTitle}
</p>
<button
onClick={props.closeMathEditorOverlay}
className="serlo-button-editor-secondary py-0"
aria-label={mathStrings.closeMathFormulaEditor}
data-qa="plugin-math-close-formula-editor"
>
<FaIcon icon={faXmark} />
</button>
</div>
{!isVisualMode && (
<MathEditorTextArea {...props} defaultValue={state} />
)}
</div>
)
return children
}
}
1 change: 1 addition & 0 deletions src/serlo-editor/math/renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const MathRenderer = React.memo(
}
}
{...additionalContainerProps}
data-qa="plugin-math-renderer"
/>
)

Expand Down
1 change: 1 addition & 0 deletions src/serlo-editor/plugins/equations/editor/inline-math.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export function InlineMath(props: InlineMathProps) {
onChange={onChange}
onMoveOutRight={onFocusNext}
onMoveOutLeft={onFocusPrevious}
closeMathEditorOverlay={onFocusNext}
/>
)
}
67 changes: 33 additions & 34 deletions src/serlo-editor/plugins/text/components/math-element.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export function MathElement({
const editor = useSlate()
const selected = useSelected()
const preferences = useContext(PreferenceContext)
const isVisualMode = !!preferences.getKey(visualEditorPreferenceKey)

const isInsideListElement = useMemo(() => {
return isElementWithinList(element, editor)
Expand All @@ -53,7 +54,38 @@ export function MathElement({
)
}

const isVisualMode = !!preferences.getKey(visualEditorPreferenceKey)
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
<VoidWrapper {...attributes} tabIndex={-1} contentEditable={false}>
<MathEditor
autofocus
state={element.src}
inline={element.inline}
readOnly={false}
visual={isVisualMode}
disableBlock={isInsideListElement}
onInlineChange={handleInlineChange}
onChange={(src) => updateElement({ src })}
closeMathEditorOverlay={transformOutOfElement}
onMoveOutRight={transformOutOfElement}
onMoveOutLeft={() => {
transformOutOfElement({ reverse: true })
}}
onDeleteOutRight={() => {
transformOutOfElement({ shouldDelete: true })
}}
onDeleteOutLeft={() => {
transformOutOfElement({ shouldDelete: true, reverse: true })
}}
onEditorChange={(visual) =>
preferences.setKey(visualEditorPreferenceKey, visual)
}
/>
{children}
</VoidWrapper>
)

function updateElement(update: Partial<MathElementType>) {
const path = ReactEditor.findPath(editor, element)
Expand Down Expand Up @@ -156,43 +188,10 @@ export function MathElement({
const unit = 'character'

Transforms.move(editor, { unit, reverse })

if (shouldDelete) {
Transforms.delete(editor, { unit, reverse })
}

ReactEditor.focus(editor)
}

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
<VoidWrapper {...attributes} tabIndex={-1} contentEditable={false}>
<MathEditor
autofocus
state={element.src}
inline={element.inline}
readOnly={false}
visual={isVisualMode}
disableBlock={isInsideListElement}
onInlineChange={handleInlineChange}
onChange={(src) => 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}
</VoidWrapper>
)
}

0 comments on commit 9347c96

Please sign in to comment.