add auto-highlighting of selected/updated code
This commit is contained in:
73
src/presentation/Scripts/ScriptsTree/SelectableTree/LiquorTree/LiquorTree.d.ts
vendored
Normal file
73
src/presentation/Scripts/ScriptsTree/SelectableTree/LiquorTree/LiquorTree.d.ts
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
// Two ways of typing other libraries: https://stackoverflow.com/a/53070501
|
||||
|
||||
declare module 'liquor-tree' {
|
||||
import { PluginObject } from 'vue';
|
||||
import { VueClass } from 'vue-class-component/lib/declarations';
|
||||
|
||||
// https://github.com/amsik/liquor-tree/blob/master/src/lib/Tree.js
|
||||
export interface ILiquorTree {
|
||||
readonly model: ReadonlyArray<ILiquorTreeExistingNode>;
|
||||
filter(query: string): void;
|
||||
clearFilter(): void;
|
||||
setModel(nodes: ReadonlyArray<ILiquorTreeNewNode>): void;
|
||||
}
|
||||
interface ICustomLiquorTreeData {
|
||||
type: number;
|
||||
documentationUrls: ReadonlyArray<string>;
|
||||
isReversible: boolean;
|
||||
}
|
||||
// https://github.com/amsik/liquor-tree/blob/master/src/lib/Node.js
|
||||
export interface ILiquorTreeNodeState {
|
||||
checked: boolean;
|
||||
}
|
||||
|
||||
export interface ILiquorTreeNode {
|
||||
id: string;
|
||||
data: ICustomLiquorTreeData;
|
||||
children: ReadonlyArray<ILiquorTreeNode> | undefined;
|
||||
}
|
||||
/**
|
||||
* Returned from Node tree view events.
|
||||
* See constructor in https://github.com/amsik/liquor-tree/blob/master/src/lib/Node.js
|
||||
*/
|
||||
export interface ILiquorTreeExistingNode extends ILiquorTreeNode {
|
||||
data: ILiquorTreeNodeData;
|
||||
states: ILiquorTreeNodeState | undefined;
|
||||
children: ReadonlyArray<ILiquorTreeExistingNode> | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sent to liquor tree to define of new nodes.
|
||||
* https://github.com/amsik/liquor-tree/blob/master/src/lib/Node.js
|
||||
*/
|
||||
export interface ILiquorTreeNewNode extends ILiquorTreeNode {
|
||||
text: string;
|
||||
state: ILiquorTreeNodeState | undefined;
|
||||
children: ReadonlyArray<ILiquorTreeNewNode> | undefined;
|
||||
}
|
||||
|
||||
// https://amsik.github.io/liquor-tree/#Component-Options
|
||||
export interface ILiquorTreeOptions {
|
||||
multiple: boolean;
|
||||
checkbox: boolean;
|
||||
checkOnSelect: boolean;
|
||||
autoCheckChildren: boolean;
|
||||
parentSelect: boolean;
|
||||
keyboardNavigation: boolean;
|
||||
filter: ILiquorTreeFilter;
|
||||
deletion(node: ILiquorTreeNode): boolean;
|
||||
}
|
||||
|
||||
interface ILiquorTreeNodeData extends ICustomLiquorTreeData {
|
||||
text: string;
|
||||
}
|
||||
|
||||
// https://github.com/amsik/liquor-tree/blob/master/src/components/TreeRoot.vue
|
||||
export interface ILiquorTreeFilter {
|
||||
emptyText: string;
|
||||
matcher(query: string, node: ILiquorTreeExistingNode): boolean;
|
||||
}
|
||||
|
||||
const LiquorTree: PluginObject<any> & VueClass<any>;
|
||||
export default LiquorTree;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ILiquorTreeOptions, ILiquorTreeFilter, ILiquorTreeNode } from 'liquor-tree';
|
||||
|
||||
export class LiquorTreeOptions implements ILiquorTreeOptions {
|
||||
public multiple = true;
|
||||
public checkbox = true;
|
||||
public checkOnSelect = true;
|
||||
/* For checkbox mode only. Children will have the same checked state as their parent.
|
||||
This is false as it's handled manually to be able to batch select for performance + highlighting */
|
||||
public autoCheckChildren = false;
|
||||
public parentSelect = false;
|
||||
public keyboardNavigation = true;
|
||||
constructor(public filter: ILiquorTreeFilter) { }
|
||||
public deletion(node: ILiquorTreeNode): boolean {
|
||||
return false; // no op
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ILiquorTreeFilter, ILiquorTreeExistingNode } from 'liquor-tree';
|
||||
import { convertExistingToNode } from './NodeTranslator';
|
||||
import { INode } from '../Node/INode';
|
||||
|
||||
export type FilterPredicate = (node: INode) => boolean;
|
||||
|
||||
export class NodePredicateFilter implements ILiquorTreeFilter {
|
||||
public emptyText: string = '🕵️Hmm.. Can not see one 🧐';
|
||||
constructor(private readonly filterPredicate: FilterPredicate) {
|
||||
if (!filterPredicate) {
|
||||
throw new Error('filterPredicate is undefined');
|
||||
}
|
||||
}
|
||||
public matcher(query: string, node: ILiquorTreeExistingNode): boolean {
|
||||
return this.filterPredicate(convertExistingToNode(node));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { ILiquorTreeExistingNode, ILiquorTreeNewNode, ILiquorTreeNodeState, ILiquorTreeNode } from 'liquor-tree';
|
||||
import { NodeType } from './../Node/INode';
|
||||
|
||||
export function updateNodesCheckedState(
|
||||
oldNodes: ReadonlyArray<ILiquorTreeExistingNode>,
|
||||
selectedNodeIds: ReadonlyArray<string>): ReadonlyArray<ILiquorTreeNewNode> {
|
||||
const result = new Array<ILiquorTreeNewNode>();
|
||||
for (const oldNode of oldNodes) {
|
||||
const newState = oldNode.states;
|
||||
newState.checked = getNewCheckedState(oldNode, selectedNodeIds);
|
||||
const newNode: ILiquorTreeNewNode = {
|
||||
id: oldNode.id,
|
||||
text: oldNode.data.text,
|
||||
data: {
|
||||
type: oldNode.data.type,
|
||||
documentationUrls: oldNode.data.documentationUrls,
|
||||
isReversible: oldNode.data.isReversible,
|
||||
},
|
||||
children: !oldNode.children ? [] : updateNodesCheckedState(oldNode.children, selectedNodeIds),
|
||||
state: newState,
|
||||
};
|
||||
result.push(newNode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getNewCheckedState(
|
||||
oldNode: ILiquorTreeNode,
|
||||
selectedNodeIds: ReadonlyArray<string>): boolean {
|
||||
switch (oldNode.data.type) {
|
||||
case NodeType.Script:
|
||||
return selectedNodeIds.some((id) => id === oldNode.id);
|
||||
case NodeType.Category:
|
||||
return parseAllSubScriptIds(oldNode).every((id) => selectedNodeIds.includes(id));
|
||||
default:
|
||||
throw new Error('Unknown node type');
|
||||
}
|
||||
}
|
||||
|
||||
function parseAllSubScriptIds(categoryNode: ILiquorTreeNode): ReadonlyArray<string> {
|
||||
if (categoryNode.data.type !== NodeType.Category) {
|
||||
throw new Error('Not a category node');
|
||||
}
|
||||
if (!categoryNode.children) {
|
||||
return [];
|
||||
}
|
||||
const ids = new Array<string>();
|
||||
for (const child of categoryNode.children) {
|
||||
addNodeIds(child, ids);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
function addNodeIds(node: ILiquorTreeNode, ids: string[]) {
|
||||
switch (node.data.type) {
|
||||
case NodeType.Script:
|
||||
ids.push(node.id);
|
||||
break;
|
||||
case NodeType.Category:
|
||||
const subCategoryIds = parseAllSubScriptIds(node);
|
||||
ids.push(...subCategoryIds);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unknown node type');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { ILiquorTreeNewNode, ILiquorTreeExistingNode } from 'liquor-tree';
|
||||
import { INode } from './../Node/INode';
|
||||
|
||||
// Functions to translate INode to LiqourTree models and vice versa for anti-corruption
|
||||
|
||||
export function convertExistingToNode(liquorTreeNode: ILiquorTreeExistingNode): INode {
|
||||
if (!liquorTreeNode) { throw new Error('liquorTreeNode is undefined'); }
|
||||
return {
|
||||
id: liquorTreeNode.id,
|
||||
type: liquorTreeNode.data.type,
|
||||
text: liquorTreeNode.data.text,
|
||||
// selected: liquorTreeNode.states && liquorTreeNode.states.checked,
|
||||
children: convertChildren(liquorTreeNode.children, convertExistingToNode),
|
||||
documentationUrls: liquorTreeNode.data.documentationUrls,
|
||||
isReversible : liquorTreeNode.data.isReversible,
|
||||
};
|
||||
}
|
||||
|
||||
export function toNewLiquorTreeNode(node: INode): ILiquorTreeNewNode {
|
||||
if (!node) { throw new Error('node is undefined'); }
|
||||
return {
|
||||
id: node.id,
|
||||
text: node.text,
|
||||
state: {
|
||||
checked: false,
|
||||
},
|
||||
children: convertChildren(node.children, toNewLiquorTreeNode),
|
||||
data: {
|
||||
documentationUrls: node.documentationUrls,
|
||||
isReversible: node.isReversible,
|
||||
type: node.type,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function convertChildren<TOldNode, TNewNode>(
|
||||
oldChildren: readonly TOldNode[],
|
||||
callback: (value: TOldNode) => TNewNode): TNewNode[] {
|
||||
if (!oldChildren || oldChildren.length === 0) {
|
||||
return [];
|
||||
}
|
||||
return oldChildren.map((childNode) => callback(childNode));
|
||||
}
|
||||
Reference in New Issue
Block a user