mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-27 18:53:20 +01:00
chore (rewrite): drag and drop to move things around
This commit is contained in:
parent
f0895fc483
commit
f1375f27d0
5 changed files with 59 additions and 42 deletions
|
|
@ -1,8 +1,14 @@
|
|||
import { animate, slideYOut, slideYIn, opacityOut } from "../lib/animate.js";
|
||||
import { loadCSS } from "../helpers/loader.js";
|
||||
|
||||
import { mv } from "../pages/filespage/model_files.js";
|
||||
import { extractPath, isDir } from "../pages/filespage/helper.js";
|
||||
import { extractPath, isDir, isNativeFileUpload } from "../pages/filespage/helper.js";
|
||||
import { mv as mv$ } from "../pages/filespage/model_files.js";
|
||||
import { mv as mvVL, withVirtualLayer } from "../pages/filespage/model_virtual_layer.js";
|
||||
|
||||
const mv = (from, to) => withVirtualLayer(
|
||||
mv$(from, to),
|
||||
mvVL(from, to),
|
||||
);
|
||||
|
||||
class ComponentBreadcrumb extends window.HTMLDivElement {
|
||||
constructor() {
|
||||
|
|
@ -158,13 +164,6 @@ class ComponentBreadcrumb extends window.HTMLDivElement {
|
|||
setupDragDropTarget() {
|
||||
this.querySelectorAll("a.label").forEach(($folder) => {
|
||||
const $path = $folder.closest(".component_path-element");
|
||||
$folder.ondragover = (e) => {
|
||||
e.preventDefault();
|
||||
$path.classList.add("highlight");
|
||||
};
|
||||
$folder.ondragleave = () => {
|
||||
$path.classList.remove("highlight");
|
||||
};
|
||||
$folder.ondrop = async (e) => {
|
||||
$path.classList.remove("highlight");
|
||||
const from = e.dataTransfer.getData("path");
|
||||
|
|
@ -175,6 +174,14 @@ class ComponentBreadcrumb extends window.HTMLDivElement {
|
|||
if (isDir(from)) to += "/";
|
||||
await mv(from, to).toPromise();
|
||||
};
|
||||
$folder.ondragover = (e) => {
|
||||
if (isNativeFileUpload(e)) return;
|
||||
e.preventDefault();
|
||||
$path.classList.add("highlight");
|
||||
};
|
||||
$folder.ondragleave = () => {
|
||||
$path.classList.remove("highlight");
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { loadCSS } from "../../helpers/loader.js";
|
|||
import { qs } from "../../lib/dom.js";
|
||||
import { AjaxError } from "../../lib/error.js";
|
||||
import assert from "../../lib/assert.js";
|
||||
import { currentPath } from "./helper.js";
|
||||
import { currentPath, isNativeFileUpload } from "./helper.js";
|
||||
import { mkdir, save } from "./model_virtual_layer.js";
|
||||
import t from "../../locales/index.js";
|
||||
|
||||
|
|
@ -55,22 +55,17 @@ function componentUploadFAB(render, { workers$ }) {
|
|||
}
|
||||
|
||||
function componentFilezone(render, { workers$ }) {
|
||||
const $target = document.body.querySelector(`[data-bind="filemanager-children"]`);
|
||||
const selector = `[data-bind="filemanager-children"]`;
|
||||
const $target = document.body.querySelector(selector);
|
||||
|
||||
$target.ondragenter = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!isNativeFileUpload(e)) return;
|
||||
$target.classList.add("dropzone");
|
||||
e.dataTransfer.setData("type", "fileupload");
|
||||
};
|
||||
$target.ondragover = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
$target.ondragleave = () => {
|
||||
// console.log("DRAGLEAVE");
|
||||
};
|
||||
$target.ondrop = async (e) => {
|
||||
if (!isNativeFileUpload(e)) return;
|
||||
$target.classList.remove("dropzone");
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const loadID = setTimeout(() => render(createElement("<div>LOADING</div>")), 2000);
|
||||
if (e.dataTransfer.items instanceof window.DataTransferItemList) {
|
||||
workers$.next(await processItems(e.dataTransfer.items));
|
||||
|
|
@ -79,10 +74,17 @@ function componentFilezone(render, { workers$ }) {
|
|||
} else {
|
||||
assert.fail("NOT_IMPLEMENTED - unknown entry type in ctrl_upload.js", entry);
|
||||
}
|
||||
$target.classList.remove("dropzone");
|
||||
clearTimeout(loadID);
|
||||
render(createFragment(""));
|
||||
};
|
||||
$target.ondragleave = (e) => {
|
||||
if (!isNativeFileUpload(e)) return;
|
||||
if (!(e.relatedTarget === null || // eg: drag outside the window
|
||||
!e.relatedTarget.closest(selector) // eg: drag on the breadcrumb, ...
|
||||
)) return;
|
||||
$target.classList.remove("dropzone");
|
||||
};
|
||||
$target.ondragover = (e) => e.preventDefault();
|
||||
}
|
||||
|
||||
const MAX_WORKERS = 4;
|
||||
|
|
@ -113,13 +115,12 @@ function componentUploadQueue(render, { workers$ }) {
|
|||
|
||||
// feature1: close the queue
|
||||
onClick(qs($page, `img[alt="close"]`)).pipe(
|
||||
rxjs.mergeMap(() => animate($page, { time: 200, keyframes: slideYOut(50) })),
|
||||
// rxjs.mergeMap(() => animate($page, { time: 200, keyframes: slideYOut(50) })),
|
||||
rxjs.tap(() => $page.classList.add("hidden")),
|
||||
).subscribe();
|
||||
|
||||
// feature2: setup the task queue in the dom
|
||||
workers$.subscribe(({ tasks }) => {
|
||||
console.log("TASKS SETUP DOM", tasks);
|
||||
if (tasks.length === 0) return;
|
||||
$page.classList.remove("hidden");
|
||||
const $fragment = document.createDocumentFragment();
|
||||
|
|
@ -216,7 +217,6 @@ function componentUploadQueue(render, { workers$ }) {
|
|||
};
|
||||
const noFailureAllowed = (fn) => fn().catch(() => noFailureAllowed(fn));
|
||||
workers$.subscribe(async ({ tasks: newTasks }) => {
|
||||
console.log("TASKS PROCESS", newTasks);
|
||||
tasks = tasks.concat(newTasks); // add new tasks to the pool
|
||||
while(true) {
|
||||
const nworker = reservations.indexOf(false);
|
||||
|
|
|
|||
|
|
@ -111,3 +111,5 @@ function _moveHiddenFilesDownward(fileA, fileB) {
|
|||
if (!aIsHidden && bIsHidden) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
export const isNativeFileUpload = (e) => JSON.stringify(e.dataTransfer.types) === '["Files"]';
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export function touch(path) {
|
|||
async afterSuccess() {
|
||||
removeLoading(virtualFiles$, basepath, filename);
|
||||
onDestroy(() => statePop(virtualFiles$, basepath, filename));
|
||||
await fscache().update(basepath, ({ files, ...rest }) => ({
|
||||
await fscache().update(basepath, ({ files = [], ...rest }) => ({
|
||||
files: files.concat([file]),
|
||||
...rest,
|
||||
}));
|
||||
|
|
@ -100,7 +100,7 @@ export function mkdir(path) {
|
|||
async afterSuccess() {
|
||||
removeLoading(virtualFiles$, basepath, dirname);
|
||||
onDestroy(() => statePop(virtualFiles$, basepath, dirname));
|
||||
await fscache().update(basepath, ({ files, ...rest }) => ({
|
||||
await fscache().update(basepath, ({ files = [], ...rest }) => ({
|
||||
files: files.concat([file]),
|
||||
...rest,
|
||||
}));
|
||||
|
|
@ -137,7 +137,7 @@ export function save(path, size) {
|
|||
async afterSuccess() {
|
||||
removeLoading(virtualFiles$, basepath, filename);
|
||||
onDestroy(() => statePop(virtualFiles$, basepath, filename));
|
||||
await fscache().update(basepath, ({ files, ...rest }) => ({
|
||||
await fscache().update(basepath, ({ files = [], ...rest }) => ({
|
||||
files: files.concat([file]),
|
||||
...rest,
|
||||
}));
|
||||
|
|
@ -190,7 +190,7 @@ export function rm(...paths) {
|
|||
});
|
||||
onDestroy(() => statePop(mutationFiles$, basepath, basepath));
|
||||
await Promise.all(paths.map((path) => fscache().remove(path, false)));
|
||||
await fscache().update(basepath, ({ files, ...rest }) => ({
|
||||
await fscache().update(basepath, ({ files = [], ...rest }) => ({
|
||||
files: files.filter(({ name }) => {
|
||||
for (let i=0;i<arr.length;i+=2) {
|
||||
if (name === arr[i+1]) {
|
||||
|
|
@ -278,7 +278,7 @@ export function mv(fromPath, toPath) {
|
|||
return file;
|
||||
},
|
||||
});
|
||||
await fscache().update(fromBasepath, ({ files, ...rest }) => {
|
||||
await fscache().update(fromBasepath, ({ files = [], ...rest }) => {
|
||||
return {
|
||||
files: files.map((file) => {
|
||||
if (file.name === fromName) {
|
||||
|
|
@ -300,11 +300,11 @@ export function mv(fromPath, toPath) {
|
|||
});
|
||||
onDestroy(() => statePop(mutationFiles$, fromBasepath, fromName));
|
||||
statePop(virtualFiles$, toBasepath, toName);
|
||||
await fscache().update(fromBasepath, ({ files, ...rest }) => ({
|
||||
await fscache().update(fromBasepath, ({ files = [], ...rest }) => ({
|
||||
files: files.filter((file) => file.name === fromName ? false : true),
|
||||
...rest,
|
||||
}))
|
||||
await fscache().update(toBasepath, ({ files, ...rest }) => ({
|
||||
await fscache().update(toBasepath, ({ files = [], ...rest }) => ({
|
||||
files: files.concat([{
|
||||
name: fromName,
|
||||
time: new Date().getTime(),
|
||||
|
|
|
|||
|
|
@ -3,12 +3,19 @@ import { qs } from "../../lib/dom.js";
|
|||
import { animate, opacityIn } from "../../lib/animate.js";
|
||||
import assert from "../../lib/assert.js";
|
||||
|
||||
import { extractPath, isDir } from "./helper.js";
|
||||
import { mv } from "./model_files.js";
|
||||
import { extractPath, isDir, isNativeFileUpload } from "./helper.js";
|
||||
import { get as getConfig } from "./model_config.js";
|
||||
import { files$ } from "./ctrl_filesystem.js";
|
||||
import { addSelection, isSelected, clearSelection } from "./state_selection.js";
|
||||
|
||||
import { mv as mv$ } from "./model_files.js";
|
||||
import { mv as mvVL, withVirtualLayer } from "./model_virtual_layer.js";
|
||||
|
||||
const mv = (from, to) => withVirtualLayer(
|
||||
mv$(from, to),
|
||||
mvVL(from, to),
|
||||
);
|
||||
|
||||
const IMAGE = {
|
||||
FILE: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiPgogIDxwYXRoIHN0eWxlPSJjb2xvcjojMDAwMDAwO3RleHQtaW5kZW50OjA7dGV4dC10cmFuc2Zvcm06bm9uZTtmaWxsOiM4YzhjOGM7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlLXdpZHRoOjAuOTg0ODEwNDEiIGQ9Im0gMiwxMy4wODI0MTIgMC4wMTk0NjIsMS40OTIzNDcgYyA1ZS02LDAuMjIyMTQ1IDAuMjA1NTkwMiwwLjQyNDI2MiAwLjQzMTE1MDIsMC40MjQyNzIgTCAxMy41ODk2MTIsMTUgQyAxMy44MTUxNzMsMTQuOTk5OTk1IDEzLjk5OTk5LDE0Ljc5Nzg3NCAxNCwxNC41NzU3MjkgdiAtMS40OTMzMTcgYyAtNC4xNzE4NjkyLDAuNjYyMDIzIC03LjY1MTY5MjgsMC4zOTg2OTYgLTEyLDAgeiIgLz4KICA8cGF0aCBzdHlsZT0iY29sb3I6IzAwMDAwMDt0ZXh0LWluZGVudDowO3RleHQtdHJhbnNmb3JtOm5vbmU7ZGlzcGxheTppbmxpbmU7ZmlsbDojYWFhYWFhO3N0cm9rZS13aWR0aDowLjk4NDA4MTI3IiBkPSJNIDIuMzUwMSwxLjAwMTMzMTIgQyAyLjE1MjU5LDEuMDM4MzI0NyAxLjk5NjU5LDEuMjI3MjcyMyAyLjAwMDA5LDEuNDI0OTM1NiBWIDE0LjEzMzQ1NyBjIDVlLTYsMC4yMjE4MTYgMC4yMDUyMywwLjQyMzYzNCAwLjQzMDc5LDAuNDIzNjQ0IGwgMTEuMTM5LC0xLjAxZS00IGMgMC4yMjU1NiwtNmUtNiAwLjQzMDExLC0wLjIwMDc1OCAwLjQzMDEyLC0wLjQyMjU3NCBsIDYuN2UtNCwtOS44MjI2NDI2IGMgLTIuNDg0MDQ2LC0xLjM1NTAwNiAtMi40MzUyMzQsLTIuMDMxMjI1NCAtMy41MDAxLC0zLjMwOTcwNyAtMC4wNDMsLTAuMDE1ODgyIDAuMDQ2LDAuMDAxNzQgMCwwIEwgMi40MzA2NywxLjAwMTEwOCBDIDIuNDAzODMsMC45OTg1OSAyLjM3Njc0LDAuOTk4NTkgMi4zNDk5LDEuMDAxMTA4IFoiIC8+CiAgPHBhdGggc3R5bGU9ImRpc3BsYXk6aW5saW5lO2ZpbGw6IzhjOGM4YztmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzllNzU3NTtzdHJva2Utd2lkdGg6MDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIiBkPSJtIDEwLjUwMDU3LDEuMDAyMDc2NCBjIDAsMy4yNzY4MDI4IC0wLjAwNTIsMy4xNzM5MTYxIDAuMzYyOTIxLDMuMjY5ODIwMiAwLjI4MDEwOSwwLjA3Mjk4NCAzLjEzNzE4LDAuMDM5ODg3IDMuMTM3MTgsMC4wMzk4ODcgLTEuMTIwMDY3LC0xLjA1NTY2OTIgLTIuMzMzNCwtMi4yMDY0NzEzIC0zLjUwMDEsLTMuMzA5NzA3NCB6IiAvPgo8L3N2Zz4K",
|
||||
FOLDER: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiPgogIDxnIHRyYW5zZm9ybT0ibWF0cml4KDAuODY2NjY0MzEsMCwwLDAuODY2NjcsLTE3Mi4wNDU3OCwtODY0LjMyNzU5KSIgc3R5bGU9ImZpbGw6Izc1YmJkOTtmaWxsLW9wYWNpdHk6MC45NDExNzY0NztmaWxsLXJ1bGU6ZXZlbm9kZCI+CiAgICA8cGF0aCBzdHlsZT0iZmlsbDojNzViYmQ5O2ZpbGwtb3BhY2l0eTowLjk0MTE3NjQ3O2ZpbGwtcnVsZTpldmVub2RkIiBkPSJtIDIwMC4yLDk5OS43MiBjIC0wLjI4OTEzLDAgLTAuNTMxMjUsMC4yNDIxIC0wLjUzMTI1LDAuNTMxMiB2IDEyLjc4NCBjIDAsMC4yOTg1IDAuMjMyNjQsMC41MzEyIDAuNTMxMjUsMC41MzEyIGggMTUuMDkxIGMgMC4yOTg2LDAgMC41MzEyNCwtMC4yMzI3IDAuNTMxMjQsLTAuNTMxMiBsIDRlLTQsLTEwLjQ3NCBjIDAsLTAuMjg4OSAtMC4yNDIxMSwtMC41MzM4IC0wLjUzMTI0LC0wLjUzMzggbCAtNy41NDU3LDVlLTQgLTIuMzA3NiwtMi4zMDc4MyB6IiAvPgogIDwvZz4KICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgwLjg2NjY3LDAsMCwwLjg2NjY3LC0xNzIuMDQ2OTIsLTg2NC43ODM0KSIgc3R5bGU9ImZpbGw6IzlhZDFlZDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZCI+CiAgICA8cGF0aCBzdHlsZT0iZmlsbDojOWFkMWVkO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkIiBkPSJtIDIwMC4yLDk5OS43MiBjIC0wLjI4OTEzLDAgLTAuNTMxMjUsMC4yNDIxIC0wLjUzMTI1LDAuNTMxMiB2IDEyLjc4NCBjIDAsMC4yOTg1IDAuMjMyNjQsMC41MzEyIDAuNTMxMjUsMC41MzEyIGggMTUuMDkxIGMgMC4yOTg2LDAgMC41MzEyNCwtMC4yMzI3IDAuNTMxMjQsLTAuNTMxMiBsIDRlLTQsLTEwLjQ3NCBjIDAsLTAuMjg4OSAtMC4yNDIxMSwtMC41MzM4IC0wLjUzMTI0LC0wLjUzMzggbCAtNy41NDU3LDVlLTQgLTIuMzA3NiwtMi4zMDc4MyB6IiAvPgogIDwvZz4KPC9zdmc+Cg==",
|
||||
|
|
@ -153,14 +160,6 @@ export function createThing({
|
|||
e.dataTransfer.setData("path", path);
|
||||
e.dataTransfer.setDragImage($thing, e.offsetX, -10);
|
||||
};
|
||||
$thing.ondragover = (e) => {
|
||||
if ($thing.getAttribute("data-droptarget") !== "true") return;
|
||||
e.preventDefault();
|
||||
$thing.classList.add("hover");
|
||||
};
|
||||
$thing.ondragleave = () => {
|
||||
$thing.classList.remove("hover");
|
||||
};
|
||||
$thing.ondrop = async (e) => {
|
||||
$thing.classList.remove("hover");
|
||||
const from = e.dataTransfer.getData("path");
|
||||
|
|
@ -173,6 +172,15 @@ export function createThing({
|
|||
}
|
||||
await mv(from, to).toPromise();
|
||||
};
|
||||
$thing.ondragover = (e) => {
|
||||
if(isNativeFileUpload(e)) return;
|
||||
else if ($thing.getAttribute("data-droptarget") !== "true") return;
|
||||
e.preventDefault();
|
||||
$thing.classList.add("hover");
|
||||
};
|
||||
$thing.ondragleave = () => {
|
||||
$thing.classList.remove("hover");
|
||||
};
|
||||
return $thing;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue