Add markdown editor toolbar customization

This commit is contained in:
Damien Duhamel 2018-04-06 21:41:16 +02:00 committed by Shawn Erquhart
parent 9975c7e914
commit 106968990d
4 changed files with 100 additions and 133 deletions

View File

@ -1,4 +1,5 @@
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import React from 'react';
import { Editor as Slate } from 'slate-react';
import Plain from 'slate-plain-serializer';
@ -50,12 +51,13 @@ export default class RawEditor extends React.Component {
};
render() {
const { className } = this.props;
const { className, field } = this.props;
return (
<div className="nc-rawEditor-rawWrapper">
<div className="nc-visualEditor-editorControlBar">
<Toolbar
onToggleMode={this.handleToggleMode}
buttons={field.get('buttons')}
className="nc-markdownWidget-toolbarRaw"
disabled
rawMode
@ -77,4 +79,5 @@ RawEditor.propTypes = {
onMode: PropTypes.func.isRequired,
className: PropTypes.string.isRequired,
value: PropTypes.string,
field: ImmutablePropTypes.map
};

View File

@ -17,6 +17,7 @@ export default class Toolbar extends React.Component {
getAsset: PropTypes.func,
disabled: PropTypes.bool,
className: PropTypes.string,
buttons: ImmutablePropTypes.list
};
constructor(props) {
@ -26,7 +27,7 @@ export default class Toolbar extends React.Component {
};
}
render() {
getToolbarButtonsList() {
const {
onMarkClick,
onBlockClick,
@ -34,18 +35,64 @@ export default class Toolbar extends React.Component {
selectionHasMark,
selectionHasBlock,
selectionHasLink,
disabled,
buttons
} = this.props;
const toolbarButtons = [
{ type: 'bold', label: 'Bold', icon: 'bold', onClick: onMarkClick, isActive: selectionHasMark, disabled },
{ type: 'italic', label: 'Italic', icon: 'italic', onClick: onMarkClick, isActive: selectionHasMark, disabled },
{ type: 'code', label: 'Code', icon: 'code', onClick: onMarkClick, isActive: selectionHasMark, disabled },
{ type: 'link', label: 'Link', icon: 'link', onClick: onLinkClick, isActive: selectionHasLink, disabled },
{ type: 'heading-one', label: 'Header 1', icon: 'h1', onClick: onBlockClick, isActive: selectionHasBlock, disabled },
{ type: 'heading-two', label: 'Header 2', icon: 'h2', onClick: onBlockClick, isActive: selectionHasBlock, disabled },
{ type: 'quote', label: 'Quote', icon: 'quote', onClick: onBlockClick, isActive: selectionHasBlock, disabled },
{ type: 'code', label: 'Code Block', icon: 'code-block', onClick: onBlockClick, isActive: selectionHasBlock, disabled },
{ type: 'bulleted-list', label: 'Bulleted List', icon: 'list-bulleted', onClick: onBlockClick, isActive: selectionHasBlock, disabled },
{ type: 'numbered-list', label: 'Numbered List', icon: 'list-numbered', onClick: onBlockClick, isActive: selectionHasBlock, disabled }
];
return buttons === undefined ? toolbarButtons : toolbarButtons.filter(button => buttons.includes(button.type));
}
renderToolbarButton(button) {
const { type, label, icon, onClick, isActive, disabled } = button;
return <ToolbarButton key={label} type={type} label={label} icon={icon} onClick={onClick} isActive={isActive} disabled={disabled}/>;
}
renderToolbarDropdown() {
const { plugins, disabled, onSubmit } = this.props;
return (
<div className="nc-toolbar-dropdown">
<Dropdown
dropdownTopOverlap="36px"
button={
<ToolbarButton
label="Add Component"
icon="add-with"
onClick={this.handleComponentsMenuToggle}
disabled={disabled}
/>
}
>
{plugins && plugins.toList().map((plugin, idx) => (
<DropdownItem key={idx} label={plugin.get('label')} onClick={() => onSubmit(plugin.get('id'))} />
))}
</Dropdown>
</div>
);
}
renderToolbarToggle() {
const {
onToggleMode,
rawMode,
plugins,
onAddAsset,
getAsset,
disabled,
onSubmit,
className,
className
} = this.props;
const { activePlugin } = this.state;
/**
* Because the toggle labels change font weight for active/inactive state,
* we need to set estimated widths for them to maintain position without
@ -57,132 +104,43 @@ export default class Toolbar extends React.Component {
const toggleOnLabelWidth = '70px';
return (
<div className={c(className, 'nc-toolbar-Toolbar')}>
<div className="nc-markdownWidget-toolbar-toggle">
<span
style={{ width: toggleOffLabelWidth }}
className={c(
'nc-markdownWidget-toolbar-toggle-label',
{ 'nc-markdownWidget-toolbar-toggle-label-active': !rawMode },
)}
>
{toggleOffLabel}
</span>
<Toggle
active={rawMode}
onChange={onToggleMode}
className="nc-markdownWidget-toolbar-toggle"
classNameBackground="nc-markdownWidget-toolbar-toggle-background"
/>
<span
style={{ width: toggleOnLabelWidth }}
className={c(
'nc-markdownWidget-toolbar-toggle-label',
{ 'nc-markdownWidget-toolbar-toggle-label-active': rawMode },
)}
>
{toggleOnLabel}
</span>
</div>
);
}
render() {
return (
<div className={c(this.props.className, 'nc-toolbar-Toolbar')}>
<div>
<ToolbarButton
type="bold"
label="Bold"
icon="bold"
onClick={onMarkClick}
isActive={selectionHasMark}
disabled={disabled}
/>
<ToolbarButton
type="italic"
label="Italic"
icon="italic"
onClick={onMarkClick}
isActive={selectionHasMark}
disabled={disabled}
/>
<ToolbarButton
type="code"
label="Code"
icon="code"
onClick={onMarkClick}
isActive={selectionHasMark}
disabled={disabled}
/>
<ToolbarButton
type="link"
label="Link"
icon="link"
onClick={onLinkClick}
isActive={selectionHasLink}
disabled={disabled}
/>
<ToolbarButton
type="heading-one"
label="Header 1"
icon="h1"
onClick={onBlockClick}
isActive={selectionHasBlock}
disabled={disabled}
/>
<ToolbarButton
type="heading-two"
label="Header 2"
icon="h2"
onClick={onBlockClick}
isActive={selectionHasBlock}
disabled={disabled}
/>
<ToolbarButton
type="quote"
label="Quote"
icon="quote"
onClick={onBlockClick}
isActive={selectionHasBlock}
disabled={disabled}
/>
<ToolbarButton
type="code"
label="Code Block"
icon="code-block"
onClick={onBlockClick}
isActive={selectionHasBlock}
disabled={disabled}
/>
<ToolbarButton
type="bulleted-list"
label="Bulleted List"
icon="list-bulleted"
onClick={onBlockClick}
isActive={selectionHasBlock}
disabled={disabled}
/>
<ToolbarButton
type="numbered-list"
label="Numbered List"
icon="list-numbered"
onClick={onBlockClick}
isActive={selectionHasBlock}
disabled={disabled}
/>
<div className="nc-toolbar-dropdown">
<Dropdown
dropdownTopOverlap="36px"
button={
<ToolbarButton
label="Add Component"
icon="add-with"
onClick={this.handleComponentsMenuToggle}
disabled={disabled}
/>
}
>
{plugins && plugins.toList().map((plugin, idx) => (
<DropdownItem key={idx} label={plugin.get('label')} onClick={() => onSubmit(plugin.get('id'))} />
))}
</Dropdown>
</div>
</div>
<div className="nc-markdownWidget-toolbar-toggle">
<span
style={{ width: toggleOffLabelWidth }}
className={c(
'nc-markdownWidget-toolbar-toggle-label',
{ 'nc-markdownWidget-toolbar-toggle-label-active': !rawMode },
)}
>
{toggleOffLabel}
</span>
<Toggle
active={rawMode}
onChange={onToggleMode}
className="nc-markdownWidget-toolbar-toggle"
classNameBackground="nc-markdownWidget-toolbar-toggle-background"
/>
<span
style={{ width: toggleOnLabelWidth }}
className={c(
'nc-markdownWidget-toolbar-toggle-label',
{ 'nc-markdownWidget-toolbar-toggle-label-active': rawMode },
)}
>
{toggleOnLabel}
</span>
{this.getToolbarButtonsList().map((toolbarButton) => this.renderToolbarButton(toolbarButton))}
{this.renderToolbarDropdown()}
</div>
{this.renderToolbarToggle()}
</div>
);
}

View File

@ -1,4 +1,5 @@
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import React, { Component } from 'react';
import { get, isEmpty, debounce } from 'lodash';
import { Map } from 'immutable';
@ -33,6 +34,7 @@ export default class Editor extends Component {
onMode: PropTypes.func.isRequired,
className: PropTypes.string.isRequired,
value: PropTypes.string,
field: ImmutablePropTypes.map
};
constructor(props) {
@ -189,7 +191,7 @@ export default class Editor extends Component {
}
render() {
const { onAddAsset, getAsset, className } = this.props;
const { onAddAsset, getAsset, className, field } = this.props;
return (
<div className="nc-visualEditor-wrapper">
@ -206,6 +208,7 @@ export default class Editor extends Component {
onSubmit={this.handlePluginAdd}
onAddAsset={onAddAsset}
getAsset={getAsset}
buttons={field.get('buttons')}
/>
</div>
<Slate

View File

@ -45,6 +45,7 @@ export default class MarkdownControl extends React.Component {
getAsset,
value,
classNameWrapper,
field
} = this.props;
const { mode } = this.state;
@ -57,6 +58,7 @@ export default class MarkdownControl extends React.Component {
getAsset={getAsset}
className={classNameWrapper}
value={value}
field={field}
/>
</div>
);
@ -69,6 +71,7 @@ export default class MarkdownControl extends React.Component {
getAsset={getAsset}
className={classNameWrapper}
value={value}
field={field}
/>
</div>
);