I need to add custom dropdown menu in toolbar section.
here attached image similar to want dropdown menu this is possible ?
<img src=".png" alt="Dropdown menu editor">
find the detailed image below
I used react-draft-wysiwyg content editor.
add custom dropdown menu in toolbar section.
I need to add custom dropdown menu in toolbar section.
here attached image similar to want dropdown menu this is possible ?
<img src="https://i.imgur.com/OhYeFsL.png" alt="Dropdown menu editor">
find the detailed image below
I used react-draft-wysiwyg content editor.
https://github.com/jpuri/react-draft-wysiwyg
https://jpuri.github.io/react-draft-wysiwyg/#/d
add custom dropdown menu in toolbar section.
Share Improve this question edited Oct 12, 2018 at 15:01 mschuurmans 1,0861 gold badge12 silver badges26 bronze badges asked Oct 12, 2018 at 13:47 itjayaprakashitjayaprakash 851 silver badge12 bronze badges3 Answers
Reset to default 18I hope this is still relevant, but here is my way.
For the custom dropdown, I created a new component and used method for "adding new option to the toolbar" from the documentation https://jpuri.github.io/react-draft-wysiwyg/#/docs
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { EditorState, Modifier } from 'draft-js';
class Placeholders extends Component {
static propTypes = {
onChange: PropTypes.func,
editorState: PropTypes.object,
}
state = {
open: false
}
openPlaceholderDropdown = () => this.setState({open: !this.state.open})
addPlaceholder = (placeholder) => {
const { editorState, onChange } = this.props;
const contentState = Modifier.replaceText(
editorState.getCurrentContent(),
editorState.getSelection(),
placeholder,
editorState.getCurrentInlineStyle(),
);
onChange(EditorState.push(editorState, contentState, 'insert-characters'));
}
placeholderOptions = [
{key: "firstName", value: "{{firstName}}", text: "First Name"},
{key: "lastName", value: "{{lastName}}", text: "Last name"},
{key: "company", value: "{{company}}", text: "Company"},
{key: "address", value: "{{address}}", text: "Address"},
{key: "zip", value: "{{zip}}", text: "Zip"},
{key: "city", value: "{{city}}", text: "City"}
]
listItem = this.placeholderOptions.map(item => (
<li
onClick={this.addPlaceholder.bind(this, item.value)}
key={item.key}
className="rdw-dropdownoption-default placeholder-li"
>{item.text}</li>
))
render() {
return (
<div onClick={this.openPlaceholderDropdown} className="rdw-block-wrapper" aria-label="rdw-block-control">
<div className="rdw-dropdown-wrapper rdw-block-dropdown" aria-label="rdw-dropdown">
<div className="rdw-dropdown-selectedtext" title="Placeholders">
<span>Placeholder</span>
<div className={`rdw-dropdown-caretto${this.state.open? "close": "open"}`}></div>
</div>
<ul className={`rdw-dropdown-optionwrapper ${this.state.open? "": "placeholder-ul"}`}>
{this.listItem}
</ul>
</div>
</div>
);
}
}
export default Placeholders;
I used a custom dropdown for adding placeholders. But the essence still stays the same because I use the example from the documentation for a custom button.
To render the button itself I used the same styling, classes, and structure as is used for the other dropdown buttons. I just switched the anchor tag to div tag and added custom classes for hover style and carrot change. I also used events to toggle classes.
.placeholder-ul{
visibility: hidden;
}
.placeholder-li:hover {
background: #F1F1F1;
}
Lastly, don't forget to import and add a custom button to the editor.
<Editor
editorState={this.state.editorState}
onEditorStateChange={this.onEditorStateChange}
toolbarCustomButtons={[<Placeholders />]}
/>
I'v used Tomas his code and updated it a bit to TypeScript / Function components. Can concur that this solution is still working in 2020 with Draft.js v0.10.5
type ReplacementsProps = {
onChange?: (editorState: EditorState) => void,
editorState: EditorState,
}
export const Replacements = ({onChange, editorState}: ReplacementsProps) => {
const [open, setOpen] = useState<boolean>(false);
const addPlaceholder = (placeholder: string): void => {
const contentState = Modifier.replaceText(
editorState.getCurrentContent(),
editorState.getSelection(),
placeholder,
editorState.getCurrentInlineStyle(),
);
const result = EditorState.push(editorState, contentState, 'insert-characters');
if (onChange) {
onChange(result);
}
};
return (
<div onClick={() => setOpen(!open)} className="rdw-block-wrapper" aria-label="rdw-block-control" role="button" tabIndex={0}>
<div className="rdw-dropdown-wrapper rdw-block-dropdown" aria-label="rdw-dropdown" style={{width: 180}}>
<div className="rdw-dropdown-selectedtext">
<span>YOuR TITLE HERE</span>
<div className={`rdw-dropdown-caretto${open ? 'close' : 'open'}`} />
</div>
<ul className={`rdw-dropdown-optionwrapper ${open ? '' : 'placeholder-ul'}`}>
{placeholderOptions.map(item => (
<li
onClick={() => addPlaceholder(item.value)}
key={item.value}
className="rdw-dropdownoption-default placeholder-li"
>
{item.text}
</li>
))}
</ul>
</div>
</div>
);
};
This answer was too long for a comment.
Tomas's answer is correct. This is an addendum to answer Tadej's question/comment.
In your Placeholder component, set a ref on your outermost div.
Here is how you would handle it according to the functional component paradigm in React:
const [open, setOpen] = useState(false);
const openPlaceholder = () => setOpen(!open);
useEffect(() => {
const handleClickOutside = (event) => {
if (smart.current && !smart.current.contains(event.target)) {
setOpen(false);
}
};
document.addEventListener('click', handleClickOutside);
return () => {
document.removeEventListener('click', handleClickOutside);
};
}, []);
return (
<div ref={smart} onClick={() => openPlaceholder()} className="rdw-block-wrapper" aria-label="rdw-block-control">
// ... the rest of the dropdown.
</div>
);