vslinko / obsidian-outliner

Work with your lists like in Workflowy or RoamResearch
MIT License
1.01k stars 59 forks source link

[BUG] Drag and drop does not work in some documents #520

Open kuyper opened 5 months ago

kuyper commented 5 months ago

Describe the bug I have several documents in obsidian where the drag and drop feature of outliner does not work.

To Reproduce Use this file and try to drag and drop


# Lorem ipsum

- šŸ”“ Lorem ipsum
- šŸŸ” Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 

# Lorem ipsum

- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com)  (Lorem, ipsum [[07.05.2024]])
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ” Lorem ipsum
- šŸ”“ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum

# Lorem ipsum

- šŸŸ” Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
# Lorem ipsum

- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum

# Lorem ipsum

- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum

# Lorem ipsum

- ā“ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum

# Lorem ipsum

- šŸ”“ Lorem ipsum
- šŸŸ” Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 

# Lorem ipsum

- šŸ”“ Lorem ipsum
    - [Lorem ipsum](https://www.google.com) 
- šŸ”“ Lorem ipsum
- šŸ”“ Lorem ipsum
- šŸŸ” Lorem ipsum
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- Lorem ipsum
    - šŸ”“ Lorem ipsum
    - šŸŸ¢ Lorem ipsum
    - šŸŸ¢ Lorem ipsum
    - šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
    - šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
    - šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
    - šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 

# Lorem ipsum

- šŸ”“ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 

# Lorem ipsum

- šŸ”“ Lorem ipsum
- šŸŸ¢ Lorem ipsum
- šŸŸ¢ Lorem ipsum

# Lorem ipsum

- šŸ”“ Lorem ipsum

# Lorem ipsum

- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸŸ¢ Lorem ipsum [Lorem ipsum](https://www.google.com) 
- šŸ”“ Lorem ipsum
- šŸ”“ Lorem ipsum
- šŸ”“ Lorem ipsum
- šŸ”“ Lorem ipsum
- šŸ”“ Lorem ipsum
- šŸ”“ Lorem ipsum

# Lorem ipsum

- šŸ”“ Lorem ipsum
- šŸ”“ Lorem ipsum
- šŸ”“ Lorem ipsum

The error message I get on the console is

plugin:obsidian-outliner:1371 Uncaught TypeError: Cannot read properties of null (reading 'left')
    at DragAndDropState.calculateLeftPadding (plugin:obsidian-outliner:1371:56)
    at new DragAndDropState (plugin:obsidian-outliner:1287:14)
    at DragAndDrop.startDragging (plugin:obsidian-outliner:1181:23)
    at DragAndDrop.handleMouseMove (plugin:obsidian-outliner:1098:22)

and sometimes

plugin:obsidian-outliner:1381 Uncaught TypeError: Cannot read properties of null (reading 'left')
    at DragAndDropState.calculateTabWidth (plugin:obsidian-outliner:1381:35)
    at new DragAndDropState (plugin:obsidian-outliner:1288:14)
    at DragAndDrop.startDragging (plugin:obsidian-outliner:1181:23)
    at DragAndDrop.handleMouseMove (plugin:obsidian-outliner:1098:22)
calculateTabWidth @ plugin:obsidian-outliner:1381
DragAndDropState @ plugin:obsidian-outliner:1288
startDragging @ plugin:obsidian-outliner:1181
DragAndDrop.handleMouseMove @ plugin:obsidian-outliner:1098

Expected behavior Drag and drop should work

Environment (please complete the following information):

kazerniel commented 5 months ago

It stopped working for me too, can't pin down the reason.

(Windows 10 desktop)

Edit: actually it works in source mode, but not in live preview

Edit 2: nvm, it doesn't work even in source mode in another note

vaughanvandyk commented 5 months ago

This has started happening to me too since a few days ago for any list, even a simple one with no sublists in a new blank note.

The error that displays in the Inspector whenever trying to drag a list item is as follows:

Uncaught TypeError: Cannot read properties of null (reading 'left')
    at DragAndDropState.calculateLeftPadding (DragAndDrop.ts:434:52)
    at new DragAndDropState (DragAndDrop.ts:327:10)
    at DragAndDrop.startDragging (DragAndDrop.ts:161:19)
    at DragAndDrop.handleMouseMove (DragAndDrop.ts:131:12)
calculateLeftPadding @ DragAndDrop.ts:434
DragAndDropState @ DragAndDrop.ts:327
startDragging @ DragAndDrop.ts:161
DragAndDrop.handleMouseMove @ DragAndDrop.ts:131
vaughanvandyk commented 5 months ago

After lots of testing in various vaults with combinations of themes and plugins, Outliner's drag-and-drop of list items works perfectly until I add any property to the note (either as YAML frontmatter or using the Properties plugin). If I remove all properties for the note, the drag-and-drop functionality returns for that note.

@kuyper and @kazerniel can you confirm this is the same your side please?

I have since found this was already logged as a bug here: #506

A fix is discussed here: https://forum.obsidian.md/t/fix-for-drag-and-drop-functionality-in-outliner-plugin/76425

Essentially, replace the calculateLeftPadding() function in main.js of the Outliner plugin.

kazerniel commented 5 months ago

@vaughanvandyk Thank you for your comment! After testing on a new note without frontmatter, I can confirm that drag-and-drop works before adding frontmatter, and breaks after adding it.

I tried using just the forum snippet, then combined with the Github snippet as well, but they unfortunately still don't fix that note with the long list that I had issues with earlier. It works on other shorter notes and on certain launches works in source mode on that longer list, but never in live preview mode.

vaughanvandyk commented 5 months ago

Thanks for confirming, @kazerniel -- I've been testing with your example list a bit further and it works when we remove the last few sections or so. It can't be due to there being too many list items, because I have a separate test with a list of 250+ items and DnD works perfectly there. Do you perhaps have other list examples that this occurs in to see if there's a pattern?

fenberg commented 5 months ago

I am not very good at reading and writing English, so I am using translation. I apologize if it is difficult to read.

I am not a programmer, and this is my first time posting on GitHub, but I was facing a similar issue where the problem occurred when writing more than 40 lines. I consulted with an AI and somehow managed to rewrite the following function as instructed, and it started working, so I'm sharing it.

calculateNearestDropVariant(x, y) {
    const { view, editor } = this;
    const dropVariants = this.getDropVariants();

    for (const v of dropVariants) {
        const { placeToMove } = v;
        v.left = this.leftPadding + (v.level - 1) * this.tabWidth;
        const positionAfterList = v.whereToMove === "after" || v.whereToMove === "inside";
        const line = positionAfterList
            ? placeToMove.getContentEndIncludingChildren().line
            : placeToMove.getFirstLineContentStart().line;
        const linePos = editor.posToOffset({
            line,
            ch: 0,
        });
        const coords = view.coordsAtPos(linePos, -1);
        if (coords) {
            v.top = coords.top;
            if (positionAfterList) {
                v.top += view.lineBlockAt(linePos).height;
            }
            // Better vertical alignment
            v.top -= 8;
        } else {
            v.top = -1;
        }
    }

    const nearestLineTop = dropVariants
        .filter((v) => v.top !== -1)
        .sort((a, b) => Math.abs(y - a.top) - Math.abs(y - b.top))
        .first()?.top;

    if (nearestLineTop === undefined) {
        this.dropVariant = null;
        return;
    }

    const variantsOnNearestLine = dropVariants.filter((v) => Math.abs(v.top - nearestLineTop) <= 4);
    this.dropVariant = variantsOnNearestLine
        .sort((a, b) => Math.abs(x - a.left) - Math.abs(x - b.left))
        .first();
}

calculateLeftPadding() {
    const { view } = this;
    let leftPadding = 0;

    for (let i = 0; i < view.state.doc.length; i++) {
        const coords = view.coordsAtPos(i);
        if (coords) {
            leftPadding = coords.left;
            break;
        }
    }

    this.leftPadding = leftPadding;
}

calculateTabWidth() {
    const { view } = this;
    const singleIndent = language.indentString(view.state, language.getIndentUnit(view.state));

    for (let i = 1; i <= view.state.doc.lines; i++) {
        const line = view.state.doc.line(i);
        if (line.text.startsWith(singleIndent)) {
            const a = view.coordsAtPos(line.from, -1);
            const b = view.coordsAtPos(line.from + singleIndent.length, -1);
            if (a && b) {
                this.tabWidth = b.left - a.left;
                return;
            }
        }
    }

    // Fallback to default tab width if no indented lines are found
    this.tabWidth = view.defaultCharacterWidth * language.getIndentUnit(view.state);
}
kazerniel commented 5 months ago

@vaughanvandyk, I think you pinged the wrong user, @kuyper posted that example list. (I'm travelling right now but I can post my own example list, that the forum and Github code fixes didn't work on, when I get home next week.)