mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-10-29 09:07:30 +08:00
chore (rewrite): filesystem elements
This commit is contained in:
@ -21,7 +21,7 @@ export default function(ctrl) {
|
||||
render($page);
|
||||
|
||||
// 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
|
||||
const $main = qs($page, `[data-bind="filemanager-children"]`);
|
||||
|
||||
@ -45,7 +45,7 @@ class Loader extends window.HTMLElement {
|
||||
|
||||
customElements.define("component-loader", Loader);
|
||||
export function createLoader($parent, opts = {}) {
|
||||
const { wait = 500 } = opts;
|
||||
const { wait = 250 } = opts;
|
||||
const cancel = effect(new rxjs.Observable((observer) => {
|
||||
const $icon = createElement(`
|
||||
<div class="component_loader">
|
||||
@ -61,7 +61,7 @@ export function createLoader($parent, opts = {}) {
|
||||
`);
|
||||
const id = window.setTimeout(() => {
|
||||
$parent.replaceChildren($icon);
|
||||
animate($icon, { time: 1000, keyframes: opacityIn() });
|
||||
animate($icon, { time: 750, keyframes: opacityIn() });
|
||||
}, wait);
|
||||
return () => {
|
||||
clearTimeout(id);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
[is="component_filesystem"] .component_filesystem {
|
||||
padding-top: 5px;
|
||||
padding-top: 3px;
|
||||
}
|
||||
[is="component_filesystem"] .empty {
|
||||
text-align: center;
|
||||
|
||||
@ -49,15 +49,15 @@ export default async function(render) {
|
||||
if (!!state.search) {
|
||||
const removeLoader = createLoader($header);
|
||||
return search(state.search_q).pipe(rxjs.map((files) => ({
|
||||
...rest, files, ...state,
|
||||
...rest, files, ...state, read_only: true,
|
||||
})), removeLoader);
|
||||
}
|
||||
return rxjs.of({ ...rest, files, ...state });
|
||||
return rxjs.of({ ...rest, files, ...state, read_only: false });
|
||||
}))),
|
||||
rxjs.mergeMap(({ show_hidden, files, ...rest }) => {
|
||||
if (show_hidden === false) files = files.filter(({ name }) => name[0] !== ".");
|
||||
console.log(rest);
|
||||
files = sort(files, rest.sort);
|
||||
files = sort(files, rest.sort, rest.order);
|
||||
return rxjs.of({ ...rest, files })
|
||||
}),
|
||||
removeLoader,
|
||||
@ -69,7 +69,7 @@ export default async function(render) {
|
||||
return rxjs.of({...rest, files });
|
||||
}),
|
||||
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;
|
||||
let FILE_HEIGHT, COLUMN_PER_ROW;
|
||||
switch(view) {
|
||||
@ -99,7 +99,7 @@ export default async function(render) {
|
||||
$fs.appendChild(createThing({
|
||||
...file,
|
||||
...createLink(file, path),
|
||||
view,
|
||||
view, read_only,
|
||||
n: i,
|
||||
}));
|
||||
}
|
||||
@ -129,9 +129,10 @@ export default async function(render) {
|
||||
$listAfter.style.height = `${height - size}px`;
|
||||
};
|
||||
setHeight(0);
|
||||
// const top = ($node) => $node.getBoundingClientRect().top;
|
||||
const top = ($node) => $node.getBoundingClientRect().top;
|
||||
return rxjs.of({
|
||||
files,
|
||||
read_only,
|
||||
path,
|
||||
view,
|
||||
currentState: 0,
|
||||
@ -140,11 +141,11 @@ export default async function(render) {
|
||||
FILE_HEIGHT,
|
||||
BLOCK_SIZE,
|
||||
COLUMN_PER_ROW,
|
||||
MARGIN: 35, // TODO: top($list) - top($list.closest(".scroll-y"));
|
||||
MARGIN: top($list) - top($list.closest(".scroll-y")),
|
||||
});
|
||||
}),
|
||||
rxjs.mergeMap(({
|
||||
files, path, view,
|
||||
files, path, view, read_only,
|
||||
BLOCK_SIZE, COLUMN_PER_ROW, FILE_HEIGHT,
|
||||
MARGIN,
|
||||
currentState,
|
||||
@ -207,7 +208,7 @@ export default async function(render) {
|
||||
type: "hidden",
|
||||
}));
|
||||
else $fs.appendChild(createThing({
|
||||
...file,
|
||||
...file, read_only,
|
||||
...createLink(file, path),
|
||||
view,
|
||||
n: i,
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 3;
|
||||
padding: 5px 0;
|
||||
padding: 4px 0;
|
||||
backdrop-filter: blur(20px) saturate(2);
|
||||
transition: 0.2s ease box-shadow;
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ import componentDelete from "./modal_delete.js";
|
||||
|
||||
import { getSelection$, clearSelection } from "./state_selection.js";
|
||||
import { getAction$, setAction } from "./state_event.js";
|
||||
import { setState } from "./state_filesystem.js";
|
||||
import { setState, getState$ } from "./state_filesystem.js";
|
||||
|
||||
const modalOpt = {
|
||||
withButtonsRight: "OK",
|
||||
@ -38,7 +38,7 @@ export default async function(render) {
|
||||
componentRight(createRender(qs($page, ".action.right")));
|
||||
|
||||
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.startWith(false),
|
||||
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" />
|
||||
</button>
|
||||
<div class="component_dropdown view sort" data-target="sort">
|
||||
<div class="dropdown_container">
|
||||
<ul>
|
||||
<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 data-target="date">
|
||||
Sort By Date
|
||||
</li>
|
||||
<li data-target="name">
|
||||
Sort By Name
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="dropdown_container">
|
||||
<ul>
|
||||
<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 data-target="date">
|
||||
Sort By Date
|
||||
</li>
|
||||
<li data-target="name">
|
||||
Sort By Name
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`))),
|
||||
rxjs.mergeMap(($page) => rxjs.merge(
|
||||
@ -208,18 +208,23 @@ function componentRight(render) {
|
||||
}
|
||||
const $lis = qsa($page, `.dropdown_container li`);
|
||||
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(
|
||||
"sort", $el.getAttribute("data-target"),
|
||||
"order", !!$el.querySelector("img") ? "asc" : "des",
|
||||
"order", order === "asc" ? "des" : "asc",
|
||||
);
|
||||
[...$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" />`));
|
||||
$sort.classList.remove("active");
|
||||
}),
|
||||
rxjs.tap(() => $sort.classList.remove("active")),
|
||||
);
|
||||
}),
|
||||
),
|
||||
|
||||
@ -1,85 +1,94 @@
|
||||
import { extname } from "../../lib/path.js";
|
||||
|
||||
export function sort(files, type) {
|
||||
if (type === "name") {
|
||||
return sortByName(files);
|
||||
} else if (type === "date") {
|
||||
return sortByDate(files);
|
||||
} else {
|
||||
return sortByType(files);
|
||||
export function sort(files, type, order) {
|
||||
switch(type) {
|
||||
case "name": return sortByName(files, order);
|
||||
case "date": return sortByDate(files, order);
|
||||
default: return sortByType(files, order);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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) {
|
||||
if (fileA.icon === "loading" && fileB.icon !== "loading") {
|
||||
return +1;
|
||||
} else if (fileA.icon !== "loading" && fileB.icon === "loading") {
|
||||
return -1;
|
||||
}
|
||||
const aIsLoading = fileA.icon === "loading";
|
||||
const bIsLoading = fileB.icon === "loading";
|
||||
|
||||
if (aIsLoading && !bIsLoading) return +1;
|
||||
else if (!aIsLoading && bIsLoading) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function _moveFolderUpward(fileA, fileB) {
|
||||
if (["directory", "link"].indexOf(fileA.type) === -1 &&
|
||||
["directory", "link"].indexOf(fileB.type) !== -1) {
|
||||
return +1;
|
||||
} else if (["directory", "link"].indexOf(fileA.type) !== -1 &&
|
||||
["directory", "link"].indexOf(fileB.type) === -1) {
|
||||
return -1;
|
||||
}
|
||||
const aIsDirectory = ["directory", "link"].indexOf(fileA.type) !== -1;
|
||||
const bIsDirectory = ["directory", "link"].indexOf(fileB.type) !== -1;
|
||||
|
||||
if (!aIsDirectory && bIsDirectory) return +1;
|
||||
else if (aIsDirectory && !bIsDirectory) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function _moveHiddenFilesDownward(fileA, fileB) {
|
||||
if (fileA.name[0] === "." && fileB.name[0] !== ".") return +1;
|
||||
else if (fileA.name[0] !== "." && fileB.name[0] === ".") return -1;
|
||||
const aIsHidden = fileA.name[0] === ".";
|
||||
const bIsHidden = fileB.name[0] === ".";
|
||||
|
||||
if (aIsHidden && !bIsHidden) return +1;
|
||||
if (!aIsHidden && bIsHidden) return -1;
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@ export function createThing({
|
||||
// permissions = {}
|
||||
view = "",
|
||||
n = 0,
|
||||
read_only = false,
|
||||
}) {
|
||||
const $thing = $tmpl.cloneNode(true);
|
||||
assert.type($thing, window.HTMLElement);
|
||||
@ -60,7 +61,12 @@ export function createThing({
|
||||
$thing.classList.add("view-" + view);
|
||||
$time.textContent = formatTime(new Date(time));
|
||||
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) {
|
||||
e.preventDefault();
|
||||
|
||||
Reference in New Issue
Block a user