Add markdown editor toolbar customization
This commit is contained in:
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,111 +35,35 @@ export default class Toolbar extends React.Component {
|
|||||||
selectionHasMark,
|
selectionHasMark,
|
||||||
selectionHasBlock,
|
selectionHasBlock,
|
||||||
selectionHasLink,
|
selectionHasLink,
|
||||||
onToggleMode,
|
|
||||||
rawMode,
|
|
||||||
plugins,
|
|
||||||
onAddAsset,
|
|
||||||
getAsset,
|
|
||||||
disabled,
|
disabled,
|
||||||
onSubmit,
|
buttons
|
||||||
className,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { activePlugin } = this.state;
|
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));
|
||||||
* Because the toggle labels change font weight for active/inactive state,
|
}
|
||||||
* we need to set estimated widths for them to maintain position without
|
|
||||||
* moving other inline items on font weight change.
|
renderToolbarButton(button) {
|
||||||
*/
|
const { type, label, icon, onClick, isActive, disabled } = button;
|
||||||
const toggleOffLabel = 'Rich text';
|
return <ToolbarButton key={label} type={type} label={label} icon={icon} onClick={onClick} isActive={isActive} disabled={disabled}/>;
|
||||||
const toggleOffLabelWidth = '62px';
|
}
|
||||||
const toggleOnLabel = 'Markdown';
|
|
||||||
const toggleOnLabelWidth = '70px';
|
renderToolbarDropdown() {
|
||||||
|
const { plugins, disabled, onSubmit } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={c(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">
|
<div className="nc-toolbar-dropdown">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownTopOverlap="36px"
|
dropdownTopOverlap="36px"
|
||||||
@ -156,7 +81,29 @@ export default class Toolbar extends React.Component {
|
|||||||
))}
|
))}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderToolbarToggle() {
|
||||||
|
const {
|
||||||
|
onToggleMode,
|
||||||
|
rawMode,
|
||||||
|
plugins,
|
||||||
|
disabled,
|
||||||
|
className
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because the toggle labels change font weight for active/inactive state,
|
||||||
|
* we need to set estimated widths for them to maintain position without
|
||||||
|
* moving other inline items on font weight change.
|
||||||
|
*/
|
||||||
|
const toggleOffLabel = 'Rich text';
|
||||||
|
const toggleOffLabelWidth = '62px';
|
||||||
|
const toggleOnLabel = 'Markdown';
|
||||||
|
const toggleOnLabelWidth = '70px';
|
||||||
|
|
||||||
|
return (
|
||||||
<div className="nc-markdownWidget-toolbar-toggle">
|
<div className="nc-markdownWidget-toolbar-toggle">
|
||||||
<span
|
<span
|
||||||
style={{ width: toggleOffLabelWidth }}
|
style={{ width: toggleOffLabelWidth }}
|
||||||
@ -183,6 +130,17 @@ export default class Toolbar extends React.Component {
|
|||||||
{toggleOnLabel}
|
{toggleOnLabel}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className={c(this.props.className, 'nc-toolbar-Toolbar')}>
|
||||||
|
<div>
|
||||||
|
{this.getToolbarButtonsList().map((toolbarButton) => this.renderToolbarButton(toolbarButton))}
|
||||||
|
{this.renderToolbarDropdown()}
|
||||||
|
</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>
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user