diff --git a/public/assets/components/dropdown.css b/public/assets/components/dropdown.css
index 2edab593..e69de29b 100644
--- a/public/assets/components/dropdown.css
+++ b/public/assets/components/dropdown.css
@@ -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);
-}
diff --git a/public/assets/css/designsystem_darkmode.css b/public/assets/css/designsystem_darkmode.css
index 63fb63ef..71ecab47 100644
--- a/public/assets/css/designsystem_darkmode.css
+++ b/public/assets/css/designsystem_darkmode.css
@@ -5,6 +5,7 @@ body.dark-mode {
--border: #303438;
+ --dark: #2b2d30;
}
body.dark-mode input {
diff --git a/public/assets/css/designsystem_dropdown.css b/public/assets/css/designsystem_dropdown.css
index 7c2e0f60..70774df0 100644
--- a/public/assets/css/designsystem_dropdown.css
+++ b/public/assets/css/designsystem_dropdown.css
@@ -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);
-}
diff --git a/public/assets/lib/rx.js b/public/assets/lib/rx.js
index d30c45fd..3afdb324 100644
--- a/public/assets/lib/rx.js
+++ b/public/assets/lib/rx.js
@@ -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) {
diff --git a/public/assets/pages/connectpage/ctrl_form.js b/public/assets/pages/connectpage/ctrl_form.js
index ccfa9d85..56baf0da 100644
--- a/public/assets/pages/connectpage/ctrl_form.js
+++ b/public/assets/pages/connectpage/ctrl_form.js
@@ -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
diff --git a/public/assets/pages/ctrl_error.js b/public/assets/pages/ctrl_error.js
index 79fa54f4..c847de21 100644
--- a/public/assets/pages/ctrl_error.js
+++ b/public/assets/pages/ctrl_error.js
@@ -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(`
-
+
home
@@ -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;
+}
diff --git a/public/assets/pages/filespage/ctrl_filesystem.js b/public/assets/pages/filespage/ctrl_filesystem.js
index a5119212..f025be1e 100644
--- a/public/assets/pages/filespage/ctrl_filesystem.js
+++ b/public/assets/pages/filespage/ctrl_filesystem.js
@@ -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(`
@@ -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(`
+
+
+
+
+
There is nothing here
+
+ `));
+}
+
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 };
}
diff --git a/public/assets/pages/filespage/ctrl_filesystem_state.js b/public/assets/pages/filespage/ctrl_filesystem_state.js
index a670226b..c892cb9d 100644
--- a/public/assets/pages/filespage/ctrl_filesystem_state.js
+++ b/public/assets/pages/filespage/ctrl_filesystem_state.js
@@ -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
{
- 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)),
+));
diff --git a/public/assets/pages/filespage/ctrl_submenu.js b/public/assets/pages/filespage/ctrl_submenu.js
index d3e2e501..54c23085 100644
--- a/public/assets/pages/filespage/ctrl_submenu.js
+++ b/public/assets/pages/filespage/ctrl_submenu.js
@@ -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) {
- -
-
- Sort By Type
-
-
-
-
-
+ -
+ Sort By Type
+
- -
-
Sort By Date
+ -
+ Sort By Date
- -
-
Sort By Name
+ -
+ Sort By Name
@@ -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(`

`));
+ }));
})),
)),
));
diff --git a/public/assets/pages/filespage/ctrl_upload_queue.js b/public/assets/pages/filespage/ctrl_upload_queue.js
index 842bc846..f74070c2 100644
--- a/public/assets/pages/filespage/ctrl_upload_queue.js
+++ b/public/assets/pages/filespage/ctrl_upload_queue.js
@@ -5,7 +5,7 @@ import { qs } from "../../lib/dom.js";
export default function(render) {
const $page = createElement(`
-