-
Notifications
You must be signed in to change notification settings - Fork 14
/
toolbar.js
122 lines (118 loc) · 3.73 KB
/
toolbar.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import React, { Component, PropTypes } from 'react';
import Portal from 'react-portal';
import ReactDOM from 'react-dom';
import classNames from 'classnames';
export default (options = {}) => Block => {
let { actions, manual, remove, move } = options;
if (!actions) actions = () => [];
return class BlockToolbarDecorator extends Component {
static slate = Block.slate;
static propTypes = {
isFocused: PropTypes.bool,
children: PropTypes.node,
actions: PropTypes.array,
}
static defaultProps = {
actions: [],
}
state = { menu: null };
componentDidMount() {
if (manual) return;
const rect = ReactDOM.findDOMNode(this).getBoundingClientRect();
this.setToolbarPosition(rect);
}
componentDidUpdate() {
this.componentDidMount();
}
setToolbarPosition = (rect) => {
const { menu } = this.state;
if (!menu) return;
if (!rect) return;
const top = (rect.top + window.scrollY) - menu.offsetHeight;
const left = rect.left + window.scrollX - menu.offsetWidth / 2 + rect.width / 2; // eslint-disable-line
menu.style.opacity = 1;
menu.style.top = `${top - 3}px`;
menu.style.left = `${left}px`;
}
onOpen = ({ firstChild: menu }) => {
this.setState({ menu });
}
onClick = action => e => {
e.preventDefault();
action();
}
renderToolbar = () => {
// Get actions from props and from decorator arguments
const allActions = [...this.props.actions, ...actions(this.props)];
// Add remove action if (remove = true)
if (remove) allActions.push(removeAction(this.props));
// Add move up/down actions if (move = true)
if (move) moveActions(this.props).forEach(action => allActions.push(action));
return (
<Portal onOpen={this.onOpen} isOpened={!!allActions.length} key="toolbar-0">
<div className="slate-toolbar">
{allActions.map(({ toggle, type, active, icon, separated }) => (
<span key={type} className={classNames('slate-toolbar-item', { separated })} onMouseDown={this.onClick(toggle)} data-active={active}>
<i className={`fa fa-${icon}`} />
</span>
))}
</div>
</Portal>
);
}
render() {
const { isFocused } = this.props;
const children = isFocused
? [...this.props.children, this.renderToolbar()]
: this.props.children;
return (
<Block
{...this.props}
children={children}
setToolbarPosition={this.setToolbarPosition}
/>
);
}
};
};
// Toolbar action to remove a block
const removeAction = ({ editor, state, node }) => ({
type: 'block.remove',
icon: 'trash-o',
separated: true,
toggle: () => {
let newState = state.transform().unsetSelection();
editor.onChange(
newState.removeNodeByKey(node.key).apply()
);
},
});
// Toolbar actions to move a block up/down
const moveActions = ({ editor, state, node }) => ([{
type: 'block.moveUp',
icon: 'arrow-up',
separated: true,
toggle: () => {
const { document } = state;
const parent = document.getParent(node);
const index = parent.nodes.indexOf(node) - 1;
let newState = state
.transform()
.moveNodeByKey(node, parent, index === -1 ? 0 : index)
.apply();
editor.onChange(newState);
},
}, {
type: 'block.moveDown',
icon: 'arrow-down',
toggle: () => {
const { document } = state;
const parent = document.getParent(node);
const index = parent.nodes.indexOf(node) + 1;
let newState = state
.transform()
.moveNodeByKey(node, parent, index > parent.nodes.count() ? parent.nodes.count() : index)
.apply();
editor.onChange(newState);
},
}]);