Skip to content

Commit

Permalink
Merge pull request #39 from niuware/development
Browse files Browse the repository at this point in the history
Add image alignment
  • Loading branch information
niuware authored Oct 17, 2019
2 parents cd9be87 + e6421cf commit 4b1a385
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 130 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,16 @@ Object.assign(defaultTheme, {
|component|`React.FunctionComponent`|required|The React component to use for rendering the decorator.|
|regex|`RegExp`|required|The regular expression to match a decorator.|

## Changelog

Check the [release notes](https://github.com/niuware/mui-rte/releases) for the changelog.

## Development

For development use:

```
$ npm run watch
$ npm run serve
```

Expand Down
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.6.0",
"version": "1.6.1",
"description": "Material-UI Rich Text Editor and Viewer",
"keywords": [
"material-ui",
Expand Down
127 changes: 46 additions & 81 deletions src/MUIRichTextEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ import {
Editor, EditorState, convertFromRaw, RichUtils, AtomicBlockUtils,
CompositeDecorator, convertToRaw, DefaultDraftBlockRenderMap, DraftEditorCommand,
DraftHandleValue, DraftStyleMap, ContentBlock, DraftDecorator, getVisibleSelectionRect,
SelectionState, Modifier, ContentState
SelectionState
} from 'draft-js'
import EditorControls, { TEditorControl, TCustomControl } from './components/EditorControls'
import Link from './components/Link'
import Image from './components/Image'
import Blockquote from './components/Blockquote'
import CodeBlock from './components/CodeBlock'
import UrlPopover from './components/UrlPopover'
import { getSelectionInfo, getCompatibleSpacing } from './utils'
import UrlPopover, { TAlignment, TUrlData } from './components/UrlPopover'
import { getSelectionInfo, getCompatibleSpacing, removeBlockFromMap } from './utils'

const styles = ({ spacing, typography, palette }: Theme) => createStyles({
root: {
Expand Down Expand Up @@ -95,12 +95,10 @@ interface IMUIRichTextEditorProps extends WithStyles<typeof styles> {

type IMUIRichTextEditorState = {
anchorUrlPopover?: HTMLElement
urlValue?: string
urlKey?: string
urlWidth?: number
urlHeight?: number
urlData?: TUrlData
urlIsMedia?: boolean
toolbarPosition?: TToolbarPosition
sizeProps?: boolean
}

type TStateOffset = {
Expand Down Expand Up @@ -340,55 +338,55 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
const selectionInfo = getSelectionInfo(editorState)
const contentState = editorState.getCurrentContent()
const linkKey = selectionInfo.linkKey

let url = ''
let data = undefined
let urlKey = undefined
if (linkKey) {
const linkInstance = contentState.getEntity(linkKey)
url = linkInstance.getData().url
data = linkInstance.getData()
urlKey = linkKey
}
setState({
...state,
urlValue: url,
urlData: data,
urlKey: urlKey,
toolbarPosition: !toolbarMode ? undefined : state.toolbarPosition,
anchorUrlPopover: !toolbarMode ? document.getElementById("mui-rte-link-control")!
: document.getElementById("mui-rte-link-control-toolbar")!,
sizeProps: undefined
urlIsMedia: undefined
})
}
}

const handlePromptForMedia = (style: string, toolbarMode: boolean, newState?: EditorState) => {
const lastState = newState || editorState
let url = ''
let width = undefined
let height = undefined
let urlKey = undefined
let data = undefined
const selectionInfo = getSelectionInfo(lastState)
const contentState = lastState.getCurrentContent()
const linkKey = selectionInfo.linkKey

if (linkKey) {
const linkInstance = contentState.getEntity(linkKey)
url = linkInstance.getData().url
width = linkInstance.getData().width
height = linkInstance.getData().height
data = linkInstance.getData()
urlKey = linkKey
}
setState({
urlValue: url,
urlKey: urlKey,
urlWidth: width,
urlHeight: height,
urlData: data,
toolbarPosition: !toolbarMode ? undefined : state.toolbarPosition,
anchorUrlPopover: !toolbarMode ? document.getElementById("mui-rte-image-control")!
: document.getElementById("mui-rte-image-control-toolbar")!,
sizeProps: true
urlIsMedia: true
})
}

const handleConfirmPrompt = (isMedia?: boolean, ...args: any) => {
if (isMedia) {
confirmMedia(...args)
return
}
confirmLink(...args)
}

const toggleMouseUpListener = (addAfter = false) => {
const editor: HTMLElement = (editorRef.current as any).editor
if (!editor) {
Expand Down Expand Up @@ -421,22 +419,16 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =

const contentState = editorState.getCurrentContent()
let replaceEditorState = null
const data = {
url: url
}

if (urlKey) {
contentState.replaceEntityData(urlKey, {
url: url
})
contentState.replaceEntityData(urlKey, data)
replaceEditorState = EditorState.push(editorState, contentState, "apply-entity")
}
else {
const contentStateWithEntity = contentState.createEntity(
'LINK',
'MUTABLE',
{
url: url
}
)

const contentStateWithEntity = contentState.createEntity('LINK', 'MUTABLE', data)
const entityKey = contentStateWithEntity.getLastCreatedEntityKey()
const newEditorState = EditorState.set(editorState, { currentContent: contentStateWithEntity })
replaceEditorState = RichUtils.toggleLink(
Expand All @@ -451,26 +443,12 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
const blockKey = editorState.getSelection().getStartKey()
const contentState = editorState.getCurrentContent()
const mediaBlock = contentState.getBlockForKey(blockKey)
const removeBlockContentState = Modifier.removeRange(
contentState,
new SelectionState({
anchorKey: mediaBlock.getKey(),
anchorOffset: 0,
focusKey: mediaBlock.getKey(),
focusOffset: mediaBlock.getLength(),
}),
'backward'
)
const blockMap = removeBlockContentState.getBlockMap().delete(mediaBlock.getKey())
var withoutAtomic = removeBlockContentState.merge({
blockMap,
selectionAfter: contentState.getSelectionAfter()
})
const newEditorState = EditorState.push(editorState, withoutAtomic as ContentState, "remove-range")
const newContentState = removeBlockFromMap(editorState, mediaBlock)
const newEditorState = EditorState.push(editorState, newContentState, "remove-range")
setEditorState(newEditorState)
}

const confirmMedia = (url?: string, width?: number, height?: number) => {
const confirmMedia = (url?: string, width?: number, height?: number, alignment?: TAlignment) => {
const { urlKey } = state
if (!url) {
if (urlKey) {
Expand All @@ -484,36 +462,27 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
}

const contentState = editorState.getCurrentContent()
let replaceEditorState = null
const data = {
url: url,
width: width,
height: height,
alignment: alignment
}

if (urlKey) {
contentState.replaceEntityData(urlKey, {
url: url,
width: width,
height: height
})
contentState.replaceEntityData(urlKey, data)
const newEditorState = EditorState.push(editorState, contentState, "apply-entity")
replaceEditorState = EditorState.forceSelection(newEditorState, newEditorState.getCurrentContent().getSelectionAfter())
updateStateForPopover(EditorState.forceSelection(newEditorState, newEditorState.getCurrentContent().getSelectionAfter()))
}
else {
const contentStateWithEntity = contentState.createEntity(
'IMAGE',
'IMMUTABLE',
{
url: url,
width: width,
height: height
}
)
const contentStateWithEntity = contentState.createEntity('IMAGE', 'IMMUTABLE',data)
const entityKey = contentStateWithEntity.getLastCreatedEntityKey()
const newEditorStateRaw = EditorState.set(editorState, { currentContent: contentStateWithEntity })
const newEditorState = AtomicBlockUtils.insertAtomicBlock(
newEditorStateRaw,
entityKey, ' ')
replaceEditorState = EditorState.forceSelection(newEditorState, newEditorState.getCurrentContent().getSelectionAfter())
const newEditorState = AtomicBlockUtils.insertAtomicBlock(newEditorStateRaw, entityKey, ' ')

updateStateForPopover(EditorState.forceSelection(newEditorState, newEditorState.getCurrentContent().getSelectionAfter()))
}
setFocusImageKey("")
updateStateForPopover(replaceEditorState)
}

const updateStateForPopover = (editorState: EditorState) => {
Expand All @@ -522,11 +491,9 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
setState({
...state,
anchorUrlPopover: undefined,
urlValue: undefined,
urlKey: undefined,
sizeProps: undefined,
urlWidth: undefined,
urlHeight: undefined,
urlIsMedia: undefined,
urlData: undefined
})
}

Expand Down Expand Up @@ -689,12 +656,10 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
</div>
{state.anchorUrlPopover ?
<UrlPopover
url={state.urlValue}
width={state.urlWidth}
height={state.urlHeight}
data={state.urlData}
anchor={state.anchorUrlPopover}
onConfirm={state.sizeProps ? confirmMedia : confirmLink}
useSize={state.sizeProps}
onConfirm={handleConfirmPrompt}
isMedia={state.urlIsMedia}
/>
: null}
</div>
Expand Down
49 changes: 33 additions & 16 deletions src/components/Image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/s

const styles = ({ shadows }: Theme) => createStyles({
root: {
margin: "5px 0 1px"
},
editable: {
cursor: "pointer",
Expand All @@ -14,6 +15,15 @@ const styles = ({ shadows }: Theme) => createStyles({
},
focused: {
boxShadow: shadows[3]
},
centered: {
textAlign: "center"
},
leftAligned: {
textAlign: "left"
},
rightAligned: {
textAlign: "right"
}
})

Expand All @@ -25,24 +35,31 @@ interface IImageProps extends WithStyles<typeof styles> {
}

const Image: FunctionComponent<IImageProps> = (props) => {
const { url, width, height } = props.contentState.getEntity(props.block.getEntityAt(0)).getData()
const { url, width, height, alignment } = props.contentState.getEntity(props.block.getEntityAt(0)).getData()
const { onClick, readOnly, focusKey } = props.blockProps

return (
<img
src={url}
className={classNames(props.classes.root, {
[props.classes.editable]: !readOnly,
[props.classes.focused]: !readOnly && focusKey === props.block.getKey()
})}
width={width}
height={height}
onClick={() => {
if (readOnly) {
return
}
onClick(props.block)
}}
/>
<div className={classNames({
[props.classes.centered]: alignment === "center",
[props.classes.leftAligned]: alignment === "left",
[props.classes.rightAligned]: alignment === "right"
})}>
<img
src={url}
className={classNames(props.classes.root, {
[props.classes.editable]: !readOnly,
[props.classes.focused]: !readOnly && focusKey === props.block.getKey()
})}
width={width}
height={height}
onClick={() => {
if (readOnly) {
return
}
onClick(props.block)
}}
/>
</div>
)
}

Expand Down
Loading

0 comments on commit 4b1a385

Please sign in to comment.