🔍 support for search

This commit is contained in:
undergroundwires
2020-01-09 23:02:36 +01:00
parent cafe6e809a
commit 89862b2775
19 changed files with 202 additions and 88 deletions

View File

@@ -4,12 +4,12 @@
<span class="part">
<span
class="part"
v-bind:class="{ 'disabled': isGrouped, 'enabled': !isGrouped}"
@click="!isGrouped ? toggleGrouping() : undefined">Cards</span>
v-bind:class="{ 'disabled': cardsSelected, 'enabled': !cardsSelected}"
@click="groupByCard()">Cards</span>
<span class="part">|</span>
<span class="part"
v-bind:class="{ 'disabled': !isGrouped, 'enabled': isGrouped}"
@click="isGrouped ? toggleGrouping() : undefined">None</span>
v-bind:class="{ 'disabled': noneSelected, 'enabled': !noneSelected}"
@click="groupByNone()">None</span>
</span>
</div>
</template>
@@ -22,12 +22,30 @@ import { Grouping } from './Grouping';
@Component
export default class TheGrouper extends StatefulVue {
public currentGrouping: Grouping;
public isGrouped = true;
public cardsSelected = false;
public noneSelected = false;
public toggleGrouping() {
this.currentGrouping = this.currentGrouping === Grouping.None ? Grouping.Cards : Grouping.None;
this.isGrouped = this.currentGrouping === Grouping.Cards;
private currentGrouping: Grouping;
public mounted() {
this.changeGrouping(Grouping.Cards);
}
public groupByCard() {
this.changeGrouping(Grouping.Cards);
}
public groupByNone() {
this.changeGrouping(Grouping.None);
}
private changeGrouping(newGrouping: Grouping) {
if (this.currentGrouping === newGrouping) {
return;
}
this.currentGrouping = newGrouping;
this.cardsSelected = newGrouping === Grouping.Cards;
this.noneSelected = newGrouping === Grouping.None;
this.$emit('groupingChanged', this.currentGrouping);
}
}

View File

@@ -20,6 +20,13 @@ export function parseSingleCategory(categoryId: number, state: IApplicationState
return tree;
}
export function getScriptNodeId(script: IScript): string {
return script.id;
}
export function getCategoryNodeId(category: ICategory): string {
return `${category.id}`;
}
function parseCategoryRecursively(
parentCategory: ICategory,
selection: IUserSelection): INode[] {
@@ -44,7 +51,7 @@ function parseCategoryRecursively(
function convertCategoryToNode(
category: ICategory, children: readonly INode[]): INode {
return {
id: `${category.id}`,
id: getCategoryNodeId(category),
text: category.name,
selected: false,
children,
@@ -54,7 +61,7 @@ function convertCategoryToNode(
function convertScriptToNode(script: IScript, selection: IUserSelection): INode {
return {
id: `${script.id}`,
id: getScriptNodeId(script),
text: script.name,
selected: selection.isSelected(script),
children: undefined,

View File

@@ -1,12 +1,12 @@
<template>
<span id="container">
<span v-if="nodes != null && nodes.length > 0">
<SelectableTree
:nodes="nodes"
:selectedNodeIds="selectedNodeIds"
:filterPredicate="filterPredicate"
:filterText="filterText"
v-on:nodeSelected="checkNodeAsync($event)">
<SelectableTree
:nodes="nodes"
:selectedNodeIds="selectedNodeIds"
:filterPredicate="filterPredicate"
:filterText="filterText"
v-on:nodeSelected="checkNodeAsync($event)">
</SelectableTree>
</span>
<span v-else>Nooo 😢</span>
@@ -19,9 +19,11 @@
import { Category } from '@/domain/Category';
import { IRepository } from '@/infrastructure/Repository/IRepository';
import { IScript } from '@/domain/IScript';
import { ICategory } from '@/domain/ICategory';
import { IApplicationState, IUserSelection } from '@/application/State/IApplicationState';
import { IFilterMatches } from '@/application/State/Filter/IFilterMatches';
import { parseAllCategories, parseSingleCategory } from './ScriptNodeParser';
import { IFilterResult } from '@/application/State/Filter/IFilterResult';
import { parseAllCategories, parseSingleCategory, getScriptNodeId, getCategoryNodeId } from './ScriptNodeParser';
import SelectableTree, { FilterPredicate } from './SelectableTree/SelectableTree.vue';
import { INode } from './SelectableTree/INode';
@@ -37,11 +39,11 @@
public selectedNodeIds?: string[] = null;
public filterText?: string = null;
private matches?: IFilterMatches;
private filtered?: IFilterResult;
public async mounted() {
// React to state changes
const state = await this.getCurrentStateAsync();
// React to state changes
state.selection.changed.on(this.handleSelectionChanged);
state.filter.filterRemoved.on(this.handleFilterRemoved);
state.filter.filtered.on(this.handleFiltered);
@@ -72,7 +74,10 @@
}
public filterPredicate(node: INode): boolean {
return this.matches.scriptMatches.some((script: IScript) => script.id === node.id);
return this.filtered.scriptMatches.some(
(script: IScript) => node.id === getScriptNodeId(script))
|| this.filtered.categoryMatches.some(
(category: ICategory) => node.id === getCategoryNodeId(category));
}
private handleSelectionChanged(selectedScripts: ReadonlyArray<IScript>) {
@@ -83,9 +88,9 @@
this.filterText = '';
}
private handleFiltered(matches: IFilterMatches) {
this.filterText = matches.query;
this.matches = matches;
private handleFiltered(result: IFilterResult) {
this.filterText = result.query;
this.filtered = result;
}
}

View File

@@ -5,8 +5,18 @@
<TheGrouper class="right"
v-on:groupingChanged="onGroupingChanged($event)" />
</div>
<CardList v-if="showCards" />
<ScriptsTree v-if="showList" />
<div class="scripts">
<div v-if="!isSearching || searchHasMatches">
<CardList v-if="showCards" />
<div v-else-if="showList" class="tree">
<ScriptsTree />
</div>
</div>
<div v-else class="search-no-matches">
Search has no matches 😞
Feel free to extend the scripts <a :href="repositoryUrl" target="_blank" class="child github" >here</a>.
</div>
</div>
</div>
</template>
@@ -14,11 +24,13 @@
import { Component, Prop, Vue, Emit } from 'vue-property-decorator';
import { Category } from '@/domain/Category';
import { StatefulVue } from '@/presentation/StatefulVue';
import { Grouping } from './Grouping/Grouping';
import { IFilterResult } from '@/application/State/Filter/IFilterResult';
import TheGrouper from '@/presentation/Scripts/Grouping/TheGrouper.vue';
import TheSelector from '@/presentation/Scripts/Selector/TheSelector.vue';
import ScriptsTree from '@/presentation/Scripts/ScriptsTree/ScriptsTree.vue';
import CardList from '@/presentation/Scripts/Cards/CardList.vue';
import { Grouping } from './Grouping/Grouping';
/** Shows content of single category or many categories */
@Component({
@@ -30,33 +42,70 @@
},
})
export default class TheScripts extends StatefulVue {
public showCards = true;
public showCards = false;
public showList = false;
public repositoryUrl = '';
private isSearching = false;
private searchHasMatches = false;
@Prop() public data!: Category | Category[];
private currentGrouping: Grouping;
public async mounted() {
const state = await this.getCurrentStateAsync();
this.repositoryUrl = state.app.repositoryUrl;
state.filter.filterRemoved.on(() => {
this.isSearching = false;
this.updateGroups();
});
state.filter.filtered.on((result: IFilterResult) => {
this.isSearching = true;
this.searchHasMatches = result.hasAnyMatches();
this.updateGroups();
});
}
public onGroupingChanged(group: Grouping) {
switch (group) {
case Grouping.Cards:
this.showCards = true;
this.showList = false;
break;
case Grouping.None:
this.showCards = false;
this.showList = true;
break;
default:
throw new Error('Unknown grouping');
}
this.currentGrouping = group;
this.updateGroups();
}
private updateGroups(): void {
this.showCards = !this.isSearching && this.currentGrouping === Grouping.Cards;
this.showList = this.isSearching || this.currentGrouping === Grouping.None;
}
}
</script>
<style scoped lang="scss">
@import "@/presentation/styles/colors.scss";
@import "@/presentation/styles/fonts.scss";
.scripts {
margin-top:10px;
.search-no-matches {
word-break:break-word;
color: $white;
text-transform: uppercase;
color: $light-gray;
font-size: 1.5em;
background-color: $slate;
padding:5%;
text-align:center;
> a {
color: $gray;
}
}
.tree {
padding-left: 3%;
margin-top: 15px; // Card margin
}
}
.help-container {
display: flex;
justify-content: space-between;
align-items: center;
.center {
justify-content: center;
}
.left {
justify-content: flex-start;
}
@@ -64,4 +113,5 @@
justify-content: flex-end;
}
}
</style>