mirror of
https://github.com/mickael-kerjean/filestash
synced 2026-01-02 22:04:48 +01:00
chore (rewrite): frontend rewrite of filesystem
This commit is contained in:
parent
f5753e8de7
commit
7e4480981d
13 changed files with 174 additions and 158 deletions
|
|
@ -1,64 +0,0 @@
|
|||
.component_dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.component_dropdown .dropdown_container {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
.component_dropdown .dropdown_button {
|
||||
border: 1px solid rgba(0, 0, 0, 0);
|
||||
border-radius: 4px;
|
||||
padding: 5px;
|
||||
min-width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.component_dropdown .dropdown_container {
|
||||
padding-top: 5px;
|
||||
z-index: 3;
|
||||
}
|
||||
.component_dropdown .dropdown_container:before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 1px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid white;
|
||||
}
|
||||
.component_dropdown .dropdown_container ul {
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
background: white;
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 1px 1px 2px var(--border);
|
||||
color: rgba(0,10,20,0.85);
|
||||
border-radius: 3px;
|
||||
padding: 3px 0px;
|
||||
font-size: 0.92em;
|
||||
}
|
||||
.component_dropdown .dropdown_container ul li {
|
||||
display: flex;
|
||||
}
|
||||
.component_dropdown .dropdown_container ul li > div {
|
||||
width: 160px;
|
||||
padding: 8px 5px 8px 10px;
|
||||
}
|
||||
|
||||
.component_dropdown.active .dropdown_container {
|
||||
display: block;
|
||||
}
|
||||
.component_dropdown.active .dropdown_container li {
|
||||
background: white;
|
||||
transition: background 0.1s ease-out;
|
||||
}
|
||||
.component_dropdown.active .dropdown_container li:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.component_dropdown.active .dropdown_button {
|
||||
border-color: var(--bg-color);
|
||||
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ body.dark-mode {
|
|||
|
||||
|
||||
--border: #303438;
|
||||
--dark: #2b2d30;
|
||||
}
|
||||
|
||||
body.dark-mode input {
|
||||
|
|
|
|||
|
|
@ -5,16 +5,8 @@
|
|||
display: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
.component_dropdown .dropdown_button {
|
||||
border: 1px solid rgba(0, 0, 0, 0);
|
||||
border-radius: 4px;
|
||||
padding: 5px;
|
||||
min-width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.component_dropdown .dropdown_container {
|
||||
padding-top: 9px;
|
||||
padding-top: 10px;
|
||||
margin-top: 3px;
|
||||
z-index: 3;
|
||||
}
|
||||
.component_dropdown .dropdown_container:before {
|
||||
|
|
@ -26,42 +18,43 @@
|
|||
height: 0;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-bottom: 10px solid white;
|
||||
border-bottom: 10px solid var(--dark);
|
||||
}
|
||||
.component_dropdown .dropdown_container ul {
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
background: white;
|
||||
border: 1px solid var(--bg-color);
|
||||
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
|
||||
color: var(--color);
|
||||
color: var(--bg-color);
|
||||
background: var(--dark);
|
||||
border-radius: 3px;
|
||||
padding: 3px;
|
||||
font-size: 0.92em;
|
||||
}
|
||||
.component_dropdown .dropdown_container ul li {
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
}
|
||||
.component_dropdown .dropdown_container ul li > div {
|
||||
width: 150px;
|
||||
padding: 5px 5px 5px 10px;
|
||||
background: var(--dark);
|
||||
}
|
||||
.dark-mode .component_dropdown .dropdown_container ul { color: var(--color); }
|
||||
|
||||
.component_dropdown.active .dropdown_container {
|
||||
display: block;
|
||||
}
|
||||
.component_dropdown.active .dropdown_container li {
|
||||
background: white;
|
||||
transition: background 0.1s ease-out;
|
||||
}
|
||||
.component_dropdown.active .dropdown_container li img.component_icon{
|
||||
border: 2px solid rgba(0,0,0,0);
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
box-sizing: border-box;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.component_dropdown.active .dropdown_container li:hover {
|
||||
background: var(--border);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.component_dropdown.active .dropdown_button {
|
||||
border-color: var(--bg-color);
|
||||
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,10 +40,16 @@ export function preventDefault() {
|
|||
}
|
||||
|
||||
export function onClick($node) {
|
||||
assert.type($node, window.HTMLElement);
|
||||
return rxjs.fromEvent($node, "click").pipe(
|
||||
rxjs.map(() => $node)
|
||||
const sideE = ($node) => {
|
||||
assert.type($node, window.HTMLElement);
|
||||
return rxjs.fromEvent($node, "click").pipe(
|
||||
rxjs.map(() => $node)
|
||||
);
|
||||
};
|
||||
if ($node instanceof window.NodeList) return rxjs.merge(
|
||||
...[...$node].map(($n) => sideE($n)),
|
||||
);
|
||||
return sideE($node);
|
||||
}
|
||||
|
||||
export function onLoad($node) {
|
||||
|
|
|
|||
|
|
@ -189,7 +189,6 @@ export default async function(render) {
|
|||
rxjs.mergeMap((url) => ajax({ url, responseType: "json" })),
|
||||
rxjs.tap(({ responseJSON }) => location.href = responseJSON.result),
|
||||
rxjs.catchError(ctrlError()),
|
||||
rxjs.mergeMap(() => rxjs.EMPTY),
|
||||
);
|
||||
}),
|
||||
rxjs.mergeMap((formData) => { // CASE 3: regular login
|
||||
|
|
|
|||
|
|
@ -8,12 +8,13 @@ import { AjaxError, ApplicationError } from "../lib/error.js";
|
|||
import "../components/icon.js";
|
||||
|
||||
export default function(render = createRender(qs(document.body, "[role=\"main\"]"))) {
|
||||
return async function(err) {
|
||||
return function(err) {
|
||||
const [msg, trace] = processError(err);
|
||||
|
||||
const $page = createElement(`
|
||||
<div>
|
||||
<style>${css}</style>
|
||||
<a href="/" class="backnav">
|
||||
<a href="${calculateBacklink(location.pathname)}" class="backnav">
|
||||
<component-icon name="arrow_left"></component-icon>
|
||||
home
|
||||
</a>
|
||||
|
|
@ -46,7 +47,7 @@ export default function(render = createRender(qs(document.body, "[role=\"main\"]
|
|||
rxjs.tap(() => location.reload())
|
||||
));
|
||||
|
||||
return rxjs.of(err);
|
||||
return rxjs.EMPTY;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -122,3 +123,20 @@ const css = `
|
|||
vertical-align: middle;
|
||||
}
|
||||
`;
|
||||
|
||||
function calculateBacklink(pathname) {
|
||||
let url = "/";
|
||||
const listPath = pathname.replace(new RegExp("/$"), "").split("/");
|
||||
switch (listPath[1]) {
|
||||
case "view": // in view mode, navigate to current folder
|
||||
listPath[1] = "files";
|
||||
listPath.pop();
|
||||
url = listPath.join("/") + "/";
|
||||
break;
|
||||
case "files": // in file browser mode, navigate to parent folder
|
||||
listPath.pop();
|
||||
url = listPath.join("/") + "/";
|
||||
break;
|
||||
}
|
||||
return url === "/files/" ? "/" : url;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ import { createLoader } from "../../components/loader.js";
|
|||
import ctrlError from "../ctrl_error.js";
|
||||
|
||||
import { createThing } from "./thing.js";
|
||||
// import { handleError, getFiles } from "./ctrl_filesystem_state.js";
|
||||
import { getState$ } from "./ctrl_filesystem_state.js";
|
||||
import { ls } from "./model_files.js";
|
||||
|
||||
export default async function(render) {
|
||||
const $page = createElement(`
|
||||
<div class="component_filesystem container">
|
||||
<div class="ifscroll-before"></div>
|
||||
<div class="list"></div>
|
||||
<div data-target="list" class="list"></div>
|
||||
<div class="ifscroll-after"></div>
|
||||
<br>
|
||||
</div>
|
||||
|
|
@ -28,7 +28,19 @@ export default async function(render) {
|
|||
effect(rxjs.of(path).pipe(
|
||||
ls(),
|
||||
removeLoader,
|
||||
rxjs.mergeMap(({ files, path }) => { // STEP1: setup the list of files
|
||||
rxjs.mergeMap(({ files, ...rest }) => getState$().pipe(rxjs.map((p) => {
|
||||
// files = files.sort()
|
||||
if (p.show_hidden === false) files = files.filter(({ name }) => name[0] !== ".");
|
||||
return { ...rest, files, ...p };
|
||||
}))),
|
||||
rxjs.mergeMap(({ files, ...rest }) => {
|
||||
if (files.length === 0) {
|
||||
renderEmpty(render);
|
||||
return rxjs.EMPTY;
|
||||
}
|
||||
return rxjs.of({...rest, files });
|
||||
}),
|
||||
rxjs.mergeMap(({ files, path, view }) => { // STEP1: setup the list of files
|
||||
const FILE_HEIGHT = 160;
|
||||
const BLOCK_SIZE = Math.ceil(document.body.clientHeight / FILE_HEIGHT) + 1;
|
||||
// const BLOCK_SIZE = 6;
|
||||
|
|
@ -38,26 +50,28 @@ export default async function(render) {
|
|||
if (size > VIRTUAL_SCROLL_MINIMUM_TRIGGER) {
|
||||
size = Math.min(files.length, BLOCK_SIZE * COLUMN_PER_ROW);
|
||||
}
|
||||
const $list = qs($page, ".list");
|
||||
const $list = qs($page, `[data-target="list"]`);
|
||||
$list.closest(".scroll-y").scrollTop = 0;
|
||||
const $fs = document.createDocumentFragment();
|
||||
for (let i = 0; i < size; i++) {
|
||||
const file = files[i];
|
||||
$fs.appendChild(createThing({
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
link: createLink(file, path),
|
||||
...createLink(file, path),
|
||||
view,
|
||||
}));
|
||||
}
|
||||
animate($list, { time: 200, keyframes: slideYIn(5) });
|
||||
$list.appendChild($fs);
|
||||
$list.replaceChildren($fs);
|
||||
|
||||
/// //////////////////////////////////////
|
||||
//////////////////////////////////////
|
||||
// CASE 1: virtual scroll isn't enabled
|
||||
if (files.length <= VIRTUAL_SCROLL_MINIMUM_TRIGGER) {
|
||||
return rxjs.EMPTY;
|
||||
}
|
||||
|
||||
/// //////////////////////////////////////
|
||||
//////////////////////////////////////
|
||||
// CASE 2: with virtual scroll
|
||||
const $listBefore = qs($page, ".ifscroll-before");
|
||||
const $listAfter = qs($page, ".ifscroll-after");
|
||||
|
|
@ -148,9 +162,8 @@ export default async function(render) {
|
|||
}));
|
||||
else $fs.appendChild(createThing({
|
||||
name: file.name,
|
||||
// name: `file ${i}`,
|
||||
type: file.type,
|
||||
link: createLink(file, path),
|
||||
...createLink(file, path),
|
||||
}));
|
||||
n += 1;
|
||||
}
|
||||
|
|
@ -171,6 +184,17 @@ export default async function(render) {
|
|||
));
|
||||
}
|
||||
|
||||
function renderEmpty(render) {
|
||||
render(createElement(`
|
||||
<div class="error">
|
||||
<p class="empty_image no-select">
|
||||
<img class="component_icon" draggable="false" src="/assets/icons/empty_folder.svg" alt="empty_folder">
|
||||
</p>
|
||||
<p class="label">There is nothing here</p>
|
||||
</div>
|
||||
`));
|
||||
}
|
||||
|
||||
export function init() {
|
||||
return Promise.all([
|
||||
loadCSS(import.meta.url, "./ctrl_filesystem.css"),
|
||||
|
|
@ -178,9 +202,10 @@ export function init() {
|
|||
]);
|
||||
}
|
||||
|
||||
function createLink(file, path) {
|
||||
if (file.type === "file") {
|
||||
return "/view" + path + file.name;
|
||||
}
|
||||
return "/files" + path + file.name + "/";
|
||||
function createLink(file, filepath) {
|
||||
let path = filepath + file.name;
|
||||
let link = "";
|
||||
if (file.type === "directory") path += "/";
|
||||
link = file.type === "directory" ? "/files" + path : "/view" + path;
|
||||
return { path, link };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +1,24 @@
|
|||
import rxjs from "../../lib/rx.js";
|
||||
import rxjs, { effect } from "../../lib/rx.js";
|
||||
|
||||
const state$ = new rxjs.BehaviorSubject({
|
||||
search: null,
|
||||
view: "grid",
|
||||
sort: null,
|
||||
view: null,
|
||||
acl: {},
|
||||
path: "/",
|
||||
mutation: {},
|
||||
error: null
|
||||
order: null,
|
||||
show_hidden: false,
|
||||
});
|
||||
|
||||
export const getState$ = () => state$.asObservable();
|
||||
|
||||
export const onNewFile = () => {
|
||||
console.log("CLICK NEW FILE");
|
||||
};
|
||||
export const setState = (...args) => {
|
||||
const obj = { ...state$.value };
|
||||
for (let i=0; i<args.length; i+=2) {
|
||||
obj[args[i]] = args[i+1];
|
||||
}
|
||||
state$.next(obj);
|
||||
}
|
||||
|
||||
export const handleError = () => {
|
||||
return rxjs.catchError((err) => {
|
||||
if (err) {
|
||||
state$.next({
|
||||
...state$.value,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
return rxjs.empty();
|
||||
});
|
||||
};
|
||||
|
||||
export const onNewDirectory = () => {
|
||||
console.log("CLICK NEW DIRECTORY");
|
||||
};
|
||||
|
||||
export const onSearch = () => {
|
||||
console.log("SEARCH");
|
||||
};
|
||||
|
||||
export const getFiles = (n) => {};
|
||||
effect(rxjs.fromEvent(window, "keydown").pipe(
|
||||
rxjs.tap((e) => e.preventDefault()),
|
||||
rxjs.filter((e) => e.ctrlKey && e.key === "h"),
|
||||
rxjs.tap(() => setState("show_hidden", !state$.value.show_hidden)),
|
||||
));
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { createElement, createRender, createFragment, onDestroy, nop } from "../
|
|||
import rxjs, { effect, applyMutation, onClick, preventDefault } from "../../lib/rx.js";
|
||||
import { animate } from "../../lib/animate.js";
|
||||
import { loadCSS } from "../../helpers/loader.js";
|
||||
import { qs } from "../../lib/dom.js";
|
||||
import { qs, qsa } from "../../lib/dom.js";
|
||||
import { getSelection$, clearSelection } from "./model_files.js";
|
||||
import { setAction } from "./model_action.js";
|
||||
|
||||
|
|
@ -14,8 +14,10 @@ import componentDelete from "./modal_delete.js";
|
|||
|
||||
import "../../components/dropdown.js";
|
||||
import "../../components/icon.js";
|
||||
|
||||
import { createModal } from "../../components/modal.js";
|
||||
|
||||
import { setState } from "./ctrl_filesystem_state.js";
|
||||
|
||||
const modalOpt = {
|
||||
withButtonsRight: "OK",
|
||||
withButtonsLeft: "CANCEL",
|
||||
|
|
@ -121,6 +123,7 @@ function componentRight(render) {
|
|||
MAGNIFYING_GLASS: "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojNjI2NDY5O2ZpbGwtb3BhY2l0eToxIiBkPSJNNTA1IDQ0Mi43TDQwNS4zIDM0M2MtNC41LTQuNS0xMC42LTctMTctN0gzNzJjMjcuNi0zNS4zIDQ0LTc5LjcgNDQtMTI4QzQxNiA5My4xIDMyMi45IDAgMjA4IDBTMCA5My4xIDAgMjA4czkzLjEgMjA4IDIwOCAyMDhjNDguMyAwIDkyLjctMTYuNCAxMjgtNDR2MTYuM2MwIDYuNCAyLjUgMTIuNSA3IDE3bDk5LjcgOTkuN2M5LjQgOS40IDI0LjYgOS40IDMzLjkgMGwyOC4zLTI4LjNjOS40LTkuNCA5LjQtMjQuNi4xLTM0ek0yMDggMzM2Yy03MC43IDAtMTI4LTU3LjItMTI4LTEyOCAwLTcwLjcgNTcuMi0xMjggMTI4LTEyOCA3MC43IDAgMTI4IDU3LjIgMTI4IDEyOCAwIDcwLjctNTcuMiAxMjgtMTI4IDEyOHoiIC8+Cjwvc3ZnPgo=",
|
||||
|
||||
SORT: "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMjAgNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojNjI2NDY5O2ZpbGwtb3BhY2l0eToxIiBkPSJNNDEgMjg4aDIzOGMyMS40IDAgMzIuMSAyNS45IDE3IDQxTDE3NyA0NDhjLTkuNCA5LjQtMjQuNiA5LjQtMzMuOSAwTDI0IDMyOWMtMTUuMS0xNS4xLTQuNC00MSAxNy00MXptMjU1LTEwNUwxNzcgNjRjLTkuNC05LjQtMjQuNi05LjQtMzMuOSAwTDI0IDE4M2MtMTUuMSAxNS4xLTQuNCA0MSAxNyA0MWgyMzhjMjEuNCAwIDMyLjEtMjUuOSAxNy00MXoiIC8+Cjwvc3ZnPgo=",
|
||||
CHECK: "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojOTA5MDkwO2ZpbGwtb3BhY2l0eToxIiBkPSJNMTczLjg5OCA0MzkuNDA0bC0xNjYuNC0xNjYuNGMtOS45OTctOS45OTctOS45OTctMjYuMjA2IDAtMzYuMjA0bDM2LjIwMy0zNi4yMDRjOS45OTctOS45OTggMjYuMjA3LTkuOTk4IDM2LjIwNCAwTDE5MiAzMTIuNjkgNDMyLjA5NSA3Mi41OTZjOS45OTctOS45OTcgMjYuMjA3LTkuOTk3IDM2LjIwNCAwbDM2LjIwMyAzNi4yMDRjOS45OTcgOS45OTcgOS45OTcgMjYuMjA2IDAgMzYuMjA0bC0yOTQuNCAyOTQuNDAxYy05Ljk5OCA5Ljk5Ny0yNi4yMDcgOS45OTctMzYuMjA0LS4wMDF6IiAvPgo8L3N2Zz4K",
|
||||
};
|
||||
|
||||
effect(getSelection$().pipe(
|
||||
|
|
@ -145,20 +148,15 @@ function componentRight(render) {
|
|||
<div class="component_dropdown view sort" data-target="sort">
|
||||
<div class="dropdown_container">
|
||||
<ul>
|
||||
<li>
|
||||
<div>
|
||||
Sort By Type <span>
|
||||
<span style="float: right;">
|
||||
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj4KICA8cGF0aCBzdHlsZT0iZmlsbDojOTA5MDkwO2ZpbGwtb3BhY2l0eToxIiBkPSJNMTczLjg5OCA0MzkuNDA0bC0xNjYuNC0xNjYuNGMtOS45OTctOS45OTctOS45OTctMjYuMjA2IDAtMzYuMjA0bDM2LjIwMy0zNi4yMDRjOS45OTctOS45OTggMjYuMjA3LTkuOTk4IDM2LjIwNCAwTDE5MiAzMTIuNjkgNDMyLjA5NSA3Mi41OTZjOS45OTctOS45OTcgMjYuMjA3LTkuOTk3IDM2LjIwNCAwbDM2LjIwMyAzNi4yMDRjOS45OTcgOS45OTcgOS45OTcgMjYuMjA2IDAgMzYuMjA0bC0yOTQuNCAyOTQuNDAxYy05Ljk5OCA5Ljk5Ny0yNi4yMDcgOS45OTctMzYuMjA0LS4wMDF6IiAvPgo8L3N2Zz4K" alt="check">
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<li data-target="type">
|
||||
Sort By Type
|
||||
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,${ICONS.CHECK}" alt="check" />
|
||||
</li>
|
||||
<li>
|
||||
<div>Sort By Date</div>
|
||||
<li data-target="date">
|
||||
Sort By Date
|
||||
</li>
|
||||
<li>
|
||||
<div>Sort By Name</div>
|
||||
<li data-target="name">
|
||||
Sort By Name
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -190,8 +188,32 @@ function componentRight(render) {
|
|||
}
|
||||
}),
|
||||
),
|
||||
onClick(qs($page, `[data-action="sort"]`)).pipe(rxjs.tap(() => {
|
||||
onClick(qs($page, `[data-action="view"]`)).pipe(rxjs.tap(($button) => {
|
||||
const $img = $button.querySelector("img");
|
||||
if ($img.getAttribute("alt") === "list") {
|
||||
setState("view", "grid");
|
||||
$img.setAttribute("alt", "grid");
|
||||
$img.setAttribute("src", "data:image/svg+xml;base64," + ICONS.GRID_VIEW);
|
||||
} else {
|
||||
setState("view", "list");
|
||||
$img.setAttribute("alt", "list");
|
||||
$img.setAttribute("src", "data:image/svg+xml;base64," + ICONS.LIST_VIEW);
|
||||
}
|
||||
})),
|
||||
onClick(qs($page, `[data-action="sort"]`)).pipe(rxjs.mergeMap(() => {
|
||||
qs($page, `[data-target="sort"]`).classList.toggle("active");
|
||||
const $lis = qsa($page, `.dropdown_container li`);
|
||||
return onClick($lis).pipe(rxjs.tap(($el) => {
|
||||
setState(
|
||||
"sort", $el.getAttribute("data-target"),
|
||||
"order", !!$el.querySelector("img") ? "asc" : "des",
|
||||
);
|
||||
[...$lis].map(($li) => {
|
||||
const $img = $li.querySelector("img");
|
||||
if ($img) $img.remove();
|
||||
});
|
||||
$el.appendChild(createElement(`<img class="component_icon" src="data:image/svg+xml;base64,${ICONS.CHECK}" alt="check" />`));
|
||||
}));
|
||||
})),
|
||||
)),
|
||||
));
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { qs } from "../../lib/dom.js";
|
|||
|
||||
export default function(render) {
|
||||
const $page = createElement(`
|
||||
<div class="component_upload_queue">
|
||||
<div class="component_upload_queue hidden">
|
||||
<h2>CURRENT UPLOAD <div class="count_block">
|
||||
<span class="completed">24</span>
|
||||
<span class="grandTotal">24</span>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
.component_thing {
|
||||
clear: both;
|
||||
}
|
||||
.component_thing:hover .box, .component_thing .highlight.box {
|
||||
.component_thing:hover .box, .component_thing .highlight.box, .component_thing.hover .box, .component_thing .highlight.box {
|
||||
transition: 0.1s ease-out background;
|
||||
background: var(--border);
|
||||
border-color: var(--super-light);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const IMAGE = {
|
|||
};
|
||||
|
||||
const $tmpl = createElement(`
|
||||
<div class="component_thing view-grid not-selected" draggable="true">
|
||||
<div class="component_thing not-selected view-grid" draggable="true">
|
||||
<a href="__TEMPLATE__" data-link>
|
||||
<div class="box">
|
||||
<div class="component_checkbox"><input type="checkbox"><span class="indicator"></span></div>
|
||||
|
|
@ -35,6 +35,7 @@ const $tmpl = createElement(`
|
|||
export function createThing({
|
||||
name = null,
|
||||
type = "N/A",
|
||||
path = null,
|
||||
// size = 0,
|
||||
// time = null,
|
||||
link = "",
|
||||
|
|
@ -48,6 +49,7 @@ export function createThing({
|
|||
$label.textContent = name;
|
||||
$thing.querySelector("a").setAttribute("href", link);
|
||||
$thing.querySelector("img").setAttribute("src", (type === "file" ? IMAGE.FILE : IMAGE.FOLDER));
|
||||
$thing.setAttribute("data-droptarget", type === "directory");
|
||||
if (type === "hidden") $thing.classList.add("hidden");
|
||||
|
||||
$thing.querySelector(".component_checkbox").onclick = function(e) {
|
||||
|
|
@ -55,5 +57,34 @@ export function createThing({
|
|||
e.stopPropagation();
|
||||
addSelection(name, type);
|
||||
};
|
||||
$thing.ondragstart = (e) => {
|
||||
e.dataTransfer.setData("path", path);
|
||||
$thing.classList.add("hover");
|
||||
|
||||
const crt = $thing.cloneNode(true);
|
||||
$thing.style.opacity = "0.7";
|
||||
const $box = crt.querySelector(".box");
|
||||
crt.style.opacity = "0.2 "
|
||||
crt.style.backgroundColor = "var(--border)";
|
||||
$box.style.backgroundColor = "inherit";
|
||||
$box.style.border = "none";
|
||||
$box.style.borderRadius = "0";
|
||||
|
||||
$thing.closest("[data-target=\"list\"]").appendChild(crt);
|
||||
e.dataTransfer.setDragImage(crt, 0, 0);
|
||||
};
|
||||
$thing.ondragover = (e) => {
|
||||
if ($thing.getAttribute("data-droptarget") !== "true") return;
|
||||
|
||||
e.preventDefault();
|
||||
$thing.classList.add("hover");
|
||||
};
|
||||
$thing.ondragleave = () => {
|
||||
$thing.classList.remove("hover");
|
||||
};
|
||||
$thing.ondrop = (e) => {
|
||||
$thing.classList.remove("hover");
|
||||
console.log("DROPPED!", e.dataTransfer.getData("path"));
|
||||
};
|
||||
return $thing;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,8 +94,8 @@ export default async function(render) {
|
|||
return editor;
|
||||
}),
|
||||
rxjs.tap((editor) => requestAnimationFrame(() => editor.refresh())),
|
||||
rxjs.share(),
|
||||
rxjs.catchError(ctrlError()),
|
||||
rxjs.share(),
|
||||
);
|
||||
effect(setup$);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue