diff --git a/public/assets/components/breadcrumb.css b/public/assets/components/breadcrumb.css index 28562bd6..98220782 100644 --- a/public/assets/components/breadcrumb.css +++ b/public/assets/components/breadcrumb.css @@ -3,19 +3,16 @@ z-index: 5; } .component_breadcrumb .breadcrumb { - background: white; - border-bottom: 2px solid rgba(100, 100, 100, 0.05); - transition: 1s ease border-bottom; margin: 0 0 0px 0; z-index: 1000; - padding: 3px 0; + padding: 3px 0 3px 10px; } .component_breadcrumb .breadcrumb .ul { list-style-type: none; - margin: 0 auto; - width: 95%; - max-width: 800px; - padding: 0; + margin: 0; + width: 100%; + padding: 0 10px 0 10px; + box-sizing: border-box; } .component_breadcrumb .breadcrumb .ul > span { display: block; @@ -24,9 +21,9 @@ .component_breadcrumb .breadcrumb .ul div, .component_breadcrumb .breadcrumb .ul .li { display: inline-block; } -.component_breadcrumb:hover .breadcrumb { - border-bottom: 2px solid rgba(100, 100, 100, 0.1); -} +/* .component_breadcrumb:hover .breadcrumb { */ +/* border-bottom: 2px solid rgba(100, 100, 100, 0.1); */ +/* } */ .component_breadcrumb .component_logout { float: right; display: inline-block; diff --git a/public/assets/components/breadcrumb.js b/public/assets/components/breadcrumb.js index f1ffb0e2..0a365d5a 100644 --- a/public/assets/components/breadcrumb.js +++ b/public/assets/components/breadcrumb.js @@ -16,7 +16,6 @@ class ComponentBreadcrumb extends HTMLDivElement { `; const paths = (this.getAttribute("path") || "").replace(new RegExp("/$"), "").split("/"); - console.log(paths); const htmlPathChunks = paths.map((chunk, idx) => { const label = idx === 0 ? "Filestash" : chunk; const link = paths.slice(0, idx + 1).join("/") + "/"; diff --git a/public/assets/components/decorator_shell_filemanager.css b/public/assets/components/decorator_shell_filemanager.css new file mode 100644 index 00000000..a940d7e0 --- /dev/null +++ b/public/assets/components/decorator_shell_filemanager.css @@ -0,0 +1,63 @@ +.component_filemanager_shell { + display: flex; + height: 100%; +} +.component_filemanager_shell .component_sidebar { + width: 240px; + padding: 10px; +} +.component_filemanager_shell .component_sidebar > h3 { + padding-left: 10px; + margin-bottom: 5px; + color: var(--light); + font-size: 1rem; +} +.component_filemanager_shell .component_sidebar > div { + background: rgba(0,0,0,0.1); + padding-left: 10px; + width: 90%; + height: 50px; + margin: 0 auto; +} +.component_filemanager_shell [is="component-breadcrumb"]:hover ~ [data-bind="filemanager-children"], +.component_filemanager_shell [data-bind="sidebar"]:hover ~ div > [data-bind="filemanager-children"] { + border-color: rgba(100, 100, 100, 0.1); +} + +.component_filemanager_shell [data-bind="filemanager-children"] { + border-top: 2px solid rgba(0,0,0,0.02); + border-left: 2px solid rgba(0,0,0,0.02); + transition: 1000ms ease border-color; + border-top-left-radius: 15px; + display: flex; + height: 100%; + overflow: hidden; + background: white; + padding-top: 20px; +} + + +.component_filemanager_shell [data-bind="sidebar"].hidden ~ div > [data-bind="filemanager-children"] { + border-top-left-radius: 0; + border-left: none; +} +.component_filemanager_shell [data-bind="sidebar"].hidden ~ div > [is="component-breadcrumb"] > .component_breadcrumb, +.component_filemanager_shell [data-bind="sidebar"].hidden ~ div > [data-bind="filemanager-children"] > div { + width: 95%; + margin: 0 auto; + max-width: 820px; +} +.component_filemanager_shell [data-bind="sidebar"].hidden ~ div [is="component-breadcrumb"] { + background: white; +} +.component_filemanager_shell [data-bind="sidebar"].hidden ~ div > [data-bind="filemanager-children"] { + background: var(--bg-color); +} +.component_filemanager_shell .component_filesystem { + padding: 15px; +} + +.component_filemanager_shell [data-bind="filemanager-children"] > div, +.component_filemanager_shell [is="component-breadcrumb"] { + max-width: 2100px; +} diff --git a/public/assets/components/decorator_shell_filemanager.js b/public/assets/components/decorator_shell_filemanager.js new file mode 100644 index 00000000..e4dfd186 --- /dev/null +++ b/public/assets/components/decorator_shell_filemanager.js @@ -0,0 +1,56 @@ +import { createElement, createRender } from "../lib/skeleton/index.js"; +import { qs } from "../lib/dom.js"; +import { CSS } from "../helpers/loader.js"; + +export default function(ctrl) { + const $page = createElement(` +
+ +
+
+
+
+
+ `); + + return async function(render) { + render($page); + + // feature1: setup the childrens + ctrl(createRender(qs($page, `[data-bind="filemanager-children"]`))); + ctrlSidebar(createRender(qs($page, `[data-bind="sidebar"]`))); + + qs($page, `[is="component-breadcrumb"]`).setAttribute( + "path", + decodeURIComponent(location.pathname).replace(new RegExp("/files"), ""), + ) + } +} + + +async function ctrlSidebar(render) { + // Quick Access: + // - home folders + // - indexedDB folders + // Shared Drive: + // - shared links (rwd) + // Tags: + // - name + color + const $comp = createElement(` +
+

+ +

Quick Access

+
+ +

Shared Drive

+
+ +

Tags

+
+ + +
+ `); + render($comp); +} diff --git a/public/assets/components/loader.js b/public/assets/components/loader.js index ca3dc49b..1f142520 100644 --- a/public/assets/components/loader.js +++ b/public/assets/components/loader.js @@ -42,10 +42,25 @@ class Loader extends window.HTMLElement { } } + +// function tap(sideEffectFn) { +// return (source) => { +// // TODO: use source.lift +// console.log("SOURCE", source) +// source.lift({ +// call: (subscriber, source) => { +// console.log(source, subscriber); +// return subscriber; +// }, +// }) +// } +// } +const tap = rxjs.tap; + customElements.define("component-loader", Loader); export default createElement(""); export function toggle($node, show = false) { - if (show === true) return rxjs.tap(() => $node.appendChild(createElement(""))); - else return rxjs.tap(() => $node.querySelector("component-loader")?.remove()); + if (show === true) return tap(() => $node.appendChild(createElement(""))); + else return tap(() => $node.querySelector("component-loader")?.remove()); } diff --git a/public/assets/css/reset.css b/public/assets/css/designsystem.css similarity index 97% rename from public/assets/css/reset.css rename to public/assets/css/designsystem.css index f14ab50a..3ca6d14f 100644 --- a/public/assets/css/reset.css +++ b/public/assets/css/designsystem.css @@ -49,21 +49,21 @@ } :root { - --bg-color: #fafafa; + --bg-color: #fafafa; /*#fafafa;*/ --color: #57595A; --emphasis: #466372; --primary: #9AD1ED; --emphasis-primary: #c5e2f1; --emphasis-secondary: #466372; --light: #909090; - --super-light: #f9fafc; + --super-light: #fafafa; --error: #f26d6d; --success: #63d9b1; --dark: #313538; } html { - font-family: "Roboto", sans-serif; + font-family: system-ui, sans-serif; -webkit-text-size-adjust:100%; } body { diff --git a/public/assets/pages/ctrl_error.js b/public/assets/pages/ctrl_error.js index 9000240a..7ac6e07c 100644 --- a/public/assets/pages/ctrl_error.js +++ b/public/assets/pages/ctrl_error.js @@ -22,8 +22,8 @@ export default function(render = createRender(qs(document.body, "[role=\"main\"]

${t("Oops!")}

${t(msg)}

- - + +

diff --git a/public/assets/pages/ctrl_filespage.css b/public/assets/pages/ctrl_filespage.css index 581a2234..3e6198df 100644 --- a/public/assets/pages/ctrl_filespage.css +++ b/public/assets/pages/ctrl_filespage.css @@ -2,6 +2,7 @@ display: flex; flex-direction: column; height: 100%; + width: 100%; } .component_page_filespage .error { cursor: pointer; diff --git a/public/assets/pages/ctrl_filespage.js b/public/assets/pages/ctrl_filespage.js index ca60f68c..fae7cba8 100644 --- a/public/assets/pages/ctrl_filespage.js +++ b/public/assets/pages/ctrl_filespage.js @@ -1,24 +1,21 @@ import { createElement, createRender } from "../lib/skeleton/index.js"; import rxjs, { effect } from "../lib/rx.js"; +import { qs } from "../lib/dom.js"; import { CSS } from "../helpers/loader.js"; +import WithShell from "../components/decorator_shell_filemanager.js" -import { getState$ } from "./filespage/state.js"; -import componentFilesystem from "./filespage/filesystem.js"; +import { getState$ } from "./filespage/ctrl_filesystem_state.js"; +import componentFilesystem from "./filespage/ctrl_filesystem.js"; +import componentSubmenu from "./filespage/ctrl_submenu.js"; import "../components/breadcrumb.js"; -export default function(render) { - const currentPath = decodeURIComponent(location.pathname).replace(new RegExp("/files"), ""); +export default WithShell(function(render) { const $page = createElement(`
-
-
-
-
-
-
-
-
+ + +
`); @@ -31,7 +28,10 @@ export default function(render) { )); // feature2: render the filesystem - componentFilesystem(createRender($page.querySelector("[is=\"component-filesystem\"]"))); -} + componentFilesystem(createRender(qs($page, "[is=\"component_filesystem\"]"))); + + // feature3: render the menubar + componentSubmenu(createRender(qs($page, "[is=\"component_submenu\"]"))) +}); const css = await CSS(import.meta.url, "ctrl_filespage.css"); diff --git a/public/assets/pages/ctrl_viewerpage.js b/public/assets/pages/ctrl_viewerpage.js index 5c424202..2aeb0834 100644 --- a/public/assets/pages/ctrl_viewerpage.js +++ b/public/assets/pages/ctrl_viewerpage.js @@ -1,16 +1,13 @@ import { createElement } from "../lib/skeleton/index.js"; +import WithShell from "../components/decorator_shell_filemanager.js" import "../components/breadcrumb.js"; -export default function(render) { - const currentPath = decodeURIComponent(location.pathname).replace(new RegExp("/view"), ""); +export default WithShell(function(render) { const $page = createElement(` -
-
-
+
viewerpage - TODO -
`); render($page); -} +}) diff --git a/public/assets/pages/filespage/ctrl_filesystem.css b/public/assets/pages/filespage/ctrl_filesystem.css new file mode 100644 index 00000000..2352a6fd --- /dev/null +++ b/public/assets/pages/filespage/ctrl_filesystem.css @@ -0,0 +1,4 @@ +.list .container_fs { + content-visibility: auto; + contain-intrinsic-size: 1px 2700px; +} diff --git a/public/assets/pages/filespage/ctrl_filesystem.js b/public/assets/pages/filespage/ctrl_filesystem.js new file mode 100644 index 00000000..9e8a2ac9 --- /dev/null +++ b/public/assets/pages/filespage/ctrl_filesystem.js @@ -0,0 +1,169 @@ +import { createElement } from "../../lib/skeleton/index.js"; +import { animate, slideYIn } from "../../lib/animate.js"; +import rxjs, { effect } from "../../lib/rx.js"; +import { CSS } from "../../helpers/loader.js"; +import { qs } from "../../lib/dom.js"; +import { ApplicationError } from "../../lib/error.js"; +import { toggle as toggleLoader } from "../../components/loader.js"; +import ctrlError from "../ctrl_error.js"; + +import { createThing, allocateMemory, css } from "./thing.js"; +import { handleError, getFiles } from "./ctrl_filesystem_state.js"; +import { ls } from "./model_files.js"; + +export default async function(render) { + const $page = createElement(` +
+
+
+
+
+
+
+
+
+
+ + +
+ `); + render($page); + + // feature: virtual scrolling + const path = location.pathname.replace(new RegExp("^/files"), ""); + effect(rxjs.of(path).pipe( + toggleLoader($page, true), + rxjs.mergeMap(() => new Promise((done) => setTimeout(() => done({ + files: new Array(400).fill(1), + }), 1000))), + toggleLoader($page, false), + rxjs.mergeMap(({ files }) => { + const BLOCK_SIZE = 8; + const COLUMN_PER_ROW = 2; + const FILE_HEIGHT = 160; + const size = Math.min(files.length, BLOCK_SIZE * COLUMN_PER_ROW); + allocateMemory(BLOCK_SIZE * COLUMN_PER_ROW); + const $fs = document.createDocumentFragment(); + for (let i = 0; i < size; i++) { + $fs.appendChild(createThing({ + name: `file ${i}`, + type: "file", + link: "/view/test.txt", + })); + } + const $list = qs($page, ".list"); + const $listBefore = qs($page, ".ifscroll-before"); + const $listAfter = qs($page, ".ifscroll-after"); + + const height = (Math.floor(files.length / COLUMN_PER_ROW) - BLOCK_SIZE) * FILE_HEIGHT; + const setHeight = (size) => { + $listBefore.style.height = `${size}px`; + $listAfter.style.height = `${height - size}px`; + }; + setHeight(0); + animate($list, { time: 200, keyframes: slideYIn(5) }); + $list.appendChild($fs); + if (files.length === size) return rxjs.EMPTY; + return rxjs.of({ + files, + currentState: 0, + BLOCK_SIZE, COLUMN_PER_ROW, FILE_HEIGHT, + setHeight, + $list, + }); + }), + rxjs.mergeMap(({ + files, + BLOCK_SIZE, COLUMN_PER_ROW, FILE_HEIGHT, + currentState, + height, setHeight, + $list, + }) => rxjs.fromEvent( + $page.parentElement.parentElement.parentElement, + "scroll", { passive: true }, + ).pipe( + rxjs.map((e) => Math.min( + Math.ceil(Math.max(0, e.target.scrollTop) / FILE_HEIGHT), + // cap state value when BLOCK_SIZE is larger than minimum value. This is to + // prevent issues when scrolling fast to the bottom (aka diff > 1) + Math.ceil(files.length / COLUMN_PER_ROW) - BLOCK_SIZE, + )), + rxjs.distinctUntilChanged(), + rxjs.debounce(() => new rxjs.Observable((observer) => { + const id = requestAnimationFrame(() => observer.next()); + return () => cancelAnimationFrame(id); + })), + rxjs.tap((nextState) => { + // STEP1: calculate the virtual scroll paramameters + let diff = nextState - currentState; + const diffSgn = Math.sign(diff); + if (Math.abs(diff) > BLOCK_SIZE) diff = diffSgn * BLOCK_SIZE; // fast scroll + let fileStart = nextState * COLUMN_PER_ROW; + if (diffSgn > 0) { // => scroll down + // eg: files[15] BLOCK_SIZE=1 COLUMN_PER_ROW=1 + // -----------[currentState:0]--------------------------[nextState:5]--------- + // -----------[fileStart=5+1=6] + fileStart += BLOCK_SIZE * COLUMN_PER_ROW; + // -----------[fileStart=6-min(5,1)=5] + fileStart -= Math.min(diff, BLOCK_SIZE) * COLUMN_PER_ROW; + } + let fileEnd = fileStart + diffSgn * diff * COLUMN_PER_ROW; + + if (fileStart >= files.length) throw new ApplicationError( + "INTERNAL_ERROR", + `assert failed in virtual scroll range[${fileStart}:${fileEnd}] length[${files.length}]`, + ); else if (fileEnd > files.length) { + // occur when files.length isn't a multiple of COLUMN_PER_ROW and + // we've scrolled to the bottom of the list + nextState = Math.floor(files.length / COLUMN_PER_ROW) - BLOCK_SIZE; + fileEnd = files.length; + do { + // add some padding to fileEnd to balance the list to the + // nearest COLUMN_PER_ROW + fileEnd += 1; + } while (fileEnd % COLUMN_PER_ROW !== 0); + } + + // STEP2: create the new elements + const $fs = document.createDocumentFragment(); + let n = 0; + for (let i = fileStart; i < fileEnd; i++) { + const file = files[i]; + if (file === undefined) $fs.appendChild(createThing({ + name: "dummy", + })) + else $fs.appendChild(createThing({ + name: `file - ${i}`, + type: "file", + link: "/view/test.txt", + })); + n += 1; + } + // console.log(`n[${n}] state[${currentState} -> ${nextState}] files[${fileStart}:${fileEnd}]`) + + // STEP3: update the DOM + if (diffSgn > 0) { // scroll down + // console.log("DOWN", n); + // // if (!isInViewport($list.firstChild)) // TODO + $list.appendChild($fs); + for (let i = 0; i < n; i++) $list.firstChild.remove(); + } else { // scroll up + // console.log("UP", n) + $list.insertBefore($fs, $list.firstChild); + for (let i = 0; i < n; i++) $list.lastChild.remove(); + } + setHeight(nextState * FILE_HEIGHT); + currentState = nextState; + }), + )), + rxjs.catchError(ctrlError()), + )); + + // feature2: fs in "search" mode + // TODO +} + +function isInViewport(element) { + const rect = element.getBoundingClientRect(); + return rect.bottom > 0; +} diff --git a/public/assets/pages/filespage/state.js b/public/assets/pages/filespage/ctrl_filesystem_state.js similarity index 95% rename from public/assets/pages/filespage/state.js rename to public/assets/pages/filespage/ctrl_filesystem_state.js index 2cce69b5..3c455c34 100644 --- a/public/assets/pages/filespage/state.js +++ b/public/assets/pages/filespage/ctrl_filesystem_state.js @@ -35,3 +35,5 @@ export const onNewDirectory = () => { export const onSearch = () => { console.log("SEARCH"); }; + +export const getFiles = (n) => {} diff --git a/public/assets/pages/filespage/ctrl_submenu.css b/public/assets/pages/filespage/ctrl_submenu.css new file mode 100644 index 00000000..7e2237c8 --- /dev/null +++ b/public/assets/pages/filespage/ctrl_submenu.css @@ -0,0 +1,133 @@ +.component_submenu.sticky { + position: sticky; + top: 0; + z-index: 3; + background: var(--bg-color); + opacity: 0.95; +} + +.component_submenu { + padding-left: 0; + padding-right: 0; + padding-bottom: 0; + margin-bottom: -10px; +} +.component_submenu .menubar { + font-size: 15px; + line-height: 15px; + height: 15px; + margin-top: 5px; + color: var(--light); + margin: 5px 0 10px 0; + /* Create Buttons */ +} +.component_submenu .menubar > span, .component_submenu .menubar form label { + display: inline-block; + margin-top: -5px; + border-radius: 2px; + cursor: pointer; + background-position: center; +} +.component_submenu .menubar > span:active, .component_submenu .menubar > span:hover, .component_submenu .menubar form label:active, .component_submenu .menubar form label:hover { + color: var(--color); +} +.component_submenu .menubar > span:hover, .component_submenu .menubar form label:hover { + transition: background 0.4s; + background: #ffffff00 radial-gradient(circle, transparent 1%, var(--bg-color) 1%) center/15000%; +} +.component_submenu .menubar > span:active, .component_submenu .menubar form label:active { + background-color: #00000033; + background-size: 100%; + transition: background 0s; +} +.component_submenu .menubar > span { + padding: 5px 8px; + letter-spacing: 0.06em; + display: inline-block; + text-transform: uppercase; + font-size: 12px; + color: var(--light); + vertical-align: text-bottom; +} +.component_submenu .menubar > div { + cursor: pointer; + margin-right: 15px; +} +.component_submenu .menubar .view { + float: right; + padding: 2px; + transition: 0.15s ease-out background; + margin-right: 0px; + margin-left: 0px; +} +.component_submenu .menubar .view .search, .component_submenu .menubar .view.list-grid, .component_submenu .menubar .view > .dropdown_button, .component_submenu .menubar .view form > label { + min-width: inherit; + border-radius: 2px; + padding: 5px; + margin: 2px 0 2px 0; +} +.component_submenu .menubar .view.list-grid, .component_submenu .menubar .view .dropdown_button, .component_submenu .menubar .view .search { + background-position: center; +} +.component_submenu .menubar .view.list-grid:hover, .component_submenu .menubar .view .dropdown_button:hover, .component_submenu .menubar .view .search:hover { + transition: background 0.4s; + background: #ffffff00 radial-gradient(circle, transparent 1%, var(--bg-color) 1%) center/15000%; +} +.component_submenu .menubar .view.list-grid:active, .component_submenu .menubar .view .dropdown_button:active, .component_submenu .menubar .view .search:active { + background-color: #00000033; + background-size: 100%; + transition: background 0s; +} +.component_submenu .menubar .view .dropdown_container .component_icon { + box-sizing: border-box; + border: 2px solid rgba(0, 0, 0, 0); +} +.component_submenu .menubar .view.list-grid, .component_submenu .menubar .view .search { + margin-top: -5px !important; +} +.component_submenu .menubar .view.component_dropdown { + margin-top: -10px; +} +.component_submenu .menubar .view.component_dropdown.active .dropdown_button { + box-shadow: none; +} +.component_submenu .menubar.search_focus form input { + transition: 0.2s width ease-in; + opacity: 1; +} +.component_submenu .menubar form { + float: right; + margin-top: -3px; +} +.component_submenu .menubar form input { + opacity: 0; + font-size: 1em; + margin-top: -3px; + border: none; + background: inherit; + border-bottom: 2px solid var(--light); + color: var(--color); +} +.component_submenu .menubar.search_focus > span { + display: none; +} +.component_submenu .menubar .component_icon { + height: 15px; + width: 15px; +} + +.component_submenu .submenuwithSelection-appear { + display: inline-block; + opacity: 0; + transform: translateY(3px); + transition: opacity 0.2s ease, transform 0.4s ease; +} + +.component_submenu .submenuwithSelection-appear.submenuwithSelection-appear-active { + opacity: 1; + transform: translateY(0px); +} + +.dark-mode .component_submenu input#search { + color: rgba(255, 255, 255, 0.8); +} diff --git a/public/assets/pages/filespage/ctrl_submenu.js b/public/assets/pages/filespage/ctrl_submenu.js new file mode 100644 index 00000000..5c248f53 --- /dev/null +++ b/public/assets/pages/filespage/ctrl_submenu.js @@ -0,0 +1,87 @@ +import { createElement, createRender } from "../../lib/skeleton/index.js"; +import { CSS } from "../../helpers/loader.js"; + +export default async function(render) { + + const $page = createElement(` +
+ +
+ + +
+
+ + + +
+
+ `) + render($page); +} + +const css = ` +.component_submenu { + display: flex; + justify-content: space-between; + padding: 0 15px; +} +.component_submenu .action.left { + margin-left: 2px; + margin-right: 5px; +} +.component_submenu .action.right { + float: right; + margin-right: 10px; +} +.component_submenu .action.right button img { + width: 16px; + height: 16px; +} +.component_submenu .action.right button, +.component_submenu .action.left button { + background: var(--bg-color); + border: 2px solid rgba(100, 100, 100, 0.05); + padding: 5px 10px; +} +` + +export async function OldOne(render) { + const $page = createElement(` +
+ + +
+
+`); + + render($page); +} diff --git a/public/assets/pages/filespage/filesystem.js b/public/assets/pages/filespage/filesystem.js deleted file mode 100644 index 50613add..00000000 --- a/public/assets/pages/filespage/filesystem.js +++ /dev/null @@ -1,45 +0,0 @@ -import { createElement } from "../../lib/skeleton/index.js"; -import rxjs, { effect } from "../../lib/rx.js"; -import { qs } from "../../lib/dom.js"; - -import { toggle as toggleLoader } from "../../components/loader.js"; - -import { createThing, css } from "./thing.js"; -import { handleError } from "./state.js"; -import { ls } from "./model_files.js"; - -export default async function(render) { - const $page = createElement(` -
-
- -
- `); - render($page); - - // feature1: files on the current path - const path = location.pathname.replace(new RegExp("^/files"), ""); - effect(rxjs.of(path).pipe( - toggleLoader($page, true), - ls(), // TODO: ls_from_cache then ls_from_server - toggleLoader($page, false), - rxjs.tap(({ files }) => { - const $fs = document.createDocumentFragment(); - for (let i = 0; i < files.length && i < 100; i++) { - if (!files[i]) continue; - $fs.appendChild(createThing({ - name: files[i].name, - // type: files[i].type, - // size: files[i].size, - // time: files[i].time, - link: (files[i].type === "file" ? "/view" : "/files") + path + files[i].name + (files[i].type === "file" ? "" : "/"), - })); - } - qs($page, ".list").appendChild($fs); - }), - handleError() - )); - - // feature2: fs in "search" mode - // TODO -} diff --git a/public/assets/pages/filespage/thing.css b/public/assets/pages/filespage/thing.css index 8d15f4e9..cba2da0e 100644 --- a/public/assets/pages/filespage/thing.css +++ b/public/assets/pages/filespage/thing.css @@ -109,8 +109,8 @@ /* GRID & LIST VIEW */ .list { - display: flex; - flex-wrap: wrap; + display: grid; + grid-template-columns: repeat(auto-fill, 50%); } .list > .component_thing { width: 100%; @@ -119,60 +119,60 @@ margin: 0px 0 2px 0; } .list > .component_thing.view-grid { - width: 25%; + width: 100%; text-align: center; transition: 0.1s ease transform; } .list > .component_thing.view-grid .box { - height: 160px; -} -@media (max-width: 699px) { - .list > .component_thing.view-grid { - width: 33.33%; - } - .list > .component_thing.view-grid .box { - height: 200px; - } -} -@media (max-width: 670px) { - .list > .component_thing.view-grid .box { - height: 170px; - } -} -@media (max-width: 550px) { - .list > .component_thing.view-grid { - width: 50%; - } - .list > .component_thing.view-grid .box { - height: 200px; - } -} -@media (max-width: 480px) { - .list > .component_thing.view-grid .box { - height: 170px; - } -} -@media (max-width: 400px) { - .list > .component_thing.view-grid .box { - height: 140px; - } - .list > .component_thing.view-grid .box .info_extension { - font-size: 0.8em !important; - padding: 3px 10px; - } -} -@media (max-width: 340px) { - .list > .component_thing.view-grid .box { - height: 130px; - } + height: 156px; } +/* @media (max-width: 699px) { */ +/* .list > .component_thing.view-grid { */ +/* width: 33.33%; */ +/* } */ +/* .list > .component_thing.view-grid .box { */ +/* height: 200px; */ +/* } */ +/* } */ +/* @media (max-width: 670px) { */ +/* .list > .component_thing.view-grid .box { */ +/* height: 170px; */ +/* } */ +/* } */ +/* @media (max-width: 550px) { */ +/* .list > .component_thing.view-grid { */ +/* width: 50%; */ +/* } */ +/* .list > .component_thing.view-grid .box { */ +/* height: 200px; */ +/* } */ +/* } */ +/* @media (max-width: 480px) { */ +/* .list > .component_thing.view-grid .box { */ +/* height: 170px; */ +/* } */ +/* } */ +/* @media (max-width: 400px) { */ +/* .list > .component_thing.view-grid .box { */ +/* height: 140px; */ +/* } */ +/* .list > .component_thing.view-grid .box .info_extension { */ +/* font-size: 0.8em !important; */ +/* padding: 3px 10px; */ +/* } */ +/* } */ +/* @media (max-width: 340px) { */ +/* .list > .component_thing.view-grid .box { */ +/* height: 130px; */ +/* } */ +/* } */ .list > .component_thing.view-grid .box { margin: 2px; padding: 0; position: relative; border-width: 0; box-sizing: border-box; - border: 2px solid rgba(100, 100, 100, 0.1); + border: 2px solid rgba(100, 100, 100, 0.05); border-radius: 5px; } .list > .component_thing.view-grid .box > span > img { diff --git a/public/assets/pages/filespage/thing.js b/public/assets/pages/filespage/thing.js index 30fde6d3..30ab6cbc 100644 --- a/public/assets/pages/filespage/thing.js +++ b/public/assets/pages/filespage/thing.js @@ -7,7 +7,7 @@ const $tmpl = createElement(`
- directory + directory @@ -15,17 +15,15 @@ const $tmpl = createElement(` 06/06/2020 -
- edit - delete - share -
+
`); +export const css = CSS(import.meta.url, "thing.css"); + // a filesystem "thing" is typically either a file or folder which have a lot of behavior builtin. // Probably one day we can rename that to something more clear but the gist is a thing can be // displayed in list mode / grid mode, have some substate to enable loading state for upload, @@ -39,7 +37,7 @@ export function createThing({ link = "", // permissions = {} }) { - const $thing = $tmpl.cloneNode(true); + const $thing = $get(); if ($thing instanceof HTMLElement) { const $label = $thing.querySelector(".component_filename .file-details > span"); if ($label instanceof HTMLElement) $label.textContent = name; @@ -48,4 +46,19 @@ export function createThing({ return $thing; } -export const css = CSS(import.meta.url, "thing.css"); +function $get() { + // the very first implementation was: + return $tmpl.cloneNode(true); + // the major issue was cloneNode is slow and would often make us miss an animationFrame. A much more + // efficient approach is to use a ring buffer of node we reuse as we scroll around + if (bufferIdx >= $tmplBuffer.length) bufferIdx = 0; + const $node = $tmplBuffer[bufferIdx]; + bufferIdx += 1; + // console.log($node); + return $node; +} +let $tmplBuffer = []; +let bufferIdx = 0; +export function allocateMemory(size) { + $tmplBuffer = Array.apply(null, {length: size}).map(() => $tmpl.cloneNode(true)) +} diff --git a/public/index.backoffice.html b/public/index.backoffice.html index 6cd1cf8c..47025580 100644 --- a/public/index.backoffice.html +++ b/public/index.backoffice.html @@ -4,7 +4,7 @@ - + Admin Console diff --git a/public/index.frontoffice.html b/public/index.frontoffice.html index 9b58d688..7b6d149d 100644 --- a/public/index.frontoffice.html +++ b/public/index.frontoffice.html @@ -4,7 +4,7 @@ - + @@ -21,10 +21,11 @@ }); - - + + +