Skip to content

Commit

Permalink
feat(NoteManager): Improve filtering by tags in note search
Browse files Browse the repository at this point in the history
  • Loading branch information
rexy712 committed Nov 10, 2024
1 parent 5221159 commit b37339f
Show file tree
Hide file tree
Showing 4 changed files with 363 additions and 25 deletions.
4 changes: 4 additions & 0 deletions client/src/fa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
faLocationDot,
faLock,
faMagnifyingGlass,
faMinus,
faMinusSquare,
faNoteSticky,
faPaintBrush,
Expand All @@ -74,6 +75,7 @@ import {
faUserTag,
faUsers,
faVideo,
faX,
} from "@fortawesome/free-solid-svg-icons";

export function loadFontAwesome(): void {
Expand Down Expand Up @@ -123,6 +125,7 @@ export function loadFontAwesome(): void {
faLocationDot,
faLock,
faMagnifyingGlass,
faMinus,
faMinusSquare,
faNoteSticky,
faPaintBrush,
Expand Down Expand Up @@ -153,6 +156,7 @@ export function loadFontAwesome(): void {
faVideo,
faWindowClose,
faWindowRestore,
faX,
);

dom.watch();
Expand Down
7 changes: 6 additions & 1 deletion client/src/game/systems/notes/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import type { ApiNote } from "../../../apiTypes";
import type { DistributiveOmit } from "../../../core/types";

export type NoteTag = {
name: string;
colour: string;
};

export type ClientNote = DistributiveOmit<ApiNote, "tags"> & {
tags: { name: string; colour: string }[];
tags: NoteTag[];
};

export enum NoteManagerMode {
Expand Down
150 changes: 126 additions & 24 deletions client/src/game/ui/notes/NoteList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import { mostReadable } from "../../../core/utils";
import { coreStore } from "../../../store/core";
import { locationStore } from "../../../store/location";
import { noteState } from "../../systems/notes/state";
import { NoteManagerMode, type ClientNote } from "../../systems/notes/types";
import { NoteManagerMode, type ClientNote, type NoteTag } from "../../systems/notes/types";
import { popoutNote } from "../../systems/notes/ui";
import { propertiesState } from "../../systems/properties/state";
import { locationSettingsState } from "../../systems/settings/location/state";
import TagAutoCompleteSearch from "./TagAutoCompleteSearch.vue";
const emit = defineEmits<(e: "mode", mode: NoteManagerMode) => void>();
const noteTypes = ["global", "local"] as const;
Expand All @@ -29,8 +31,10 @@ const searchFilters = reactive({
});
const searchBar = ref<HTMLInputElement | null>(null);
const searchTags = ref<NoteTag[]>([]);
const searchFilter = ref("");
const showSearchFilters = ref(false);
const showTagSearch = ref(false);
const searchPage = ref(1);
const shapeFiltered = computed(() => noteState.reactive.shapeFilter !== undefined);
Expand All @@ -45,6 +49,17 @@ const shapeName = computed(() => {
// searchBar.value?.focus();
// });
const availableTags = computed(() => {
const tagList = new Map<string, NoteTag>();
for (const [_, note] of noteState.reactive.notes) {
for (const tag of note.tags) {
tagList.set(tag.name, tag);
}
}
return Array.from(tagList, ([name, value]) => value).sort((a, b) => a.name.localeCompare(b.name));
});
const noteArray = computed(() => {
let it: Iterable<DeepReadonly<ClientNote>> = noteState.reactive.notes.values();
if (!searchFilters.includeArchivedLocations) {
Expand All @@ -56,6 +71,16 @@ const noteArray = computed(() => {
}));
return Array.from(it2);
});
function containsSearchTags(note: typeof noteArray.value[number]): boolean {
for (const tag of searchTags.value) {
if (!note.tags.some((t) => t.name === tag.name)) {
return false;
}
}
return true;
}
const filteredNotes = computed(() => {
const sf = searchFilter.value.trim().toLowerCase();
const searchLocal = selectedNoteTypes.value === "local";
Expand All @@ -80,6 +105,10 @@ const filteredNotes = computed(() => {
}
}
if (!containsSearchTags(note)) {
continue;
}
if (sf.length === 0) {
notes.push(note);
continue;
Expand Down Expand Up @@ -110,6 +139,15 @@ const visibleNotes = computed(() => {
};
});
function toggleTagInSearch(tag: NoteTag): void {
const tempArray = searchTags.value.filter((x) => x.name !== tag.name);
if (tempArray.length === searchTags.value.length) {
searchTags.value.push(tag);
} else {
searchTags.value = tempArray;
}
}
function editNote(noteId: string): void {
noteState.mutableReactive.currentNote = noteId;
emit("mode", NoteManagerMode.Edit);
Expand All @@ -125,7 +163,7 @@ function clearShapeFilter(): void {
<div>NOTES {{ shapeName ? `for ${shapeName}` : "" }}</div>
</header>
<div id="notes-search" :class="shapeFiltered ? 'disabled' : ''">
<div>
<div id="search-bar">
<ToggleGroup
v-show="!shapeFiltered"
id="kind-selector"
Expand All @@ -135,7 +173,6 @@ function clearShapeFilter(): void {
active-color="rgba(173, 216, 230, 0.5)"
/>
<font-awesome-icon icon="magnifying-glass" @click="searchBar?.focus()" />
<div v-if="shapeName" class="shape-name" @click="clearShapeFilter">{{ shapeName }}</div>
<input ref="searchBar" v-model="searchFilter" type="text" placeholder="search through your notes.." />
<div v-show="showSearchFilters" id="search-filter">
<fieldset>
Expand All @@ -144,10 +181,6 @@ function clearShapeFilter(): void {
<input id="note-search-title" v-model="searchFilters.title" type="checkbox" />
<label for="note-search-title">title</label>
</div>
<div>
<input id="note-search-tags" v-model="searchFilters.tags" type="checkbox" />
<label for="note-search-tags">tags</label>
</div>
<div>
<input id="note-search-text" v-model="searchFilters.text" type="checkbox" />
<label for="note-search-text">text</label>
Expand Down Expand Up @@ -211,6 +244,27 @@ function clearShapeFilter(): void {
/>
</div>
</div>
<div id="search-filters">
<span style="padding-right:1rem;">Filters:</span>
<div id="filter-bubbles">
<div v-if="shapeName" class="shape-name tag-bubble removable" @click="clearShapeFilter">{{ shapeName }}</div>
<div
v-for="tag of searchTags"
:key="tag.name"
:style="{ color: mostReadable(tag.colour), backgroundColor: tag.colour }"
class="tag-bubble removable"
@click="toggleTagInSearch(tag)"
>
{{ tag.name }}
</div>
</div>
<div id="add-filters">
<TagAutoCompleteSearch v-show="showTagSearch" id="tag-search-bar" placeholder="Search Tags..." :options="availableTags" @picked="toggleTagInSearch" />
<font-awesome-icon v-if="showTagSearch" id="tag-search-show" icon="minus" title="Hide Tag Search" @click="showTagSearch = false" />
<font-awesome-icon v-else id="tag-search-hide" icon="plus" title="Show Tag Search" @click="showTagSearch = true" />
</div>
</div>

</div>
<template v-if="visibleNotes.notes.length === 0">
<div id="no-notes">
Expand Down Expand Up @@ -251,6 +305,9 @@ function clearShapeFilter(): void {
v-for="tag of note.tags"
:key="tag.name"
:style="{ color: mostReadable(tag.colour), backgroundColor: tag.colour }"
class="tag-bubble"
:title='"Toggle \"" + tag.name + "\" filter"'
@click="toggleTagInSearch(tag)"
>
{{ tag.name }}
</div>
Expand Down Expand Up @@ -293,7 +350,7 @@ header {
margin: 1rem 0;
position: relative;
> div {
> #search-bar {
position: relative;
display: flex;
align-items: center;
Expand All @@ -311,16 +368,6 @@ header {
margin-left: 1rem;
}
> .shape-name {
margin-left: 0.5rem;
font-weight: bold;
&:hover {
text-decoration: line-through;
cursor: pointer;
}
}
> input {
padding: 0.5rem 1rem;
flex-grow: 1;
Expand Down Expand Up @@ -365,6 +412,52 @@ header {
}
}
}
> #search-filters {
margin: 0 1rem 0;
display: flex;
flex-direction: row;
align-items: center;
min-height: 2.7rem;
border-bottom: solid 2px black;
> #filter-bubbles {
flex: 2 0 0;
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
row-gap: 0.5rem;
height: 100%;
padding: 0.25rem;
> .shape-name {
font-weight: bold;
border: solid 2px black;
}
> div {
flex: 0 0 auto;
}
}
> #add-filters {
display: flex;
flex-direction: row;
align-items: center;
height: 100%;
> #tag-search-bar {
height: 1.5rem;
min-width: 8rem;
}
> #tag-search-show,
> #tag-search-hide {
margin: 0 0.5rem;
}
}
}
}
#no-notes {
Expand Down Expand Up @@ -412,12 +505,6 @@ header {
.note-tags {
display: flex;
> div {
padding: 0.25rem 0.5rem;
border-radius: 0.5rem;
margin-right: 0.5rem;
}
}
.note-actions {
Expand All @@ -440,6 +527,21 @@ header {
}
}
}
.tag-bubble {
padding: 0.25rem 0.5rem;
border-radius: 0.5rem;
margin-right: 0.5rem;
}
.tag-bubble.is-active,
.tag-bubble:hover {
filter: brightness(85%);
cursor: pointer;
}
.removable:hover {
text-decoration: line-through;
}
footer {
display: flex;
Expand Down
Loading

0 comments on commit b37339f

Please sign in to comment.