From 4c8613484f2c700d3f7ce83e8ac3657f5f8cb6df Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 15 Sep 2023 16:45:48 +0200 Subject: [PATCH 1/4] Fixed multiple issues and improved overall UX --- .../private-components/tag.tsx | 40 +++++++++++-- .../private-utils/treeData.ts | 11 ---- .../SmartNodeSelector/smartNodeSelector.tsx | 58 ++++++++++--------- .../VectorSelector/vectorSelector.tsx | 2 - 4 files changed, 66 insertions(+), 45 deletions(-) diff --git a/frontend/src/lib/components/SmartNodeSelector/private-components/tag.tsx b/frontend/src/lib/components/SmartNodeSelector/private-components/tag.tsx index f9734183c..38b476cf5 100644 --- a/frontend/src/lib/components/SmartNodeSelector/private-components/tag.tsx +++ b/frontend/src/lib/components/SmartNodeSelector/private-components/tag.tsx @@ -33,6 +33,7 @@ type TagProps = { removeTag: (e: React.MouseEvent, index: number) => void; updateSelectedTagsAndNodes: () => void; shake: boolean; + maxNumSelectedNodes: number; }; /** @@ -126,13 +127,29 @@ export class Tag extends React.Component { private createMatchesCounter(nodeSelection: TreeNodeSelection, index: number): JSX.Element | null { if (nodeSelection.containsWildcard() && nodeSelection.countExactlyMatchedNodePaths() > 0) { const matches = nodeSelection.countExactlyMatchedNodePaths(); + let fromMax = ""; + let title = "This expression matches " + matches + " options."; + if (this.props.maxNumSelectedNodes !== -1 && matches > this.props.maxNumSelectedNodes) { + fromMax = " / " + this.props.maxNumSelectedNodes; + title = `This expression matches ${matches} node${matches !== 1 && "s"}, but only ${ + this.props.maxNumSelectedNodes + } can be selected.`; + } + return ( this.props.maxNumSelectedNodes, + } + )} + title={title} > {matches} + {fromMax} ); } @@ -227,8 +244,13 @@ export class Tag extends React.Component { } private tagTitle(nodeSelection: TreeNodeSelection, index: number): string { - const { countTags, checkIfDuplicate } = this.props; - if (index === countTags - 1 && !nodeSelection.displayAsTag()) { + const { countTags, checkIfDuplicate, maxNumSelectedNodes } = this.props; + if ( + index === countTags - 1 && + !nodeSelection.displayAsTag() && + nodeSelection.displayText() === "" && + maxNumSelectedNodes !== 1 + ) { return "Enter a new name"; } else if (!nodeSelection.isValid()) { return "Invalid"; @@ -237,7 +259,15 @@ export class Tag extends React.Component { } else if (!nodeSelection.isComplete()) { return "Incomplete"; } else { - return nodeSelection.exactlyMatchedNodePaths().join("\n"); + const exactlyMatchedNodePaths = nodeSelection.exactlyMatchedNodePaths(); + if (exactlyMatchedNodePaths.length === -1 || exactlyMatchedNodePaths.length <= maxNumSelectedNodes) { + return exactlyMatchedNodePaths.join("\n"); + } + return `Matched ${exactlyMatchedNodePaths.length} node${ + exactlyMatchedNodePaths.length !== 1 && "s" + } but only ${maxNumSelectedNodes} ${ + maxNumSelectedNodes === 1 ? "is" : "are" + } allowed.\n\nSelected nodes:\n${exactlyMatchedNodePaths.slice(0, maxNumSelectedNodes).join("\n")}`; } } diff --git a/frontend/src/lib/components/SmartNodeSelector/private-utils/treeData.ts b/frontend/src/lib/components/SmartNodeSelector/private-utils/treeData.ts index 23b71c166..47771a77d 100644 --- a/frontend/src/lib/components/SmartNodeSelector/private-utils/treeData.ts +++ b/frontend/src/lib/components/SmartNodeSelector/private-utils/treeData.ts @@ -12,25 +12,21 @@ export class TreeData { private _stringifiedData: string; private _nodeData: TreeDataNodeMetaData[]; private _allowOrOperator: boolean; - private _allowWildcards: boolean; constructor({ treeData, delimiter, allowOrOperator, - allowWildcards, }: { treeData: TreeDataNode[]; delimiter: string; allowOrOperator: boolean; - allowWildcards: boolean; }) { this._treeData = treeData; this._delimiter = delimiter; this._nodeData = []; this._stringifiedData = ""; this._allowOrOperator = allowOrOperator; - this._allowWildcards = allowWildcards; this.populateNodes(); } @@ -129,13 +125,6 @@ export class TreeData { } private adjustNodeName(nodeName: string): string { - if (!this._allowWildcards) { - return this.replaceAll( - this.replaceAll(this.replaceAll(this.escapeRegExp(nodeName), ":", ""), "*", "\\*"), - "?", - "\\." - ); - } return this.activateOrStatements( this.replaceAll( this.replaceAll(this.replaceAll(this.escapeRegExp(nodeName), ":", ""), "*", '[^:"]*'), diff --git a/frontend/src/lib/components/SmartNodeSelector/smartNodeSelector.tsx b/frontend/src/lib/components/SmartNodeSelector/smartNodeSelector.tsx index ea60350ad..237f6de8f 100644 --- a/frontend/src/lib/components/SmartNodeSelector/smartNodeSelector.tsx +++ b/frontend/src/lib/components/SmartNodeSelector/smartNodeSelector.tsx @@ -160,7 +160,6 @@ export class SmartNodeSelectorComponent extends React.Component selection.setSelected(false)); this.updateState({ - currentTagIndex: newCurrentTagIndex === undefined ? this.countTags() - 1 : newCurrentTagIndex, + currentTagIndex: newCurrentTagIndex === undefined ? -1 : newCurrentTagIndex, callback: () => { if (showSuggestions) this.maybeShowSuggestions(); if (focusInput) this.focusCurrentTag(); @@ -1623,6 +1621,7 @@ export class SmartNodeSelectorComponent extends React.Component @@ -1647,30 +1646,35 @@ export class SmartNodeSelectorComponent extends React.Component - {nodeSelections.map((selection, index) => ( - void) => this.hideSuggestions({ callback: cb })} - removeTag={(e: React.MouseEvent, index: number) => - this.removeTag(index, true, e) - } - updateSelectedTagsAndNodes={this.updateSelectedTagsAndNodes} - shake={this.state.currentTagShaking && index === this.currentTagIndex()} - /> - ))} + {nodeSelections.map((selection, index) => { + const tag = ( + void) => this.hideSuggestions({ callback: cb })} + removeTag={(e: React.MouseEvent, index: number) => + this.removeTag(index, true, e) + } + updateSelectedTagsAndNodes={this.updateSelectedTagsAndNodes} + shake={this.state.currentTagShaking && index === this.currentTagIndex()} + maxNumSelectedNodes={numSelectedNodes === -1 ? -1 : numSelectedNodes} + /> + ); + numSelectedNodes -= selection.numberOfExactlyMatchedNodes(); + return tag; + })}