mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
feat: a try at flat list type of behavior
This commit is contained in:
parent
9d2d9c59ca
commit
5746b187c5
@ -1,6 +1,6 @@
|
|||||||
import { Extension } from "@tiptap/core";
|
import { Extension } from "@tiptap/core";
|
||||||
|
|
||||||
import { handleBackspace, handleDelete } from "src/ui/extensions/custom-list-keymap/list-helpers";
|
import { handleBackspace, handleDelete } from "src/ui/extensions/custom-list-keymap/list-utils";
|
||||||
|
|
||||||
export type ListKeymapOptions = {
|
export type ListKeymapOptions = {
|
||||||
listTypes: Array<{
|
listTypes: Array<{
|
||||||
|
@ -1,6 +1,26 @@
|
|||||||
import { EditorState } from "@tiptap/pm/state";
|
import { Command, EditorState, TextSelection } from "@tiptap/pm/state";
|
||||||
import { Editor, getNodeType, getNodeAtPosition, isAtEndOfNode, isAtStartOfNode, isNodeActive } from "@tiptap/core";
|
import { Editor, getNodeType, getNodeAtPosition, isAtEndOfNode, isAtStartOfNode, isNodeActive } from "@tiptap/core";
|
||||||
import { Node, NodeType } from "@tiptap/pm/model";
|
import { Node, NodeType } from "@tiptap/pm/model";
|
||||||
|
import { liftTarget } from "@tiptap/pm/transform";
|
||||||
|
|
||||||
|
const isCursorAtFirstListItem = (editor: Editor, typeOrName: string): boolean => {
|
||||||
|
const { $from } = editor.state.selection;
|
||||||
|
// const listItem = getNodeType(typeOrName, editor.state.schema);
|
||||||
|
|
||||||
|
// Check if the current node is a list item
|
||||||
|
// console.log($from.parent.type);
|
||||||
|
// if ($from.parent.type !== listItem) {
|
||||||
|
// console.log("--------------------");
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Check if the cursor is at the start of the list item
|
||||||
|
if ($from.index($from.depth - 1) === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
const findListItemPos = (typeOrName: string | NodeType, state: EditorState) => {
|
const findListItemPos = (typeOrName: string | NodeType, state: EditorState) => {
|
||||||
const { $from } = state.selection;
|
const { $from } = state.selection;
|
||||||
@ -29,6 +49,20 @@ const findListItemPos = (typeOrName: string | NodeType, state: EditorState) => {
|
|||||||
return { $pos: state.doc.resolve(currentPos), depth: targetDepth };
|
return { $pos: state.doc.resolve(currentPos), depth: targetDepth };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const prevListIsHigher = (typeOrName: string, state: EditorState) => {
|
||||||
|
const listDepth = getPrevListDepth(typeOrName, state);
|
||||||
|
const listItemPos = findListItemPos(typeOrName, state);
|
||||||
|
|
||||||
|
if (!listItemPos || !listDepth) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listDepth < listItemPos.depth) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
const nextListIsDeeper = (typeOrName: string, state: EditorState) => {
|
const nextListIsDeeper = (typeOrName: string, state: EditorState) => {
|
||||||
const listDepth = getNextListDepth(typeOrName, state);
|
const listDepth = getNextListDepth(typeOrName, state);
|
||||||
const listItemPos = findListItemPos(typeOrName, state);
|
const listItemPos = findListItemPos(typeOrName, state);
|
||||||
@ -63,6 +97,18 @@ const getPrevListDepth = (typeOrName: string, state: EditorState) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [, depth] = getNodeAtPosition(state, typeOrName, listItemPos.$pos.pos - 4);
|
||||||
|
|
||||||
|
return depth;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPrevParentListDepth = (typeOrName: string, state: EditorState) => {
|
||||||
|
const listItemPos = findListItemPos(typeOrName, state);
|
||||||
|
|
||||||
|
if (!listItemPos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let depth = 0;
|
let depth = 0;
|
||||||
const pos = listItemPos.$pos;
|
const pos = listItemPos.$pos;
|
||||||
|
|
||||||
@ -147,9 +193,11 @@ export const handleBackspace = (editor: Editor, name: string, parentListTypes: s
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const isParaSibling = isCurrentParagraphASibling(editor.state);
|
const isParaSibling = isCurrentParagraphASibling(editor.state);
|
||||||
const isCurrentListItemSublist = prevListIsHigher(name, editor.state);
|
const currentListItemIsSublist = prevParentListIsHigher(name, editor.state);
|
||||||
const listItemPos = findListItemPos(name, editor.state);
|
const listItemPos = findListItemPos(name, editor.state);
|
||||||
const nextListItemIsSibling = nextListIsSibling(name, editor.state);
|
const nextListItemIsSibling = nextListIsSibling(name, editor.state);
|
||||||
|
// __AUTO_GENERATED_PRINT_VAR_START__
|
||||||
|
console.log("handleBackspace nextListItemIsSibling: %s", nextListItemIsSibling); // __AUTO_GENERATED_PRINT_VAR_END__
|
||||||
|
|
||||||
if (!listItemPos) {
|
if (!listItemPos) {
|
||||||
return false;
|
return false;
|
||||||
@ -158,41 +206,119 @@ export const handleBackspace = (editor: Editor, name: string, parentListTypes: s
|
|||||||
const currentNode = listItemPos.$pos.node(listItemPos.depth);
|
const currentNode = listItemPos.$pos.node(listItemPos.depth);
|
||||||
const currentListItemHasSubList = listItemHasSubList(name, editor.state, currentNode);
|
const currentListItemHasSubList = listItemHasSubList(name, editor.state, currentNode);
|
||||||
|
|
||||||
if (currentListItemHasSubList && isCurrentListItemSublist && isParaSibling) {
|
const isFirstListItemAtDepth = prevListIsHigher(name, editor.state);
|
||||||
|
|
||||||
|
// current list is sublist and has sublist and the cursor is at a paragraph node is sibling inside a list item
|
||||||
|
if (currentListItemHasSubList && currentListItemIsSublist && isParaSibling) {
|
||||||
|
console.log("1");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentListItemHasSubList && isCurrentListItemSublist) {
|
if (currentListItemHasSubList && currentListItemIsSublist) {
|
||||||
editor.chain().liftListItem(name).run();
|
if (isFirstListItemAtDepth) {
|
||||||
return editor.commands.joinItemBackward();
|
console.log("2a");
|
||||||
|
editor.chain().liftListItem(name).run();
|
||||||
|
|
||||||
|
return editor.commands.joinItemBackward();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("2");
|
||||||
|
return liftListItem(editor.state, editor.view.dispatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCurrentListItemSublist && nextListItemIsSibling) {
|
// if (currentListItemIsSublist && nextListItemIsSibling) {
|
||||||
return false;
|
// console.log("3");
|
||||||
}
|
// return liftListItem(editor.state, editor.view.dispatch);
|
||||||
|
// }
|
||||||
|
|
||||||
if (isCurrentListItemSublist) {
|
if (currentListItemIsSublist) {
|
||||||
|
console.log("4");
|
||||||
|
return liftListItem(editor.state, editor.view.dispatch);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentListItemHasSubList) {
|
if (currentListItemHasSubList) {
|
||||||
|
console.log("5");
|
||||||
|
const { $anchor } = editor.state.selection;
|
||||||
|
|
||||||
|
const $listPos = editor.state.doc.resolve($anchor.before() - 1);
|
||||||
|
|
||||||
|
const listDescendants: Array<{ node: Node; pos: number }> = [];
|
||||||
|
|
||||||
|
$listPos.node().descendants((node, pos) => {
|
||||||
|
if (node.type.name === name) {
|
||||||
|
listDescendants.push({ node, pos });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const lastItem = listDescendants.at(-1);
|
||||||
|
|
||||||
|
if (!lastItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const $lastItemPos = editor.state.doc.resolve($listPos.start() + lastItem.pos + 1);
|
||||||
|
|
||||||
|
// Check if positions are within the valid range
|
||||||
|
const startPos = $anchor.start() - 1;
|
||||||
|
const endPos = $anchor.end() + 1;
|
||||||
|
if (startPos < 0 || endPos > editor.state.doc.content.size) {
|
||||||
|
return false; // Invalid position, abort operation
|
||||||
|
}
|
||||||
|
|
||||||
|
return editor.chain().cut({ from: startPos, to: endPos }, $lastItemPos.end()).joinForward().run();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasListItemBefore(name, editor.state)) {
|
if (hasListItemBefore(name, editor.state)) {
|
||||||
|
console.log("6");
|
||||||
return editor.chain().liftListItem(name).run();
|
return editor.chain().liftListItem(name).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentListItemHasSubList) {
|
if (!currentListItemHasSubList) {
|
||||||
|
console.log("7");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
console.log("8");
|
||||||
// otherwise in the end, a backspace should
|
// otherwise in the end, a backspace should
|
||||||
// always just lift the list item if
|
// always just lift the list item if
|
||||||
// joining / merging is not possible
|
// joining / merging is not possible
|
||||||
return editor.chain().liftListItem(name).run();
|
return editor.chain().liftListItem(name).run();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// If the cursor is in a list item (empty or not), attempt to lift the
|
||||||
|
/// list item out of its parent list.
|
||||||
|
export const liftListItem: Command = (state, dispatch) => {
|
||||||
|
let { $cursor } = state.selection as TextSelection;
|
||||||
|
if (!$cursor) return false;
|
||||||
|
|
||||||
|
// Check if the current node is a list item
|
||||||
|
let listItem = state.schema.nodes.listItem;
|
||||||
|
|
||||||
|
// Find the depth of the list item in the current selection
|
||||||
|
let listItemDepth = $cursor.depth;
|
||||||
|
while (listItemDepth > 0 && $cursor.node(listItemDepth).type !== listItem) {
|
||||||
|
listItemDepth--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not inside a list item, return false
|
||||||
|
if ($cursor.node(listItemDepth).type !== listItem) return false;
|
||||||
|
|
||||||
|
// Attempt to lift the list item
|
||||||
|
let range = $cursor.blockRange(),
|
||||||
|
target = range && liftTarget(range);
|
||||||
|
// __AUTO_GENERATED_PRINT_VAR_START__
|
||||||
|
console.log("liftListItem range: %s", range); // __AUTO_GENERATED_PRINT_VAR_END__
|
||||||
|
|
||||||
|
if (target == null) {
|
||||||
|
console.log("==============");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dispatch) dispatch(state.tr.lift(range!, target).scrollIntoView());
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
export const handleDelete = (editor: Editor, name: string) => {
|
export const handleDelete = (editor: Editor, name: string) => {
|
||||||
// if the cursor is not inside the current node type
|
// if the cursor is not inside the current node type
|
||||||
// do nothing and proceed
|
// do nothing and proceed
|
||||||
@ -237,8 +363,8 @@ const hasListBefore = (editorState: EditorState, name: string, parentListTypes:
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const prevListIsHigher = (typeOrName: string, state: EditorState) => {
|
const prevParentListIsHigher = (typeOrName: string, state: EditorState) => {
|
||||||
const listDepth = getPrevListDepth(typeOrName, state);
|
const listDepth = getPrevParentListDepth(typeOrName, state);
|
||||||
const listItemPos = findListItemPos(typeOrName, state);
|
const listItemPos = findListItemPos(typeOrName, state);
|
||||||
|
|
||||||
if (!listItemPos || !listDepth) {
|
if (!listItemPos || !listDepth) {
|
||||||
@ -253,14 +379,16 @@ const prevListIsHigher = (typeOrName: string, state: EditorState) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const nextListIsSibling = (typeOrName: string, state: EditorState) => {
|
const nextListIsSibling = (typeOrName: string, state: EditorState) => {
|
||||||
const listDepth = getNextListDepth(typeOrName, state);
|
const nextListDepth = getNextListDepth(typeOrName, state);
|
||||||
const listItemPos = findListItemPos(typeOrName, state);
|
const listItemPos = findListItemPos(typeOrName, state);
|
||||||
|
// __AUTO_GENERATED_PRINT_VAR_START__
|
||||||
|
console.log("nextListIsSibling listDepth: %s", nextListDepth, listItemPos?.depth); // __AUTO_GENERATED_PRINT_VAR_END__
|
||||||
|
|
||||||
if (!listItemPos || !listDepth) {
|
if (!listItemPos || !nextListDepth) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listDepth === listItemPos.depth) {
|
if (nextListDepth === listItemPos.depth) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user