add simple filtering
Mwebclient/src/components/FileBrowser.css
@@ -35,10 +35,11 @@
text-wrap: nowrap;
margin: 1rem;
}
}
& > .refresh-button {
background: none;
}
.filter-files {
border: none;
border-bottom: 1px solid var(--color-c);
}
.file-list-wrapper {
Mwebclient/src/components/FileBrowser.tsx
@@ -16,7 +16,7 @@ import {
import type { MediaFile, FileListing, Directory } from "music-server-shared/types";
import type { AppOptions, PlaylistItem } from "../types";
import { generateUUID, joinPath, LocalStorageValues, toast } from "../utils";
import { ClosedFolderSVG, DownSVG, MusicSVG, RefreshSVG, UpSVG } from "./SVG";
import { ClosedFolderSVG, DownSVG, FilterSVG, MusicSVG, RefreshSVG, UpSVG } from "./SVG";
import { clearCoverCache as deleteCover, clearDirectoryCache, getCover, listFiles } from "../offline";
import { DownloadButton } from "./DownloadButton";
import { LoadingScreen } from "./LoadingScreen";
@@ -41,6 +41,19 @@ interface FileBrowserProps {
export default function FileBrowser(props: FileBrowserProps) {
const [loadingActions, setLoadingActions] = createSignal(0);
const [showActionButtons, setShowActionButtons] = createSignal(true);
const [showFilter, setShowFilter] = createSignal(false);
const [filter, setFilter] = createSignal("");
createEffect(
on(props.currentDir, () => {
setFilter("");
setShowFilter(false);
}),
);
createEffect(
on(showFilter, (showFilter) => {
if (!showFilter) setFilter("");
}),
);
const [cover, fetchCover] = createResource<string | undefined, { dir: string; offline: boolean }>(
() => ({ dir: props.currentDir(), offline: props.isOffline() }),
async (args, info) => {
@@ -184,14 +197,25 @@ export default function FileBrowser(props: FileBrowserProps) {
});
});
function getAbsolutePathFiles() {
return props.files.latest.map(([name, item]) => {
return [joinPath(props.currentDir(), name), item] as [string, Directory | MediaFile];
});
}
//this proxy store allows us to diff changes correctly when the filelisting changes, only rerendering the changed items
//absolute paths are required here to make sure it's really the same file in the same directory
const [fileStore, setFileStore] = createStore<FlatFileListing>([]);
createEffect(() => {
const absolutePathArray = props.files.latest.map(([name, item]) => {
return [joinPath(props.currentDir(), name), item] as [string, Directory | MediaFile];
});
setFileStore(reconcile(absolutePathArray, { key: "0" }));
setFileStore(
reconcile(
getAbsolutePathFiles().filter((item) => {
const fileName = item[0].split("/").toReversed()[0];
return fileName.toLowerCase().includes(filter());
}),
{ key: "0" },
),
);
});
return (
@@ -256,6 +280,9 @@ export default function FileBrowser(props: FileBrowserProps) {
</button>
</div>
<div class="spacer" />
<button title="Filter current directory" onClick={() => setShowFilter(!showFilter())}>
<FilterSVG />
</button>
<Show when={!props.isOffline()}>
<button
class="refresh-button"
@@ -266,6 +293,15 @@ export default function FileBrowser(props: FileBrowserProps) {
</button>
</Show>
</div>
<Show when={showFilter()}>
<input
oninput={(e) => setFilter(e.target.value)}
onkeydown={(e) => e.key === "Escape" && setShowFilter(false)}
class="filter-files"
type="text"
placeholder="Filter files..."
/>
</Show>
</Show>
<div class="file-list-wrapper">
Mwebclient/src/components/SVG.tsx
@@ -494,3 +494,23 @@ export function StopSVG() {
</svg>
);
}
export function FilterSVG() {
const [hovered, setHovered] = createSignal();
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<path
fill={hovered() ? highlightColor : defaultColor}
d="M22 20v-1h-1v-1h-1v-1h-1v-1h-2v-1h1v-2h1V7h-1V5h-1V4h-1V3h-1V2h-2V1H7v1H5v1H4v1H3v1H2v2H1v6h1v2h1v1h1v1h1v1h2v1h6v-1h2v-1h1v2h1v1h1v1h1v1h1v1h2v-1h1v-2zm-10-5v1H8v-1H6v-1H5v-2H4V8h1V6h1V5h2V4h4v1h2v1h1v2h1v4h-1v2h-1v1z"
/>
</svg>
);
}