add history of searches in board
This commit is contained in:
Vendored
+102
-67
@@ -2006,6 +2006,65 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// src/ViewModel/Utility/searchViewModel.ts
|
||||||
|
var SearchViewModel = class {
|
||||||
|
// data
|
||||||
|
allObjects;
|
||||||
|
getStringsOfObject;
|
||||||
|
// state
|
||||||
|
appliedQuery = new State("");
|
||||||
|
searchInput = new State("");
|
||||||
|
matchingObjects;
|
||||||
|
suggestions;
|
||||||
|
// guards
|
||||||
|
cannotApplySearch = createProxyState(
|
||||||
|
[this.searchInput, this.appliedQuery],
|
||||||
|
() => this.searchInput.value == this.appliedQuery.value
|
||||||
|
);
|
||||||
|
// methods
|
||||||
|
search = (searchTerm) => {
|
||||||
|
this.searchInput.value = searchTerm;
|
||||||
|
this.applySearch();
|
||||||
|
};
|
||||||
|
applySearch = () => {
|
||||||
|
this.appliedQuery.value = this.searchInput.value;
|
||||||
|
console.log("applying search");
|
||||||
|
this.matchingObjects.clear();
|
||||||
|
for (const object of this.allObjects.value.values()) {
|
||||||
|
const doesMatch = this.checkDoesMatchSearch(object);
|
||||||
|
if (doesMatch == false) continue;
|
||||||
|
this.matchingObjects.add(object);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// init
|
||||||
|
constructor(allObjects, matchingObjects, getStringsOfObject, suggestions) {
|
||||||
|
this.allObjects = allObjects;
|
||||||
|
this.matchingObjects = matchingObjects;
|
||||||
|
this.getStringsOfObject = getStringsOfObject;
|
||||||
|
this.suggestions = suggestions;
|
||||||
|
this.allObjects.handleAddition((newObject) => {
|
||||||
|
const doesMatch = this.checkDoesMatchSearch(newObject);
|
||||||
|
if (doesMatch == false) {
|
||||||
|
this.matchingObjects.remove(newObject);
|
||||||
|
} else {
|
||||||
|
if (this.matchingObjects.value.has(newObject)) return;
|
||||||
|
this.matchingObjects.add(newObject);
|
||||||
|
this.allObjects.handleRemoval(newObject, () => {
|
||||||
|
this.matchingObjects.remove(newObject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// utility
|
||||||
|
checkDoesMatchSearch = (object) => {
|
||||||
|
return checkDoesObjectMatchSearch(
|
||||||
|
this.appliedQuery.value,
|
||||||
|
this.getStringsOfObject,
|
||||||
|
object
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// src/ViewModel/Pages/boardViewModel.ts
|
// src/ViewModel/Pages/boardViewModel.ts
|
||||||
var BoardViewModel = class extends TaskContainingPageViewModel {
|
var BoardViewModel = class extends TaskContainingPageViewModel {
|
||||||
// init
|
// init
|
||||||
@@ -2035,6 +2094,15 @@
|
|||||||
this.updateTaskIndices();
|
this.updateTaskIndices();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
this.searchViewModel = new SearchViewModel(
|
||||||
|
this.taskViewModels,
|
||||||
|
this.filteredTaskViewModels,
|
||||||
|
TaskViewModel.getStringsForFilter,
|
||||||
|
this.searchSuggestions
|
||||||
|
);
|
||||||
|
this.searchViewModel.appliedQuery.subscribe((newQuery) => {
|
||||||
|
this.storeSearchTerm(newQuery);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
storageModel;
|
storageModel;
|
||||||
boardsAndTasksModel;
|
boardsAndTasksModel;
|
||||||
@@ -2051,7 +2119,9 @@
|
|||||||
isSelected;
|
isSelected;
|
||||||
isPresentingSettingsModal = new State(false);
|
isPresentingSettingsModal = new State(false);
|
||||||
isPresentingFilterModal = new State(false);
|
isPresentingFilterModal = new State(false);
|
||||||
|
searchViewModel;
|
||||||
filteredTaskViewModels = new ListState();
|
filteredTaskViewModels = new ListState();
|
||||||
|
searchSuggestions = new ListState();
|
||||||
// paths
|
// paths
|
||||||
getBasePath = () => {
|
getBasePath = () => {
|
||||||
return [...this.taskPageViewModel.getBoardViewPath(this.boardInfo.fileId)];
|
return [...this.taskPageViewModel.getBoardViewPath(this.boardInfo.fileId)];
|
||||||
@@ -2059,6 +2129,9 @@
|
|||||||
getLastUsedBoardPath = () => {
|
getLastUsedBoardPath = () => {
|
||||||
return [...this.getBasePath(), "last-used-view" /* LastUsedView */];
|
return [...this.getBasePath(), "last-used-view" /* LastUsedView */];
|
||||||
};
|
};
|
||||||
|
getPreviousSearchesPath = () => {
|
||||||
|
return [...this.getBasePath(), "previous-searches" /* PreviousSearches */];
|
||||||
|
};
|
||||||
// settings
|
// settings
|
||||||
saveSettings = () => {
|
saveSettings = () => {
|
||||||
const newBoardInfoFileContent = BoardsAndTasksModel.createBoardInfoFileContent(
|
const newBoardInfoFileContent = BoardsAndTasksModel.createBoardInfoFileContent(
|
||||||
@@ -2101,6 +2174,13 @@
|
|||||||
if (lastUsedView == null) return;
|
if (lastUsedView == null) return;
|
||||||
this.selectedPage.value = lastUsedView;
|
this.selectedPage.value = lastUsedView;
|
||||||
};
|
};
|
||||||
|
storeSearchTerm = (searchTerm) => {
|
||||||
|
const path = [...this.getPreviousSearchesPath(), searchTerm];
|
||||||
|
this.storageModel.write(path, "");
|
||||||
|
if (!this.searchSuggestions.value.has(searchTerm)) {
|
||||||
|
this.searchSuggestions.add(searchTerm);
|
||||||
|
}
|
||||||
|
};
|
||||||
// view
|
// view
|
||||||
showTask = (taskFileContent) => {
|
showTask = (taskFileContent) => {
|
||||||
if (taskFileContent.boardId != this.boardInfo.fileId) {
|
if (taskFileContent.boardId != this.boardInfo.fileId) {
|
||||||
@@ -2170,9 +2250,15 @@
|
|||||||
}
|
}
|
||||||
this.updateTaskIndices();
|
this.updateTaskIndices();
|
||||||
};
|
};
|
||||||
|
loadSearchSuggestions = () => {
|
||||||
|
const dirPath = this.getPreviousSearchesPath();
|
||||||
|
const searches = this.storageModel.list(dirPath);
|
||||||
|
this.searchSuggestions.add(...searches);
|
||||||
|
};
|
||||||
loadData = () => {
|
loadData = () => {
|
||||||
this.restoreLastUsedView();
|
this.restoreLastUsedView();
|
||||||
this.loadTasks();
|
this.loadTasks();
|
||||||
|
this.loadSearchSuggestions();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -3521,83 +3607,34 @@
|
|||||||
return RibbonButton(label, icon, isSelected, select);
|
return RibbonButton(label, icon, isSelected, select);
|
||||||
}
|
}
|
||||||
|
|
||||||
// src/ViewModel/Utility/searchViewModel.ts
|
|
||||||
var SearchViewModel = class {
|
|
||||||
// data
|
|
||||||
allObjects;
|
|
||||||
getStringsOfObject;
|
|
||||||
// state
|
|
||||||
appliedQuery = new State("");
|
|
||||||
searchInput = new State("");
|
|
||||||
matchingObjects;
|
|
||||||
// guards
|
|
||||||
cannotApplySearch = createProxyState(
|
|
||||||
[this.searchInput, this.appliedQuery],
|
|
||||||
() => this.searchInput.value == this.appliedQuery.value
|
|
||||||
);
|
|
||||||
// methods
|
|
||||||
applySearch = () => {
|
|
||||||
this.appliedQuery.value = this.searchInput.value;
|
|
||||||
console.log("applying search");
|
|
||||||
this.matchingObjects.clear();
|
|
||||||
for (const object of this.allObjects.value.values()) {
|
|
||||||
const doesMatch = this.checkDoesMatchSearch(object);
|
|
||||||
if (doesMatch == false) continue;
|
|
||||||
this.matchingObjects.add(object);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// init
|
|
||||||
constructor(allObjects, matchingObjects, getStringsOfObject) {
|
|
||||||
this.allObjects = allObjects;
|
|
||||||
this.matchingObjects = matchingObjects;
|
|
||||||
this.getStringsOfObject = getStringsOfObject;
|
|
||||||
this.allObjects.handleAddition((newObject) => {
|
|
||||||
const doesMatch = this.checkDoesMatchSearch(newObject);
|
|
||||||
if (doesMatch == false) {
|
|
||||||
this.matchingObjects.remove(newObject);
|
|
||||||
} else {
|
|
||||||
if (this.matchingObjects.value.has(newObject)) return;
|
|
||||||
this.matchingObjects.add(newObject);
|
|
||||||
this.allObjects.handleRemoval(newObject, () => {
|
|
||||||
this.matchingObjects.remove(newObject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// utility
|
|
||||||
checkDoesMatchSearch = (object) => {
|
|
||||||
return checkDoesObjectMatchSearch(
|
|
||||||
this.appliedQuery.value,
|
|
||||||
this.getStringsOfObject,
|
|
||||||
object
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// src/View/Modals/searchModal.tsx
|
// src/View/Modals/searchModal.tsx
|
||||||
function SearchModal(headline, allObjects, filteredObjects, converter, getStringsOfObject, isOpen) {
|
function SearchModal(searchViewModel, headline, converter, isOpen) {
|
||||||
const viewModel = new SearchViewModel(
|
|
||||||
allObjects,
|
|
||||||
filteredObjects,
|
|
||||||
getStringsOfObject
|
|
||||||
);
|
|
||||||
function close() {
|
function close() {
|
||||||
isOpen.value = false;
|
isOpen.value = false;
|
||||||
}
|
}
|
||||||
|
const id = v4_default();
|
||||||
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isOpen }, /* @__PURE__ */ createElement("div", { style: "max-width: 64rem" }, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, headline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
|
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isOpen }, /* @__PURE__ */ createElement("div", { style: "max-width: 64rem" }, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, headline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
|
||||||
"input",
|
"input",
|
||||||
{
|
{
|
||||||
placeholder: translations.general.searchLabel,
|
placeholder: translations.general.searchLabel,
|
||||||
"bind:value": viewModel.searchInput,
|
"bind:value": searchViewModel.searchInput,
|
||||||
"on:enter": viewModel.applySearch
|
"on:enter": searchViewModel.applySearch,
|
||||||
|
list: id
|
||||||
|
}
|
||||||
|
), /* @__PURE__ */ createElement(
|
||||||
|
"datalist",
|
||||||
|
{
|
||||||
|
hidden: true,
|
||||||
|
id,
|
||||||
|
"children:append": [searchViewModel.suggestions, StringToOption]
|
||||||
}
|
}
|
||||||
), /* @__PURE__ */ createElement(
|
), /* @__PURE__ */ createElement(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
class: "primary",
|
class: "primary",
|
||||||
"aria-label": translations.general.searchButtonAudioLabel,
|
"aria-label": translations.general.searchButtonAudioLabel,
|
||||||
"on:click": viewModel.applySearch,
|
"on:click": searchViewModel.applySearch,
|
||||||
"toggle:disabled": viewModel.cannotApplySearch
|
"toggle:disabled": searchViewModel.cannotApplySearch
|
||||||
},
|
},
|
||||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "search")
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "search")
|
||||||
)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
|
)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
|
||||||
@@ -3605,7 +3642,7 @@
|
|||||||
{
|
{
|
||||||
class: "grid gap",
|
class: "grid gap",
|
||||||
style: "grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr))",
|
style: "grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr))",
|
||||||
"children:append": [filteredObjects, converter]
|
"children:append": [searchViewModel.matchingObjects, converter]
|
||||||
}
|
}
|
||||||
)), /* @__PURE__ */ createElement("button", { "on:click": close }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
|
)), /* @__PURE__ */ createElement("button", { "on:click": close }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
|
||||||
}
|
}
|
||||||
@@ -3696,11 +3733,9 @@
|
|||||||
},
|
},
|
||||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "add")
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "add")
|
||||||
))), /* @__PURE__ */ createElement("div", { class: "content main-content", "children:set": mainContent }), BoardSettingsModal(boardViewModel), SearchModal(
|
))), /* @__PURE__ */ createElement("div", { class: "content main-content", "children:set": mainContent }), BoardSettingsModal(boardViewModel), SearchModal(
|
||||||
|
boardViewModel.searchViewModel,
|
||||||
translations.chatPage.task.filterTasksHeadline,
|
translations.chatPage.task.filterTasksHeadline,
|
||||||
boardViewModel.taskViewModels,
|
|
||||||
boardViewModel.filteredTaskViewModels,
|
|
||||||
TaskViewModelToEntry,
|
TaskViewModelToEntry,
|
||||||
TaskViewModel.getStringsForFilter,
|
|
||||||
boardViewModel.isPresentingFilterModal
|
boardViewModel.isPresentingFilterModal
|
||||||
), /* @__PURE__ */ createElement("div", { "children:set": taskSettingsModal }));
|
), /* @__PURE__ */ createElement("div", { "children:set": taskSettingsModal }));
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -38,7 +38,7 @@ COLORS
|
|||||||
/* definition */
|
/* definition */
|
||||||
:root {
|
:root {
|
||||||
--background: hsla(0, 0%, 100%, 100%);
|
--background: hsla(0, 0%, 100%, 100%);
|
||||||
--background-glass: hsla(0, 0%, 100%, 40%);
|
--background-glass: hsla(0, 0%, 100%, 5%);
|
||||||
--background-transparent: hsla(0, 0%, 0%, 5%);
|
--background-transparent: hsla(0, 0%, 0%, 5%);
|
||||||
--background-hover: hsla(0, 0%, 55%, 12%);
|
--background-hover: hsla(0, 0%, 55%, 12%);
|
||||||
--background-active: hsla(0, 0%, 55%, 12%);
|
--background-active: hsla(0, 0%, 55%, 12%);
|
||||||
|
|||||||
@@ -115,11 +115,9 @@ export function BoardPage(boardViewModel: BoardViewModel) {
|
|||||||
|
|
||||||
{BoardSettingsModal(boardViewModel)}
|
{BoardSettingsModal(boardViewModel)}
|
||||||
{SearchModal(
|
{SearchModal(
|
||||||
|
boardViewModel.searchViewModel,
|
||||||
translations.chatPage.task.filterTasksHeadline,
|
translations.chatPage.task.filterTasksHeadline,
|
||||||
boardViewModel.taskViewModels,
|
|
||||||
boardViewModel.filteredTaskViewModels,
|
|
||||||
TaskViewModelToEntry,
|
TaskViewModelToEntry,
|
||||||
TaskViewModel.getStringsForFilter,
|
|
||||||
boardViewModel.isPresentingFilterModal
|
boardViewModel.isPresentingFilterModal
|
||||||
)}
|
)}
|
||||||
<div children:set={taskSettingsModal}></div>
|
<div children:set={taskSettingsModal}></div>
|
||||||
|
|||||||
@@ -1,26 +1,22 @@
|
|||||||
import * as React from "bloatless-react";
|
import * as React from "bloatless-react";
|
||||||
|
|
||||||
import SearchViewModel from "../../ViewModel/Utility/searchViewModel";
|
import SearchViewModel from "../../ViewModel/Utility/searchViewModel";
|
||||||
|
import { StringToOption } from "../Components/option";
|
||||||
import { translations } from "../translations";
|
import { translations } from "../translations";
|
||||||
|
import { v4 } from "uuid";
|
||||||
|
|
||||||
export function SearchModal<T>(
|
export function SearchModal<T>(
|
||||||
|
searchViewModel: SearchViewModel<T>,
|
||||||
headline: string,
|
headline: string,
|
||||||
allObjects: React.ListState<T> | React.MapState<T>,
|
|
||||||
filteredObjects: React.ListState<T>,
|
|
||||||
converter: React.StateItemConverter<T>,
|
converter: React.StateItemConverter<T>,
|
||||||
getStringsOfObject: (object: T) => string[],
|
|
||||||
isOpen: React.State<boolean>
|
isOpen: React.State<boolean>
|
||||||
) {
|
) {
|
||||||
const viewModel: SearchViewModel<T> = new SearchViewModel(
|
|
||||||
allObjects,
|
|
||||||
filteredObjects,
|
|
||||||
getStringsOfObject
|
|
||||||
);
|
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
isOpen.value = false;
|
isOpen.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const id = v4();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="modal" toggle:open={isOpen}>
|
<div class="modal" toggle:open={isOpen}>
|
||||||
<div style="max-width: 64rem">
|
<div style="max-width: 64rem">
|
||||||
@@ -29,14 +25,20 @@ export function SearchModal<T>(
|
|||||||
<div class="flex-row width-input">
|
<div class="flex-row width-input">
|
||||||
<input
|
<input
|
||||||
placeholder={translations.general.searchLabel}
|
placeholder={translations.general.searchLabel}
|
||||||
bind:value={viewModel.searchInput}
|
bind:value={searchViewModel.searchInput}
|
||||||
on:enter={viewModel.applySearch}
|
on:enter={searchViewModel.applySearch}
|
||||||
|
list={id}
|
||||||
></input>
|
></input>
|
||||||
|
<datalist
|
||||||
|
hidden
|
||||||
|
id={id}
|
||||||
|
children:append={[searchViewModel.suggestions, StringToOption]}
|
||||||
|
></datalist>
|
||||||
<button
|
<button
|
||||||
class="primary"
|
class="primary"
|
||||||
aria-label={translations.general.searchButtonAudioLabel}
|
aria-label={translations.general.searchButtonAudioLabel}
|
||||||
on:click={viewModel.applySearch}
|
on:click={searchViewModel.applySearch}
|
||||||
toggle:disabled={viewModel.cannotApplySearch}
|
toggle:disabled={searchViewModel.cannotApplySearch}
|
||||||
>
|
>
|
||||||
<span class="icon">search</span>
|
<span class="icon">search</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -47,7 +49,7 @@ export function SearchModal<T>(
|
|||||||
<div
|
<div
|
||||||
class="grid gap"
|
class="grid gap"
|
||||||
style="grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr))"
|
style="grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr))"
|
||||||
children:append={[filteredObjects, converter]}
|
children:append={[searchViewModel.matchingObjects, converter]}
|
||||||
></div>
|
></div>
|
||||||
</main>
|
</main>
|
||||||
<button on:click={close}>
|
<button on:click={close}>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import BoardsAndTasksModel, {
|
|||||||
|
|
||||||
import { Color } from "../../colors";
|
import { Color } from "../../colors";
|
||||||
import CoreViewModel from "../Global/coreViewModel";
|
import CoreViewModel from "../Global/coreViewModel";
|
||||||
|
import SearchViewModel from "../Utility/searchViewModel";
|
||||||
import StorageModel from "../../Model/Global/storageModel";
|
import StorageModel from "../../Model/Global/storageModel";
|
||||||
import TaskContainingPageViewModel from "./taskContainingPageViewModel";
|
import TaskContainingPageViewModel from "./taskContainingPageViewModel";
|
||||||
import TaskPageViewModel from "./taskPageViewModel";
|
import TaskPageViewModel from "./taskPageViewModel";
|
||||||
@@ -35,8 +36,10 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
|||||||
isPresentingSettingsModal: React.State<boolean> = new React.State(false);
|
isPresentingSettingsModal: React.State<boolean> = new React.State(false);
|
||||||
isPresentingFilterModal: React.State<boolean> = new React.State(false);
|
isPresentingFilterModal: React.State<boolean> = new React.State(false);
|
||||||
|
|
||||||
|
searchViewModel: SearchViewModel<TaskViewModel>;
|
||||||
filteredTaskViewModels: React.ListState<TaskViewModel> =
|
filteredTaskViewModels: React.ListState<TaskViewModel> =
|
||||||
new React.ListState();
|
new React.ListState();
|
||||||
|
searchSuggestions: React.ListState<string> = new React.ListState();
|
||||||
|
|
||||||
// paths
|
// paths
|
||||||
getBasePath = (): string[] => {
|
getBasePath = (): string[] => {
|
||||||
@@ -47,6 +50,10 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
|||||||
return [...this.getBasePath(), BoardViewModelSubPath.LastUsedView];
|
return [...this.getBasePath(), BoardViewModelSubPath.LastUsedView];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getPreviousSearchesPath = (): string[] => {
|
||||||
|
return [...this.getBasePath(), BoardViewModelSubPath.PreviousSearches];
|
||||||
|
};
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
saveSettings = (): void => {
|
saveSettings = (): void => {
|
||||||
const newBoardInfoFileContent: BoardInfoFileContent =
|
const newBoardInfoFileContent: BoardInfoFileContent =
|
||||||
@@ -70,7 +77,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
|||||||
// methods
|
// methods
|
||||||
createTask = (): void => {
|
createTask = (): void => {
|
||||||
this.createTaskFromBoardId(this.boardInfo.fileId);
|
this.createTaskFromBoardId(this.boardInfo.fileId);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleDropWithinBoard = (category?: string, status?: string): void => {
|
handleDropWithinBoard = (category?: string, status?: string): void => {
|
||||||
const draggedObject: any = this.coreViewModel.draggedObject.value;
|
const draggedObject: any = this.coreViewModel.draggedObject.value;
|
||||||
@@ -99,6 +106,14 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
|||||||
this.selectedPage.value = lastUsedView as BoardPageType;
|
this.selectedPage.value = lastUsedView as BoardPageType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
storeSearchTerm = (searchTerm: string): void => {
|
||||||
|
const path: string[] = [...this.getPreviousSearchesPath(), searchTerm];
|
||||||
|
this.storageModel.write(path, "");
|
||||||
|
if (!this.searchSuggestions.value.has(searchTerm)) {
|
||||||
|
this.searchSuggestions.add(searchTerm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// view
|
// view
|
||||||
showTask = (taskFileContent: TaskFileContent): void => {
|
showTask = (taskFileContent: TaskFileContent): void => {
|
||||||
if (taskFileContent.boardId != this.boardInfo.fileId) {
|
if (taskFileContent.boardId != this.boardInfo.fileId) {
|
||||||
@@ -186,9 +201,16 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
|||||||
this.updateTaskIndices();
|
this.updateTaskIndices();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
loadSearchSuggestions = (): void => {
|
||||||
|
const dirPath: string[] = this.getPreviousSearchesPath();
|
||||||
|
const searches: string[] = this.storageModel.list(dirPath);
|
||||||
|
this.searchSuggestions.add(...searches);
|
||||||
|
};
|
||||||
|
|
||||||
loadData = (): void => {
|
loadData = (): void => {
|
||||||
this.restoreLastUsedView();
|
this.restoreLastUsedView();
|
||||||
this.loadTasks();
|
this.loadTasks();
|
||||||
|
this.loadSearchSuggestions();
|
||||||
};
|
};
|
||||||
|
|
||||||
// init
|
// init
|
||||||
@@ -234,11 +256,23 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
|
|||||||
this.updateTaskIndices();
|
this.updateTaskIndices();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// search
|
||||||
|
this.searchViewModel = new SearchViewModel(
|
||||||
|
this.taskViewModels,
|
||||||
|
this.filteredTaskViewModels,
|
||||||
|
TaskViewModel.getStringsForFilter,
|
||||||
|
this.searchSuggestions
|
||||||
|
);
|
||||||
|
this.searchViewModel.appliedQuery.subscribe((newQuery) => {
|
||||||
|
this.storeSearchTerm(newQuery);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum BoardViewModelSubPath {
|
export enum BoardViewModelSubPath {
|
||||||
LastUsedView = "last-used-view",
|
LastUsedView = "last-used-view",
|
||||||
|
PreviousSearches = "previous-searches",
|
||||||
}
|
}
|
||||||
|
|
||||||
// types
|
// types
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ export default class SearchViewModel<T> {
|
|||||||
searchInput: React.State<string> = new React.State("");
|
searchInput: React.State<string> = new React.State("");
|
||||||
matchingObjects: React.ListState<T>;
|
matchingObjects: React.ListState<T>;
|
||||||
|
|
||||||
|
suggestions: React.ListState<string>;
|
||||||
|
|
||||||
// guards
|
// guards
|
||||||
cannotApplySearch: React.State<boolean> = React.createProxyState(
|
cannotApplySearch: React.State<boolean> = React.createProxyState(
|
||||||
[this.searchInput, this.appliedQuery],
|
[this.searchInput, this.appliedQuery],
|
||||||
@@ -19,6 +21,11 @@ export default class SearchViewModel<T> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
|
search = (searchTerm: string): void => {
|
||||||
|
this.searchInput.value = searchTerm;
|
||||||
|
this.applySearch();
|
||||||
|
};
|
||||||
|
|
||||||
applySearch = (): void => {
|
applySearch = (): void => {
|
||||||
this.appliedQuery.value = this.searchInput.value;
|
this.appliedQuery.value = this.searchInput.value;
|
||||||
console.log("applying search");
|
console.log("applying search");
|
||||||
@@ -36,11 +43,13 @@ export default class SearchViewModel<T> {
|
|||||||
constructor(
|
constructor(
|
||||||
allObjects: React.ListState<T> | React.MapState<T>,
|
allObjects: React.ListState<T> | React.MapState<T>,
|
||||||
matchingObjects: React.ListState<T>,
|
matchingObjects: React.ListState<T>,
|
||||||
getStringsOfObject: (object: T) => string[]
|
getStringsOfObject: (object: T) => string[],
|
||||||
|
suggestions: React.ListState<string>
|
||||||
) {
|
) {
|
||||||
this.allObjects = allObjects;
|
this.allObjects = allObjects;
|
||||||
this.matchingObjects = matchingObjects;
|
this.matchingObjects = matchingObjects;
|
||||||
this.getStringsOfObject = getStringsOfObject;
|
this.getStringsOfObject = getStringsOfObject;
|
||||||
|
this.suggestions = suggestions;
|
||||||
|
|
||||||
// handle new objects
|
// handle new objects
|
||||||
this.allObjects.handleAddition((newObject: T) => {
|
this.allObjects.handleAddition((newObject: T) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user