From acf3eb99297feba0c2328f461bf7709bd3e80e08 Mon Sep 17 00:00:00 2001 From: Erik Lopez Date: Wed, 4 Nov 2020 17:58:16 +0900 Subject: [PATCH] Remove auto focus on load (#205) * Add block map setup functions * Add style map setup functions * Use customStyleFn instead of customStyleMap property * Remove style and block map states * Rely on mouse to set focus cursor position * Add isActive property to Toolbar * Set active state to false to buttons when toolbar is not active * Bump up package version * Update Toolbar UTs --- package.json | 2 +- src/MUIRichTextEditor.tsx | 117 +++++++++++++++++++++++-------------- src/components/Toolbar.tsx | 6 +- test/Toolbar.test.tsx | 3 + 4 files changed, 82 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index 47ab27e..5ef0630 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mui-rte", - "version": "1.25.0", + "version": "1.26.0", "description": "Material-UI Rich Text Editor and Viewer", "keywords": [ "material-ui", diff --git a/src/MUIRichTextEditor.tsx b/src/MUIRichTextEditor.tsx index 0f072ee..32142a4 100644 --- a/src/MUIRichTextEditor.tsx +++ b/src/MUIRichTextEditor.tsx @@ -1,5 +1,5 @@ import React, { FunctionComponent, useEffect, useState, useRef, - forwardRef, useImperativeHandle, RefForwardingComponent, SyntheticEvent } from 'react' + forwardRef, useImperativeHandle, RefForwardingComponent } from 'react' import Immutable from 'immutable' import classNames from 'classnames' import { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/styles' @@ -8,7 +8,7 @@ import { Editor, EditorState, convertFromRaw, RichUtils, AtomicBlockUtils, CompositeDecorator, convertToRaw, DefaultDraftBlockRenderMap, DraftEditorCommand, DraftHandleValue, DraftStyleMap, ContentBlock, DraftDecorator, - SelectionState, KeyBindingUtil, getDefaultKeyBinding, Modifier + SelectionState, KeyBindingUtil, getDefaultKeyBinding, Modifier, DraftBlockRenderMap } from 'draft-js' import Toolbar, { TToolbarControl, TCustomControl, TToolbarButtonSize } from './components/Toolbar' import Link from './components/Link' @@ -248,10 +248,6 @@ const MUIRichTextEditor: RefForwardingComponent(0) const [editorState, setEditorState] = useState(() => useEditorState(props)) const [focusMediaKey, setFocusMediaKey] = useState("") - const [customRenderers, setCustomRenderers] = useState({ - style: undefined, - block: undefined - }) const editorRef = useRef(null) const editorId = props.id || "mui-rte" @@ -262,6 +258,9 @@ const MUIRichTextEditor: RefForwardingComponent(undefined) const autocompleteLimit = props.autocomplete ? props.autocomplete.suggestLimit || 5 : 5 const isFirstFocus = useRef(true) + const customBlockMapRef = useRef(undefined) + const customStyleMapRef = useRef(undefined) + const isFocusedWithMouse = useRef(false) const selectionRef = useRef({ start: 0, end: 0 @@ -290,31 +289,7 @@ const MUIRichTextEditor: RefForwardingComponent { const editorState = useEditorState(props) - const customBlockMap: any = {} - const customStyleMap = JSON.parse(JSON.stringify(styleRenderMap)) - if (props.customControls) { - props.customControls.forEach(control => { - if (control.type === "inline" && control.inlineStyle) { - customStyleMap[control.name.toUpperCase()] = control.inlineStyle - } - else if (control.type === "block" && control.blockWrapper) { - customBlockMap[control.name.toUpperCase()] = { - element: "div", - wrapper: control.blockWrapper - } - } - }) - } - setCustomRenderers({ - style: customStyleMap, - block: DefaultDraftBlockRenderMap.merge(blockRenderMap, Immutable.Map(customBlockMap)) - }) - const nextEditorState = EditorState.forceSelection(editorState, editorState.getSelection()) - if (props.readOnly === true) { - setEditorState(nextEditorState) - } else { - setEditorState(EditorState.moveFocusToEnd(nextEditorState)) - } + setEditorState(editorState) toggleMouseUpListener(true) return () => { toggleMouseUpListener() @@ -505,16 +480,10 @@ const MUIRichTextEditor: RefForwardingComponent { - if (!event.hasOwnProperty("relatedTarget") || (event as any).relatedTarget == null) { - return false - } - return true - } - - const handleEditorFocus = (event: SyntheticEvent) => { + const handleEditorFocus = () => { handleFocus() - if (!isSyntheticEventTriggeredByTab(event)) { + if (isFocusedWithMouse.current === true) { + isFocusedWithMouse.current = false return } const nextEditorState = EditorState.forceSelection(editorState, editorState.getSelection()) @@ -543,6 +512,7 @@ const MUIRichTextEditor: RefForwardingComponent { + isFocusedWithMouse.current = false setFocus(false) if (props.onBlur) { props.onBlur() @@ -556,8 +526,15 @@ const MUIRichTextEditor: RefForwardingComponent { + isFocusedWithMouse.current = true + } + const handleClearFormat = () => { - const withoutStyles = clearInlineStyles(editorState, customRenderers.style) + if (customStyleMapRef.current === undefined) { + return + } + const withoutStyles = clearInlineStyles(editorState, customStyleMapRef.current) const selectionInfo = getSelectionInfo(editorState) const newEditorState = EditorState.push(editorState, withoutStyles, 'change-inline-style') setEditorState(RichUtils.toggleBlockType(newEditorState, selectionInfo.blockType)) @@ -903,6 +880,47 @@ const MUIRichTextEditor: RefForwardingComponent { + if (customStyleMapRef.current === undefined) { + setupStyleMap() + } + return customStyleMapRef.current! + } + + const setupStyleMap = () => { + const customStyleMap = JSON.parse(JSON.stringify(styleRenderMap)) + if (props.customControls) { + props.customControls.forEach(control => { + if (control.type === "inline" && control.inlineStyle) { + customStyleMap[control.name.toUpperCase()] = control.inlineStyle + } + }) + } + customStyleMapRef.current = customStyleMap + } + + const getBlockMap = (): DraftBlockRenderMap => { + if (customBlockMapRef.current === undefined) { + setupBlockMap() + } + return customBlockMapRef.current! + } + + const setupBlockMap = () => { + const customBlockMap: any = {} + if (props.customControls) { + props.customControls.forEach(control => { + if (control.type === "block" && control.blockWrapper) { + customBlockMap[control.name.toUpperCase()] = { + element: "div", + wrapper: control.blockWrapper + } + } + }) + } + customBlockMapRef.current = DefaultDraftBlockRenderMap.merge(blockRenderMap, Immutable.Map(customBlockMap)) + } + const blockRenderer = (contentBlock: ContentBlock) => { const blockType = contentBlock.getType() if (blockType === 'atomic') { @@ -936,6 +954,15 @@ const MUIRichTextEditor: RefForwardingComponent { + const customStyleMap = getStyleMap() + const styleNames = style.toJS() + return styleNames.reduce((styles: any, styleName: string) => { + styles = customStyleMap[styleName] + return styles + }, {}) + } + const insertAtomicBlock = (editorState: EditorState, type: string, data: any, options?: any) => { const contentState = editorState.getCurrentContent() const contentStateWithEntity = contentState.createEntity(type, 'IMMUTABLE', data) @@ -1060,6 +1087,7 @@ const MUIRichTextEditor: RefForwardingComponent : null} @@ -1073,6 +1101,7 @@ const MUIRichTextEditor: RefForwardingComponent : null} {placeholder} @@ -1080,11 +1109,11 @@ const MUIRichTextEditor: RefForwardingComponent + })} onMouseDown={handleMouseDown} onBlur={handleBlur}> = (props) => { } let active = false const action = props.onClick - if (style.type === "inline") { + if (!props.isActive) { + active = false + } + else if (style.type === "inline") { active = editorState.getCurrentInlineStyle().has(style.style) } else if (style.type === "block") { diff --git a/test/Toolbar.test.tsx b/test/Toolbar.test.tsx index 30505f0..2899442 100644 --- a/test/Toolbar.test.tsx +++ b/test/Toolbar.test.tsx @@ -18,6 +18,7 @@ describe('', () => { id="mui-rte" editorState={editorState} onClick={() => {}} + isActive={true} /> ) const result = wrapper.find(EditorButton) @@ -41,6 +42,7 @@ describe('', () => { editorState={editorState} controls={controls} onClick={() => {}} + isActive={true} /> ) const result = wrapper.find(EditorButton).map(item => { @@ -56,6 +58,7 @@ describe('', () => { editorState={editorState} controls={[]} onClick={() => {}} + isActive={true} /> ) const result = wrapper.find(EditorButton)