Home Reference Source

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();
            }
        }

    };
}