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 5 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
61 changes: 45 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,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.closeMathEditorOverlay()
},
{
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 @@ -221,7 +241,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 +261,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 +272,7 @@ export function MathEditor(props: MathEditorProps) {
</div>
)}

{hasError || !useVisualEditor ? renderOverlayPortal() : null}
{hasError || !isVisualMode ? renderOverlayPortal() : null}
</>
)
}
Expand All @@ -267,19 +287,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()}
CodingDive marked this conversation as resolved.
Show resolved Hide resolved
className="mr-0.5 mt-1 text-sm font-bold text-gray-600 hover:bg-gray-200 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-gray-700"
CodingDive marked this conversation as resolved.
Show resolved Hide resolved
aria-label="Close math formula editor"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

follow up: i18n or borrow shorter close string from e.g. strings.share.close.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the English translation, the rest is done automagically through crowdin?

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>
)
}