improve list handling

This commit is contained in:
Shawn Erquhart 2017-07-26 21:44:39 -04:00
parent ae7bd79c7a
commit 7a744bef84

View File

@ -203,10 +203,12 @@ const BackspaceCloseBlock = (options = {}) => ({
} }
}); });
const EditListPlugin = EditList({ types: ['bulleted-list', 'numbered-list'], typeItem: 'list-item' });
const slatePlugins = [ const slatePlugins = [
SoftBreak({ ignoreIn: ['paragraph', 'list-item', 'numbered-list', 'bulleted-list', 'table', 'table-row', 'table-cell'], closeAfter: 1 }), SoftBreak({ ignoreIn: ['paragraph', 'list-item', 'numbered-list', 'bulleted-list', 'table', 'table-row', 'table-cell'], closeAfter: 1 }),
BackspaceCloseBlock({ ignoreIn: ['paragraph', 'list-item', 'bulleted-list', 'numbered-list', 'table', 'table-row', 'table-cell'] }), BackspaceCloseBlock({ ignoreIn: ['paragraph', 'list-item', 'bulleted-list', 'numbered-list', 'table', 'table-row', 'table-cell'] }),
EditList({ types: ['bulleted-list', 'numbered-list'], typeItem: 'list-item' }), EditListPlugin,
EditTable({ typeTable: 'table', typeRow: 'table-row', typeCell: 'table-cell' }), EditTable({ typeTable: 'table', typeRow: 'table-row', typeCell: 'table-cell' }),
]; ];
@ -228,6 +230,10 @@ export default class Editor extends Component {
nodes: NODE_COMPONENTS, nodes: NODE_COMPONENTS,
marks: MARK_COMPONENTS, marks: MARK_COMPONENTS,
rules: [ rules: [
/**
* If the editor is ever in an empty state, insert an empty
* paragraph block.
*/
{ {
match: object => object.kind === 'document', match: object => object.kind === 'document',
validate: doc => { validate: doc => {
@ -276,29 +282,30 @@ export default class Editor extends Component {
}; };
if (data.key === 'enter') { if (data.key === 'enter') {
/** /**
* If a single void block is selected, and it's a direct descendant of the * If "Enter" is pressed while a single void block is selected, a new
* document (top level), a new paragraph should be added above or below it * paragraph should be added above or below it, and the current selection
* when 'Enter' is pressed, and the current selection should move to the * should be collapsed to the start of the new paragraph.
* new paragraph.
* *
* If the selected block is the first block in the document, create the * If the selected block is the first block in the document, create the
* new block above it. If not, create the new block below it. * new block above it. If not, create the new block below it.
*/ */
const { document: doc, selection, anchorBlock, focusBlock } = state; const { document: doc, selection, anchorBlock, focusBlock } = state;
const focusBlockIndex = doc.nodes.indexOf(focusBlock);
const focusBlockIsTopLevel = focusBlockIndex > -1;
const focusBlockIsFirstChild = focusBlockIndex === 0;
const singleBlockSelected = anchorBlock === focusBlock; const singleBlockSelected = anchorBlock === focusBlock;
if (!singleBlockSelected || !focusBlock.isVoid) return;
if (focusBlock.isVoid && focusBlockIsTopLevel && singleBlockSelected) { e.preventDefault();
e.preventDefault();
const newBlock = createDefaultBlock(); const focusBlockParent = doc.getParent(focusBlock.key);
const newBlockIndex = focusBlockIsFirstChild ? 0 : focusBlockIndex + 1; const focusBlockIndex = focusBlockParent.nodes.indexOf(focusBlock);
return state.transform() const focusBlockIsFirstChild = focusBlockIndex === 0;
.insertNodeByKey(doc.key, newBlockIndex, newBlock)
.collapseToStartOf(newBlock) const newBlock = createDefaultBlock();
.apply(); const newBlockIndex = focusBlockIsFirstChild ? 0 : focusBlockIndex + 1;
}
return state.transform()
.insertNodeByKey(focusBlockParent.key, newBlockIndex, newBlock)
.collapseToStartOf(newBlock)
.apply();
} }
if (data.isMod) { if (data.isMod) {
@ -340,41 +347,30 @@ export default class Editor extends Component {
handleBlockClick = (event, type) => { handleBlockClick = (event, type) => {
event.preventDefault(); event.preventDefault();
let { editorState } = this.state; let { editorState } = this.state;
const { document: doc, selection } = editorState;
const transform = editorState.transform(); const transform = editorState.transform();
const doc = editorState.document;
const isList = this.hasBlock('list-item')
// Handle everything except list buttons. // Handle everything except list buttons.
if (!['bulleted-list', 'numbered-list'].includes(type)) { if (!['bulleted-list', 'numbered-list'].includes(type)) {
const isActive = this.hasBlock(type); const isActive = this.hasBlock(type);
const transformed = transform.setBlock(isActive ? DEFAULT_NODE : type); const transformed = transform.setBlock(isActive ? DEFAULT_NODE : type);
if (isList) {
transformed
.unwrapBlock('bulleted-list')
.unwrapBlock('numbered-list');
}
} }
// Handle the extra wrapping required for list buttons. // Handle the extra wrapping required for list buttons.
else { else {
const isType = editorState.blocks.some(block => { const isSameListType = editorState.blocks.some(block => {
return !!doc.getClosest(block.key, parent => parent.type === type); return !!doc.getClosest(block.key, parent => parent.type === type);
}); });
const isInList = EditListPlugin.utils.isSelectionInList(editorState);
if (isList && isType) { if (isInList && isSameListType) {
transform EditListPlugin.transforms.unwrapList(transform, type);
.setBlock(DEFAULT_NODE) } else if (isInList) {
.unwrapBlock('bulleted-list') const currentListType = type === 'bulleted-list' ? 'numbered-list' : 'bulleted-list';
.unwrapBlock('numbered-list'); EditListPlugin.transforms.unwrapList(transform, currentListType);
} else if (isList) { EditListPlugin.transforms.wrapInList(transform, type);
transform
.unwrapBlock(type === 'bulleted-list' ? 'numbered-list' : 'bulleted-list')
.wrapBlock(type);
} else { } else {
transform EditListPlugin.transforms.wrapInList(transform, type);
.setBlock('list-item')
.wrapBlock(type);
} }
} }