Skip to content

Commit

Permalink
Merge pull request #54 from niuware/development
Browse files Browse the repository at this point in the history
Enhance callback custom component
  • Loading branch information
niuware authored Nov 14, 2019
2 parents fe7c587 + 877f6cc commit 034700a
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 15 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,12 @@ Check [this sample](https://github.com/niuware/mui-rte/blob/master/examples/atom

### Adding a custom callback control

This sample adds a control that will trigger a custom callback function:
This sample adds a control that will trigger a custom callback function to clear the editor state:

```js
import MUIRichTextEditor from 'mui-rte'
import DoneIcon from '@material-ui/icons/Done'
import { EditorState } from 'draft-js'

<MUIRichTextEditor
controls={["my-callback"]}
Expand All @@ -130,6 +131,7 @@ import DoneIcon from '@material-ui/icons/Done'
type: "callback",
onClick: (editorState, name, anchor) => {
console.log(`Clicked ${name} control`)
return EditorState.createEmpty()
}
}
]}
Expand Down Expand Up @@ -222,6 +224,7 @@ Object.assign(defaultTheme, {

|Property|Type||description|
|---|---|---|---|
|id|`string`|optional|Base Id name for the component HTML elements.|
|label|`string`|optional|String to show when there is no content.|
|readOnly|`boolean`|optional|Read only mode. The toolbar is disabled by default.|
|value|`string`|optional|Default content to load. Should be a stringified `Draft.Model.Encoding.RawDraftContentState` object.|
Expand Down Expand Up @@ -251,7 +254,7 @@ Object.assign(defaultTheme, {
|inlineStyle|`string`|optional|The `React.CSSProperties` object for styling the text when using a custom inline style.|
|blockWrapper|`React.ReactElement`|optional|The custom React component used for rendering a custom block.|
|atomicComponent|`React.FunctionComponent`|optional|The custom React FunctionComponent used for rendering a custom atomic block.|
|onClick|`(editorState: EditorState, name: string, anchor: HTMLElement | null) => void`|optional|The callback function triggered when the custom control is clicked. The received arguments include the current `EditorState` object, the name of the clicked control and the `HTMLElement` from which the click was raised. |
|onClick|`(editorState: EditorState, name: string, anchor: HTMLElement | null) => EditorState | void`|optional|The callback function triggered when the custom control is clicked. The received arguments include the current `EditorState` object, the name of the clicked control and the `HTMLElement` from which the click was raised. If a new `EditorState` object is returned it will be replace the current one in the editor (useful to explicitly modify the `EditorState`).|

<br />

Expand Down
22 changes: 21 additions & 1 deletion examples/custom-controls/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Chip, Avatar, Button } from '@material-ui/core'
import InvertColorsIcon from '@material-ui/icons/InvertColors'
import MUIRichTextEditor from '../../'
import { TToolbarComponentProps } from '../../src/components/Toolbar'
import { EditorState } from 'draft-js'

const save = (data: string) => {
console.log(data)
Expand Down Expand Up @@ -32,6 +33,17 @@ const MyCallbackComponent: FunctionComponent<TToolbarComponentProps> = (props) =
)
}

const ClearComponent: FunctionComponent<TToolbarComponentProps> = (props) => {
return (
<Chip
id={props.id}
onClick={props.onMouseDown}
label="Clear all"
disabled={props.disabled}
/>
)
}

const MyBlockComponent: FunctionComponent<TToolbarComponentProps> = (props) => {
return (
<Button
Expand All @@ -51,7 +63,7 @@ const CustomControls = () => {
<MUIRichTextEditor
label="Type something here..."
onSave={save}
controls={["title", "bold", "my-block", "my-style", "clear", "my-callback", "save"]}
controls={["title", "bold", "my-block", "my-style", "clear", "my-callback", "clear-callback", "save"]}
customControls={[
{
name: "my-style",
Expand All @@ -75,6 +87,14 @@ const CustomControls = () => {
onClick: (_, name) => {
console.log(`Clicked ${name} control`)
}
},
{
name: "clear-callback",
component: ClearComponent,
type: "callback",
onClick: () => {
return EditorState.createEmpty()
}
}
]}
/>
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.9.0",
"version": "1.9.1",
"description": "Material-UI Rich Text Editor and Viewer",
"keywords": [
"material-ui",
Expand Down
28 changes: 21 additions & 7 deletions src/MUIRichTextEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export type TDecorator = {
}

interface IMUIRichTextEditorProps extends WithStyles<typeof styles> {
id?: string
value?: any
label?: string,
readOnly?: boolean
Expand Down Expand Up @@ -155,7 +156,7 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
const editorStateRef = useRef<EditorState | null>(editorState)

/**
* Expose the save method
* Expose methods
*/
useImperativeHandle(ref, () => ({
save: () => {
Expand Down Expand Up @@ -331,7 +332,18 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
if (control.name.toUpperCase() === style) {
if (control.onClick) {
setTimeout(() => (editorRef.current as any).blur(), 0)
control.onClick(editorState, control.name, document.getElementById(id))
const newState = control.onClick(editorState, control.name, document.getElementById(id))
if (newState) {
if (newState.getSelection().isCollapsed()) {
setEditorState(newState)
}
else {
updateStateForPopover(newState)
}
}
else {
refocus()
}
}
break
}
Expand Down Expand Up @@ -626,7 +638,7 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
const renderToolbar = props.toolbar === undefined || props.toolbar
const inlineToolbarControls = props.inlineToolbarControls || ["bold", "italic", "underline", "clear"]
const editable = props.readOnly === undefined || !props.readOnly

const id = props.id || "mui-rte"
let className = ""
let placeholder: React.ReactElement | null = null
if (!focus) {
Expand All @@ -647,8 +659,8 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
}

return (
<div className={classes.root}>
<div className={classNames(classes.container, {
<div id={`${id}-root`} className={classes.root}>
<div id={`${id}-container`} className={classNames(classes.container, {
[classes.inheritFontSize]: props.inheritFontSize
})}>
{props.inlineToolbar && editable && state.toolbarPosition ?
Expand All @@ -657,6 +669,7 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
left: state.toolbarPosition.left
}}>
<Toolbar
id={id}
editorState={editorState}
onClick={handleToolbarClick}
controls={inlineToolbarControls}
Expand All @@ -667,6 +680,7 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
: null}
{editable || renderToolbar ?
<Toolbar
id={id}
editorState={editorState}
onClick={handleToolbarClick}
controls={controls}
Expand All @@ -676,8 +690,8 @@ const MUIRichTextEditor: RefForwardingComponent<any, IMUIRichTextEditorProps> =
/>
: null}
{placeholder}
<div className={classes.editor}>
<div className={classNames(className, classes.editorContainer, {
<div id={`${id}-editor`} className={classes.editor}>
<div id={`${id}-editor-container`} className={classNames(className, classes.editorContainer, {
[classes.editorReadOnly]: !editable,
[classes.error]: props.error
})} onClick={handleFocus} onBlur={handleBlur}>
Expand Down
10 changes: 6 additions & 4 deletions src/components/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export type TCustomControl = {
inlineStyle?: React.CSSProperties
blockWrapper?: React.ReactElement
atomicComponent?: FunctionComponent
onClick?: (editorState: EditorState, name: string, anchor: HTMLElement | null) => void
onClick?: (editorState: EditorState, name: string, anchor: HTMLElement | null) => EditorState | void
}

type TStyleType = {
Expand Down Expand Up @@ -174,7 +174,8 @@ const STYLE_TYPES: TStyleType[] = [
}
]

interface IBlockStyleControlsProps {
type TToolbarProps = {
id: string
editorState: EditorState
controls?: Array<TToolbarControl>
customControls?: TCustomControl[]
Expand All @@ -184,9 +185,10 @@ interface IBlockStyleControlsProps {
disabled?: boolean
}

const Toolbar: FunctionComponent<IBlockStyleControlsProps> = (props) => {
const Toolbar: FunctionComponent<TToolbarProps> = (props) => {
const [availableControls, setAvailableControls] = useState(props.controls ? [] : STYLE_TYPES)
const {editorState} = props
const id = props.inlineMode ? "-inline-toolbar" : "-toolbar"

useEffect(() => {
if (!props.controls) {
Expand Down Expand Up @@ -220,7 +222,7 @@ const Toolbar: FunctionComponent<IBlockStyleControlsProps> = (props) => {
}, [props.controls, props.customControls])

return (
<div className={props.className}>
<div id={`${props.id}${id}`} className={props.className}>
{availableControls.map(style => {
if (props.inlineMode &&
(style.type !== "inline" && (style.name !== "link" && style.name !== "clear"))) {
Expand Down

0 comments on commit 034700a

Please sign in to comment.