@finos/legend-query-builder 4.14.7 → 4.14.9
Sign up to get free protection for your applications and to get access to all the features.
- package/lib/components/QueryBuilderPropertyExpressionEditor.d.ts +2 -0
- package/lib/components/QueryBuilderPropertyExpressionEditor.d.ts.map +1 -1
- package/lib/components/QueryBuilderPropertyExpressionEditor.js +6 -3
- package/lib/components/QueryBuilderPropertyExpressionEditor.js.map +1 -1
- package/lib/components/explorer/QueryBuilderExplorerPanel.d.ts.map +1 -1
- package/lib/components/explorer/QueryBuilderExplorerPanel.js +12 -9
- package/lib/components/explorer/QueryBuilderExplorerPanel.js.map +1 -1
- package/lib/components/explorer/QueryBuilderPropertySearchPanel.d.ts.map +1 -1
- package/lib/components/explorer/QueryBuilderPropertySearchPanel.js +6 -5
- package/lib/components/explorer/QueryBuilderPropertySearchPanel.js.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +19 -5
- package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
- package/lib/components/shared/QueryBuilderPropertyInfoTooltip.d.ts +3 -0
- package/lib/components/shared/QueryBuilderPropertyInfoTooltip.d.ts.map +1 -1
- package/lib/components/shared/QueryBuilderPropertyInfoTooltip.js +3 -3
- package/lib/components/shared/QueryBuilderPropertyInfoTooltip.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/explorer/QueryBuilderExplorerState.d.ts +5 -0
- package/lib/stores/explorer/QueryBuilderExplorerState.d.ts.map +1 -1
- package/lib/stores/explorer/QueryBuilderExplorerState.js +32 -0
- package/lib/stores/explorer/QueryBuilderExplorerState.js.map +1 -1
- package/package.json +5 -5
- package/src/components/QueryBuilderPropertyExpressionEditor.tsx +16 -2
- package/src/components/explorer/QueryBuilderExplorerPanel.tsx +18 -8
- package/src/components/explorer/QueryBuilderPropertySearchPanel.tsx +8 -5
- package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +65 -24
- package/src/components/shared/QueryBuilderPropertyInfoTooltip.tsx +29 -2
- package/src/stores/explorer/QueryBuilderExplorerState.ts +35 -0
@@ -144,8 +144,9 @@ const QueryBuilderProjectionColumnContextMenu = observer(
|
|
144
144
|
const QueryBuilderSimpleProjectionColumnEditor = observer(
|
145
145
|
(props: {
|
146
146
|
projectionColumnState: QueryBuilderSimpleProjectionColumnState;
|
147
|
+
setIsEditingColumnName: (isEditing: boolean) => void;
|
147
148
|
}) => {
|
148
|
-
const { projectionColumnState } = props;
|
149
|
+
const { projectionColumnState, setIsEditingColumnName } = props;
|
149
150
|
const onPropertyExpressionChange = (
|
150
151
|
node: QueryBuilderExplorerTreePropertyNodeData,
|
151
152
|
): void =>
|
@@ -158,10 +159,12 @@ const QueryBuilderSimpleProjectionColumnEditor = observer(
|
|
158
159
|
return (
|
159
160
|
<div className="query-builder__projection__column__value__property">
|
160
161
|
<QueryBuilderPropertyExpressionBadge
|
162
|
+
columnName={projectionColumnState.columnName}
|
161
163
|
propertyExpressionState={
|
162
164
|
projectionColumnState.propertyExpressionState
|
163
165
|
}
|
164
166
|
onPropertyExpressionChange={onPropertyExpressionChange}
|
167
|
+
setIsEditingColumnName={setIsEditingColumnName}
|
165
168
|
/>
|
166
169
|
</div>
|
167
170
|
);
|
@@ -378,6 +381,13 @@ const QueryBuilderProjectionColumnEditor = observer(
|
|
378
381
|
) => projectionColumnState.setColumnName(event.target.value);
|
379
382
|
const isDuplicatedColumnName =
|
380
383
|
projectionColumnState.tdsState.isDuplicateColumn(projectionColumnState);
|
384
|
+
const [isEditingColumnName, setIsEditingColumnName] = useState(false);
|
385
|
+
const columnNameInputRef = useRef<HTMLInputElement>(null);
|
386
|
+
useEffect(() => {
|
387
|
+
if (isEditingColumnName) {
|
388
|
+
columnNameInputRef.current?.focus();
|
389
|
+
}
|
390
|
+
}, [isEditingColumnName, columnNameInputRef]);
|
381
391
|
|
382
392
|
// aggregation
|
383
393
|
const aggregateColumnState = tdsState.aggregationState.columns.find(
|
@@ -742,29 +752,60 @@ const QueryBuilderProjectionColumnEditor = observer(
|
|
742
752
|
dragSourceConnector={dragHandleRef}
|
743
753
|
className="query-builder__projection__column__drag-handle__container"
|
744
754
|
/>
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
755
|
+
{projectionColumnState instanceof
|
756
|
+
QueryBuilderSimpleProjectionColumnState &&
|
757
|
+
(isEditingColumnName ? (
|
758
|
+
<div className="query-builder__projection__column__name">
|
759
|
+
<InputWithInlineValidation
|
760
|
+
className="query-builder__projection__column__name__input input-group__input"
|
761
|
+
spellCheck={false}
|
762
|
+
value={projectionColumnState.columnName}
|
763
|
+
onChange={changeColumnName}
|
764
|
+
onKeyDown={(
|
765
|
+
event: React.KeyboardEvent<HTMLInputElement>,
|
766
|
+
) => {
|
767
|
+
if (event.key === 'Enter') {
|
768
|
+
setIsEditingColumnName(false);
|
769
|
+
}
|
770
|
+
}}
|
771
|
+
onBlur={() => {
|
772
|
+
setIsEditingColumnName(false);
|
773
|
+
}}
|
774
|
+
error={
|
775
|
+
isDuplicatedColumnName ? 'Duplicated column' : undefined
|
776
|
+
}
|
777
|
+
ref={columnNameInputRef}
|
778
|
+
/>
|
779
|
+
</div>
|
780
|
+
) : (
|
781
|
+
<div className="query-builder__projection__column__value">
|
782
|
+
<QueryBuilderSimpleProjectionColumnEditor
|
783
|
+
projectionColumnState={projectionColumnState}
|
784
|
+
setIsEditingColumnName={setIsEditingColumnName}
|
785
|
+
/>
|
786
|
+
</div>
|
787
|
+
))}
|
788
|
+
{projectionColumnState instanceof
|
789
|
+
QueryBuilderDerivationProjectionColumnState && (
|
790
|
+
<>
|
791
|
+
<div className="query-builder__projection__column__name query-builder__projection__column__name__derivation">
|
792
|
+
<InputWithInlineValidation
|
793
|
+
className="query-builder__projection__column__name__input input-group__input"
|
794
|
+
spellCheck={false}
|
795
|
+
value={projectionColumnState.columnName}
|
796
|
+
onChange={changeColumnName}
|
797
|
+
error={
|
798
|
+
isDuplicatedColumnName ? 'Duplicated column' : undefined
|
799
|
+
}
|
800
|
+
/>
|
801
|
+
</div>
|
802
|
+
<div className="query-builder__projection__column__value">
|
803
|
+
<QueryBuilderDerivationProjectionColumnEditor
|
804
|
+
projectionColumnState={projectionColumnState}
|
805
|
+
/>
|
806
|
+
</div>
|
807
|
+
</>
|
808
|
+
)}
|
768
809
|
<div className="query-builder__projection__column__aggregate">
|
769
810
|
<div className="query-builder__projection__column__aggregate__operator">
|
770
811
|
{aggregateColumnState && (
|
@@ -14,7 +14,11 @@
|
|
14
14
|
* limitations under the License.
|
15
15
|
*/
|
16
16
|
|
17
|
-
import {
|
17
|
+
import {
|
18
|
+
ShareBoxIcon,
|
19
|
+
type TooltipPlacement,
|
20
|
+
Tooltip,
|
21
|
+
} from '@finos/legend-art';
|
18
22
|
import {
|
19
23
|
type AbstractProperty,
|
20
24
|
type Type,
|
@@ -25,6 +29,7 @@ import {
|
|
25
29
|
PURE_DOC_TAG,
|
26
30
|
} from '@finos/legend-graph';
|
27
31
|
import { useState } from 'react';
|
32
|
+
import type { QueryBuilderExplorerState } from '../../stores/explorer/QueryBuilderExplorerState.js';
|
28
33
|
|
29
34
|
export const QueryBuilderTaggedValueInfoTooltip: React.FC<{
|
30
35
|
taggedValues: TaggedValue[];
|
@@ -112,14 +117,25 @@ export const QueryBuilderTaggedValueInfoTooltip: React.FC<{
|
|
112
117
|
};
|
113
118
|
|
114
119
|
export const QueryBuilderPropertyInfoTooltip: React.FC<{
|
120
|
+
title: string;
|
115
121
|
property: AbstractProperty;
|
116
122
|
path: string;
|
117
123
|
isMapped: boolean;
|
118
124
|
children: React.ReactElement;
|
119
125
|
placement?: TooltipPlacement | undefined;
|
120
126
|
type?: Type | undefined;
|
127
|
+
explorerState?: QueryBuilderExplorerState | undefined;
|
121
128
|
}> = (props) => {
|
122
|
-
const {
|
129
|
+
const {
|
130
|
+
title,
|
131
|
+
property,
|
132
|
+
path,
|
133
|
+
isMapped,
|
134
|
+
children,
|
135
|
+
placement,
|
136
|
+
type,
|
137
|
+
explorerState,
|
138
|
+
} = props;
|
123
139
|
|
124
140
|
return (
|
125
141
|
<Tooltip
|
@@ -138,6 +154,7 @@ export const QueryBuilderPropertyInfoTooltip: React.FC<{
|
|
138
154
|
}}
|
139
155
|
title={
|
140
156
|
<div className="query-builder__tooltip__content">
|
157
|
+
<div className="query-builder__tooltip__header">{title}</div>
|
141
158
|
<div className="query-builder__tooltip__item">
|
142
159
|
<div className="query-builder__tooltip__item__label">Type</div>
|
143
160
|
<div className="query-builder__tooltip__item__value">
|
@@ -147,6 +164,16 @@ export const QueryBuilderPropertyInfoTooltip: React.FC<{
|
|
147
164
|
<div className="query-builder__tooltip__item">
|
148
165
|
<div className="query-builder__tooltip__item__label">Path</div>
|
149
166
|
<div className="query-builder__tooltip__item__value">{path}</div>
|
167
|
+
{explorerState && (
|
168
|
+
<div className="query-builder__tooltip__item__action">
|
169
|
+
<button
|
170
|
+
onClick={() => explorerState.highlightTreeNode(path)}
|
171
|
+
title="Show in tree"
|
172
|
+
>
|
173
|
+
<ShareBoxIcon color="white" />
|
174
|
+
</button>
|
175
|
+
</div>
|
176
|
+
)}
|
150
177
|
</div>
|
151
178
|
<div className="query-builder__tooltip__item">
|
152
179
|
<div className="query-builder__tooltip__item__label">
|
@@ -72,6 +72,7 @@ import { QueryBuilderPropertySearchState } from './QueryBuilderPropertySearchSta
|
|
72
72
|
import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../graph/QueryBuilderMetaModelConst.js';
|
73
73
|
import { propertyExpression_setFunc } from '../shared/ValueSpecificationModifierHelper.js';
|
74
74
|
import { QueryBuilderTelemetryHelper } from '../../__lib__/QueryBuilderTelemetryHelper.js';
|
75
|
+
import { createRef } from 'react';
|
75
76
|
|
76
77
|
export enum QUERY_BUILDER_EXPLORER_TREE_DND_TYPE {
|
77
78
|
ROOT = 'ROOT',
|
@@ -97,6 +98,7 @@ export interface QueryBuilderExplorerTreeDragSource {
|
|
97
98
|
export abstract class QueryBuilderExplorerTreeNodeData implements TreeNodeData {
|
98
99
|
isSelected?: boolean | undefined;
|
99
100
|
isOpen?: boolean | undefined;
|
101
|
+
isHighlighting?: boolean | undefined;
|
100
102
|
id: string;
|
101
103
|
label: string;
|
102
104
|
dndText: string;
|
@@ -104,6 +106,7 @@ export abstract class QueryBuilderExplorerTreeNodeData implements TreeNodeData {
|
|
104
106
|
isPartOfDerivedPropertyBranch: boolean;
|
105
107
|
type: Type;
|
106
108
|
mappingData: QueryBuilderExplorerTreeNodeMappingData;
|
109
|
+
elementRef: React.RefObject<HTMLDivElement>;
|
107
110
|
|
108
111
|
constructor(
|
109
112
|
id: string,
|
@@ -114,7 +117,9 @@ export abstract class QueryBuilderExplorerTreeNodeData implements TreeNodeData {
|
|
114
117
|
mappingData: QueryBuilderExplorerTreeNodeMappingData,
|
115
118
|
) {
|
116
119
|
makeObservable(this, {
|
120
|
+
isHighlighting: observable,
|
117
121
|
isSelected: observable,
|
122
|
+
setIsHighlighting: action,
|
118
123
|
setIsSelected: action,
|
119
124
|
});
|
120
125
|
|
@@ -124,11 +129,16 @@ export abstract class QueryBuilderExplorerTreeNodeData implements TreeNodeData {
|
|
124
129
|
this.isPartOfDerivedPropertyBranch = isPartOfDerivedPropertyBranch;
|
125
130
|
this.type = type;
|
126
131
|
this.mappingData = mappingData;
|
132
|
+
this.elementRef = createRef();
|
127
133
|
}
|
128
134
|
|
129
135
|
setIsSelected(val: boolean | undefined): void {
|
130
136
|
this.isSelected = val;
|
131
137
|
}
|
138
|
+
|
139
|
+
setIsHighlighting(val: boolean | undefined): void {
|
140
|
+
this.isHighlighting = val;
|
141
|
+
}
|
132
142
|
}
|
133
143
|
|
134
144
|
export type QueryBuilderExplorerTreeNodeMappingData = {
|
@@ -663,6 +673,7 @@ export class QueryBuilderExplorerState {
|
|
663
673
|
setHumanizePropertyName: action,
|
664
674
|
setShowUnmappedProperties: action,
|
665
675
|
setHighlightUsedProperties: action,
|
676
|
+
highlightTreeNode: action,
|
666
677
|
analyzeMappingModelCoverage: flow,
|
667
678
|
previewData: flow,
|
668
679
|
});
|
@@ -717,6 +728,30 @@ export class QueryBuilderExplorerState {
|
|
717
728
|
);
|
718
729
|
}
|
719
730
|
|
731
|
+
highlightTreeNode(key: string): void {
|
732
|
+
const nodeToHighlight = this.treeData?.nodes.get(key);
|
733
|
+
if (nodeToHighlight instanceof QueryBuilderExplorerTreePropertyNodeData) {
|
734
|
+
let nodeToOpen: QueryBuilderExplorerTreeNodeData | null =
|
735
|
+
this.treeData?.nodes.get(nodeToHighlight.parentId) ?? null;
|
736
|
+
while (nodeToOpen !== null) {
|
737
|
+
if (!nodeToOpen.isOpen) {
|
738
|
+
nodeToOpen.isOpen = true;
|
739
|
+
}
|
740
|
+
nodeToOpen =
|
741
|
+
nodeToOpen instanceof QueryBuilderExplorerTreePropertyNodeData
|
742
|
+
? this.treeData?.nodes.get(nodeToOpen.parentId) ?? null
|
743
|
+
: null;
|
744
|
+
}
|
745
|
+
this.refreshTree();
|
746
|
+
nodeToHighlight.setIsHighlighting(true);
|
747
|
+
// scrollIntoView must be called in a setTimeout because it must happen after
|
748
|
+
// the tree nodes are recursively opened and the tree is refreshed.
|
749
|
+
setTimeout(() => {
|
750
|
+
nodeToHighlight.elementRef.current?.scrollIntoView();
|
751
|
+
}, 0);
|
752
|
+
}
|
753
|
+
}
|
754
|
+
|
720
755
|
*analyzeMappingModelCoverage(): GeneratorFn<void> {
|
721
756
|
// We will only refetch if the analysis result's mapping has changed.
|
722
757
|
// This makes the assumption that the mapping has not been edited, which is a valid assumption since query is not for editing mappings
|