Skip to content

Commit

Permalink
Refactoring (niuware#116)
Browse files Browse the repository at this point in the history
* Rename TToolbarPosition type to TPosition

* Move constants out of class

* Refactor keyBindingFn function

* Refactor getAutocompleteKeyEvent

* Refactor insert autocomplete atomic block

* Refactor insert autocomplete text

* Move default inline toolbar controls out of component

* Move type defintions to upper part

* Move type definitions upper on Toolbar component

* Move type definitinos upper on UrlPopover component

* Replace quotes for single quotes in imports

* Removing explicit type

* Move type defintions upper on Media component

* Changing interface to type for Link component props type

* Refactoring import on CodeBlock component

* Refactoring import on Blockquote component

* Update package version
  • Loading branch information
niuware authored May 5, 2020
1 parent c84ad18 commit ca1c7ab
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 152 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mui-rte",
"version": "1.17.3",
"version": "1.17.4",
"description": "Material-UI Rich Text Editor and Viewer",
"keywords": [
"material-ui",
Expand Down
243 changes: 132 additions & 111 deletions src/MUIRichTextEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,62 +19,6 @@ import UrlPopover, { TAlignment, TUrlData, TMediaType } from './components/UrlPo
import Autocomplete, { TAutocompleteItem } from './components/Autocomplete'
import { getSelectionInfo, removeBlockFromMap, atomicBlockExists, isGt, clearInlineStyles, getRects, getLine } from './utils'

const styles = ({ spacing, typography, palette }: Theme) => createStyles({
root: {
},
container: {
margin: spacing(1, 0, 0, 0),
position: "relative",
fontFamily: typography.body1.fontFamily,
fontSize: typography.body1.fontSize,
'& figure': {
margin: 0
}
},
inheritFontSize: {
fontSize: "inherit"
},
editor: {
},
editorContainer: {
margin: spacing(1, 0, 0, 0),
cursor: "text",
width: "100%",
padding: spacing(0, 0, 1, 0)
},
editorReadOnly: {
borderBottom: "none"
},
error: {
borderBottom: "2px solid red"
},
hidePlaceholder: {
display: "none"
},
placeHolder: {
color: palette.grey[600],
position: "absolute"
},
linkPopover: {
padding: spacing(2, 2, 2, 2)
},
linkTextField: {
width: "100%"
},
anchorLink: {
textDecoration: "underline",
color: palette.secondary.main
},
toolbar: {
},
inlineToolbar: {
maxWidth: "180px",
position: "absolute",
padding: "5px",
zIndex: 10
}
})

export type TDecorator = {
component: FunctionComponent
regex: RegExp
Expand Down Expand Up @@ -130,15 +74,15 @@ type IMUIRichTextEditorState = {
urlKey?: string
urlData?: TUrlData
urlIsMedia?: boolean
toolbarPosition?: TToolbarPosition
toolbarPosition?: TPosition
}

type TStateOffset = {
start: number,
end: number
}

type TToolbarPosition = {
type TPosition = {
top: number
left: number
}
Expand All @@ -148,6 +92,62 @@ type TCustomRenderers = {
block?: Immutable.Map<any, any>
}

const styles = ({ spacing, typography, palette }: Theme) => createStyles({
root: {
},
container: {
margin: spacing(1, 0, 0, 0),
position: "relative",
fontFamily: typography.body1.fontFamily,
fontSize: typography.body1.fontSize,
'& figure': {
margin: 0
}
},
inheritFontSize: {
fontSize: "inherit"
},
editor: {
},
editorContainer: {
margin: spacing(1, 0, 0, 0),
cursor: "text",
width: "100%",
padding: spacing(0, 0, 1, 0)
},
editorReadOnly: {
borderBottom: "none"
},
error: {
borderBottom: "2px solid red"
},
hidePlaceholder: {
display: "none"
},
placeHolder: {
color: palette.grey[600],
position: "absolute"
},
linkPopover: {
padding: spacing(2, 2, 2, 2)
},
linkTextField: {
width: "100%"
},
anchorLink: {
textDecoration: "underline",
color: palette.secondary.main
},
toolbar: {
},
inlineToolbar: {
maxWidth: "180px",
position: "absolute",
padding: "5px",
zIndex: 10
}
})

const blockRenderMap = Immutable.Map({
'blockquote': {
element: "blockquote",
Expand All @@ -168,6 +168,9 @@ const styleRenderMap: DraftStyleMap = {
}

const { hasCommandModifier } = KeyBindingUtil
const autocompleteMinSearchCharCount = 2
const lineHeight = 26
const defaultInlineToolbarControls = ["bold", "italic", "underline", "clear"]

const findLinkEntities = (contentBlock: any, callback: any, contentState: any) => {
contentBlock.findEntityRanges(
Expand Down Expand Up @@ -230,14 +233,12 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
start: 0,
end: 0
})
const toolbarPositionRef = useRef<TToolbarPosition | undefined>(undefined)
const toolbarPositionRef = useRef<TPosition | undefined>(undefined)
const editorStateRef = useRef<EditorState | null>(editorState)
const currentAutocompleteRef = useRef<TAutocompleteStrategy | undefined>(undefined)
const acSelectionStateRef = useRef<SelectionState | undefined>(undefined)
const autocompletePosition = useRef<TToolbarPosition | undefined>(undefined)
const autocompletePosition = useRef<TPosition | undefined>(undefined)
const autocompleteLimit = props.autocomplete ? props.autocomplete.suggestLimit || 5 : 5
const autocompleteMinSearchCharCount = 2
const lineHeight = 26
const editorId = props.id || "mui-rte"

/**
Expand Down Expand Up @@ -374,6 +375,42 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
autocompletePosition.current = position
}

const insertAutocompleteSuggestionAsAtomicBlock = (name: string, selection: SelectionState, value: any) => {
const block = atomicBlockExists(name, props.customControls)
if (!block) {
return
}
const contentState = Modifier.removeRange(editorStateRef.current!.getCurrentContent(),
selection,
"forward")
const newEditorState = EditorState.push(editorStateRef.current!, contentState, "remove-range")
const withAtomicBlock = insertAtomicBlock(newEditorState, name.toUpperCase(), {
value: value
}, {
selection: newEditorState.getCurrentContent().getSelectionAfter()
})
handleChange(withAtomicBlock)
}

const insertAutocompleteSuggestionAsText = (selection: SelectionState, value: string) => {
const currentContentState = editorState.getCurrentContent()
const entityKey = currentContentState.createEntity("AC_ITEM", 'IMMUTABLE').getLastCreatedEntityKey();
const contentState = Modifier.replaceText(editorStateRef.current!.getCurrentContent(),
selection,
value,
editorStateRef.current!.getCurrentInlineStyle(),
entityKey)
const newEditorState = EditorState.push(editorStateRef.current!, contentState, "insert-characters")
if (currentAutocompleteRef.current!.insertSpaceAfter === false) {
handleChange(newEditorState)
} else {
const addSpaceState = Modifier.insertText(newEditorState.getCurrentContent(),
newEditorState.getSelection(), " ",
newEditorState.getCurrentInlineStyle())
handleChange(EditorState.push(newEditorState, addSpaceState, "insert-characters"))
}
}

const handleAutocompleteSelected = (index?: number) => {
const itemIndex = index || selectedIndex
const items = getAutocompleteItems()
Expand All @@ -386,38 +423,11 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
'anchorOffset': currentSelection.getAnchorOffset(),
'focusOffset': currentSelection.getFocusOffset() + searchTerm.length + 1
})
const currentContentState = editorState.getCurrentContent()
if (currentAutocompleteRef.current!.atomicBlockName) {
const name = currentAutocompleteRef.current!.atomicBlockName
const block = atomicBlockExists(name, props.customControls)
if (block) {
const contentState = Modifier.removeRange(editorStateRef.current!.getCurrentContent(),
newSelection,
"forward")
const newEditorState = EditorState.push(editorStateRef.current!, contentState, "remove-range")
const withAtomicBlock = insertAtomicBlock(newEditorState, name.toUpperCase(), {
value: item.value
}, {
selection: newEditorState.getCurrentContent().getSelectionAfter()
})
handleChange(withAtomicBlock)
}
insertAutocompleteSuggestionAsAtomicBlock(name, newSelection, item.value)
} else {
const entityKey = currentContentState.createEntity("AC_ITEM", 'IMMUTABLE').getLastCreatedEntityKey();
const contentState = Modifier.replaceText(editorStateRef.current!.getCurrentContent(),
newSelection,
item.value,
editorStateRef.current!.getCurrentInlineStyle(),
entityKey)
const newEditorState = EditorState.push(editorStateRef.current!, contentState, "insert-characters")
if (currentAutocompleteRef.current!.insertSpaceAfter === false) {
handleChange(newEditorState)
} else {
const addSpaceState = Modifier.insertText(newEditorState.getCurrentContent(),
newEditorState.getSelection(), " ",
newEditorState.getCurrentInlineStyle())
handleChange(EditorState.push(newEditorState, addSpaceState, "insert-characters"))
}
insertAutocompleteSuggestionAsText(newSelection, item.value)
}
}
handleAutocompleteClosed()
Expand Down Expand Up @@ -823,40 +833,34 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
return AtomicBlockUtils.insertAtomicBlock(newEditorStateRaw, entityKey, ' ')
}

const keyBindingFn = (e: React.KeyboardEvent<{}>): string | null => {
if (hasCommandModifier(e) && props.keyCommands) {
const comm = props.keyCommands.find(comm => comm.key === e.keyCode)
if (comm) {
return comm.name
}
}
if (searchTerm) {
const itemsLength = getAutocompleteItems().length
const limit = autocompleteLimit > itemsLength ? itemsLength : autocompleteLimit
if (e.key === "ArrowDown") {
const getAutocompleteKeyEvent = (keyboardEvent: React.KeyboardEvent<{}>): string | null => {
const itemsLength = getAutocompleteItems().length
const limit = autocompleteLimit > itemsLength ? itemsLength : autocompleteLimit
switch (keyboardEvent.key) {
case "ArrowDown":
if ((selectedIndex === 0 && itemsLength === 1) || (selectedIndex + 1 === limit)) {
setSelectedIndex(0)
} else {
setSelectedIndex(selectedIndex + 1 < limit ? selectedIndex + 1 : selectedIndex)
}
return "mui-autocomplete-navigate"
}
if (e.key === "ArrowUp") {
case "ArrowUp":
if (selectedIndex) {
setSelectedIndex(selectedIndex - 1)
} else {
setSelectedIndex(limit - 1)
}
return "mui-autocomplete-navigate"
}
if (e.key === "Enter") {
case "Enter":
return "mui-autocomplete-insert"
}
if (e.key === "Escape") {
case "Escape":
return "mui-autocomplete-end"
}
default:
return null
}
const keyBinding = getDefaultKeyBinding(e)
}

const updateSearchTermForKeyBinding = (keyBinding: DraftEditorCommand | null) => {
const text = editorStateRef.current!.getCurrentContent().getLastBlock().getText()

if (keyBinding === "backspace"
Expand All @@ -872,12 +876,29 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
|| keyBinding === "split-block")) {
clearSearch()
}
}

const keyBindingFn = (e: React.KeyboardEvent<{}>): string | null => {
if (hasCommandModifier(e) && props.keyCommands) {
const comm = props.keyCommands.find(comm => comm.key === e.keyCode)
if (comm) {
return comm.name
}
}
if (searchTerm) {
const autocompleteEvent = getAutocompleteKeyEvent(e)
if (autocompleteEvent) {
return autocompleteEvent
}
}
const keyBinding = getDefaultKeyBinding(e)
updateSearchTermForKeyBinding(keyBinding)

return keyBinding
}

const renderToolbar = props.toolbar === undefined || props.toolbar
const inlineToolbarControls = props.inlineToolbarControls || ["bold", "italic", "underline", "clear"]
const inlineToolbarControls = props.inlineToolbarControls || defaultInlineToolbarControls
const editable = props.readOnly === undefined || !props.readOnly
let className = ""
let placeholder: React.ReactElement | null = null
Expand Down
4 changes: 2 additions & 2 deletions src/components/Blockquote.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as React from 'react'
import React, { FunctionComponent } from 'react'
import { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/styles'

const styles = ({ palette }: Theme) => createStyles({
Expand All @@ -13,7 +13,7 @@ interface IBlockquoteProps extends WithStyles<typeof styles> {
children?: React.ReactNode
}

const Blockquote: React.FC<IBlockquoteProps> = (props: IBlockquoteProps) => {
const Blockquote: FunctionComponent<IBlockquoteProps> = (props) => {
return (
<div className={props.classes.root}>
{props.children}
Expand Down
4 changes: 2 additions & 2 deletions src/components/CodeBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as React from 'react'
import React, { FunctionComponent } from 'react'
import { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/styles'

const styles = ({ spacing, palette }: Theme) => createStyles({
Expand All @@ -12,7 +12,7 @@ interface IBlockquoteProps extends WithStyles<typeof styles> {
children?: React.ReactNode
}

const CodeBlock: React.FC<IBlockquoteProps> = (props: IBlockquoteProps) => {
const CodeBlock: FunctionComponent<IBlockquoteProps> = (props) => {
return (
<div className={props.classes.root}>
{props.children}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Link.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React, { FunctionComponent } from 'react'
import { ContentState } from 'draft-js'

interface ILinkProps {
type TLinkProps = {
children?: React.ReactNode
contentState: ContentState
entityKey: string
}

const Link: FunctionComponent<ILinkProps> = (props: ILinkProps) => {
const Link: FunctionComponent<TLinkProps> = (props) => {
const { url } = props.contentState.getEntity(props.entityKey).getData()
return (
<a
Expand Down
Loading

0 comments on commit ca1c7ab

Please sign in to comment.