Files
privacy.sexy/src/presentation/components/Scripts/ScriptsTree/ScriptsTree.vue
undergroundwires f3c7413f52 restructure presentation layer
- Move most GUI related code to /presentation
- Move components to /components (separate from bootstrap and style)
- Move shared components helpers to /components/shared
- Rename Bootstrapping to bootstrapping to enforce same naming
  convention in /presentation
2021-03-07 19:37:54 +01:00

136 lines
4.8 KiB
Vue

<template>
<span id="container">
<span v-if="nodes != null && nodes.length > 0">
<SelectableTree
:initialNodes="nodes"
:selectedNodeIds="selectedNodeIds"
:filterPredicate="filterPredicate"
:filterText="filterText"
v-on:nodeSelected="toggleNodeSelectionAsync($event)"
>
</SelectableTree>
</span>
<span v-else>Nooo 😢</span>
</span>
</template>
<script lang="ts">
import { Component, Prop, Watch } from 'vue-property-decorator';
import { StatefulVue } from '@/presentation/components/Shared/StatefulVue';
import { IScript } from '@/domain/IScript';
import { ICategory } from '@/domain/ICategory';
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult';
import { parseAllCategories, parseSingleCategory, getScriptNodeId,
getCategoryNodeId, getCategoryId, getScriptId } from './ScriptNodeParser';
import SelectableTree from './SelectableTree/SelectableTree.vue';
import { INode, NodeType } from './SelectableTree/Node/INode';
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
import { INodeSelectedEvent } from './SelectableTree/INodeSelectedEvent';
@Component({
components: {
SelectableTree,
},
})
export default class ScriptsTree extends StatefulVue {
@Prop() public categoryId?: number;
public nodes?: ReadonlyArray<INode> = null;
public selectedNodeIds?: ReadonlyArray<string> = [];
public filterText?: string = null;
private filtered?: IFilterResult;
public async toggleNodeSelectionAsync(event: INodeSelectedEvent) {
const context = await this.getCurrentContextAsync();
switch (event.node.type) {
case NodeType.Category:
toggleCategoryNodeSelection(event, context.state);
break;
case NodeType.Script:
toggleScriptNodeSelection(event, context.state);
break;
default:
throw new Error(`Unknown node type: ${event.node.id}`);
}
}
@Watch('categoryId', { immediate: true })
public async setNodesAsync(categoryId?: number) {
const context = await this.getCurrentContextAsync();
if (categoryId) {
this.nodes = parseSingleCategory(categoryId, context.state.collection);
} else {
this.nodes = parseAllCategories(context.state.collection);
}
this.selectedNodeIds = context.state.selection.selectedScripts
.map((selected) => getScriptNodeId(selected.script));
}
public filterPredicate(node: INode): boolean {
return this.filtered.scriptMatches.some(
(script: IScript) => node.id === getScriptNodeId(script))
|| this.filtered.categoryMatches.some(
(category: ICategory) => node.id === getCategoryNodeId(category));
}
protected async handleCollectionState(newState: ICategoryCollectionState) {
this.setCurrentFilter(newState.filter.currentFilter);
if (!this.categoryId) {
this.nodes = parseAllCategories(newState.collection);
}
this.events.unsubscribeAll();
this.subscribeState(newState);
}
private subscribeState(state: ICategoryCollectionState) {
this.events.register(
state.selection.changed.on(this.handleSelectionChanged),
state.filter.filterRemoved.on(this.handleFilterRemoved),
state.filter.filtered.on(this.handleFiltered),
);
}
private setCurrentFilter(currentFilter: IFilterResult | undefined) {
if (!currentFilter) {
this.handleFilterRemoved();
} else {
this.handleFiltered(currentFilter);
}
}
private handleSelectionChanged(selectedScripts: ReadonlyArray<SelectedScript>): void {
this.selectedNodeIds = selectedScripts
.map((node) => node.id);
}
private handleFilterRemoved() {
this.filterText = '';
}
private handleFiltered(result: IFilterResult) {
this.filterText = result.query;
this.filtered = result;
}
}
function toggleCategoryNodeSelection(event: INodeSelectedEvent, state: ICategoryCollectionState): void {
const categoryId = getCategoryId(event.node.id);
if (event.isSelected) {
state.selection.addOrUpdateAllInCategory(categoryId, false);
} else {
state.selection.removeAllInCategory(categoryId);
}
}
function toggleScriptNodeSelection(event: INodeSelectedEvent, state: ICategoryCollectionState): void {
const scriptId = getScriptId(event.node.id);
const actualToggleState = state.selection.isSelected(scriptId);
const targetToggleState = event.isSelected;
if (targetToggleState && !actualToggleState) {
state.selection.addSelectedScript(scriptId, false);
} else if (!targetToggleState && actualToggleState) {
state.selection.removeSelectedScript(scriptId);
}
}
</script>
<style scoped lang="scss">
</style>