From 0e3a62e2123841347c51ef971e3bdae08730f718 Mon Sep 17 00:00:00 2001 From: Erik Lopez Date: Thu, 10 Oct 2019 15:58:34 +0900 Subject: [PATCH 1/3] Rewrite class component as FunctionComponent * Optimize 'EditorControls' rendering performance * Update 'ref-save' example --- examples/custom-inline-toolbar/index.tsx | 5 + examples/ref-save/index.tsx | 55 +- index.ts | 2 +- package.json | 2 +- src/MUIRichTextEditor.tsx | 643 ++++++++++++----------- src/components/EditorControls.tsx | 11 +- src/components/UrlPopover.tsx | 7 +- src/utils.ts | 16 +- test/setup.ts | 4 +- 9 files changed, 383 insertions(+), 362 deletions(-) diff --git a/examples/custom-inline-toolbar/index.tsx b/examples/custom-inline-toolbar/index.tsx index 4c1d964..a1b92a3 100644 --- a/examples/custom-inline-toolbar/index.tsx +++ b/examples/custom-inline-toolbar/index.tsx @@ -2,12 +2,17 @@ import React from 'react' import MUIRichTextEditor from '../../' import InvertColorsIcon from '@material-ui/icons/InvertColors' +const save = (data: string) => { + console.log(data) +} + const CustomInlineToolbar = () => { return ( { + + const ref = useRef() - handleClick = () => { - this.ref.current.save() + const handleClick = () => { + (ref as any).current.save() } - handleSave = (data: string) => { + const handleSave = (data: string) => { console.log(data) } - render() { - return ( -
- Save editor state from external button: - - -
- ) - } + return ( +
+ Save editor state from external button: + + +
+ ) } export default RefSave \ No newline at end of file diff --git a/index.ts b/index.ts index c7c5c1e..bd2b1ad 100644 --- a/index.ts +++ b/index.ts @@ -1 +1 @@ -export {default} from './src/MUIRichTextEditor' \ No newline at end of file +export {default} from './src/MUIRichTextEditor' diff --git a/package.json b/package.json index f20962f..06e76d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mui-rte", - "version": "1.4.0", + "version": "1.5.0", "description": "Material-UI Rich Text Editor and Viewer", "keywords": [ "material-ui", diff --git a/src/MUIRichTextEditor.tsx b/src/MUIRichTextEditor.tsx index 7a19371..8406092 100644 --- a/src/MUIRichTextEditor.tsx +++ b/src/MUIRichTextEditor.tsx @@ -1,4 +1,5 @@ -import * as React from 'react' +import React, { FunctionComponent, useEffect, useState, useRef, + forwardRef, useImperativeHandle, RefForwardingComponent } from 'react' import Immutable from 'immutable' import classNames from 'classnames' import { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/styles' @@ -71,7 +72,7 @@ const styles = ({ spacing, typography, palette }: Theme) => createStyles({ }) export type TDecorator = { - component: React.FC + component: FunctionComponent regex: RegExp } @@ -85,113 +86,164 @@ interface IMUIRichTextEditorProps extends WithStyles { onSave?: (data: string) => void onChange?: (state: EditorState) => void customControls?: TCustomControl[], - decorators?: TDecorator[], - ref?: any + decorators?: TDecorator[] toolbar?: boolean inlineToolbar?: boolean inlineToolbarControls?: Array } type IMUIRichTextEditorState = { - editorState: EditorState - focused: boolean anchorLinkPopover?: HTMLElement anchorMediaPopover?: HTMLElement urlValue?: string urlKey?: string urlWidth?: number urlHeight?: number - toolbarPosition?: { - top: number - left: number + toolbarPosition?: TToolbarPosition +} + +type TStateOffset = { + start: number, + end: number +} + +type TToolbarPosition = { + top: number + left: number +} + +type TCustomRenderers = { + style?: DraftStyleMap + block?: Immutable.Map +} + +const blockRenderMap = Immutable.Map({ + 'blockquote': { + element: "blockquote", + wrapper:
+ }, + 'code-block': { + element: "pre", + wrapper: + } +}) +const styleRenderMap: DraftStyleMap = { + 'STRIKETROUGH': { + textDecoration: "line-through" + }, + 'HIGHLIGHT': { + backgroundColor: "yellow" } } -class MUIRichTextEditor extends React.Component { - private blockRenderMap = Immutable.Map({ - 'blockquote': { - element: "blockquote", - wrapper:
- }, - 'code-block': { - element: "pre", - wrapper: - } +const MUIRichTextEditor: RefForwardingComponent = (props, ref) => { + const { classes, controls, customControls } = props + const [state, setState] = useState({}) + const [focus, setFocus] = useState(false) + const [editorState, setEditorState] = useState(EditorState.createEmpty()) + const [customRenderers, setCustomRenderers] = useState({ + style: undefined, + block: undefined }) - private styleRenderMap: DraftStyleMap = { - 'STRIKETROUGH': { - textDecoration: "line-through" - }, - 'HIGHLIGHT': { - backgroundColor: "yellow" + + const editorRef = useRef(null) + const selectionRef = useRef({ + start: 0, + end: 0 + }) + const toolbarPositionRef = useRef(undefined) + const editorStateRef = useRef(editorState) + + /** + * Expose the save method + */ + useImperativeHandle(ref, () => ({ + save: () => { + handleSave() } - } - private extendedBlockRenderMap: Immutable.Map - constructor(props: IMUIRichTextEditorProps) { - super(props) + })) + + useEffect(() => { const decorators: DraftDecorator[] = [ { - strategy: this.findLinkEntities, + strategy: findLinkEntities, component: Link, } ] if (props.decorators) { props.decorators.forEach(deco => decorators.push({ strategy: (contentBlock: any, callback: any, contentState: any) => { - this.findDecoWithRegex(deco.regex, contentBlock, callback) + findDecoWithRegex(deco.regex, contentBlock, callback) }, component: deco.component })) } const decorator = new CompositeDecorator(decorators) - const editorState = (this.props.value) - ? EditorState.createWithContent(convertFromRaw(JSON.parse(this.props.value)), decorator) + const editorState = (props.value) + ? EditorState.createWithContent(convertFromRaw(JSON.parse(props.value)), decorator) : EditorState.createEmpty(decorator) - let customBlockRenderMap: any = {} - if (this.props.customControls) { - this.props.customControls.forEach(control => { + const customBlockMap: any = {} + const customStyleMap = JSON.parse(JSON.stringify(styleRenderMap)) + if (props.customControls) { + props.customControls.forEach(control => { if (control.type === "inline" && control.inlineStyle) { - this.styleRenderMap[control.name.toUpperCase()] = control.inlineStyle + customStyleMap[control.name.toUpperCase()] = control.inlineStyle } else if (control.type === "block" && control.blockWrapper) { - customBlockRenderMap[control.name.toUpperCase()] = { + customBlockMap[control.name.toUpperCase()] = { element: "div", wrapper: control.blockWrapper } } }) } - this.state = { - editorState: editorState, - focused: false + setCustomRenderers({ + style: customStyleMap, + block: DefaultDraftBlockRenderMap.merge(blockRenderMap, Immutable.Map(customBlockMap)) + }) + setEditorState(editorState) + const editor: HTMLElement = (editorRef.current as any).editor + editor.addEventListener("mouseup", handleSetToolbarPosition) + return () => { + const editor: HTMLElement = (editorRef.current as any).editor + editor.removeEventListener("mouseup", handleSetToolbarPosition) } - this.extendedBlockRenderMap = DefaultDraftBlockRenderMap.merge(this.blockRenderMap, Immutable.Map(customBlockRenderMap)) - } + }, []) - componentDidMount() { - if (this.refs.editor) { - const editor: HTMLElement = (this.refs.editor as any).editor - editor.addEventListener("mouseup", this.handleSetToolbarPosition) - } - } + useEffect(() => { + editorStateRef.current = editorState + }, [editorState]) - componentWillUnmount() { - if (this.refs.editor) { - const editor: HTMLElement = (this.refs.editor as any).editor - editor.removeEventListener("mouseup", this.handleSetToolbarPosition) + + useEffect(() => { + toolbarPositionRef.current = state.toolbarPosition + }, [state.toolbarPosition]) + + useEffect(() => { + if (state.anchorLinkPopover === undefined) { + refocus() } - } + }, [state.anchorLinkPopover]) - handleSetToolbarPosition = () => { + const handleSetToolbarPosition = () => { setTimeout(() => { - const selection = this.state.editorState.getSelection() - if (selection.isCollapsed()) { - this.setState({ - toolbarPosition: undefined - }) + const selection = (editorStateRef.current as any).getSelection() + if (selection.isCollapsed() || (toolbarPositionRef !== undefined && + selectionRef.current.start === selection.getStartOffset() && + selectionRef.current.end === selection.getEndOffset())) { + setState({ + ...state, + toolbarPosition: undefined + }) return } - const editor: HTMLElement = (this.refs.editor as any).editor + + selectionRef.current = { + start: selection.getStartOffset(), + end: selection.getEndOffset() + } + + const editor: HTMLElement = (editorRef.current as any).editor const selectionRect = getVisibleSelectionRect(window) const editorRect = editor.getBoundingClientRect() if (!selectionRect) { @@ -201,23 +253,36 @@ class MUIRichTextEditor extends React.Component { - this.setState({ - editorState: state - }) - if (this.props.onChange) { - this.props.onChange(state) + const handleChange = (state: EditorState) => { + setEditorState(state) + if (props.onChange) { + props.onChange(state) } } - handleClearFormat = () => { - const { editorState } = this.state + const handleFocus = () => { + setFocus(true) + setTimeout(() => (editorRef.current as any).focus(), 0) + } + + const handleBlur = () => { + setFocus(false) + if (!state.anchorLinkPopover) { + setState({ + ...state, + toolbarPosition: undefined + }) + } + } + + const handleClearFormat = () => { const selectionInfo = getSelectionInfo(editorState) let newEditorState = editorState selectionInfo.inlineStyle.forEach((effect) => { @@ -226,106 +291,47 @@ class MUIRichTextEditor extends React.Component { - (this.refs.editor as any).focus() - this.setState({ - focused: true - }) + setEditorState(newEditorState) } - handleBlur = () => { - this.setState({ - focused: false - }) - if (!this.state.anchorLinkPopover) { - this.setState({ - toolbarPosition: undefined - }) + const handleSave = () => { + if (props.onSave) { + props.onSave(JSON.stringify(convertToRaw(editorState.getCurrentContent()))) } } - handleCloseAnchorLinkPopover = () => { - this.setState({ - anchorLinkPopover: undefined - }) - } - - handleKeyCommand = (command: DraftEditorCommand, editorState: EditorState): DraftHandleValue => { + const handleKeyCommand = (command: DraftEditorCommand, editorState: EditorState): DraftHandleValue => { const newState = RichUtils.handleKeyCommand(editorState, command) if (newState) { - this.handleChange(newState) + handleChange(newState) return "handled" } return "not-handled" } - handleCustomClick = (style: any) => { - if (!this.props.customControls) { + const handleCustomClick = (style: any) => { + if (!props.customControls) { return } - for (let control of this.props.customControls) { + for (let control of props.customControls) { if (control.name.toUpperCase() === style) { if (control.onClick) { - control.onClick(this.state.editorState, control.name) + control.onClick(editorState, control.name) } break } } } - handleUndo = () => { - this.setState(prevState => { - return { - editorState: EditorState.undo(prevState.editorState) - } - }) - } - - handleRedo = () => { - this.setState(prevState => { - return { - editorState: EditorState.redo(prevState.editorState) - } - }) - } - - save = () => { - if (this.props.onSave) { - this.props.onSave(JSON.stringify(convertToRaw(this.state.editorState.getCurrentContent()))) - } + const handleUndo = () => { + setEditorState(EditorState.undo(editorState)) } - updateAndFocus = (state: EditorState) => { - this.setState({ - editorState: state - }, () => { - setTimeout(() => this.handleFocus(), 0) - }) - } - - toggleBlockType = (blockType: any) => { - this.updateAndFocus( - RichUtils.toggleBlockType( - this.state.editorState, - blockType - ) - ) - } - - toggleInlineStyle = (inlineStyle: any) => { - this.updateAndFocus( - RichUtils.toggleInlineStyle( - this.state.editorState, - inlineStyle - ) - ) + const handleRedo = () => { + setEditorState(EditorState.redo(editorState)) } - promptForLink = (style: string, toolbarMode: boolean) => { - const { editorState } = this.state + const handlePromptForLink = (style: string, toolbarMode: boolean) => { const selection = editorState.getSelection() if (!selection.isCollapsed()) { @@ -340,35 +346,60 @@ class MUIRichTextEditor extends React.Component { - setTimeout(() => document.getElementById("mui-rte-link-popover")!.focus(), 0) + toolbarPosition: !toolbarMode ? undefined : state.toolbarPosition, + anchorLinkPopover: !toolbarMode ? document.getElementById("mui-rte-link-control")! + : document.getElementById("mui-rte-link-control-toolbar")! }) } } - removeLink = () => { - const { editorState } = this.state + const handlePromptForMedia = (style: string, toolbarMode: boolean) => { + let url = '' + let width = undefined + let height = undefined + let urlKey = undefined + const selectionInfo = getSelectionInfo(editorState) + const contentState = editorState.getCurrentContent() + const linkKey = selectionInfo.linkKey + + if (linkKey) { + const linkInstance = contentState.getEntity(linkKey) + url = linkInstance.getData().url + width = linkInstance.getData().width + height = linkInstance.getData().height + urlKey = linkKey + } + setState({ + ...state, + urlValue: url, + urlKey: urlKey, + urlWidth: width, + urlHeight: height, + toolbarPosition: !toolbarMode ? undefined : state.toolbarPosition, + anchorMediaPopover: !toolbarMode ? document.getElementById("mui-rte-image-control")! + : document.getElementById("mui-rte-image-control-toolbar")! + }) + } + + const removeLink = () => { const selection = editorState.getSelection() - this.updateStateForPopover(RichUtils.toggleLink(editorState, selection, null)) + updateStateForPopover(RichUtils.toggleLink(editorState, selection, null)) } - confirmLink = (url?: string) => { - const { editorState, urlKey } = this.state + const confirmLink = (url?: string) => { + const { urlKey } = state if (!url) { if (urlKey) { - this.removeLink() + removeLink() return } - this.setState({ + setState({ + ...state, anchorLinkPopover: undefined - }, () => { - this.refocus() }) return } @@ -398,46 +429,15 @@ class MUIRichTextEditor extends React.Component { - const { editorState } = this.state - let url = '' - let width = undefined - let height = undefined - let urlKey = undefined - const selectionInfo = getSelectionInfo(editorState) - const contentState = editorState.getCurrentContent() - const linkKey = selectionInfo.linkKey - - if (linkKey) { - const linkInstance = contentState.getEntity(linkKey) - url = linkInstance.getData().url - width = linkInstance.getData().width - height = linkInstance.getData().height - urlKey = linkKey - } - this.setState({ - urlValue: url, - urlKey: urlKey, - urlWidth: width, - urlHeight: height, - toolbarPosition: !toolbarMode ? undefined : this.state.toolbarPosition, - anchorMediaPopover: !toolbarMode ? document.getElementById("mui-rte-image-control")! - : document.getElementById("mui-rte-image-control-toolbar")! - }, () => { - setTimeout(() => document.getElementById("mui-rte-media-popover")!.focus(), 0) - }) + updateStateForPopover(replaceEditorState) } - confirmMedia = (url?: string, width?: number, height?: number) => { - const { editorState, urlKey } = this.state + const confirmMedia = (url?: string, width?: number, height?: number) => { + const { urlKey } = state if (!url) { - this.setState({ + setState({ + ...state, anchorMediaPopover: undefined - }, () => { - this.refocus() }) return } @@ -449,7 +449,7 @@ class MUIRichTextEditor extends React.Component { - this.setState({ - editorState: editorState, + const updateStateForPopover = (editorState: EditorState) => { + setEditorState(editorState) + setState({ + ...state, anchorLinkPopover: undefined, anchorMediaPopover: undefined, urlValue: undefined, urlKey: undefined - }, () => { - this.refocus() }) } - refocus = () => { - setTimeout(() => (this.refs.editor as any).blur(), 0) - setTimeout(() => this.handleFocus(), 1) + const refocus = () => { + setTimeout(() => (editorRef.current as any).blur(), 0) + setTimeout(() => (editorRef.current as any).focus(), 1) } - findLinkEntities(contentBlock: any, callback: any, contentState: any) { - contentBlock.findEntityRanges( - (character: any) => { - const entityKey = character.getEntity() - return ( - entityKey !== null && - contentState.getEntity(entityKey).getType() === 'LINK' - ) - }, - callback + const toggleBlockType = (blockType: any) => { + setEditorState( + RichUtils.toggleBlockType( + editorState, + blockType + ) ) } - findDecoWithRegex(regex: RegExp, contentBlock: any, callback: any) { - const text = contentBlock.getText() - let matchArr, start - while ((matchArr = regex.exec(text)) !== null) { - start = matchArr.index - callback(start, start + matchArr[0].length) - } + const toggleInlineStyle = (inlineStyle: any) => { + setEditorState( + RichUtils.toggleInlineStyle( + editorState, + inlineStyle + ) + ) } - blockRenderer = (contentBlock: ContentBlock) => { + const blockRenderer = (contentBlock: ContentBlock) => { const blockType = contentBlock.getType() if (blockType === 'atomic') { - const contentState = this.state.editorState.getCurrentContent() + const contentState = editorState.getCurrentContent() const entity = contentBlock.getEntityAt(0) if (entity) { const type = contentState.getEntity(entity).getType() @@ -531,112 +526,128 @@ class MUIRichTextEditor extends React.Component { + contentBlock.findEntityRanges( + (character: any) => { + const entityKey = character.getEntity() + return ( + entityKey !== null && + contentState.getEntity(entityKey).getType() === 'LINK' + ) + }, + callback + ) + } + + const findDecoWithRegex = (regex: RegExp, contentBlock: any, callback: any) => { + const text = contentBlock.getText() + let matchArr, start + while ((matchArr = regex.exec(text)) !== null) { + start = matchArr.index + callback(start, start + matchArr[0].length) + } + } + + const renderToolbar = props.toolbar === undefined || props.toolbar + const inlineToolbarControls = props.inlineToolbarControls || ["bold", "italic", "underline", "clear"] + const editable = props.readOnly === undefined || !props.readOnly + + let className = "" + let placeholder: React.ReactElement | null = null + if (!focus) { + const contentState = editorState.getCurrentContent() + if (!contentState.hasText()) { placeholder = (
- {this.props.label || ""} + {props.label || ""}
) className = classes.hidePlaceholder } + } - return ( -
-
- {this.props.inlineToolbar && editable && this.state.toolbarPosition ? - { - this.setState({ - toolbarPosition: undefined - }) - }}> - - - : null} - {editable && renderToolbar ? + return ( +
+
+ {props.inlineToolbar && editable && state.toolbarPosition ? + - : null} - {placeholder} -
-
- -
-
- {this.state.anchorLinkPopover ? - - : null} - {this.state.anchorMediaPopover ? - + : null} + {editable && renderToolbar ? + + : null} + {placeholder} +
+
+ - : null} +
+ {state.anchorLinkPopover ? + + : null} + {state.anchorMediaPopover ? + + : null}
- ) - } +
+ ) } -export default withStyles(styles, { withTheme: true, name: "MUIRichTextEditor" })(MUIRichTextEditor) \ No newline at end of file +export default withStyles(styles, { withTheme: true, name: "MUIRichTextEditor" })(forwardRef(MUIRichTextEditor)) diff --git a/src/components/EditorControls.tsx b/src/components/EditorControls.tsx index c1e4d68..3b04f9f 100644 --- a/src/components/EditorControls.tsx +++ b/src/components/EditorControls.tsx @@ -193,7 +193,7 @@ interface IBlockStyleControlsProps extends KeyString { const EditorControls: FunctionComponent = (props) => { const [availableControls, setAvailableControls] = useState(props.controls ? [] : STYLE_TYPES) - const selectionInfo = getSelectionInfo(props.editorState) + const {editorState} = props useEffect(() => { if (!props.controls) { @@ -234,15 +234,18 @@ const EditorControls: FunctionComponent = (props) => { let active = false let action = null if (style.type === "inline") { - active = selectionInfo.inlineStyle.has(style.style) + active = editorState.getCurrentInlineStyle().has(style.style) action = props.onToggleInline } else if (style.type === "block") { - active = style.style === selectionInfo.blockType + const selection = editorState.getSelection() + active = style.style === editorState.getCurrentContent().getBlockForKey(selection.getStartKey()).getType() action = props.onToggleBlock } else { - active = style.style === selectionInfo.entityType + if (style.style === "IMAGE" || style.style === "LINK") { + active = style.style === getSelectionInfo(editorState).entityType + } action = props[style.clickFnName!] } diff --git a/src/components/UrlPopover.tsx b/src/components/UrlPopover.tsx index 2160175..152133d 100644 --- a/src/components/UrlPopover.tsx +++ b/src/components/UrlPopover.tsx @@ -15,7 +15,6 @@ const styles = ({spacing}: Theme) => createStyles({ }) interface IUrlPopoverStateProps extends WithStyles { - id: string url?: string width?: number height?: number @@ -32,14 +31,15 @@ type TUrlPopoverState = { } const UrlPopover: FunctionComponent = (props) => { - const [state, setState] = useState({ urlError: false, urlValue: props.url, width: props.width, height: props.height }) - const {classes, id} = props + + const {classes} = props + const onSizeChange = (data: any, prop: "width" | "height") => { if (data === "") { setState({...state, [prop]: undefined}) @@ -71,7 +71,6 @@ const UrlPopover: FunctionComponent = (props) => { {setState({...state, urlValue: event.target.value})}} label="URL" diff --git a/src/utils.ts b/src/utils.ts index 5405edf..e56bcc7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,20 +1,26 @@ import { EditorState, DraftBlockType } from 'draft-js' import Immutable from 'immutable' -const getSelectionInfo = (editorState: EditorState): { +export type TSelectionInfo = { inlineStyle: Immutable.OrderedSet, blockType: DraftBlockType, entityType: string | null, linkKey: string -} => { +} + +/** + * Get the current selection details + */ +const getSelectionInfo = (editorState: EditorState): TSelectionInfo => { const selection = editorState.getSelection() - const startOffset = editorState.getSelection().getStartOffset() - const contentBlock = editorState.getCurrentContent().getBlockForKey(selection.getStartKey()) + const startOffset = selection.getStartOffset() + const currentContent = editorState.getCurrentContent() + const contentBlock = currentContent.getBlockForKey(selection.getStartKey()) const currentStyle = editorState.getCurrentInlineStyle() const linkKey = contentBlock.getEntityAt(startOffset) let entityType = null if (linkKey) { - const linkInstance = editorState.getCurrentContent().getEntity(linkKey) + const linkInstance = currentContent.getEntity(linkKey) entityType = linkInstance.getType() } return { diff --git a/test/setup.ts b/test/setup.ts index 72e0ad9..5ab098f 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -2,4 +2,6 @@ import 'jsdom-global/register' import Enzyme from 'enzyme' import Adapter from 'enzyme-adapter-react-16' -Enzyme.configure({adapter: new Adapter()}) \ No newline at end of file +Enzyme.configure({adapter: new Adapter()}) +const noop = () => {} +Object.defineProperty(window, 'scrollTo', { value: noop, writable: true }) From 9dad90dd47bb614e04815df2eca589fde060bcba Mon Sep 17 00:00:00 2001 From: Erik Lopez Date: Thu, 10 Oct 2019 16:50:10 +0900 Subject: [PATCH 2/3] Refactor 'UrlPopover' component usage --- src/MUIRichTextEditor.tsx | 48 ++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/src/MUIRichTextEditor.tsx b/src/MUIRichTextEditor.tsx index 8406092..26a4af4 100644 --- a/src/MUIRichTextEditor.tsx +++ b/src/MUIRichTextEditor.tsx @@ -93,13 +93,13 @@ interface IMUIRichTextEditorProps extends WithStyles { } type IMUIRichTextEditorState = { - anchorLinkPopover?: HTMLElement - anchorMediaPopover?: HTMLElement + anchorUrlPopover?: HTMLElement urlValue?: string urlKey?: string urlWidth?: number urlHeight?: number toolbarPosition?: TToolbarPosition + sizeProps?: boolean } type TStateOffset = { @@ -220,10 +220,10 @@ const MUIRichTextEditor: RefForwardingComponent = }, [state.toolbarPosition]) useEffect(() => { - if (state.anchorLinkPopover === undefined) { + if (state.anchorUrlPopover === undefined) { refocus() } - }, [state.anchorLinkPopover]) + }, [state.anchorUrlPopover]) const handleSetToolbarPosition = () => { setTimeout(() => { @@ -274,7 +274,7 @@ const MUIRichTextEditor: RefForwardingComponent = const handleBlur = () => { setFocus(false) - if (!state.anchorLinkPopover) { + if (!state.anchorUrlPopover) { setState({ ...state, toolbarPosition: undefined @@ -351,8 +351,9 @@ const MUIRichTextEditor: RefForwardingComponent = urlValue: url, urlKey: urlKey, toolbarPosition: !toolbarMode ? undefined : state.toolbarPosition, - anchorLinkPopover: !toolbarMode ? document.getElementById("mui-rte-link-control")! - : document.getElementById("mui-rte-link-control-toolbar")! + anchorUrlPopover: !toolbarMode ? document.getElementById("mui-rte-link-control")! + : document.getElementById("mui-rte-link-control-toolbar")!, + sizeProps: undefined }) } } @@ -374,14 +375,14 @@ const MUIRichTextEditor: RefForwardingComponent = urlKey = linkKey } setState({ - ...state, urlValue: url, urlKey: urlKey, urlWidth: width, urlHeight: height, toolbarPosition: !toolbarMode ? undefined : state.toolbarPosition, - anchorMediaPopover: !toolbarMode ? document.getElementById("mui-rte-image-control")! - : document.getElementById("mui-rte-image-control-toolbar")! + anchorUrlPopover: !toolbarMode ? document.getElementById("mui-rte-image-control")! + : document.getElementById("mui-rte-image-control-toolbar")!, + sizeProps: true }) } @@ -399,7 +400,7 @@ const MUIRichTextEditor: RefForwardingComponent = } setState({ ...state, - anchorLinkPopover: undefined + anchorUrlPopover: undefined }) return } @@ -437,7 +438,7 @@ const MUIRichTextEditor: RefForwardingComponent = if (!url) { setState({ ...state, - anchorMediaPopover: undefined + anchorUrlPopover: undefined }) return } @@ -478,10 +479,12 @@ const MUIRichTextEditor: RefForwardingComponent = setEditorState(editorState) setState({ ...state, - anchorLinkPopover: undefined, - anchorMediaPopover: undefined, + anchorUrlPopover: undefined, urlValue: undefined, - urlKey: undefined + urlKey: undefined, + sizeProps: undefined, + urlWidth: undefined, + urlHeight: undefined, }) } @@ -628,21 +631,14 @@ const MUIRichTextEditor: RefForwardingComponent = />
- {state.anchorLinkPopover ? - - : null} - {state.anchorMediaPopover ? + {state.anchorUrlPopover ? : null} From b86c37b43b2d7d796f70a46427dd9c541f69e407 Mon Sep 17 00:00:00 2001 From: Erik Lopez Date: Thu, 10 Oct 2019 17:11:16 +0900 Subject: [PATCH 3/3] Update documentation --- README.md | 2 +- examples/theme/index.tsx | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8e6e788..c2d308b 100644 --- a/README.md +++ b/README.md @@ -266,7 +266,7 @@ $ npm run serve ## Future plans -- Code refactor +- Add new features - Increase test coverage ## Suggestions and issues diff --git a/examples/theme/index.tsx b/examples/theme/index.tsx index 49ce0f5..46f178e 100644 --- a/examples/theme/index.tsx +++ b/examples/theme/index.tsx @@ -2,7 +2,13 @@ import React from 'react' import { createMuiTheme, Theme, MuiThemeProvider } from '@material-ui/core/styles' import MUIRichTextEditor from '../../' -export const defaultTheme: Theme = createMuiTheme() +export const defaultTheme: Theme = createMuiTheme({ + palette: { + primary: { + main: "#000000" + } + } +}) Object.assign(defaultTheme, { overrides: { @@ -10,17 +16,21 @@ Object.assign(defaultTheme, { root: { backgroundColor: "#ebebeb", }, + container: { + display: "flex", + flexDirection: "column-reverse" + }, editor: { - borderBottom: "1px solid gray", backgroundColor: "#ebebeb", padding: "0 20px" }, toolbar: { + borderTop: "1px solid gray", backgroundColor: "#ebebeb" }, placeHolder: { backgroundColor: "#ebebeb", - paddingLeft: "20px", + paddingLeft: 20, width: "inherit" } }