application/components/common/rich-editor2/plugins/grid.js
import React from 'react';
import ToolbarButton from '../common/toolbar-button';
import {Block} from 'slate';
import {ensureEmptyGraf} from '../util/ensure-empty-graf';
import { Toolbar } from 'material-ui';
export const SERIALIZER_RULES = [
{
deserialize(el, next) {
if (el.tagName.toLowerCase() == 'div') {
const className = el.getAttribute('class');
if (className && className.search(/__ceo-flex-container/) !== -1) {
return {
object: 'block',
type: 'grid',
nodes: next(el.childNodes)
};
} else if (className && className.search(/__ceo-flex-col/) !== -1) {
return {
object: 'block',
type: 'grid-cell',
nodes: next(el.childNodes)
};
}
}
},
serialize(obj, children) {
if (obj.object == 'block') {
switch (obj.type) {
case 'grid':
return <div className="__ceo-flex-container">{children}</div>;
case 'grid-cell':
return <div className="__ceo-flex-col">{children}</div>;
}
}
}
}
];
const onBackspace = function(e, editor, next) {
const {value} = editor;
const {selection} = value;
if (selection.start.offset != 0) {
return next();
}
// stop backspace at the head of a cell
event.preventDefault();
}
const onDelete = function(e, editor, next) {
const {value} = editor;
const {selection} = value;
if (selection.end.offset != value.startText.text.length) {
return next();
}
// stop delete at the end of a cell
event.preventDefault();
}
const onEnter = function(e, editor, next) {
// nothing atm
}
export function gridClickHandler(e) {
const {editor} = this;
const {editorContent} = this.state;
const {start} = editor.value.selection;
const hasGrid = editor.value.document.getAncestors(start.key).find((block) => {
return block.type == 'grid';
})
console.log('HASGRID', hasGrid);
if (!hasGrid) {
const sibling = editor.value.document.getNextNode(start.key);
editor
.setBlocks('paragraph')
.wrapBlock('grid')
.wrapBlock('grid-cell');
// insert a second cell
const parentRow = editor.value.document.getAncestors(start.key).find((block) => {
return block.type == 'grid';
})
if (parentRow) {
// there's really no reason there shouldn't be but... javascript
const blankCell = Block.create({
type: 'grid-cell',
nodes: [
Block.create({
type: 'paragraph'
})
]
});
editor.insertNodeByKey(parentRow.key, parentRow.nodes.size, blankCell);
}
if (!sibling) {
// make sure we have a blank line
ensureEmptyGraf(editor);
}
} else {
// editor.setBlocks('paragraph')
// .unwrapBlock('table-cell')
// .unwrapBlock('table-row')
// .unwrapBlock('table-table');
return
}
}
export function gridAddCellHandler(e) {
const {editor} = this;
const {editorContent} = this.state;
// are we currently in a grid
const {start} = editor.value.selection;
const grid = editor.value.document.getAncestors(start.key).find((block) => {
return block.type == 'grid';
})
if (!grid) {
return;
}
editor.insertNodeByKey(grid.key, grid.nodes.size, Block.create({
type: 'grid-cell',
nodes: [
Block.create({
type: 'paragraph'
})
]
}));
}
export function gridRemoveCellHandler(e) {
const {editor} = this;
const {editorContent} = this.state;
const {start} = editor.value.selection;
const grid = editor.value.document.getAncestors(start.key).find((block) => {
return block.type == 'grid';
});
if (!grid) {
return;
}
const selectedCell = editor.value.document.getAncestors(start.key).find((block) => {
return block.type == 'grid-cell';
});
editor.removeNodeByKey(selectedCell.key);
}
export class GridButton extends ToolbarButton
{
handleOnClick(e) {
return gridClickHandler.call(this.props.editor, e);
}
getLabel() {
return 'GR';
}
}
export class GridCellAddButton extends ToolbarButton
{
handleOnClick(e) {
return gridAddCellHandler.call(this.props.editor, e);
}
getLabel() {
return 'GC+';
}
}
export class GridCellRemoveButton extends ToolbarButton
{
handleOnClick(e) {
return gridRemoveCellHandler.call(this.props.editor, e);
}
getLabel() {
return 'GC-';
}
}
export function GridPlugin(options) {
return {
renderNode: (props, editor, next) => {
const {attributes, children, node} = props;
switch(node.type) {
case 'grid':
return (
<div {...attributes} className='grid-container-node'>
{children}
</div>
);
case 'grid-cell':
return <div {...attributes} className='grid-cell-node'>{children}</div>;
default:
return next();
}
},
onKeyDown: (e, editor, next) => {
const {value} = editor;
const {document, selection} = value;
const {start, isCollapsed} = selection;
const startNode = document.getDescendant(start.key);
if (isCollapsed && start.isAtStartOfNode(startNode)) {
const previous = document.getPreviousText(startNode.key);
if (!previous) {
return next();
}
const previousBlock = document.getClosestBlock(previous.key);
if (previousBlock.type === 'grid-cell') {
if (['Backspace', 'Delete', 'Enter'].includes(e.key)) {
// we're in a grid cell
e.preventDefault();
} else {
// we're not in a grid cell
return next();
}
}
}
if (value.startBlock.type != 'grid-cell') {
return next();
}
switch(e.key) {
case 'Backspace':
return onBackspace(e, editor, next);
case 'Delete':
return onDelete(e, editor, next);
case 'Enter':
return onEnter(e, editor, next);
default:
return next();
}
}
};
}