mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
widgetizing tree WIP
This commit is contained in:
parent
d1f679ab90
commit
b12e38c231
@ -1,6 +1,5 @@
|
|||||||
import cloning from './services/cloning.js';
|
import cloning from './services/cloning.js';
|
||||||
import contextMenu from './services/tree_context_menu.js';
|
import contextMenu from './services/tree_context_menu.js';
|
||||||
import dragAndDropSetup from './services/drag_and_drop.js';
|
|
||||||
import link from './services/link.js';
|
import link from './services/link.js';
|
||||||
import ws from './services/ws.js';
|
import ws from './services/ws.js';
|
||||||
import noteDetailService from './services/note_detail.js';
|
import noteDetailService from './services/note_detail.js';
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import treeService from "./services/tree.js";
|
import treeService from "./services/tree.js";
|
||||||
import noteDetailService from "./services/note_detail.js";
|
import noteDetailService from "./services/note_detail.js";
|
||||||
import dragAndDropSetup from "./services/drag_and_drop.js";
|
|
||||||
import treeCache from "./services/tree_cache.js";
|
import treeCache from "./services/tree_cache.js";
|
||||||
import treeBuilder from "./services/tree_builder.js";
|
import treeBuilder from "./services/tree_builder.js";
|
||||||
import contextMenuWidget from "./services/context_menu.js";
|
import contextMenuWidget from "./services/context_menu.js";
|
||||||
|
@ -68,6 +68,7 @@ async function moveToNode(branchIdsToMove, newParentNoteId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME used for finding a next note to activate after a delete
|
||||||
async function getNextNode(nodes) {
|
async function getNextNode(nodes) {
|
||||||
// following code assumes that nodes contain only top-most selected nodes - getSelectedNodes has been
|
// following code assumes that nodes contain only top-most selected nodes - getSelectedNodes has been
|
||||||
// called with stopOnParent=true
|
// called with stopOnParent=true
|
||||||
@ -84,10 +85,10 @@ async function getNextNode(nodes) {
|
|||||||
return treeUtils.getNotePath(next);
|
return treeUtils.getNotePath(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteNodes(nodes) {
|
async function deleteNodes(branchIdsToDelete) {
|
||||||
nodes = await filterRootNote(nodes);
|
branchIdsToDelete = await filterRootNote(branchIdsToDelete);
|
||||||
|
|
||||||
if (nodes.length === 0) {
|
if (branchIdsToDelete.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +97,15 @@ async function deleteNodes(nodes) {
|
|||||||
.append($('<label for="delete-clones-checkbox">')
|
.append($('<label for="delete-clones-checkbox">')
|
||||||
.text("delete also all note clones")
|
.text("delete also all note clones")
|
||||||
.attr("title", "all clones of selected notes will be deleted and as such the whole note will be deleted."));
|
.attr("title", "all clones of selected notes will be deleted and as such the whole note will be deleted."));
|
||||||
const $nodeTitles = $("<ul>").append(...nodes.map(node => $("<li>").text(node.title)));
|
|
||||||
|
const $nodeTitles = $("<ul>");
|
||||||
|
|
||||||
|
for (const branchId of branchIdsToDelete) {
|
||||||
|
const note = await treeCache.getBranch(branchId).getNote();
|
||||||
|
|
||||||
|
$nodeTitles.append($("<li>").text(note.title));
|
||||||
|
}
|
||||||
|
|
||||||
const $confirmText = $("<div>")
|
const $confirmText = $("<div>")
|
||||||
.append($("<p>").text('This will delete the following notes and their sub-notes: '))
|
.append($("<p>").text('This will delete the following notes and their sub-notes: '))
|
||||||
.append($nodeTitles)
|
.append($nodeTitles)
|
||||||
@ -114,31 +123,31 @@ async function deleteNodes(nodes) {
|
|||||||
|
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
|
|
||||||
for (const node of nodes) {
|
for (const branchIdToDelete of branchIdsToDelete) {
|
||||||
counter++;
|
counter++;
|
||||||
|
|
||||||
const last = counter === nodes.length;
|
const last = counter === branchIdsToDelete.length;
|
||||||
const query = `?taskId=${taskId}&last=${last ? 'true' : 'false'}`;
|
const query = `?taskId=${taskId}&last=${last ? 'true' : 'false'}`;
|
||||||
|
|
||||||
if (deleteClones) {
|
const branch = treeCache.getBranch(branchIdToDelete);
|
||||||
await server.remove(`notes/${node.data.noteId}` + query);
|
|
||||||
|
|
||||||
noteDetailService.noteDeleted(node.data.noteId);
|
if (deleteClones) {
|
||||||
|
await server.remove(`notes/${branch.noteId}` + query);
|
||||||
|
|
||||||
|
noteDetailService.noteDeleted(branch.noteId);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const {noteDeleted} = await server.remove(`branches/${node.data.branchId}` + query);
|
const {noteDeleted} = await server.remove(`branches/${branchIdToDelete}` + query);
|
||||||
|
|
||||||
if (noteDeleted) {
|
if (noteDeleted) {
|
||||||
noteDetailService.noteDeleted(node.data.noteId);
|
noteDetailService.noteDeleted(branch.noteId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextNotePath = await getNextNode(nodes);
|
|
||||||
|
|
||||||
const noteIds = Array.from(new Set(nodes.map(node => node.getParent().data.noteId)));
|
const noteIds = Array.from(new Set(nodes.map(node => node.getParent().data.noteId)));
|
||||||
|
|
||||||
await treeService.reloadNotes(noteIds, nextNotePath);
|
await treeService.reloadNotes(noteIds);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,75 +0,0 @@
|
|||||||
import treeService from './tree.js';
|
|
||||||
import treeChangesService from './branches.js';
|
|
||||||
import hoistedNoteService from './hoisted_note.js';
|
|
||||||
|
|
||||||
const dragAndDropSetup = {
|
|
||||||
autoExpandMS: 600,
|
|
||||||
dragStart: (node, data) => {
|
|
||||||
// don't allow dragging root node
|
|
||||||
if (node.data.noteId === hoistedNoteService.getHoistedNoteNoPromise()
|
|
||||||
|| node.getParent().data.noteType === 'search') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
node.setSelected(true);
|
|
||||||
|
|
||||||
const notes = treeService.getSelectedNodes().map(node => { return {
|
|
||||||
noteId: node.data.noteId,
|
|
||||||
title: node.title
|
|
||||||
}});
|
|
||||||
|
|
||||||
data.dataTransfer.setData("text", JSON.stringify(notes));
|
|
||||||
|
|
||||||
// This function MUST be defined to enable dragging for the tree.
|
|
||||||
// Return false to cancel dragging of node.
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
dragEnter: (node, data) => true, // allow drop on any node
|
|
||||||
dragOver: (node, data) => true,
|
|
||||||
dragDrop: async (node, data) => {
|
|
||||||
if ((data.hitMode === 'over' && node.data.noteType === 'search') ||
|
|
||||||
(['after', 'before'].includes(data.hitMode)
|
|
||||||
&& (node.data.noteId === hoistedNoteService.getHoistedNoteNoPromise() || node.getParent().data.noteType === 'search'))) {
|
|
||||||
|
|
||||||
const infoDialog = await import('../dialogs/info.js');
|
|
||||||
|
|
||||||
await infoDialog.info("Dropping notes into this location is not allowed.");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dataTransfer = data.dataTransfer;
|
|
||||||
|
|
||||||
if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
|
|
||||||
const files = [...dataTransfer.files]; // chrome has issue that dataTransfer.files empties after async operation
|
|
||||||
|
|
||||||
const importService = await import('./import.js');
|
|
||||||
|
|
||||||
importService.uploadFiles(node.data.noteId, files, {
|
|
||||||
safeImport: true,
|
|
||||||
shrinkImages: true,
|
|
||||||
textImportedAsText: true,
|
|
||||||
codeImportedAsCode: true,
|
|
||||||
explodeArchives: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// This function MUST be defined to enable dropping of items on the tree.
|
|
||||||
// data.hitMode is 'before', 'after', or 'over'.
|
|
||||||
|
|
||||||
const selectedNodes = treeService.getSelectedNodes();
|
|
||||||
|
|
||||||
if (data.hitMode === "before") {
|
|
||||||
treeChangesService.moveBeforeNode(selectedNodes, node);
|
|
||||||
} else if (data.hitMode === "after") {
|
|
||||||
treeChangesService.moveAfterNode(selectedNodes, node);
|
|
||||||
} else if (data.hitMode === "over") {
|
|
||||||
treeChangesService.moveToNode(selectedNodes, node);
|
|
||||||
} else {
|
|
||||||
throw new Error("Unknown hitMode=" + data.hitMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default dragAndDropSetup;
|
|
@ -1,5 +1,3 @@
|
|||||||
import contextMenuWidget from './context_menu.js';
|
|
||||||
import dragAndDropSetup from './drag_and_drop.js';
|
|
||||||
import ws from './ws.js';
|
import ws from './ws.js';
|
||||||
import noteDetailService from './note_detail.js';
|
import noteDetailService from './note_detail.js';
|
||||||
import protectedSessionHolder from './protected_session_holder.js';
|
import protectedSessionHolder from './protected_session_holder.js';
|
||||||
@ -9,10 +7,8 @@ import server from './server.js';
|
|||||||
import treeCache from './tree_cache.js';
|
import treeCache from './tree_cache.js';
|
||||||
import toastService from "./toast.js";
|
import toastService from "./toast.js";
|
||||||
import treeBuilder from "./tree_builder.js";
|
import treeBuilder from "./tree_builder.js";
|
||||||
import treeKeyBindingService from "./tree_keybindings.js";
|
|
||||||
import hoistedNoteService from '../services/hoisted_note.js';
|
import hoistedNoteService from '../services/hoisted_note.js';
|
||||||
import optionsService from "../services/options.js";
|
import optionsService from "../services/options.js";
|
||||||
import TreeContextMenu from "./tree_context_menu.js";
|
|
||||||
import bundle from "./bundle.js";
|
import bundle from "./bundle.js";
|
||||||
import keyboardActionService from "./keyboard_actions.js";
|
import keyboardActionService from "./keyboard_actions.js";
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ class TreeContextMenu {
|
|||||||
protectedSessionService.protectSubtree(this.node.data.noteId, false);
|
protectedSessionService.protectSubtree(this.node.data.noteId, false);
|
||||||
}
|
}
|
||||||
else if (cmd === "copy") {
|
else if (cmd === "copy") {
|
||||||
clipboard.copy(this.treeWidget.getSelectedOrActiveNodes(this.node));
|
clipboard.copy(this.getSelectedOrActiveBranchIds());
|
||||||
}
|
}
|
||||||
else if (cmd === "cloneTo") {
|
else if (cmd === "cloneTo") {
|
||||||
const nodes = this.treeWidget.getSelectedOrActiveNodes(this.node);
|
const nodes = this.treeWidget.getSelectedOrActiveNodes(this.node);
|
||||||
@ -142,7 +142,7 @@ class TreeContextMenu {
|
|||||||
import("../dialogs/clone_to.js").then(d => d.showDialog(noteIds));
|
import("../dialogs/clone_to.js").then(d => d.showDialog(noteIds));
|
||||||
}
|
}
|
||||||
else if (cmd === "cut") {
|
else if (cmd === "cut") {
|
||||||
clipboard.cut(this.treeWidget.getSelectedOrActiveNodes(this.node));
|
clipboard.cut(this.getSelectedOrActiveBranchIds());
|
||||||
}
|
}
|
||||||
else if (cmd === "moveTo") {
|
else if (cmd === "moveTo") {
|
||||||
const nodes = this.treeWidget.getSelectedOrActiveNodes(this.node);
|
const nodes = this.treeWidget.getSelectedOrActiveNodes(this.node);
|
||||||
@ -150,13 +150,13 @@ class TreeContextMenu {
|
|||||||
import("../dialogs/move_to.js").then(d => d.showDialog(nodes));
|
import("../dialogs/move_to.js").then(d => d.showDialog(nodes));
|
||||||
}
|
}
|
||||||
else if (cmd === "pasteAfter") {
|
else if (cmd === "pasteAfter") {
|
||||||
clipboard.pasteAfter(this.treeWidget, this.node);
|
clipboard.pasteAfter(this.node.data.branchId);
|
||||||
}
|
}
|
||||||
else if (cmd === "pasteInto") {
|
else if (cmd === "pasteInto") {
|
||||||
clipboard.pasteInto(this.node);
|
clipboard.pasteInto(this.node.data.noteId);
|
||||||
}
|
}
|
||||||
else if (cmd === "delete") {
|
else if (cmd === "delete") {
|
||||||
treeChangesService.deleteNodes(this.treeWidget.getSelectedOrActiveNodes(this.node));
|
treeChangesService.deleteNodes(this.getSelectedOrActiveBranchIds());
|
||||||
}
|
}
|
||||||
else if (cmd === "export") {
|
else if (cmd === "export") {
|
||||||
const exportDialog = await import('../dialogs/export.js');
|
const exportDialog = await import('../dialogs/export.js');
|
||||||
@ -193,6 +193,12 @@ class TreeContextMenu {
|
|||||||
ws.logError("Unknown command: " + cmd);
|
ws.logError("Unknown command: " + cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSelectedOrActiveBranchIds() {
|
||||||
|
const nodes = this.treeWidget.getSelectedOrActiveNodes(this.node);
|
||||||
|
|
||||||
|
return nodes.map(node => node.data.branchId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TreeContextMenu;
|
export default TreeContextMenu;
|
@ -6,161 +6,186 @@ import clipboard from "./clipboard.js";
|
|||||||
import utils from "./utils.js";
|
import utils from "./utils.js";
|
||||||
import keyboardActionService from "./keyboard_actions.js";
|
import keyboardActionService from "./keyboard_actions.js";
|
||||||
|
|
||||||
const fixedKeyBindings = {
|
/**
|
||||||
// code below shouldn't be necessary normally, however there's some problem with interaction with context menu plugin
|
* @param {NoteTreeWidget} treeWidget
|
||||||
// after opening context menu, standard shortcuts don't work, but they are detected here
|
*/
|
||||||
// so we essentially takeover the standard handling with our implementation.
|
function getFixedKeyBindings(treeWidget) {
|
||||||
"left": node => {
|
return {
|
||||||
node.navigate($.ui.keyCode.LEFT, true).then(treeService.clearSelectedNodes);
|
// code below shouldn't be necessary normally, however there's some problem with interaction with context menu plugin
|
||||||
|
// after opening context menu, standard shortcuts don't work, but they are detected here
|
||||||
|
// so we essentially takeover the standard handling with our implementation.
|
||||||
|
"left": node => {
|
||||||
|
node.navigate($.ui.keyCode.LEFT, true).then(treeWidget.clearSelectedNodes);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
"right": node => {
|
"right": node => {
|
||||||
node.navigate($.ui.keyCode.RIGHT, true).then(treeService.clearSelectedNodes);
|
node.navigate($.ui.keyCode.RIGHT, true).then(treeWidget.clearSelectedNodes);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
"up": node => {
|
"up": node => {
|
||||||
node.navigate($.ui.keyCode.UP, true).then(treeService.clearSelectedNodes);
|
node.navigate($.ui.keyCode.UP, true).then(treeWidget.clearSelectedNodes);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
"down": node => {
|
"down": node => {
|
||||||
node.navigate($.ui.keyCode.DOWN, true).then(treeService.clearSelectedNodes);
|
node.navigate($.ui.keyCode.DOWN, true).then(treeWidget.clearSelectedNodes);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const templates = {
|
|
||||||
"DeleteNotes": node => {
|
|
||||||
treeChangesService.deleteNodes(treeService.getSelectedOrActiveNodes(node));
|
|
||||||
},
|
|
||||||
"MoveNoteUp": node => {
|
|
||||||
const beforeNode = node.getPrevSibling();
|
|
||||||
|
|
||||||
if (beforeNode !== null) {
|
|
||||||
treeChangesService.moveBeforeNode([node], beforeNode);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
/**
|
||||||
},
|
* @param {NoteTreeWidget} treeWidget
|
||||||
"MoveNoteDown": node => {
|
* @param {FancytreeNode} node
|
||||||
const afterNode = node.getNextSibling();
|
*/
|
||||||
if (afterNode !== null) {
|
function getSelectedOrActiveBranchIds(treeWidget, node) {
|
||||||
treeChangesService.moveAfterNode([node], afterNode);
|
const nodes = treeWidget.getSelectedOrActiveNodes(node);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return nodes.map(node => node.data.branchId);
|
||||||
},
|
}
|
||||||
"MoveNoteUpInHierarchy": node => {
|
|
||||||
treeChangesService.moveNodeUpInHierarchy(node);
|
|
||||||
|
|
||||||
return false;
|
/**
|
||||||
},
|
* @param {NoteTreeWidget} treeWidget
|
||||||
"MoveNoteDownInHierarchy": node => {
|
*/
|
||||||
const toNode = node.getPrevSibling();
|
function getTemplates(treeWidget) {
|
||||||
|
return {
|
||||||
|
"DeleteNotes": node => {
|
||||||
|
treeChangesService.deleteNodes(getSelectedOrActiveBranchIds(treeWidget, node));
|
||||||
|
},
|
||||||
|
"MoveNoteUp": node => {
|
||||||
|
const beforeNode = node.getPrevSibling();
|
||||||
|
|
||||||
if (toNode !== null) {
|
if (beforeNode !== null) {
|
||||||
treeChangesService.moveToNode([node], toNode);
|
treeChangesService.moveBeforeNode([node], beforeNode);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"AddNoteAboveToSelection": () => {
|
|
||||||
const node = treeService.getFocusedNode();
|
|
||||||
|
|
||||||
if (!node) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.isActive()) {
|
|
||||||
node.setSelected(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const prevSibling = node.getPrevSibling();
|
|
||||||
|
|
||||||
if (prevSibling) {
|
|
||||||
prevSibling.setActive(true, {noEvents: true});
|
|
||||||
|
|
||||||
if (prevSibling.isSelected()) {
|
|
||||||
node.setSelected(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prevSibling.setSelected(true);
|
return false;
|
||||||
}
|
},
|
||||||
|
"MoveNoteDown": node => {
|
||||||
return false;
|
const afterNode = node.getNextSibling();
|
||||||
},
|
if (afterNode !== null) {
|
||||||
"AddNoteBelowToSelection": () => {
|
treeChangesService.moveAfterNode([node], afterNode);
|
||||||
const node = treeService.getFocusedNode();
|
|
||||||
|
|
||||||
if (!node) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.isActive()) {
|
|
||||||
node.setSelected(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextSibling = node.getNextSibling();
|
|
||||||
|
|
||||||
if (nextSibling) {
|
|
||||||
nextSibling.setActive(true, {noEvents: true});
|
|
||||||
|
|
||||||
if (nextSibling.isSelected()) {
|
|
||||||
node.setSelected(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nextSibling.setSelected(true);
|
return false;
|
||||||
|
},
|
||||||
|
"MoveNoteUpInHierarchy": node => {
|
||||||
|
treeChangesService.moveNodeUpInHierarchy(node);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"MoveNoteDownInHierarchy": node => {
|
||||||
|
const toNode = node.getPrevSibling();
|
||||||
|
|
||||||
|
if (toNode !== null) {
|
||||||
|
treeChangesService.moveToNode([node], toNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"AddNoteAboveToSelection": () => {
|
||||||
|
const node = treeService.getFocusedNode();
|
||||||
|
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.isActive()) {
|
||||||
|
node.setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevSibling = node.getPrevSibling();
|
||||||
|
|
||||||
|
if (prevSibling) {
|
||||||
|
prevSibling.setActive(true, {noEvents: true});
|
||||||
|
|
||||||
|
if (prevSibling.isSelected()) {
|
||||||
|
node.setSelected(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
prevSibling.setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"AddNoteBelowToSelection": () => {
|
||||||
|
const node = treeWidget.getFocusedNode();
|
||||||
|
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.isActive()) {
|
||||||
|
node.setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextSibling = node.getNextSibling();
|
||||||
|
|
||||||
|
if (nextSibling) {
|
||||||
|
nextSibling.setActive(true, {noEvents: true});
|
||||||
|
|
||||||
|
if (nextSibling.isSelected()) {
|
||||||
|
node.setSelected(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
nextSibling.setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"CollapseSubtree": node => {
|
||||||
|
treeWidget.collapseTree(node);
|
||||||
|
},
|
||||||
|
"SortChildNotes": node => {
|
||||||
|
treeService.sortAlphabetically(node.data.noteId);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"SelectAllNotesInParent": node => {
|
||||||
|
for (const child of node.getParent().getChildren()) {
|
||||||
|
child.setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"CopyNotesToClipboard": node => {
|
||||||
|
clipboard.copy(getSelectedOrActiveBranchIds(treeWidget, node));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"CutNotesToClipboard": node => {
|
||||||
|
clipboard.cut(getSelectedOrActiveBranchIds(treeWidget, node));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"PasteNotesFromClipboard": node => {
|
||||||
|
clipboard.pasteInto(node.data.noteId);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"EditNoteTitle": node => {
|
||||||
|
noteDetailService.focusOnTitle();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"ActivateParentNote": async node => {
|
||||||
|
if (!await hoistedNoteService.isRootNode(node)) {
|
||||||
|
node.getParent().setActive().then(treeWidget.clearSelectedNodes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
/**
|
||||||
},
|
* @param {NoteTreeWidget} treeWidget
|
||||||
"CollapseSubtree": node => {
|
*/
|
||||||
treeService.collapseTree(node);
|
async function getKeyboardBindings(treeWidget) {
|
||||||
},
|
const bindings = Object.assign({}, getFixedKeyBindings(treeWidget));
|
||||||
"SortChildNotes": node => {
|
|
||||||
treeService.sortAlphabetically(node.data.noteId);
|
|
||||||
|
|
||||||
return false;
|
const templates = getTemplates(treeWidget);
|
||||||
},
|
|
||||||
"SelectAllNotesInParent": node => {
|
|
||||||
for (const child of node.getParent().getChildren()) {
|
|
||||||
child.setSelected(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"CopyNotesToClipboard": node => {
|
|
||||||
clipboard.copy(treeService.getSelectedOrActiveNodes(node));
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"CutNotesToClipboard": node => {
|
|
||||||
clipboard.cut(treeService.getSelectedOrActiveNodes(node));
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"PasteNotesFromClipboard": node => {
|
|
||||||
clipboard.pasteInto(node);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"EditNoteTitle": node => {
|
|
||||||
noteDetailService.focusOnTitle();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
"ActivateParentNote": async node => {
|
|
||||||
if (!await hoistedNoteService.isRootNode(node)) {
|
|
||||||
node.getParent().setActive().then(treeService.clearSelectedNodes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getKeyboardBindings() {
|
|
||||||
const bindings = Object.assign({}, fixedKeyBindings);
|
|
||||||
|
|
||||||
for (const actionName in templates) {
|
for (const actionName in templates) {
|
||||||
const action = await keyboardActionService.getAction(actionName);
|
const action = await keyboardActionService.getAction(actionName);
|
||||||
|
@ -8,10 +8,10 @@ import noteDetailService from "../services/note_detail.js";
|
|||||||
import utils from "../services/utils.js";
|
import utils from "../services/utils.js";
|
||||||
import contextMenuWidget from "../services/context_menu.js";
|
import contextMenuWidget from "../services/context_menu.js";
|
||||||
import treeKeyBindingService from "../services/tree_keybindings.js";
|
import treeKeyBindingService from "../services/tree_keybindings.js";
|
||||||
import dragAndDropSetup from "../services/drag_and_drop.js";
|
|
||||||
import treeCache from "../services/tree_cache.js";
|
import treeCache from "../services/tree_cache.js";
|
||||||
import treeBuilder from "../services/tree_builder.js";
|
import treeBuilder from "../services/tree_builder.js";
|
||||||
import TreeContextMenu from "../services/tree_context_menu.js";
|
import TreeContextMenu from "../services/tree_context_menu.js";
|
||||||
|
import treeChangesService from "../services/branches.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<style>
|
<style>
|
||||||
@ -110,9 +110,77 @@ export default class NoteTreeWidget extends BasicWidget {
|
|||||||
collapse: (event, data) => treeService.setExpandedToServer(data.node.data.branchId, false),
|
collapse: (event, data) => treeService.setExpandedToServer(data.node.data.branchId, false),
|
||||||
init: (event, data) => treeService.treeInitialized(),
|
init: (event, data) => treeService.treeInitialized(),
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
keydown: await treeKeyBindingService.getKeyboardBindings()
|
keydown: await treeKeyBindingService.getKeyboardBindings(this)
|
||||||
|
},
|
||||||
|
dnd5: {
|
||||||
|
autoExpandMS: 600,
|
||||||
|
dragStart: (node, data) => {
|
||||||
|
// don't allow dragging root node
|
||||||
|
if (node.data.noteId === hoistedNoteService.getHoistedNoteNoPromise()
|
||||||
|
|| node.getParent().data.noteType === 'search') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.setSelected(true);
|
||||||
|
|
||||||
|
const notes = this.getSelectedNodes().map(node => { return {
|
||||||
|
noteId: node.data.noteId,
|
||||||
|
title: node.title
|
||||||
|
}});
|
||||||
|
|
||||||
|
data.dataTransfer.setData("text", JSON.stringify(notes));
|
||||||
|
|
||||||
|
// This function MUST be defined to enable dragging for the tree.
|
||||||
|
// Return false to cancel dragging of node.
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
dragEnter: (node, data) => true, // allow drop on any node
|
||||||
|
dragOver: (node, data) => true,
|
||||||
|
dragDrop: async (node, data) => {
|
||||||
|
if ((data.hitMode === 'over' && node.data.noteType === 'search') ||
|
||||||
|
(['after', 'before'].includes(data.hitMode)
|
||||||
|
&& (node.data.noteId === hoistedNoteService.getHoistedNoteNoPromise() || node.getParent().data.noteType === 'search'))) {
|
||||||
|
|
||||||
|
const infoDialog = await import('../dialogs/info.js');
|
||||||
|
|
||||||
|
await infoDialog.info("Dropping notes into this location is not allowed.");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataTransfer = data.dataTransfer;
|
||||||
|
|
||||||
|
if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
|
||||||
|
const files = [...dataTransfer.files]; // chrome has issue that dataTransfer.files empties after async operation
|
||||||
|
|
||||||
|
const importService = await import('./import.js');
|
||||||
|
|
||||||
|
importService.uploadFiles(node.data.noteId, files, {
|
||||||
|
safeImport: true,
|
||||||
|
shrinkImages: true,
|
||||||
|
textImportedAsText: true,
|
||||||
|
codeImportedAsCode: true,
|
||||||
|
explodeArchives: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// This function MUST be defined to enable dropping of items on the tree.
|
||||||
|
// data.hitMode is 'before', 'after', or 'over'.
|
||||||
|
|
||||||
|
const selectedBranchIds = this.getSelectedNodes().map(node => node.data.branchId);
|
||||||
|
|
||||||
|
if (data.hitMode === "before") {
|
||||||
|
treeChangesService.moveBeforeNode(selectedBranchIds, node.data.branchId);
|
||||||
|
} else if (data.hitMode === "after") {
|
||||||
|
treeChangesService.moveAfterNode(selectedBranchIds, node.data.branchId);
|
||||||
|
} else if (data.hitMode === "over") {
|
||||||
|
treeChangesService.moveToNode(selectedBranchIds, node.data.noteId);
|
||||||
|
} else {
|
||||||
|
throw new Error("Unknown hitMode=" + data.hitMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
dnd5: dragAndDropSetup,
|
|
||||||
lazyLoad: function(event, data) {
|
lazyLoad: function(event, data) {
|
||||||
const noteId = data.node.data.noteId;
|
const noteId = data.node.data.noteId;
|
||||||
|
|
||||||
@ -195,6 +263,21 @@ export default class NoteTreeWidget extends BasicWidget {
|
|||||||
node.visit(node => node.setExpanded(false));
|
node.visit(node => node.setExpanded(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* focused & not active node can happen during multiselection where the node is selected but not activated
|
||||||
|
* (its content is not displayed in the detail)
|
||||||
|
* @return {FancytreeNode|null}
|
||||||
|
*/
|
||||||
|
getFocusedNode() {
|
||||||
|
return this.tree.getFocusNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSelectedNodes() {
|
||||||
|
for (const selectedNode of this.getSelectedNodes()) {
|
||||||
|
selectedNode.setSelected(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
createTopLevelNoteListener() { treeService.createNewTopLevelNote(); }
|
createTopLevelNoteListener() { treeService.createNewTopLevelNote(); }
|
||||||
|
|
||||||
collapseTreeListener() { this.collapseTree(); }
|
collapseTreeListener() { this.collapseTree(); }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user