Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Close math editor through ESC and new button #2697

Merged
merged 7 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
58 changes: 43 additions & 15 deletions src/serlo-editor/math/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
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

onMoveOutRight: (options?: { closeThroughModal?: boolean }) => void
hejtful marked this conversation as resolved.
Show resolved Hide resolved
onMoveOutLeft(): void
onDeleteOutRight?(): void
onDeleteOutLeft?(): void
}
Expand All @@ -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 (
<>
Expand Down Expand Up @@ -173,15 +189,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 @@ -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')
Expand All @@ -234,7 +254,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 @@ -245,7 +265,7 @@ export function MathEditor(props: MathEditorProps) {
</div>
)}

{hasError || !useVisualEditor ? renderOverlayPortal() : null}
{hasError || !isVisualMode ? renderOverlayPortal() : null}
</>
)
}
Expand All @@ -260,16 +280,24 @@ 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">
<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.onMoveOutRight({ closeThroughModal: true })}
className="mr-0.5 mt-1 text-sm font-bold text-gray-600"
data-qa="plugin-math-close-formula-editor"
>
x
</button>
CodingDive marked this conversation as resolved.
Show resolved Hide resolved
</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
86 changes: 50 additions & 36 deletions src/serlo-editor/plugins/text/components/math-element.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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)

CodingDive marked this conversation as resolved.
Show resolved Hide resolved
useEffect(() => {
setIsVisualMode(visualModePreferences)
}, [visualModePreferences])

const isInsideListElement = useMemo(() => {
return isElementWithinList(element, editor)
Expand All @@ -53,7 +59,37 @@ 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 })}
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 @@ -149,50 +185,28 @@ export function MathElement({
function transformOutOfElement({
reverse = false,
hejtful marked this conversation as resolved.
Show resolved Hide resolved
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)
// 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)
})
CodingDive marked this conversation as resolved.
Show resolved Hide resolved
} else {
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>
)
}
Loading