Stackedit/src/store/explorer.js

169 lines
5.1 KiB
JavaScript
Raw Normal View History

2017-07-31 09:04:01 +00:00
import Vue from 'vue';
import emptyFile from '../data/emptyFile';
import emptyFolder from '../data/emptyFolder';
2017-07-31 09:04:01 +00:00
const setter = propertyName => (state, value) => {
state[propertyName] = value;
};
function debounceAction(action, wait) {
let timeoutId;
return (context) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => action(context), wait);
};
}
2017-07-31 09:04:01 +00:00
const collator = new Intl.Collator(undefined, { sensitivity: 'base' });
const compare = (node1, node2) => collator.compare(node1.item.name, node2.item.name);
class Node {
2017-09-23 19:01:50 +00:00
constructor(item, locations = [], isFolder = false, isRoot = false) {
2017-07-31 09:04:01 +00:00
this.item = item;
2017-09-23 19:01:50 +00:00
this.locations = locations;
2017-07-31 09:04:01 +00:00
this.isFolder = isFolder;
this.isRoot = isRoot;
if (isFolder) {
this.folders = [];
this.files = [];
}
}
sortChildren() {
if (this.isFolder) {
this.folders.sort(compare);
this.files.sort(compare);
this.folders.forEach(child => child.sortChildren());
}
}
}
const nilFileNode = new Node(emptyFile());
nilFileNode.isNil = true;
const fakeFileNode = new Node(emptyFile());
fakeFileNode.item.id = 'fake';
2017-10-05 07:16:35 +00:00
fakeFileNode.noDrag = true;
2017-07-31 09:04:01 +00:00
function getParent(node, getters) {
if (node.isNil) {
2017-07-31 09:04:01 +00:00
return nilFileNode;
}
return getters.nodeMap[node.item.parentId] || getters.rootNode;
}
function getFolder(node, getters) {
return node.item.type === 'folder' ?
node :
getParent(node, getters);
}
2017-07-31 09:04:01 +00:00
export default {
namespaced: true,
state: {
selectedId: null,
editingId: null,
dragSourceId: null,
dragTargetId: null,
2017-07-31 09:04:01 +00:00
newChildNode: nilFileNode,
openNodes: {},
},
getters: {
nodeStructure: (state, getters, rootState, rootGetters) => {
2017-10-05 07:16:35 +00:00
const trashFolderNode = new Node(emptyFolder(), [], true);
trashFolderNode.item.id = 'trash';
trashFolderNode.item.name = 'Trash';
trashFolderNode.noDrag = true;
const nodeMap = {
trash: trashFolderNode,
};
2017-08-17 23:10:35 +00:00
rootGetters['folder/items'].forEach((item) => {
2017-09-23 19:01:50 +00:00
nodeMap[item.id] = new Node(item, [], true);
2017-07-31 09:04:01 +00:00
});
2017-08-17 23:10:35 +00:00
rootGetters['file/items'].forEach((item) => {
2017-09-23 19:01:50 +00:00
const locations = [
...rootGetters['syncLocation/groupedByFileId'][item.id] || [],
...rootGetters['publishLocation/groupedByFileId'][item.id] || [],
];
nodeMap[item.id] = new Node(item, locations);
2017-07-31 09:04:01 +00:00
});
2017-09-23 19:01:50 +00:00
const rootNode = new Node(emptyFolder(), [], true, true);
2017-12-10 23:49:20 +00:00
Object.entries(nodeMap).forEach(([id, node]) => {
2017-07-31 09:04:01 +00:00
let parentNode = nodeMap[node.item.parentId];
if (!parentNode || !parentNode.isFolder) {
2017-10-05 07:16:35 +00:00
if (id === 'trash') {
return;
}
2017-07-31 09:04:01 +00:00
parentNode = rootNode;
}
if (node.isFolder) {
parentNode.folders.push(node);
} else {
parentNode.files.push(node);
}
});
rootNode.sortChildren();
2017-10-05 07:16:35 +00:00
if (trashFolderNode.files.length) {
rootNode.folders.unshift(trashFolderNode);
}
// Add a fake file at the end of the root folder to always allow drag and drop into it.
rootNode.files.push(fakeFileNode);
2017-07-31 09:04:01 +00:00
return {
nodeMap,
rootNode,
};
},
nodeMap: (state, getters) => getters.nodeStructure.nodeMap,
rootNode: (state, getters) => getters.nodeStructure.rootNode,
newChildNodeParent: (state, getters) => getParent(state.newChildNode, getters),
selectedNode: (state, getters) => getters.nodeMap[state.selectedId] || nilFileNode,
selectedNodeFolder: (state, getters) => getFolder(getters.selectedNode, getters),
2017-07-31 09:04:01 +00:00
editingNode: (state, getters) => getters.nodeMap[state.editingId] || nilFileNode,
dragSourceNode: (state, getters) => getters.nodeMap[state.dragSourceId] || nilFileNode,
dragTargetNode: (state, getters) => {
if (state.dragTargetId === 'fake') {
return fakeFileNode;
}
return getters.nodeMap[state.dragTargetId] || nilFileNode;
},
dragTargetNodeFolder: (state, getters) => {
if (state.dragTargetId === 'fake') {
return getters.rootNode;
}
return getFolder(getters.dragTargetNode, getters);
},
2017-07-31 09:04:01 +00:00
},
mutations: {
setSelectedId: setter('selectedId'),
setEditingId: setter('editingId'),
setDragSourceId: setter('dragSourceId'),
setDragTargetId: setter('dragTargetId'),
2017-07-31 09:04:01 +00:00
setNewItem(state, item) {
2017-09-23 19:01:50 +00:00
state.newChildNode = item ? new Node(item, [], item.type === 'folder') : nilFileNode;
2017-07-31 09:04:01 +00:00
},
setNewItemName(state, name) {
state.newChildNode.item.name = name;
},
toggleOpenNode(state, id) {
Vue.set(state.openNodes, id, !state.openNodes[id]);
},
},
actions: {
openNode({ state, getters, commit, dispatch }, id) {
const node = getters.nodeMap[id];
if (node) {
if (node.isFolder && !state.openNodes[id]) {
commit('toggleOpenNode', id);
}
dispatch('openNode', node.item.parentId);
}
},
openDragTarget: debounceAction(({ state, dispatch }) => {
dispatch('openNode', state.dragTargetId);
}, 1000),
setDragTarget({ state, getters, commit, dispatch }, id) {
commit('setDragTargetId', id);
dispatch('openDragTarget');
},
2017-07-31 09:04:01 +00:00
},
};