chore (rewrite): filesystem elements

This commit is contained in:
MickaelK
2024-05-10 01:31:20 +10:00
parent 0fe75a8a18
commit 34cb374202
8 changed files with 127 additions and 106 deletions

View File

@ -21,7 +21,7 @@ export default function(ctrl) {
render($page); render($page);
// feature1: setup the breadcrumb path // feature1: setup the breadcrumb path
qs($page, `[is="component-breadcrumb"]`).setAttribute("path", urlToPath(location.pathname)); qs($page, `[is="component-breadcrumb"]`).setAttribute("path", urlToPath(location.pathname + location.hash));
// feature2: setup the childrens // feature2: setup the childrens
const $main = qs($page, `[data-bind="filemanager-children"]`); const $main = qs($page, `[data-bind="filemanager-children"]`);

View File

@ -45,7 +45,7 @@ class Loader extends window.HTMLElement {
customElements.define("component-loader", Loader); customElements.define("component-loader", Loader);
export function createLoader($parent, opts = {}) { export function createLoader($parent, opts = {}) {
const { wait = 500 } = opts; const { wait = 250 } = opts;
const cancel = effect(new rxjs.Observable((observer) => { const cancel = effect(new rxjs.Observable((observer) => {
const $icon = createElement(` const $icon = createElement(`
<div class="component_loader"> <div class="component_loader">
@ -61,7 +61,7 @@ export function createLoader($parent, opts = {}) {
`); `);
const id = window.setTimeout(() => { const id = window.setTimeout(() => {
$parent.replaceChildren($icon); $parent.replaceChildren($icon);
animate($icon, { time: 1000, keyframes: opacityIn() }); animate($icon, { time: 750, keyframes: opacityIn() });
}, wait); }, wait);
return () => { return () => {
clearTimeout(id); clearTimeout(id);

View File

@ -1,5 +1,5 @@
[is="component_filesystem"] .component_filesystem { [is="component_filesystem"] .component_filesystem {
padding-top: 5px; padding-top: 3px;
} }
[is="component_filesystem"] .empty { [is="component_filesystem"] .empty {
text-align: center; text-align: center;

View File

@ -49,15 +49,15 @@ export default async function(render) {
if (!!state.search) { if (!!state.search) {
const removeLoader = createLoader($header); const removeLoader = createLoader($header);
return search(state.search_q).pipe(rxjs.map((files) => ({ return search(state.search_q).pipe(rxjs.map((files) => ({
...rest, files, ...state, ...rest, files, ...state, read_only: true,
})), removeLoader); })), removeLoader);
} }
return rxjs.of({ ...rest, files, ...state }); return rxjs.of({ ...rest, files, ...state, read_only: false });
}))), }))),
rxjs.mergeMap(({ show_hidden, files, ...rest }) => { rxjs.mergeMap(({ show_hidden, files, ...rest }) => {
if (show_hidden === false) files = files.filter(({ name }) => name[0] !== "."); if (show_hidden === false) files = files.filter(({ name }) => name[0] !== ".");
console.log(rest); console.log(rest);
files = sort(files, rest.sort); files = sort(files, rest.sort, rest.order);
return rxjs.of({ ...rest, files }) return rxjs.of({ ...rest, files })
}), }),
removeLoader, removeLoader,
@ -69,7 +69,7 @@ export default async function(render) {
return rxjs.of({...rest, files }); return rxjs.of({...rest, files });
}), }),
rxjs.mergeMap((obj) => refreshOnResize$.pipe(rxjs.mapTo(obj))), rxjs.mergeMap((obj) => refreshOnResize$.pipe(rxjs.mapTo(obj))),
rxjs.mergeMap(({ files, path, view }) => { // STEP1: setup the list of files rxjs.mergeMap(({ files, path, view, read_only }) => { // STEP1: setup the list of files
$list.closest(".scroll-y").scrollTop = 0; $list.closest(".scroll-y").scrollTop = 0;
let FILE_HEIGHT, COLUMN_PER_ROW; let FILE_HEIGHT, COLUMN_PER_ROW;
switch(view) { switch(view) {
@ -99,7 +99,7 @@ export default async function(render) {
$fs.appendChild(createThing({ $fs.appendChild(createThing({
...file, ...file,
...createLink(file, path), ...createLink(file, path),
view, view, read_only,
n: i, n: i,
})); }));
} }
@ -129,9 +129,10 @@ export default async function(render) {
$listAfter.style.height = `${height - size}px`; $listAfter.style.height = `${height - size}px`;
}; };
setHeight(0); setHeight(0);
// const top = ($node) => $node.getBoundingClientRect().top; const top = ($node) => $node.getBoundingClientRect().top;
return rxjs.of({ return rxjs.of({
files, files,
read_only,
path, path,
view, view,
currentState: 0, currentState: 0,
@ -140,11 +141,11 @@ export default async function(render) {
FILE_HEIGHT, FILE_HEIGHT,
BLOCK_SIZE, BLOCK_SIZE,
COLUMN_PER_ROW, COLUMN_PER_ROW,
MARGIN: 35, // TODO: top($list) - top($list.closest(".scroll-y")); MARGIN: top($list) - top($list.closest(".scroll-y")),
}); });
}), }),
rxjs.mergeMap(({ rxjs.mergeMap(({
files, path, view, files, path, view, read_only,
BLOCK_SIZE, COLUMN_PER_ROW, FILE_HEIGHT, BLOCK_SIZE, COLUMN_PER_ROW, FILE_HEIGHT,
MARGIN, MARGIN,
currentState, currentState,
@ -207,7 +208,7 @@ export default async function(render) {
type: "hidden", type: "hidden",
})); }));
else $fs.appendChild(createThing({ else $fs.appendChild(createThing({
...file, ...file, read_only,
...createLink(file, path), ...createLink(file, path),
view, view,
n: i, n: i,

View File

@ -4,7 +4,7 @@
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 3; z-index: 3;
padding: 5px 0; padding: 4px 0;
backdrop-filter: blur(20px) saturate(2); backdrop-filter: blur(20px) saturate(2);
transition: 0.2s ease box-shadow; transition: 0.2s ease box-shadow;
} }

View File

@ -16,7 +16,7 @@ import componentDelete from "./modal_delete.js";
import { getSelection$, clearSelection } from "./state_selection.js"; import { getSelection$, clearSelection } from "./state_selection.js";
import { getAction$, setAction } from "./state_event.js"; import { getAction$, setAction } from "./state_event.js";
import { setState } from "./state_filesystem.js"; import { setState, getState$ } from "./state_filesystem.js";
const modalOpt = { const modalOpt = {
withButtonsRight: "OK", withButtonsRight: "OK",
@ -38,7 +38,7 @@ export default async function(render) {
componentRight(createRender(qs($page, ".action.right"))); componentRight(createRender(qs($page, ".action.right")));
effect(rxjs.fromEvent($scroll, "scroll", { passive: true }).pipe( effect(rxjs.fromEvent($scroll, "scroll", { passive: true }).pipe(
rxjs.map((e) => e.target.scrollTop > 30), rxjs.map((e) => e.target.scrollTop > 12),
rxjs.distinctUntilChanged(), rxjs.distinctUntilChanged(),
rxjs.startWith(false), rxjs.startWith(false),
rxjs.tap((scrolling) => scrolling rxjs.tap((scrolling) => scrolling
@ -157,20 +157,20 @@ function componentRight(render) {
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,${ICONS.SORT}" alt="sort" /> <img class="component_icon" draggable="false" src="data:image/svg+xml;base64,${ICONS.SORT}" alt="sort" />
</button> </button>
<div class="component_dropdown view sort" data-target="sort"> <div class="component_dropdown view sort" data-target="sort">
<div class="dropdown_container"> <div class="dropdown_container">
<ul> <ul>
<li data-target="type"> <li data-target="type">
Sort By Type Sort By Type
<img class="component_icon" draggable="false" src="data:image/svg+xml;base64,${ICONS.CHECK}" alt="check" /> <img class="component_icon" draggable="false" src="data:image/svg+xml;base64,${ICONS.CHECK}" alt="check" />
</li> </li>
<li data-target="date"> <li data-target="date">
Sort By Date Sort By Date
</li> </li>
<li data-target="name"> <li data-target="name">
Sort By Name Sort By Name
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
`))), `))),
rxjs.mergeMap(($page) => rxjs.merge( rxjs.mergeMap(($page) => rxjs.merge(
@ -208,18 +208,23 @@ function componentRight(render) {
} }
const $lis = qsa($page, `.dropdown_container li`); const $lis = qsa($page, `.dropdown_container li`);
return onClick($lis).pipe( return onClick($lis).pipe(
rxjs.tap(($el) => { rxjs.first(),
rxjs.mergeMap(($el) => getState$().pipe(rxjs.first(), rxjs.map((state) => ({
order: state.order,
$el,
})))),
rxjs.tap(({ $el, order }) => {
setState( setState(
"sort", $el.getAttribute("data-target"), "sort", $el.getAttribute("data-target"),
"order", !!$el.querySelector("img") ? "asc" : "des", "order", order === "asc" ? "des" : "asc",
); );
[...$lis].map(($li) => { [...$lis].map(($li) => {
const $img = $li.querySelector("img"); const $img = $li.querySelector("img");
if ($img) $img.remove(); if ($img) $img.remove();
}); });
$el.appendChild(createElement(`<img class="component_icon" src="data:image/svg+xml;base64,${ICONS.CHECK}" alt="check" />`)); $el.appendChild(createElement(`<img class="component_icon" src="data:image/svg+xml;base64,${ICONS.CHECK}" alt="check" />`));
$sort.classList.remove("active");
}), }),
rxjs.tap(() => $sort.classList.remove("active")),
); );
}), }),
), ),

View File

@ -1,85 +1,94 @@
import { extname } from "../../lib/path.js"; import { extname } from "../../lib/path.js";
export function sort(files, type) { export function sort(files, type, order) {
if (type === "name") { switch(type) {
return sortByName(files); case "name": return sortByName(files, order);
} else if (type === "date") { case "date": return sortByDate(files, order);
return sortByDate(files); default: return sortByType(files, order);
} else {
return sortByType(files);
} }
}; }
function sortByType(files, order) {
let tmp;
return files.sort(function(fileA, fileB) {
tmp = _moveLoadingDownward(fileA, fileB);
if (tmp !== 0) return tmp;
tmp = _moveFolderUpward(fileA, fileB);
if (tmp !== 0) return tmp;
tmp = _moveHiddenFilesDownward(fileA, fileB);
if (tmp !== 0) return tmp;
const faname = fileA.name.toLowerCase();
const fbname = fileB.name.toLowerCase();
const aExt = extname(faname);
const bExt = extname(fbname);
if (aExt === bExt) return sortString(faname, fbname, order);
return sortString(aExt, bExt, order);
});
}
function sortByName(files, order) {
let tmp;
return files.sort(function(fileA, fileB) {
tmp = _moveLoadingDownward(fileA, fileB);
if (tmp !== 0) return tmp;
tmp = _moveFolderUpward(fileA, fileB);
if (tmp !== 0) return tmp;
tmp = _moveHiddenFilesDownward(fileA, fileB);
if (tmp !== 0) return tmp;
return sortString(fileA.name.toLowerCase(), fileB.name.toLowerCase(), order);
});
}
function sortByDate(files, order) {
return files.sort(function(fileA, fileB) {
if (fileB.time === fileA.time) {
return sortString(fileA.name, fileB.name, order);
}
return sortNumber(fileA.time, fileB.time, order);
});
}
function sortString(a, b, order) {
if (order === "asc") return a > b ? +1 : -1;
return a < b ? +1 : -1;
}
function sortNumber(a, b, order) {
if (order === "asc") return b - a;
return a - b;
}
function _moveLoadingDownward(fileA, fileB) { function _moveLoadingDownward(fileA, fileB) {
if (fileA.icon === "loading" && fileB.icon !== "loading") { const aIsLoading = fileA.icon === "loading";
return +1; const bIsLoading = fileB.icon === "loading";
} else if (fileA.icon !== "loading" && fileB.icon === "loading") {
return -1; if (aIsLoading && !bIsLoading) return +1;
} else if (!aIsLoading && bIsLoading) return -1;
return 0; return 0;
} }
function _moveFolderUpward(fileA, fileB) { function _moveFolderUpward(fileA, fileB) {
if (["directory", "link"].indexOf(fileA.type) === -1 && const aIsDirectory = ["directory", "link"].indexOf(fileA.type) !== -1;
["directory", "link"].indexOf(fileB.type) !== -1) { const bIsDirectory = ["directory", "link"].indexOf(fileB.type) !== -1;
return +1;
} else if (["directory", "link"].indexOf(fileA.type) !== -1 && if (!aIsDirectory && bIsDirectory) return +1;
["directory", "link"].indexOf(fileB.type) === -1) { else if (aIsDirectory && !bIsDirectory) return -1;
return -1;
}
return 0; return 0;
} }
function _moveHiddenFilesDownward(fileA, fileB) { function _moveHiddenFilesDownward(fileA, fileB) {
if (fileA.name[0] === "." && fileB.name[0] !== ".") return +1; const aIsHidden = fileA.name[0] === ".";
else if (fileA.name[0] !== "." && fileB.name[0] === ".") return -1; const bIsHidden = fileB.name[0] === ".";
if (aIsHidden && !bIsHidden) return +1;
if (!aIsHidden && bIsHidden) return -1;
return 0; return 0;
} }
function sortByType(files) {
return files.sort((fileA, fileB) => {
let tmp = _moveLoadingDownward(fileA, fileB);
if (tmp !== 0) return tmp;
tmp = _moveFolderUpward(fileA, fileB);
if (tmp !== 0) return tmp;
tmp = _moveHiddenFilesDownward(fileA, fileB);
if (tmp !== 0) return tmp;
const aExt = extname(fileA.name.toLowerCase());
const bExt = extname(fileB.name.toLowerCase());
if (fileA.name.toLowerCase() === fileB.name.toLowerCase()) {
return fileA.name > fileB.name ? +1 : -1;
} else {
if (aExt !== bExt) return aExt > bExt ? +1 : -1;
else return fileA.name.toLowerCase() > fileB.name.toLowerCase() ? +1 : -1;
}
});
}
function sortByName(files) {
return files.sort((fileA, fileB) => {
let tmp = _moveLoadingDownward(fileA, fileB);
if (tmp !== 0) return tmp;
tmp = _moveFolderUpward(fileA, fileB);
if (tmp !== 0) return tmp;
tmp = _moveHiddenFilesDownward(fileA, fileB);
if (tmp !== 0) return tmp;
if (fileA.name.toLowerCase() === fileB.name.toLowerCase()) {
return fileA.name > fileB.name ? +1 : -1;
}
return fileA.name.toLowerCase() > fileB.name.toLowerCase() ? +1 : -1;
});
}
function sortByDate(files) {
return files.sort((fileA, fileB) => {
const tmp = _moveLoadingDownward(fileA, fileB);
if (tmp !== 0) return tmp;
if (fileB.time === fileA.time) {
return fileA.name > fileB.name ? +1 : -1;
}
return fileB.time - fileA.time;
});
}

View File

@ -46,6 +46,7 @@ export function createThing({
// permissions = {} // permissions = {}
view = "", view = "",
n = 0, n = 0,
read_only = false,
}) { }) {
const $thing = $tmpl.cloneNode(true); const $thing = $tmpl.cloneNode(true);
assert.type($thing, window.HTMLElement); assert.type($thing, window.HTMLElement);
@ -60,7 +61,12 @@ export function createThing({
$thing.classList.add("view-" + view); $thing.classList.add("view-" + view);
$time.textContent = formatTime(new Date(time)); $time.textContent = formatTime(new Date(time));
sideEffectSelection($thing, isSelected(n)); sideEffectSelection($thing, isSelected(n));
if (type === "hidden") $thing.classList.add("hidden");
if (read_only === true) return $thing;
else if (type === "hidden") {
$thing.classList.add("hidden");
return $thing;
}
$thing.querySelector(".component_checkbox").onclick = function(e) { $thing.querySelector(".component_checkbox").onclick = function(e) {
e.preventDefault(); e.preventDefault();