Add markdown editor toolbar customization
This commit is contained in:
committed by
Shawn Erquhart
parent
9975c7e914
commit
106968990d
@ -1,4 +1,5 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Editor as Slate } from 'slate-react';
|
import { Editor as Slate } from 'slate-react';
|
||||||
import Plain from 'slate-plain-serializer';
|
import Plain from 'slate-plain-serializer';
|
||||||
@ -50,12 +51,13 @@ export default class RawEditor extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { className } = this.props;
|
const { className, field } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className="nc-rawEditor-rawWrapper">
|
<div className="nc-rawEditor-rawWrapper">
|
||||||
<div className="nc-visualEditor-editorControlBar">
|
<div className="nc-visualEditor-editorControlBar">
|
||||||
<Toolbar
|
<Toolbar
|
||||||
onToggleMode={this.handleToggleMode}
|
onToggleMode={this.handleToggleMode}
|
||||||
|
buttons={field.get('buttons')}
|
||||||
className="nc-markdownWidget-toolbarRaw"
|
className="nc-markdownWidget-toolbarRaw"
|
||||||
disabled
|
disabled
|
||||||
rawMode
|
rawMode
|
||||||
@ -77,4 +79,5 @@ RawEditor.propTypes = {
|
|||||||
onMode: PropTypes.func.isRequired,
|
onMode: PropTypes.func.isRequired,
|
||||||
className: PropTypes.string.isRequired,
|
className: PropTypes.string.isRequired,
|
||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
|
field: ImmutablePropTypes.map
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,7 @@ export default class Toolbar extends React.Component {
|
|||||||
getAsset: PropTypes.func,
|
getAsset: PropTypes.func,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
|
buttons: ImmutablePropTypes.list
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -26,7 +27,7 @@ export default class Toolbar extends React.Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
getToolbarButtonsList() {
|
||||||
const {
|
const {
|
||||||
onMarkClick,
|
onMarkClick,
|
||||||
onBlockClick,
|
onBlockClick,
|
||||||
@ -34,18 +35,64 @@ export default class Toolbar extends React.Component {
|
|||||||
selectionHasMark,
|
selectionHasMark,
|
||||||
selectionHasBlock,
|
selectionHasBlock,
|
||||||
selectionHasLink,
|
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,
|
onToggleMode,
|
||||||
rawMode,
|
rawMode,
|
||||||
plugins,
|
plugins,
|
||||||
onAddAsset,
|
|
||||||
getAsset,
|
|
||||||
disabled,
|
disabled,
|
||||||
onSubmit,
|
className
|
||||||
className,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { activePlugin } = this.state;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Because the toggle labels change font weight for active/inactive state,
|
* Because the toggle labels change font weight for active/inactive state,
|
||||||
* we need to set estimated widths for them to maintain position without
|
* 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';
|
const toggleOnLabelWidth = '70px';
|
||||||
|
|
||||||
return (
|
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>
|
<div>
|
||||||
<ToolbarButton
|
{this.getToolbarButtonsList().map((toolbarButton) => this.renderToolbarButton(toolbarButton))}
|
||||||
type="bold"
|
{this.renderToolbarDropdown()}
|
||||||
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>
|
|
||||||
</div>
|
</div>
|
||||||
|
{this.renderToolbarToggle()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { get, isEmpty, debounce } from 'lodash';
|
import { get, isEmpty, debounce } from 'lodash';
|
||||||
import { Map } from 'immutable';
|
import { Map } from 'immutable';
|
||||||
@ -33,6 +34,7 @@ export default class Editor extends Component {
|
|||||||
onMode: PropTypes.func.isRequired,
|
onMode: PropTypes.func.isRequired,
|
||||||
className: PropTypes.string.isRequired,
|
className: PropTypes.string.isRequired,
|
||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
|
field: ImmutablePropTypes.map
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -189,7 +191,7 @@ export default class Editor extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { onAddAsset, getAsset, className } = this.props;
|
const { onAddAsset, getAsset, className, field } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="nc-visualEditor-wrapper">
|
<div className="nc-visualEditor-wrapper">
|
||||||
@ -206,6 +208,7 @@ export default class Editor extends Component {
|
|||||||
onSubmit={this.handlePluginAdd}
|
onSubmit={this.handlePluginAdd}
|
||||||
onAddAsset={onAddAsset}
|
onAddAsset={onAddAsset}
|
||||||
getAsset={getAsset}
|
getAsset={getAsset}
|
||||||
|
buttons={field.get('buttons')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Slate
|
<Slate
|
||||||
|
@ -45,6 +45,7 @@ export default class MarkdownControl extends React.Component {
|
|||||||
getAsset,
|
getAsset,
|
||||||
value,
|
value,
|
||||||
classNameWrapper,
|
classNameWrapper,
|
||||||
|
field
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { mode } = this.state;
|
const { mode } = this.state;
|
||||||
@ -57,6 +58,7 @@ export default class MarkdownControl extends React.Component {
|
|||||||
getAsset={getAsset}
|
getAsset={getAsset}
|
||||||
className={classNameWrapper}
|
className={classNameWrapper}
|
||||||
value={value}
|
value={value}
|
||||||
|
field={field}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -69,6 +71,7 @@ export default class MarkdownControl extends React.Component {
|
|||||||
getAsset={getAsset}
|
getAsset={getAsset}
|
||||||
className={classNameWrapper}
|
className={classNameWrapper}
|
||||||
value={value}
|
value={value}
|
||||||
|
field={field}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user