Skip to content

Commit

Permalink
feat: add text filter for directory entries
Browse files Browse the repository at this point in the history
  • Loading branch information
herteleo committed Oct 23, 2022
1 parent df0a5eb commit 36d7580
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 18 deletions.
34 changes: 34 additions & 0 deletions src/features/useDirFilter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { computed, ref, type Ref } from 'vue';
import type { CurrentDirEntry } from '@/features/useDir';

const showFilter = ref(false);
const stringFilter = ref('');

const toggleFilter = () => {
showFilter.value = !showFilter.value;
};

const resetFilter = () => {
stringFilter.value = '';
showFilter.value = false;
};

const useDirFilter = (dirEntries: Ref<CurrentDirEntry[]>) => {
const filteredDirEntries = computed(() => {
const string = stringFilter.value.toLowerCase();

if (!showFilter.value) return dirEntries.value;

return dirEntries.value.filter(({ handle }) => handle.name.toLowerCase().includes(string));
});

return {
showFilter,
stringFilter,
filteredDirEntries,
resetFilter,
toggleFilter,
};
};

export default useDirFilter;
2 changes: 2 additions & 0 deletions src/utils/icons.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export * as AreaSearch from 'iconoir/icons/area-search.svg?component';
export * as AirplaneRotation from 'iconoir/icons/airplane-rotation.svg?component';
export * as EmptyPage from 'iconoir/icons/empty-page.svg?component';
export * as Filter from 'iconoir/icons/filter.svg?component';
export * as Folder from 'iconoir/icons/folder.svg?component';
export * as GlassEmpty from 'iconoir/icons/glass-empty.svg?component';
export * as JournalPage from 'iconoir/icons/journal-page.svg?component';
Expand Down
78 changes: 60 additions & 18 deletions src/views/Dir.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { marked } from 'marked';
import { computed, ref, watch } from 'vue';
import { computed, nextTick, ref, watch } from 'vue';
import DirEntry from '@/components/DirEntry.vue';
import useDir, {
setRootDir,
Expand All @@ -10,9 +10,22 @@ import useDir, {
getDirCoverEntry,
reloadCurrentDir,
} from '@/features/useDir';
import useDirFilter from '@/features/useDirFilter';
useDir();
const { filteredDirEntries, showFilter, stringFilter, resetFilter, toggleFilter } =
useDirFilter(currentDirEntries);
const $stringFilter = ref<HTMLInputElement>();
const toggleFilterHelper = async () => {
toggleFilter();
await nextTick();
$stringFilter.value?.focus();
};
const readmeEntry = computed(() =>
currentDirEntries.value.find(
(e) => e.type === 'markdown' && e.file.name.toLowerCase() === 'readme.md'
Expand Down Expand Up @@ -48,26 +61,55 @@ const dirThumbSrc = computed(
</script>

<template>
<header
class="sticky top-0 flex items-center justify-between bg-black/75 px-4 py-2 text-gray-400 backdrop-blur"
>
<div v-text="currentDir?.name" />
<div class="flex gap-4">
<div class="animate-spin" v-if="currentDirEntriesLoading">
<app-icon class="-scale-x-100" name="AirplaneRotation" />
<header class="sticky top-0">
<div
class="flex items-center justify-between bg-black/75 px-4 py-1 text-gray-400 backdrop-blur"
>
<div v-text="currentDir?.name" />
<div class="flex gap-2">
<div class="animate-spin" v-if="currentDirEntriesLoading">
<app-icon class="-scale-x-100" name="AirplaneRotation" />
</div>
<input
v-if="showFilter"
ref="$stringFilter"
v-model="stringFilter"
class="rounded-full border border-slate-600 bg-transparent px-4 outline-none placeholder:text-slate-700 focus:bg-slate-800 focus:placeholder:text-slate-400"
type="search"
placeholder="Filter list&hellip;"
/>
<button @click="toggleFilterHelper" class="p-1" :class="{ 'text-amber-600': showFilter }">
<app-icon name="Filter" />
</button>
<button class="p-1" @click="reloadCurrentDir">
<app-icon name="Refresh" />
</button>
<button class="p-1" @click="setRootDir">
<app-icon name="Folder" />
</button>
</div>
<button @click="reloadCurrentDir">
<app-icon name="Refresh" />
</button>
<button @click="setRootDir">
<app-icon name="Folder" />
</button>
</div>
</header>
<div v-if="!currentDirEntries.length" class="grid grow place-content-center">
<div class="space-y-2 text-center text-gray-600">
<div
v-if="!currentDirEntries.length || !filteredDirEntries.length"
class="grid grow place-content-center"
>
<div
v-if="!filteredDirEntries.length && currentDirEntries.length"
class="space-y-4 text-center text-gray-600"
>
<app-icon class="mx-auto" name="AreaSearch" size="96" />
<div class="text-xl font-medium uppercase">No Results</div>
<button
class="rounded-full border border-slate-600 px-4 py-1 font-medium"
@click="resetFilter"
>
Reset Filter
</button>
</div>
<div v-else class="space-y-2 text-center text-gray-600">
<app-icon class="mx-auto" name="GlassEmpty" size="96" />
<div class="text-xl uppercase">Empty</div>
<div class="text-xl font-medium uppercase">Empty</div>
</div>
</div>
<template v-else>
Expand All @@ -87,7 +129,7 @@ const dirThumbSrc = computed(
</div>
</div>
<div class="mx-auto grid w-full max-w-screen-2xl grid-cols-5 gap-4 p-4">
<DirEntry v-for="entry in currentDirEntries" :key="entry.handle.name" :entry="entry" />
<DirEntry v-for="entry in filteredDirEntries" :key="entry.handle.name" :entry="entry" />
</div>
</template>
</template>

0 comments on commit 36d7580

Please sign in to comment.