diff --git a/public/assets/components/loader.js b/public/assets/components/loader.js
index 9d0651b9..ef595e0e 100644
--- a/public/assets/components/loader.js
+++ b/public/assets/components/loader.js
@@ -44,7 +44,6 @@ class Loader extends window.HTMLElement {
}
customElements.define("component-loader", Loader);
-
export function createLoader($parent, opts = {}) {
const { wait = 500 } = opts;
const cancel = effect(new rxjs.Observable((observer) => {
@@ -61,7 +60,7 @@ export function createLoader($parent, opts = {}) {
`);
const id = window.setTimeout(() => {
- $parent.appendChild($icon);
+ $parent.replaceChildren($icon);
animate($icon, { time: 1000, keyframes: opacityIn() });
}, wait);
return () => {
diff --git a/public/assets/pages/filespage/ctrl_filesystem.js b/public/assets/pages/filespage/ctrl_filesystem.js
index 2d063dc7..4a929ea0 100644
--- a/public/assets/pages/filespage/ctrl_filesystem.js
+++ b/public/assets/pages/filespage/ctrl_filesystem.js
@@ -1,4 +1,4 @@
-import { createElement } from "../../lib/skeleton/index.js";
+import { createElement, createRender } from "../../lib/skeleton/index.js";
import { animate, slideYIn } from "../../lib/animate.js";
import rxjs, { effect } from "../../lib/rx.js";
import { loadCSS } from "../../helpers/loader.js";
@@ -7,9 +7,10 @@ import { ApplicationError } from "../../lib/error.js";
import { createLoader } from "../../components/loader.js";
import ctrlError from "../ctrl_error.js";
+import { sort } from "./helper.js";
import { createThing } from "./thing.js";
-import { getState$ } from "./ctrl_filesystem_state.js";
-import { ls } from "./model_files.js";
+import { getState$ } from "./state_filesystem.js";
+import { ls, search } from "./model_files.js";
const ICONS = {
EMPTY_FILES: "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMzAwIiBoZWlnaHQ9IjE3MCIgdmlld0JveD0iMCAwIDMwMCAxNzAiIGZpbGw9Im5vbmUiPgogIDxwYXRoCiAgICAgZD0ibSA1Mi42Mjk5MDUsMTYwLjE2Nzg1IGMgMS41NDYsMCAyLjgsLTEuMjUzNiAyLjgsLTIuOCAwLC0xLjU0NjQgLTEuMjU0LC0yLjggLTIuOCwtMi44IC0xLjU0NywwIC0yLjgsMS4yNTM2IC0yLjgsMi44IDAsMS41NDY0IDEuMjUzLDIuOCAyLjgsMi44IHoiCiAgICAgZmlsbD0iIzkwOTA5MCIKICAgICBpZD0icGF0aDUwNjciCiAgICAgc3R5bGU9ImZpbGw6IzkwOTA5MDtmaWxsLW9wYWNpdHk6MC4xMzMzMzMiIC8+CiAgPHBhdGgKICAgICBkPSJtIDExMy4wMzAxOCwyMi4zOTM2NDkgYyAxLjU0NjQsMCAyLjgsLTEuMjUzNiAyLjgsLTIuOCAwLC0xLjU0NjQgLTEuMjUzNiwtMi44IC0yLjgsLTIuOCAtMS41NDY0LDAgLTIuOCwxLjI1MzYgLTIuOCwyLjggMCwxLjU0NjQgMS4yNTM2LDIuOCAyLjgsMi44IHoiCiAgICAgZmlsbD0iIzkwOTA5MCIKICAgICBpZD0icGF0aDUwNjkiCiAgICAgc3R5bGU9ImZpbGw6IzkwOTA5MDtmaWxsLW9wYWNpdHk6MC4xMzMzMzMiIC8+CiAgPHBhdGgKICAgICBkPSJtIDczLjI5MzU2NSwxMTIuNDYyMTYgYyAyLjg3MTg5LDAgNS4yMDAwMywtMi4zMjgxIDUuMjAwMDMsLTUuMiAwLC0yLjg3MTkgLTIuMzI4MTQsLTUuMiAtNS4yMDAwMywtNS4yIC0yLjg3MTg4LDAgLTUuMTk5OTk1LDIuMzI4MSAtNS4xOTk5OTUsNS4yIDAsMi44NzE5IDIuMzI4MTE1LDUuMiA1LjE5OTk5NSw1LjIgeiIKICAgICBmaWxsPSIjOTA5MDkwIgogICAgIGlkPSJwYXRoNTA3MSIKICAgICBzdHlsZT0iZmlsbDojOTA5MDkwO2ZpbGwtb3BhY2l0eTowLjEzMzMzMzM0IiAvPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM1MTE3Ij4KICAgIDxmaWx0ZXIKICAgICAgIGlkPSJmaWx0ZXIwX2QiCiAgICAgICB4PSIxNi4wNzYyIgogICAgICAgeT0iMTIuOTU3NSIKICAgICAgIHdpZHRoPSIxMTQuOCIKICAgICAgIGhlaWdodD0iMTM0LjYwMDAxIgogICAgICAgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4KICAgICAgPGZlRmxvb2QKICAgICAgICAgZmxvb2Qtb3BhY2l0eT0iMCIKICAgICAgICAgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiCiAgICAgICAgIGlkPSJmZUZsb29kNTA5NyIgLz4KICAgICAgPGZlQ29sb3JNYXRyaXgKICAgICAgICAgaW49IlNvdXJjZUFscGhhIgogICAgICAgICB0eXBlPSJtYXRyaXgiCiAgICAgICAgIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMTI3IDAiCiAgICAgICAgIGlkPSJmZUNvbG9yTWF0cml4NTA5OSIgLz4KICAgICAgPGZlT2Zmc2V0CiAgICAgICAgIGR5PSIxMSIKICAgICAgICAgaWQ9ImZlT2Zmc2V0NTEwMSIgLz4KICAgICAgPGZlR2F1c3NpYW5CbHVyCiAgICAgICAgIHN0ZERldmlhdGlvbj0iMTEiCiAgICAgICAgIGlkPSJmZUdhdXNzaWFuQmx1cjUxMDMiIC8+CiAgICAgIDxmZUNvbG9yTWF0cml4CiAgICAgICAgIHR5cGU9Im1hdHJpeCIKICAgICAgICAgdmFsdWVzPSIwIDAgMCAwIDAuMzk3NzA4IDAgMCAwIDAgMC40Nzc0OSAwIDAgMCAwIDAuNTc1IDAgMCAwIDAuMjcgMCIKICAgICAgICAgaWQ9ImZlQ29sb3JNYXRyaXg1MTA1IiAvPgogICAgICA8ZmVCbGVuZAogICAgICAgICBtb2RlPSJub3JtYWwiCiAgICAgICAgIGluMj0iQmFja2dyb3VuZEltYWdlRml4IgogICAgICAgICByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvdyIKICAgICAgICAgaWQ9ImZlQmxlbmQ1MTA3IiAvPgogICAgICA8ZmVCbGVuZAogICAgICAgICBtb2RlPSJub3JtYWwiCiAgICAgICAgIGluPSJTb3VyY2VHcmFwaGljIgogICAgICAgICBpbjI9ImVmZmVjdDFfZHJvcFNoYWRvdyIKICAgICAgICAgcmVzdWx0PSJzaGFwZSIKICAgICAgICAgaWQ9ImZlQmxlbmQ1MTA5IiAvPgogICAgPC9maWx0ZXI+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJwYWludDBfbGluZWFyIgogICAgICAgeDE9IjczLjQ1MzEwMiIKICAgICAgIHkxPSIyMS44NjE5IgogICAgICAgeDI9IjczLjQ1MzEwMiIKICAgICAgIHkyPSIxMTUuNTM0IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjRkRGRUZGIgogICAgICAgICBpZD0ic3RvcDUxMTIiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIG9mZnNldD0iMC45OTY0IgogICAgICAgICBzdG9wLWNvbG9yPSIjRUNGMEY1IgogICAgICAgICBpZD0ic3RvcDUxMTQiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGZpbHRlcgogICAgICAgaWQ9ImZpbHRlcjBfZC03IgogICAgICAgeD0iMC4zOTExMTMwMSIKICAgICAgIHk9IjM1LjM5NDc5OCIKICAgICAgIHdpZHRoPSIxNDUuNTk1IgogICAgICAgaGVpZ2h0PSIxMDIuOCIKICAgICAgIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CiAgICAgIDxmZUZsb29kCiAgICAgICAgIGZsb29kLW9wYWNpdHk9IjAiCiAgICAgICAgIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4IgogICAgICAgICBpZD0iZmVGbG9vZDEyMjciIC8+CiAgICAgIDxmZUNvbG9yTWF0cml4CiAgICAgICAgIGluPSJTb3VyY2VBbHBoYSIKICAgICAgICAgdHlwZT0ibWF0cml4IgogICAgICAgICB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIgogICAgICAgICBpZD0iZmVDb2xvck1hdHJpeDEyMjkiIC8+CiAgICAgIDxmZU9mZnNldAogICAgICAgICBkeT0iMTEiCiAgICAgICAgIGlkPSJmZU9mZnNldDEyMzEiIC8+CiAgICAgIDxmZUdhdXNzaWFuQmx1cgogICAgICAgICBzdGREZXZpYXRpb249IjExIgogICAgICAgICBpZD0iZmVHYXVzc2lhbkJsdXIxMjMzIiAvPgogICAgICA8ZmVDb2xvck1hdHJpeAogICAgICAgICB0eXBlPSJtYXRyaXgiCiAgICAgICAgIHZhbHVlcz0iMCAwIDAgMCAwLjM5NzcwOCAwIDAgMCAwIDAuNDc3NDkgMCAwIDAgMCAwLjU3NSAwIDAgMCAwLjI3IDAiCiAgICAgICAgIGlkPSJmZUNvbG9yTWF0cml4MTIzNSIgLz4KICAgICAgPGZlQmxlbmQKICAgICAgICAgbW9kZT0ibm9ybWFsIgogICAgICAgICBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIKICAgICAgICAgcmVzdWx0PSJlZmZlY3QxX2Ryb3BTaGFkb3ciCiAgICAgICAgIGlkPSJmZUJsZW5kMTIzNyIgLz4KICAgICAgPGZlQmxlbmQKICAgICAgICAgbW9kZT0ibm9ybWFsIgogICAgICAgICBpbj0iU291cmNlR3JhcGhpYyIKICAgICAgICAgaW4yPSJlZmZlY3QxX2Ryb3BTaGFkb3ciCiAgICAgICAgIHJlc3VsdD0ic2hhcGUiCiAgICAgICAgIGlkPSJmZUJsZW5kMTIzOSIgLz4KICAgIDwvZmlsdGVyPgogIDwvZGVmcz4KICA8cGF0aAogICAgIGQ9Im0gMjEuMjIwODMyLDkyLjI2NDAxNiBoIC00LjkgYyAtMC4zLDAgLTAuNiwwLjEgLTAuNywwLjQgbCAtMi40LDQuMyBjIC0wLjEsMC4zIC0wLjEsMC42IDAsMC45IGwgMi40LDQuMzAwMDA0IGMgMC4xLDAuMyAwLjQsMC40IDAuNywwLjQgaCA0LjkgYyAwLjMsMCAwLjYsLTAuMSAwLjcsLTAuNCBsIDIuNCwtNC4zMDAwMDQgYyAwLjEsLTAuMyAwLjEsLTAuNiAwLC0wLjkgbCAtMi40LC00LjMgYyAtMC4xLC0wLjMgLTAuNCwtMC40IC0wLjcsLTAuNCB6IgogICAgIGlkPSJwYXRoNCIKICAgICBzdHlsZT0iZmlsbDojOTA5MDkwO2ZpbGwtb3BhY2l0eTowLjEzMzMzMyIgLz4KICA8cGF0aAogICAgIGNsYXNzPSJmaWxlIgogICAgIGQ9Im0gMjcxLjA5MjU5LDExLjY1MjMxMiAtNy4zNzAxOSwzLjU4MzkxOCBjIC0wLjc2MTk0LDAuNDMxMTE2IC0xLjcwNDkxLDAuNTgzNTAxIC0yLjU3MjQ2LDAuNjA1MjIyIC0wLjg2NzU0LDAuMDIxNzIgLTEuNzg3OTEsLTAuMTYxMjQ0IC0yLjYzMjg1LC0wLjQ3NDg2NyAtMS42MTQ0NCwtMC43NTc4OTggLTMuMDU1NCwtMi4xMTI0NDIgLTMuOTM4MTUsLTMuODQxNTA5IDAsMCAtMS41MDg5NiwtMy4zMTAwMzcyIC0zLjA3MDc1LC02LjgyNDc3NDUgbCAtMC4yMzM4OCwtMC40ODM0MzY4IC0xNS45NjI1Myw3LjUwNzQ2OTMgYyAtMi41NDIyMywxLjE0NTI3MSAtMy42MTMyOSw0LjE4NTE0NCAtMi41MTkyOCw2LjczMjk5OCBsIDExLjUyODU1LDI1LjY0NDA3NCBjIDEuMDk0MDIsMi41NDc4NTUgNC4wODksMy41ODAyMDUgNi42MzEyNSwyLjQzNDkzMSBsIDI1LjU5NTg0LC0xMi4wNDk0MDIgYyAyLjU0MjI0LC0xLjE0NTI3NSAzLjYxMzMxLC00LjE4NTE0MyAyLjUxOTMsLTYuNzMyOTk4IEwgMjcxLjcwMzY1LDExLjQ4MjQ5IFoiCiAgICAgc3R5bGU9ImNsaXAtcnVsZTpldmVub2RkO2ZpbGw6IzkwOTA5MDtmaWxsLW9wYWNpdHk6MC4xMzMzMzMzNDtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2Utd2lkdGg6MS40OTQ3MSIKICAgICBpZD0icGF0aDYiIC8+CiAgPHBhdGgKICAgICBjbGFzcz0iZmlsZSIKICAgICBkPSJtIDI1OS40MTQ2OCwxMi45MjMzNjggYyAtMS4xMDE0NCwtMC40NjE3MTMgLTEuOTc2NTksLTEuMzE1MzggLTIuNjI1NDMsLTIuNTYxMDA1IGwgLTAuMDUyOCwtMC4yMDQ3IC0wLjI4NjY4LC0wLjY4ODE0NzIgLTEuMDQxMTksLTIuMzQzMTU4IC0wLjU3MzQxLC0xLjM3NjI3NiAxMy4wMDU5OSw0LjcyMTc3MDIgLTUuMTU5OTMsMi40MjEyMDIgYyAtMC40ODI3NywwLjI0Mzg2NiAtMS4wMTg0MSwwLjI4MzAyOCAtMS41NTQwMSwwLjMyMjE5NiAtMC41MzU2NCwwLjAzOTE2IC0xLjEyNDA2LC0wLjEyNjM2NCAtMS43MTI0OSwtMC4yOTE4OTkgeiIKICAgICBzdHlsZT0iY2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojOTA5MDkwO2ZpbGwtb3BhY2l0eTowLjEzMzMzMzM0O2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZS13aWR0aDoxLjQ5NDcxIgogICAgIGlkPSJwYXRoOCIgLz4KICA8cGF0aAogICAgIGQ9Im0gMjMuOTQ0NDAzLDE3LjYzNTU2MyBjIC0yLjA3NzQxOSwxLjE5OTQxNSAtMi40NzQ2NjksMy44MzI3OTggLTEuMzAzNTc5LDUuODYxMjAyIGwgMTEuMTU3OTY5LDE5LjMyNjEzMyBjIDEuMjY4NjksMi4xOTc0MzUgMy41MzE5NiwyLjkxOTEzNSA1LjY2MjY0LDEuNjg4OTc1IGwgMjQuMzQzMTIsLTE0LjA1NDQ5MSBjIDEuMzg0OTYsLTAuNzk5NjEgMS45MjQxMSwtMy4wNjQxNjkgMC42MjI4OCwtNS4zMTc5MzkgTCA1NC45Mjg1NDMsOC42ODY4NjI3IGMgLTEuMDQwOTksLTEuODAzMDExIC0zLjA3NjU0LC0yLjEzMDMxMyAtNC40NjE0NiwtMS4zMzA3MzQgbCAtMTIuNTE3ODIsNy4yMjcxNjkzIC00Ljk0OTQ0LC0yLjE3NTg2OSB6IgogICAgIGlkPSJwYXRoMTYiCiAgICAgc3R5bGU9ImZpbGw6IzkwOTA5MDtmaWxsLW9wYWNpdHk6MC4xMzMzMzMzNDtzdHJva2Utd2lkdGg6MC42MzI1OTQiIC8+CiAgPGcKICAgICBpZD0iZzIwIgogICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI0My4yODEwNCwxMzEuMDczNDYpIgogICAgIHN0eWxlPSJmaWxsOiM5MDkwOTA7ZmlsbC1vcGFjaXR5OjAuMTMzMzMzMzQiPgogICAgPHBhdGgKICAgICAgIGQ9Im0gNDIuNCwyMy4yIDguNiw5LjkgYyAwLjMsMC4zIDAuMSwwLjggLTAuMywwLjkgTCAzOCwzNi4yIGMgLTAuNCwwLjEgLTAuNywtMC4zIC0wLjYsLTAuNyBsIDQuMSwtMTIuMSBjIDAuMSwtMC40IDAuNywtMC41IDAuOSwtMC4yIHoiCiAgICAgICBpZD0icGF0aDE4IgogICAgICAgc3R5bGU9ImZpbGw6IzkwOTA5MDtmaWxsLW9wYWNpdHk6MC4xMzMzMzMzNCIgLz4KICA8L2c+CiAgPHBhdGgKICAgICBkPSJtIDI4Ni4wODgzMiw4NS43MTYxIC0yLDEgYyAtMC4zLDAuMSAtMC42LDAgLTAuOCwtMC4zIGwgLTEsLTIgYyAtMC4xLC0wLjMgMCwtMC42IDAuMywtMC44IGwgMiwtMSBjIDAuMywtMC4xIDAuNiwwIDAuOCwwLjMgbCAxLDIgYyAwLjEsMC4zIDAsMC43IC0wLjMsMC44IHoiCiAgICAgaWQ9InBhdGgxMCIKICAgICBzdHlsZT0iZmlsbDojOTA5MDkwO2ZpbGwtb3BhY2l0eTowLjEzMzMzMyIgLz4KICA8cGF0aAogICAgIGQ9Im0gMTExLjM1ODEsNjcuODkzNDgzIGggNzIuMDk5NyBjIDIuOSwwIDUuMSwyLjIgNS4xLDUuMSB2IDQ1LjY5OTk5NyBjIDAsMi45IC0yLjIsNS4xIC01LjEsNS4xIGggLTcyLjA5OTcgYyAtMi45LDAgLTUuMSwtMi4yIC01LjEsLTUuMSBWIDcyLjk5MzQ4MyBjIDAsLTIuOSAyLjQsLTUuMSA1LjEsLTUuMSB6IgogICAgIGZpbGw9IiM5MDkwOTAiCiAgICAgaWQ9InBhdGgxMTk5IiAvPgogIDxnCiAgICAgZmlsdGVyPSJ1cmwoI2ZpbHRlcjBfZCkiCiAgICAgaWQ9ImcxMjAzIgogICAgIHN0eWxlPSJmaWx0ZXI6dXJsKCNmaWx0ZXIwX2QtNykiCiAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzYuNDY2OCwzMS4xOTg2ODMpIj4KICAgIDxwYXRoCiAgICAgICBkPSJNIDExNy42OTEsNDYuNDk0OCBIIDg3LjI5MTEgYyAtMywwIC01LjgsMSAtOC4xLDIuOSBsIC04LDYuNSBjIC0yLjIsMS44IC01LjEsMi45IC04LjEsMi45IGggLTM0LjQgYyAtMy41LDAgLTYuMywyLjkgLTYuMyw2LjMgMCwwLjMgMCwwLjYgMC4xLDAuOSBsIDYuMywzMy43IGMgMC41LDMuMjAwMiAzLjIsNS41MDAyIDYuMyw1LjUwMDIgaCA3My41OTk5IGMgMy4yLDAgNS44LC0yLjIgNi4zLC01LjQwMDIgbCA4LjksLTQ2LjEgYyAwLjYsLTMuNSAtMS43LC02LjYgLTUuMiwtNy4zIC0wLjMsMC4xIC0wLjcsMC4xIC0xLDAuMSB6IgogICAgICAgZmlsbD0iI2ZmZmZmZiIKICAgICAgIGlkPSJwYXRoMTIwMSIgLz4KICA8L2c+CiAgPHBhdGgKICAgICBkPSJtIDEzNS4zNTgxLDExMi41OTM1OCBjIDEuOCwwIDMuMywtMS41IDMuMywtMy4zIDAsLTEuOCAtMS41LC0zLjMgLTMuMywtMy4zIC0xLjgsMCAtMy4zLDEuNSAtMy4zLDMuMyAwLDEuOCAxLjUsMy4zIDMuMywzLjMgeiIKICAgICBmaWxsPSIjOTA5MDkwIgogICAgIGlkPSJwYXRoMTIwNSIgLz4KICA8cGF0aAogICAgIGQ9Im0gMTYxLjA1ODEsMTEyLjQ5MzQ4IGMgMS44LDAgMy4zLC0xLjUgMy4zLC0zLjMgMCwtMS44IC0xLjUsLTMuMyAtMy4zLC0zLjMgLTEuOCwwIC0zLjMsMS41IC0zLjMsMy4zIDAsMS45IDEuNSwzLjMgMy4zLDMuMyB6IgogICAgIGZpbGw9IiM5MDkwOTAiCiAgICAgaWQ9InBhdGgxMjA3IiAvPgogIDxwYXRoCiAgICAgZD0ibSAxNDQuMTU4LDg4Ljg5MzQ4MSBjIC0zLjYsLTcgLTQuNCwtMTUuMzk5OTk4IC0yLC0yMi45OTk5OTggMi4zLC03LjYgNy44LC0xNC4xIDE0LjYsLTE3LjggMi4xLC0xLjEgNC41LC0yIDYuOSwtMi4xIDIuNCwtMC4xIDUsMC43IDYuNiwyLjcgMS42LDEuOCAxLjksNC44IDAuNiw2LjggLTEuNCwxLjkgLTQuMiwyLjcgLTYuNSwyLjEgLTMuNywtMC43IC02LjcsLTMuNiAtNy42LC03LjEgLTAuOSwtMy41IDAuMywtNy42IDMuMSwtOS45IDEuOCwtMS42IDQuMywtMi41IDYuNiwtMy4yIDExLjE5OTgsLTMuMyAyMy4zOTk4LC0zLjcgMzQuNzk5OCwtMS4yIgogICAgIHN0cm9rZT0iIzU3NTk1YSIKICAgICBzdHJva2Utd2lkdGg9IjIiCiAgICAgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIgogICAgIHN0cm9rZS1kYXNoYXJyYXk9IjQsIDQiCiAgICAgaWQ9InBhdGgxMjA5IiAvPgogIDxwYXRoCiAgICAgZD0ibSAxNTEuMzU4LDExNi4wOTM0OCBoIC02LjIgdiAxLjUgaCA2LjIgeiIKICAgICBmaWxsPSIjOTA5MDkwIgogICAgIGlkPSJwYXRoMTIxMSIgLz4KICA8cGF0aAogICAgIGQ9Im0gMjA3LjI1NzgsMzMuNTkzNDMzIGMgLTAuMSwxLjUgLTAuMiwyLjkgLTEuMywzLjIgLTEuMSwwLjMgLTEuNiwtMC43IC0yLjMsLTIuMSAtMC43LC0xLjMgLTAuMywtMi42OTk5OTYgMC45LC0yLjk5OTk5NiAxLjEsLTAuMyAyLjksMC4xIDIuNywxLjg5OTk5NiB6IgogICAgIGZpbGw9IiM5MDkwOTAiCiAgICAgaWQ9InBhdGgxMjEzIiAvPgogIDxwYXRoCiAgICAgZD0ibSAyMDYuMDU3OCw0MC43OTM0NTMgYyAwLjMsLTEuOCAwLjYsLTIuOCAtMC40LC0zLjMgLTEuMSwtMC41IC0xLjgsMC40IC0zLDEuNiAtMSwxLjEgLTAuNCwyLjcwMDAzIDAuNiwzLjIwMDAzIDEuMiwwLjYgMi41LDAgMi44LC0xLjUwMDAzIHoiCiAgICAgZmlsbD0iIzkwOTA5MCIKICAgICBpZD0icGF0aDEyMTUiIC8+CiAgPHBhdGgKICAgICBkPSJtIDIwNy40NTc4LDM3LjQ5MzUzMyBjIC0wLjEsMC43IC0wLjYsMS4yIC0xLjMsMS4zIC0wLjMsMCAtMC42LDAgLTEsMCAtMS40LC0wLjIgLTIuNSwtMS4xIC0yLjQsLTIgMC4xLC0wLjkgMS40LC0xLjQgMywtMS4yIDAuMywwIDAuNiwwLjEgMC44LDAuMiAwLjYsMC4yIDEsMC45IDAuOSwxLjcgMCwwIDAsLTAuMSAwLDAgeiIKICAgICBmaWxsPSIjNTc1OTVhIgogICAgIGlkPSJwYXRoMTIxNyIgLz4KICA8cGF0aAogICAgIGQ9Im0gOTUuODU3OSw2NS41OTM0ODMgYyAwLC0xLjcgMCwtMy40IDEuMiwtMy45IDEuMywtMC41IDIsMC43IDMsMi40IDAuOSwxLjUgMC41LDMuMSAtMC44LDMuNiAtMS4xLDAuNSAtMy40LDAuMiAtMy40LC0yLjEgeiIKICAgICBmaWxsPSIjOTA5MDkwIgogICAgIGlkPSJwYXRoMTIxOSIgLz4KICA8cGF0aAogICAgIGQ9Im0gOTYuNTU3OSw1Ny4xOTM1ODMgYyAtMC4yLDIuMSAtMC41LDMuMyAwLjgsMy44IDEuMywwLjUgMiwtMC42IDMuMywtMi4yIDEsLTEuNCAwLjMsLTMuMiAtMSwtMy43IC0xLjMsLTAuNSAtMi45LDAuNCAtMy4xLDIuMSB6IgogICAgIGZpbGw9IiM5MDkwOTAiCiAgICAgaWQ9InBhdGgxMjIxIiAvPgogIDxwYXRoCiAgICAgZD0ibSA5NS40NTgsNjEuMTkzNTgzIGMgMCwtMC44IDAuNiwtMS40IDEuMywtMS41IDAuMywtMC4xIDAuNywtMC4xIDEuMSwwIDEuNiwwLjEgMywxIDIuOSwyIC0wLjEsMSAtMS40LDEuNyAtMy4xLDEuNSAtMC4zLDAgLTAuNiwtMC4xIC0wLjksLTAuMiAtMC44LC0wLjEgLTEuMywtMC45IC0xLjMsLTEuOCB6IgogICAgIGZpbGw9IiM1NzU5NWEiCiAgICAgaWQ9InBhdGgxMjIzIiAvPgogIDxwYXRoCiAgICAgZD0ibSAxMDIuMjU4MSw2MS40OTM1ODMgYyAxMC41LDAgMjkuOSw2LjEgMzAuMiwyOC40OTk5OTgiCiAgICAgc3Ryb2tlPSIjNTc1OTVhIgogICAgIHN0cm9rZS13aWR0aD0iMiIKICAgICBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiCiAgICAgc3Ryb2tlLWRhc2hhcnJheT0iNCwgNCIKICAgICBpZD0icGF0aDEyMjUiIC8+CiAgPHBhdGgKICAgICBkPSJtIDE3NS4wNzg0MSw0LjE1ODgzOCBoIC0yLjk4ODA4IGMgLTAuMTgyOTQsMCAtMC4zNjU4OCwwLjA2NTAwMyAtMC40MjY4NywwLjI2MDAxMDIgbCAtMS40NjM1NCwyLjc5NTExMDEgYyAtMC4wNjEsMC4xOTUwMDcgLTAuMDYxLDAuMzkwMDE1IDAsMC41ODUwMjMgbCAxLjQ2MzU0LDIuNzk1MTEwNyBjIDAuMDYxLDAuMTk1MDA4IDAuMjQzOTMsMC4yNjAwMTEgMC40MjY4NywwLjI2MDAxMSBoIDIuOTg4MDggYyAwLjE4Mjk0LDAgMC4zNjU4OSwtMC4wNjUgMC40MjY4NywtMC4yNjAwMTEgbCAxLjQ2MzU1LC0yLjc5NTExMDcgYyAwLjA2MSwtMC4xOTUwMDggMC4wNjEsLTAuMzkwMDE2IDAsLTAuNTg1MDIzIGwgLTEuNDYzNTUsLTIuNzk1MTEwMSBjIC0wLjA2MSwtMC4xOTUwMDc2IC0wLjI0MzkzLC0wLjI2MDAxMDIgLTAuNDI2ODcsLTAuMjYwMDEwMiB6IgogICAgIGlkPSJwYXRoNC01IgogICAgIHN0eWxlPSJmaWxsOiM5MDkwOTA7ZmlsbC1vcGFjaXR5OjAuMTMzMzMzO3N0cm9rZS13aWR0aDowLjYyOTU5OCIgLz4KPC9zdmc+Cg==",
@@ -19,6 +20,7 @@ const ICONS = {
export default async function(render) {
const $page = createElement(`
+
@@ -37,24 +39,37 @@ export default async function(render) {
return prev[0] === curr[0] && prev[1] && curr[1]
}),
);
- effect(rxjs.of(path).pipe(
- ls(),
- removeLoader,
+ const $list = qs($page, `[data-target="list"]`);
+
+ effect(ls(path).pipe(
rxjs.mergeMap(({ files, ...rest }) => getState$().pipe(rxjs.mergeMap((state) => {
- if (state.show_hidden === false) files = files.filter(({ name }) => name[0] !== ".");
- // files = files.sort()
+ const $header = qs($page, `[data-target="header"]`);
+ $header.innerHTML = "";
+ $list.innerHTML = "";
+ if (!!state.search) {
+ const removeLoader = createLoader($header);
+ return search(state.search_q).pipe(rxjs.map((files) => ({
+ ...rest, files, ...state,
+ })), removeLoader);
+ }
return rxjs.of({ ...rest, files, ...state });
}))),
- rxjs.mergeMap(({ files, search_mode, ...rest }) => {
+ rxjs.mergeMap(({ show_hidden, files, ...rest }) => {
+ if (show_hidden === false) files = files.filter(({ name }) => name[0] !== ".");
+ console.log(rest);
+ files = sort(files, rest.sort);
+ return rxjs.of({ ...rest, files })
+ }),
+ removeLoader,
+ rxjs.mergeMap(({ files, search, ...rest }) => {
if (files.length === 0) {
- renderEmpty(render, search_mode ? ICONS.EMPTY_SEARCH : ICONS.EMPTY_FILES);
+ renderEmpty(createRender(qs($page, `[data-target="header"]`)), !!search ? ICONS.EMPTY_SEARCH : ICONS.EMPTY_FILES);
return rxjs.EMPTY;
}
return rxjs.of({...rest, files });
}),
rxjs.mergeMap((obj) => refreshOnResize$.pipe(rxjs.mapTo(obj))),
rxjs.mergeMap(({ files, path, view }) => { // STEP1: setup the list of files
- const $list = qs($page, `[data-target="list"]`);
$list.closest(".scroll-y").scrollTop = 0;
let FILE_HEIGHT, COLUMN_PER_ROW;
switch(view) {
@@ -82,8 +97,7 @@ export default async function(render) {
for (let i = 0; i < size; i++) {
const file = files[i];
$fs.appendChild(createThing({
- name: file.name,
- type: file.type,
+ ...file,
...createLink(file, path),
view,
n: i,
@@ -193,8 +207,7 @@ export default async function(render) {
type: "hidden",
}));
else $fs.appendChild(createThing({
- name: file.name,
- type: file.type,
+ ...file,
...createLink(file, path),
view,
n: i,
diff --git a/public/assets/pages/filespage/ctrl_newitem.js b/public/assets/pages/filespage/ctrl_newitem.js
index ac3fb50f..6ca1a7ec 100644
--- a/public/assets/pages/filespage/ctrl_newitem.js
+++ b/public/assets/pages/filespage/ctrl_newitem.js
@@ -4,7 +4,7 @@ import { qs } from "../../lib/dom.js";
import { animate } from "../../lib/animate.js";
import { loadCSS } from "../../helpers/loader.js";
-import { getAction$, setAction } from "./model_action.js";
+import { getAction$, setAction } from "./state_event.js";
export default async function(render) {
const $node = createElement(`
diff --git a/public/assets/pages/filespage/ctrl_submenu.js b/public/assets/pages/filespage/ctrl_submenu.js
index 4bba408a..c5d916b5 100644
--- a/public/assets/pages/filespage/ctrl_submenu.js
+++ b/public/assets/pages/filespage/ctrl_submenu.js
@@ -3,8 +3,10 @@ import rxjs, { effect, applyMutation, onClick, preventDefault } from "../../lib/
import { animate, slideYIn } from "../../lib/animate.js";
import { loadCSS } from "../../helpers/loader.js";
import { qs, qsa } from "../../lib/dom.js";
-import { getSelection$, clearSelection } from "./model_files.js";
-import { getAction$, setAction } from "./model_action.js";
+
+import "../../components/dropdown.js";
+import "../../components/icon.js";
+import { createModal } from "../../components/modal.js";
import componentShare from "./modal_share.js";
import componentEmbed from "./modal_embed.js";
@@ -12,11 +14,9 @@ import componentTag from "./modal_tag.js";
import componentRename from "./modal_rename.js";
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";
+import { getSelection$, clearSelection } from "./state_selection.js";
+import { getAction$, setAction } from "./state_event.js";
+import { setState } from "./state_filesystem.js";
const modalOpt = {
withButtonsRight: "OK",
@@ -139,13 +139,14 @@ function componentRight(render) {
effect(getSelection$().pipe(
rxjs.filter((selections) => selections.length === 0),
rxjs.map(() => render(createFragment(`
-
+
@@ -257,8 +258,15 @@ function componentRight(render) {
time: 100,
});
$input.classList.add("hidden");
+ // setState("search", ""); TODO
}
+ return $input;
}),
+ rxjs.mergeMap(($input) => rxjs.merge(
+ rxjs.fromEvent($input, "input").pipe(rxjs.debounceTime(500)),
+ rxjs.fromEvent($input, "change"),
+ ).pipe(rxjs.map(() => $input.value), rxjs.distinctUntilChanged())),
+ rxjs.tap((val) => setState("search", val)),
),
)),
));
diff --git a/public/assets/pages/filespage/helper.js b/public/assets/pages/filespage/helper.js
new file mode 100644
index 00000000..cc2af8b5
--- /dev/null
+++ b/public/assets/pages/filespage/helper.js
@@ -0,0 +1,85 @@
+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);
+ }
+};
+
+function _moveLoadingDownward(fileA, fileB) {
+ if (fileA.icon === "loading" && fileB.icon !== "loading") {
+ return +1;
+ } else if (fileA.icon !== "loading" && fileB.icon === "loading") {
+ 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;
+ }
+ 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;
+ 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;
+ });
+}
diff --git a/public/assets/pages/filespage/model_files.js b/public/assets/pages/filespage/model_files.js
index 75483839..a671aa66 100644
--- a/public/assets/pages/filespage/model_files.js
+++ b/public/assets/pages/filespage/model_files.js
@@ -1,42 +1,6 @@
-import { onDestroy } from "../../lib/skeleton/index.js";
import rxjs from "../../lib/rx.js";
import ajax from "../../lib/ajax.js";
-const selection$ = new rxjs.BehaviorSubject([]);
-
-onDestroy(() => selection$.next([]));
-
-export function addSelection({ name, type, shift, n }) {
- selection$.next(
- selection$.value
- .concat({ name, type, shift, n })
- .sort((prev, curr) => prev.n - curr.n)
- );
-}
-
-export function clearSelection() {
- selection$.next([]);
-}
-
-export function getSelection$() {
- return selection$.asObservable();
-}
-
-export function isSelected(n) {
- let isChecked = false;
- for (let i=0;i
= lowerBound;
-}
-
// export function ls() {
// return rxjs.from(new Error("missing cache")).pipe(
// rxjs.catchError(() => rxjs.of({ files: null })),
@@ -57,18 +21,22 @@ function isBetween(n, lowerBound, higherBound) {
// )
// }
-export function ls() {
- return rxjs.pipe(
- rxjs.mergeMap((path) => ajax({
- url: `/api/files/ls?path=${path}`,
- responseType: "json"
- }).pipe(rxjs.map(({ responseJSON }) => ({
- files: responseJSON.results.sort(sortByDefault),
- path,
- })))),
+export function search(term) {
+ return rxjs.of([]).pipe(
+ rxjs.delay(1500),
);
}
+export function ls(path) {
+ return ajax({
+ url: `/api/files/ls?path=${path}`,
+ responseType: "json"
+ }).pipe(rxjs.map(({ responseJSON }) => ({
+ files: responseJSON.results.sort(sortByDefault),
+ path,
+ })));
+}
+
const sortByDefault = (fileA, fileB) => {
if (fileA.type !== fileB.type) {
if (fileA.type === "file") return +1;
diff --git a/public/assets/pages/filespage/model_action.js b/public/assets/pages/filespage/state_event.js
similarity index 100%
rename from public/assets/pages/filespage/model_action.js
rename to public/assets/pages/filespage/state_event.js
diff --git a/public/assets/pages/filespage/ctrl_filesystem_state.js b/public/assets/pages/filespage/state_filesystem.js
similarity index 93%
rename from public/assets/pages/filespage/ctrl_filesystem_state.js
rename to public/assets/pages/filespage/state_filesystem.js
index 5561185a..d40c93e3 100644
--- a/public/assets/pages/filespage/ctrl_filesystem_state.js
+++ b/public/assets/pages/filespage/state_filesystem.js
@@ -2,10 +2,10 @@ import rxjs, { effect, preventDefault } from "../../lib/rx.js";
const state$ = new rxjs.BehaviorSubject({
view: "grid",
- sort: null,
+ sort: "type",
show_hidden: false,
order: null,
- search_mode: false,
+ search: "",
});
export const getState$ = () => state$.asObservable();
diff --git a/public/assets/pages/filespage/state_selection.js b/public/assets/pages/filespage/state_selection.js
new file mode 100644
index 00000000..5a0c2830
--- /dev/null
+++ b/public/assets/pages/filespage/state_selection.js
@@ -0,0 +1,38 @@
+import rxjs from "../../lib/rx.js";
+import ajax from "../../lib/ajax.js";
+import { onDestroy } from "../../lib/skeleton/index.js";
+
+const selection$ = new rxjs.BehaviorSubject([]);
+
+onDestroy(() => selection$.next([]));
+
+export function addSelection({ name, type, shift, n }) {
+ selection$.next(
+ selection$.value
+ .concat({ name, type, shift, n })
+ .sort((prev, curr) => prev.n - curr.n)
+ );
+}
+
+export function clearSelection() {
+ selection$.next([]);
+}
+
+export function getSelection$() {
+ return selection$.asObservable();
+}
+
+export function isSelected(n) {
+ let isChecked = false;
+ for (let i=0;i= lowerBound;
+}
diff --git a/public/assets/pages/filespage/thing.js b/public/assets/pages/filespage/thing.js
index dbb03e14..2d6b6e8d 100644
--- a/public/assets/pages/filespage/thing.js
+++ b/public/assets/pages/filespage/thing.js
@@ -1,5 +1,8 @@
import { createElement } from "../../lib/skeleton/index.js";
-import { addSelection, isSelected } from "./model_files.js";
+import { qs } from "../../lib/dom.js";
+import assert from "../../lib/assert.js";
+
+import { addSelection, isSelected } from "./state_selection.js";
const IMAGE = {
FILE: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiPgogIDxwYXRoIHN0eWxlPSJjb2xvcjojMDAwMDAwO3RleHQtaW5kZW50OjA7dGV4dC10cmFuc2Zvcm06bm9uZTtmaWxsOiM4YzhjOGM7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlLXdpZHRoOjAuOTg0ODEwNDEiIGQ9Im0gMiwxMy4wODI0MTIgMC4wMTk0NjIsMS40OTIzNDcgYyA1ZS02LDAuMjIyMTQ1IDAuMjA1NTkwMiwwLjQyNDI2MiAwLjQzMTE1MDIsMC40MjQyNzIgTCAxMy41ODk2MTIsMTUgQyAxMy44MTUxNzMsMTQuOTk5OTk1IDEzLjk5OTk5LDE0Ljc5Nzg3NCAxNCwxNC41NzU3MjkgdiAtMS40OTMzMTcgYyAtNC4xNzE4NjkyLDAuNjYyMDIzIC03LjY1MTY5MjgsMC4zOTg2OTYgLTEyLDAgeiIgLz4KICA8cGF0aCBzdHlsZT0iY29sb3I6IzAwMDAwMDt0ZXh0LWluZGVudDowO3RleHQtdHJhbnNmb3JtOm5vbmU7ZGlzcGxheTppbmxpbmU7ZmlsbDojYWFhYWFhO3N0cm9rZS13aWR0aDowLjk4NDA4MTI3IiBkPSJNIDIuMzUwMSwxLjAwMTMzMTIgQyAyLjE1MjU5LDEuMDM4MzI0NyAxLjk5NjU5LDEuMjI3MjcyMyAyLjAwMDA5LDEuNDI0OTM1NiBWIDE0LjEzMzQ1NyBjIDVlLTYsMC4yMjE4MTYgMC4yMDUyMywwLjQyMzYzNCAwLjQzMDc5LDAuNDIzNjQ0IGwgMTEuMTM5LC0xLjAxZS00IGMgMC4yMjU1NiwtNmUtNiAwLjQzMDExLC0wLjIwMDc1OCAwLjQzMDEyLC0wLjQyMjU3NCBsIDYuN2UtNCwtOS44MjI2NDI2IGMgLTIuNDg0MDQ2LC0xLjM1NTAwNiAtMi40MzUyMzQsLTIuMDMxMjI1NCAtMy41MDAxLC0zLjMwOTcwNyAtMC4wNDMsLTAuMDE1ODgyIDAuMDQ2LDAuMDAxNzQgMCwwIEwgMi40MzA2NywxLjAwMTEwOCBDIDIuNDAzODMsMC45OTg1OSAyLjM3Njc0LDAuOTk4NTkgMi4zNDk5LDEuMDAxMTA4IFoiIC8+CiAgPHBhdGggc3R5bGU9ImRpc3BsYXk6aW5saW5lO2ZpbGw6IzhjOGM4YztmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzllNzU3NTtzdHJva2Utd2lkdGg6MDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIiBkPSJtIDEwLjUwMDU3LDEuMDAyMDc2NCBjIDAsMy4yNzY4MDI4IC0wLjAwNTIsMy4xNzM5MTYxIDAuMzYyOTIxLDMuMjY5ODIwMiAwLjI4MDEwOSwwLjA3Mjk4NCAzLjEzNzE4LDAuMDM5ODg3IDMuMTM3MTgsMC4wMzk4ODcgLTEuMTIwMDY3LC0xLjA1NTY2OTIgLTIuMzMzNCwtMi4yMDY0NzEzIC0zLjUwMDEsLTMuMzA5NzA3NCB6IiAvPgo8L3N2Zz4K",
@@ -19,7 +22,7 @@ const $tmpl = createElement(`
__TEMPLATE__
- 06/06/2020
+
@@ -35,6 +38,7 @@ const $tmpl = createElement(`
export function createThing({
name = null,
type = "N/A",
+ time = 0,
path = null,
// size = 0,
// time = null,
@@ -44,9 +48,9 @@ export function createThing({
n = 0,
}) {
const $thing = $tmpl.cloneNode(true);
- if (!($thing instanceof window.HTMLElement)) throw new Error("assertion failed: $thing must be an HTMLELement");
- const $label = $thing.querySelector(".component_filename .file-details > span");
- if (!($label instanceof window.HTMLElement)) throw new Error("assertion failed: $label must be an HTMLELement");
+ assert.type($thing, window.HTMLElement);
+ const $label = qs($thing, ".component_filename .file-details > span");
+ const $time = qs($thing, ".component_datetime");
$label.textContent = name;
$thing.querySelector("a").setAttribute("href", link);
@@ -54,10 +58,7 @@ export function createThing({
$thing.setAttribute("data-droptarget", type === "directory");
$thing.setAttribute("data-n", n);
$thing.classList.add("view-" + view);
- const sideEffectSelection = ($el, checked) => {
- $el.classList.add(checked ? "selected" : "not-selected");
- $el.querySelector(`input[type="checkbox"]`).checked = checked;
- };
+ $time.textContent = formatTime(new Date(time));
sideEffectSelection($thing, isSelected(n));
if (type === "hidden") $thing.classList.add("hidden");
@@ -79,7 +80,7 @@ export function createThing({
const crt = $thing.cloneNode(true);
$thing.style.opacity = "0.7";
const $box = crt.querySelector(".box");
- crt.style.opacity = "0.2 "
+ crt.style.opacity = "0.2";
crt.style.backgroundColor = "var(--border)";
$box.style.backgroundColor = "inherit";
$box.style.border = "none";
@@ -102,3 +103,17 @@ export function createThing({
};
return $thing;
}
+
+function sideEffectSelection($el, checked) {
+ $el.classList.add(checked ? "selected" : "not-selected");
+ $el.querySelector(`input[type="checkbox"]`).checked = checked;
+}
+
+function formatTime(date) {
+ if (!date) return "";
+ return new Intl.DateTimeFormat(navigator.language || "en-US")
+ .format(date)
+ .split("/")
+ .map((chunk) => chunk.padStart(2, "0"))
+ .join("/");
+}