@finos/legend-query-builder 4.14.10 → 4.14.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/__lib__/QueryBuilderTesting.d.ts +3 -1
- package/lib/__lib__/QueryBuilderTesting.d.ts.map +1 -1
- package/lib/__lib__/QueryBuilderTesting.js +3 -0
- package/lib/__lib__/QueryBuilderTesting.js.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.d.ts.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.js +91 -44
- package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.js.map +1 -1
- package/lib/components/shared/LambdaEditor.d.ts.map +1 -1
- package/lib/components/shared/LambdaEditor.js +10 -5
- package/lib/components/shared/LambdaEditor.js.map +1 -1
- package/lib/components/shared/QueryBuilderPropertyInfoTooltip.d.ts.map +1 -1
- package/lib/components/shared/QueryBuilderPropertyInfoTooltip.js +12 -11
- 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/fetch-structure/tds/QueryResultSetModifierState.d.ts +2 -2
- package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.d.ts.map +1 -1
- package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.js +7 -7
- package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.js.map +1 -1
- package/package.json +5 -5
- package/src/__lib__/QueryBuilderTesting.ts +3 -0
- package/src/components/fetch-structure/QueryBuilderResultModifierPanel.tsx +185 -94
- package/src/components/shared/LambdaEditor.tsx +10 -8
- package/src/components/shared/QueryBuilderPropertyInfoTooltip.tsx +81 -65
- package/src/stores/fetch-structure/tds/QueryResultSetModifierState.ts +7 -12
@@ -31,20 +31,32 @@ import {
|
|
31
31
|
MenuContent,
|
32
32
|
MenuContentItem,
|
33
33
|
ModalFooterButton,
|
34
|
+
InputWithInlineValidation,
|
34
35
|
} from '@finos/legend-art';
|
35
36
|
import { SortColumnState } from '../../stores/fetch-structure/tds/QueryResultSetModifierState.js';
|
36
|
-
import {
|
37
|
+
import {
|
38
|
+
addUniqueEntry,
|
39
|
+
deleteEntry,
|
40
|
+
guaranteeNonNullable,
|
41
|
+
} from '@finos/legend-shared';
|
37
42
|
import { useApplicationStore } from '@finos/legend-application';
|
38
43
|
import type { QueryBuilderTDSState } from '../../stores/fetch-structure/tds/QueryBuilderTDSState.js';
|
39
44
|
import type { QueryBuilderTDSColumnState } from '../../stores/fetch-structure/tds/QueryBuilderTDSColumnState.js';
|
40
45
|
import { COLUMN_SORT_TYPE } from '../../graph/QueryBuilderMetaModelConst.js';
|
46
|
+
import { useEffect, useState } from 'react';
|
47
|
+
import type { QueryBuilderProjectionColumnState } from '../../stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.js';
|
48
|
+
import { QUERY_BUILDER_TEST_ID } from '../../__lib__/QueryBuilderTesting.js';
|
41
49
|
|
42
50
|
const ColumnSortEditor = observer(
|
43
|
-
(props: {
|
44
|
-
|
51
|
+
(props: {
|
52
|
+
sortColumns: SortColumnState[];
|
53
|
+
setSortColumns: (sortColumns: SortColumnState[]) => void;
|
54
|
+
sortState: SortColumnState;
|
55
|
+
tdsColumns: QueryBuilderTDSColumnState[];
|
56
|
+
}) => {
|
57
|
+
const { sortColumns, setSortColumns, sortState, tdsColumns } = props;
|
45
58
|
const applicationStore = useApplicationStore();
|
46
|
-
const
|
47
|
-
const projectionOptions = tdsState.tdsColumns
|
59
|
+
const projectionOptions = tdsColumns
|
48
60
|
.filter(
|
49
61
|
(projectionCol) =>
|
50
62
|
projectionCol === sortState.columnState ||
|
@@ -58,6 +70,8 @@ const ColumnSortEditor = observer(
|
|
58
70
|
label: sortState.columnState.columnName,
|
59
71
|
value: sortState,
|
60
72
|
};
|
73
|
+
const sortType = sortState.sortType;
|
74
|
+
|
61
75
|
const onChange = (
|
62
76
|
val: { label: string; value: QueryBuilderTDSColumnState } | null,
|
63
77
|
): void => {
|
@@ -65,13 +79,17 @@ const ColumnSortEditor = observer(
|
|
65
79
|
sortState.setColumnState(val.value);
|
66
80
|
}
|
67
81
|
};
|
68
|
-
const sortType = sortState.sortType;
|
69
82
|
|
70
|
-
const deleteColumnSort = (): void =>
|
71
|
-
|
83
|
+
const deleteColumnSort = (): void => {
|
84
|
+
const newSortColumns = [...sortColumns];
|
85
|
+
deleteEntry(newSortColumns, sortState);
|
86
|
+
setSortColumns(newSortColumns);
|
87
|
+
};
|
88
|
+
|
72
89
|
const changeSortBy = (sortOp: COLUMN_SORT_TYPE) => (): void => {
|
73
90
|
sortState.setSortType(sortOp);
|
74
91
|
};
|
92
|
+
|
75
93
|
return (
|
76
94
|
<div className="panel__content__form__section__list__item query-builder__projection__options__sort">
|
77
95
|
<CustomSelectorInput
|
@@ -120,6 +138,9 @@ const ColumnSortEditor = observer(
|
|
120
138
|
onClick={deleteColumnSort}
|
121
139
|
tabIndex={-1}
|
122
140
|
title="Remove"
|
141
|
+
data-testid={
|
142
|
+
QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_MODIFIER_PANEL_SORT_REMOVE_BTN
|
143
|
+
}
|
123
144
|
>
|
124
145
|
<TimesIcon />
|
125
146
|
</button>
|
@@ -129,11 +150,15 @@ const ColumnSortEditor = observer(
|
|
129
150
|
);
|
130
151
|
|
131
152
|
const ColumnsSortEditor = observer(
|
132
|
-
(props: {
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
153
|
+
(props: {
|
154
|
+
projectionColumns: QueryBuilderProjectionColumnState[];
|
155
|
+
sortColumns: SortColumnState[];
|
156
|
+
setSortColumns: (sortColumns: SortColumnState[]) => void;
|
157
|
+
tdsColumns: QueryBuilderTDSColumnState[];
|
158
|
+
}) => {
|
159
|
+
const { projectionColumns, sortColumns, setSortColumns, tdsColumns } =
|
160
|
+
props;
|
161
|
+
const projectionOptions = projectionColumns
|
137
162
|
.filter(
|
138
163
|
(projectionCol) =>
|
139
164
|
!sortColumns.some((sortCol) => sortCol.columnState === projectionCol),
|
@@ -147,7 +172,9 @@ const ColumnsSortEditor = observer(
|
|
147
172
|
const sortColumn = new SortColumnState(
|
148
173
|
guaranteeNonNullable(projectionOptions[0]).value,
|
149
174
|
);
|
150
|
-
|
175
|
+
const newSortColumns = [...sortColumns];
|
176
|
+
addUniqueEntry(newSortColumns, sortColumn);
|
177
|
+
setSortColumns(newSortColumns);
|
151
178
|
}
|
152
179
|
};
|
153
180
|
|
@@ -166,8 +193,10 @@ const ColumnsSortEditor = observer(
|
|
166
193
|
{sortColumns.map((value) => (
|
167
194
|
<ColumnSortEditor
|
168
195
|
key={value.columnState.uuid}
|
169
|
-
|
196
|
+
sortColumns={sortColumns}
|
197
|
+
setSortColumns={setSortColumns}
|
170
198
|
sortState={value}
|
199
|
+
tdsColumns={tdsColumns}
|
171
200
|
/>
|
172
201
|
))}
|
173
202
|
</div>
|
@@ -187,77 +216,140 @@ const ColumnsSortEditor = observer(
|
|
187
216
|
},
|
188
217
|
);
|
189
218
|
|
219
|
+
const cloneSortColumnStateArray = (
|
220
|
+
sortColumns: SortColumnState[],
|
221
|
+
): SortColumnState[] =>
|
222
|
+
sortColumns.map((sortColumn) => {
|
223
|
+
const newSortColumn = new SortColumnState(sortColumn.columnState);
|
224
|
+
newSortColumn.setSortType(sortColumn.sortType);
|
225
|
+
return newSortColumn;
|
226
|
+
});
|
227
|
+
|
190
228
|
export const QueryResultModifierModal = observer(
|
191
229
|
(props: { tdsState: QueryBuilderTDSState }) => {
|
192
|
-
|
230
|
+
// Read current state
|
231
|
+
const { tdsState } = props;
|
193
232
|
const resultSetModifierState = tdsState.resultSetModifierState;
|
194
|
-
const
|
195
|
-
const
|
196
|
-
const
|
197
|
-
const
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
233
|
+
const stateSortColumns = resultSetModifierState.sortColumns;
|
234
|
+
const stateDistinct = resultSetModifierState.distinct;
|
235
|
+
const stateLimitResults = resultSetModifierState.limit;
|
236
|
+
const stateSlice = resultSetModifierState.slice;
|
237
|
+
|
238
|
+
// Set up temp state for modal lifecycle
|
239
|
+
const [sortColumns, setSortColumns] = useState(
|
240
|
+
cloneSortColumnStateArray(stateSortColumns),
|
241
|
+
);
|
242
|
+
const [distinct, setDistinct] = useState(stateDistinct);
|
243
|
+
const [limitResults, setLimitResults] = useState(stateLimitResults);
|
244
|
+
const [slice, setSlice] = useState<
|
245
|
+
[number | undefined, number | undefined]
|
246
|
+
>(stateSlice ?? [undefined, undefined]);
|
247
|
+
|
248
|
+
// Sync temp state with tdsState when modal is opened/closed
|
249
|
+
useEffect(() => {
|
250
|
+
setSortColumns(cloneSortColumnStateArray(stateSortColumns));
|
251
|
+
setDistinct(stateDistinct);
|
252
|
+
setLimitResults(stateLimitResults);
|
253
|
+
setSlice(stateSlice ?? [undefined, undefined]);
|
254
|
+
}, [
|
255
|
+
resultSetModifierState.showModal,
|
256
|
+
stateSortColumns,
|
257
|
+
stateDistinct,
|
258
|
+
stateLimitResults,
|
259
|
+
stateSlice,
|
260
|
+
]);
|
204
261
|
|
205
|
-
|
206
|
-
|
207
|
-
|
262
|
+
// Handle user actions
|
263
|
+
const closeModal = (): void => resultSetModifierState.setShowModal(false);
|
264
|
+
const applyChanges = (): void => {
|
265
|
+
resultSetModifierState.setSortColumns(sortColumns);
|
266
|
+
resultSetModifierState.setDistinct(distinct);
|
267
|
+
resultSetModifierState.setLimit(limitResults);
|
268
|
+
if (slice[0] !== undefined && slice[1] !== undefined) {
|
269
|
+
resultSetModifierState.setSlice([slice[0], slice[1]]);
|
270
|
+
} else {
|
271
|
+
resultSetModifierState.setSlice(undefined);
|
272
|
+
}
|
273
|
+
resultSetModifierState.setShowModal(false);
|
208
274
|
};
|
209
275
|
|
210
|
-
const
|
211
|
-
|
276
|
+
const handleLimitResultsChange: React.ChangeEventHandler<
|
277
|
+
HTMLInputElement
|
278
|
+
> = (event) => {
|
279
|
+
const val = event.target.value.replace(/[^0-9]/g, '');
|
280
|
+
setLimitResults(val === '' ? undefined : parseInt(val, 10));
|
212
281
|
};
|
213
282
|
|
214
|
-
const
|
215
|
-
|
283
|
+
const handleSliceChange = (
|
284
|
+
start: number | undefined,
|
285
|
+
end: number | undefined,
|
286
|
+
): void => {
|
287
|
+
const newSlice: [number | undefined, number | undefined] = [start, end];
|
288
|
+
setSlice(newSlice);
|
216
289
|
};
|
217
290
|
|
218
291
|
const changeSliceStart: React.ChangeEventHandler<HTMLInputElement> = (
|
219
292
|
event,
|
220
293
|
) => {
|
221
|
-
const
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
294
|
+
const val = event.target.value.replace(/[^0-9]/g, '');
|
295
|
+
if (val === '') {
|
296
|
+
handleSliceChange(undefined, slice[1]);
|
297
|
+
} else {
|
298
|
+
const start = typeof val === 'number' ? val : parseInt(val, 10);
|
299
|
+
handleSliceChange(start, slice[1]);
|
226
300
|
}
|
227
301
|
};
|
228
302
|
const changeSliceEnd: React.ChangeEventHandler<HTMLInputElement> = (
|
229
303
|
event,
|
230
304
|
) => {
|
231
|
-
const
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
305
|
+
const val = event.target.value.replace(/[^0-9]/g, '');
|
306
|
+
if (val === '') {
|
307
|
+
handleSliceChange(slice[0], undefined);
|
308
|
+
} else {
|
309
|
+
const end = typeof val === 'number' ? val : parseInt(val, 10);
|
310
|
+
handleSliceChange(slice[0], end);
|
236
311
|
}
|
237
312
|
};
|
238
313
|
|
314
|
+
// Error states
|
315
|
+
const isInvalidSlice =
|
316
|
+
(slice[0] === undefined && slice[1] !== undefined) ||
|
317
|
+
(slice[0] !== undefined && slice[1] === undefined) ||
|
318
|
+
(slice[0] !== undefined &&
|
319
|
+
slice[1] !== undefined &&
|
320
|
+
slice[0] >= slice[1]);
|
321
|
+
|
239
322
|
return (
|
240
323
|
<Dialog
|
241
324
|
open={Boolean(resultSetModifierState.showModal)}
|
242
|
-
onClose={
|
325
|
+
onClose={closeModal}
|
243
326
|
classes={{
|
244
327
|
root: 'editor-modal__root-container',
|
245
328
|
container: 'editor-modal__container',
|
246
329
|
paper: 'editor-modal__content',
|
247
330
|
}}
|
331
|
+
data-testid={QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_MODIFIER_PANEL}
|
248
332
|
>
|
249
|
-
<Modal
|
333
|
+
<Modal
|
334
|
+
darkMode={true}
|
335
|
+
className="editor-modal query-builder__projection__modal"
|
336
|
+
>
|
250
337
|
<ModalHeader title="Result Set Modifier" />
|
251
338
|
<ModalBody className="query-builder__projection__modal__body">
|
252
339
|
<div className="query-builder__projection__options">
|
253
|
-
<ColumnsSortEditor
|
340
|
+
<ColumnsSortEditor
|
341
|
+
projectionColumns={tdsState.projectionColumns}
|
342
|
+
sortColumns={sortColumns}
|
343
|
+
setSortColumns={setSortColumns}
|
344
|
+
tdsColumns={tdsState.tdsColumns}
|
345
|
+
/>
|
254
346
|
<div className="panel__content__form__section">
|
255
347
|
<div className="panel__content__form__section__header__label">
|
256
348
|
Eliminate Duplicate Rows
|
257
349
|
</div>
|
258
350
|
<div
|
259
351
|
className="panel__content__form__section__toggler"
|
260
|
-
onClick={
|
352
|
+
onClick={() => setDistinct(!distinct)}
|
261
353
|
>
|
262
354
|
<button
|
263
355
|
className={clsx(
|
@@ -277,75 +369,74 @@ export const QueryResultModifierModal = observer(
|
|
277
369
|
</div>
|
278
370
|
</div>
|
279
371
|
<div className="panel__content__form__section">
|
280
|
-
<
|
372
|
+
<label
|
373
|
+
htmlFor="query-builder__projection__modal__limit-results-input"
|
374
|
+
className="panel__content__form__section__header__label"
|
375
|
+
>
|
281
376
|
Limit Results
|
282
|
-
</
|
377
|
+
</label>
|
283
378
|
<div className="panel__content__form__section__header__prompt">
|
284
379
|
Specify the maximum total number of rows the output will
|
285
380
|
produce
|
286
381
|
</div>
|
287
382
|
<input
|
383
|
+
id="query-builder__projection__modal__limit-results-input"
|
288
384
|
className="panel__content__form__section__input panel__content__form__section__number-input"
|
289
385
|
spellCheck={false}
|
290
|
-
type="
|
386
|
+
type="text"
|
291
387
|
value={limitResults ?? ''}
|
292
|
-
onChange={
|
388
|
+
onChange={handleLimitResultsChange}
|
293
389
|
/>
|
294
390
|
</div>
|
295
391
|
<div className="panel__content__form__section">
|
296
|
-
<
|
392
|
+
<label
|
393
|
+
htmlFor="query-builder__projection__modal__slice-start-input"
|
394
|
+
className="panel__content__form__section__header__label"
|
395
|
+
>
|
297
396
|
Slice
|
298
|
-
</
|
397
|
+
</label>
|
299
398
|
<div className="panel__content__form__section__header__prompt">
|
300
399
|
Reduce the number of rows in the provided TDS, selecting the
|
301
400
|
set of rows in the specified range between start and stop
|
302
401
|
</div>
|
303
|
-
|
304
|
-
|
305
|
-
<
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
onClick={clearSlice}
|
326
|
-
tabIndex={-1}
|
327
|
-
title="Remove"
|
328
|
-
>
|
329
|
-
<TimesIcon />
|
330
|
-
</button>
|
331
|
-
</div>
|
332
|
-
</>
|
333
|
-
) : (
|
334
|
-
<div className="panel__content__form__section__list__new-item__add">
|
335
|
-
<button
|
336
|
-
className="panel__content__form__section__list__new-item__add-btn btn btn--dark"
|
337
|
-
onClick={addSlice}
|
338
|
-
tabIndex={-1}
|
339
|
-
>
|
340
|
-
Add Slice
|
341
|
-
</button>
|
402
|
+
<div className="query-builder__result__slice">
|
403
|
+
<div className="query-builder__result__slice__input__wrapper">
|
404
|
+
<InputWithInlineValidation
|
405
|
+
id="query-builder__projection__modal__slice-start-input"
|
406
|
+
className="input--dark query-builder__result__slice__input"
|
407
|
+
spellCheck={false}
|
408
|
+
value={slice[0] ?? ''}
|
409
|
+
onChange={changeSliceStart}
|
410
|
+
type="text"
|
411
|
+
error={isInvalidSlice ? 'Invalid slice' : undefined}
|
412
|
+
/>
|
413
|
+
</div>
|
414
|
+
<div className="query-builder__result__slice__range">..</div>
|
415
|
+
<div className="query-builder__result__slice__input__wrapper">
|
416
|
+
<InputWithInlineValidation
|
417
|
+
className="input--dark query-builder__result__slice__input"
|
418
|
+
spellCheck={false}
|
419
|
+
value={slice[1] ?? ''}
|
420
|
+
onChange={changeSliceEnd}
|
421
|
+
type="text"
|
422
|
+
error={isInvalidSlice ? 'Invalid slice' : undefined}
|
423
|
+
/>
|
342
424
|
</div>
|
343
|
-
|
425
|
+
</div>
|
344
426
|
</div>
|
345
427
|
</div>
|
346
428
|
</ModalBody>
|
347
429
|
<ModalFooter>
|
348
|
-
<ModalFooterButton
|
430
|
+
<ModalFooterButton
|
431
|
+
onClick={applyChanges}
|
432
|
+
text="Apply"
|
433
|
+
disabled={isInvalidSlice}
|
434
|
+
/>
|
435
|
+
<ModalFooterButton
|
436
|
+
onClick={closeModal}
|
437
|
+
text="Cancel"
|
438
|
+
type="secondary"
|
439
|
+
/>
|
349
440
|
</ModalFooter>
|
350
441
|
</Modal>
|
351
442
|
</Dialog>
|
@@ -159,6 +159,8 @@ const LambdaEditor_Inner = observer(
|
|
159
159
|
applicationStore.alertUnhandledError,
|
160
160
|
);
|
161
161
|
setExpanded(!isExpanded);
|
162
|
+
} else if (!forceExpansion && parserError) {
|
163
|
+
setExpanded(!isExpanded);
|
162
164
|
}
|
163
165
|
};
|
164
166
|
|
@@ -399,7 +401,6 @@ const LambdaEditor_Inner = observer(
|
|
399
401
|
<button
|
400
402
|
className="lambda-editor__editor__expand-btn"
|
401
403
|
onClick={toggleExpandedMode}
|
402
|
-
disabled={Boolean(parserError)}
|
403
404
|
tabIndex={-1}
|
404
405
|
title="Toggle Expand"
|
405
406
|
>
|
@@ -410,7 +411,6 @@ const LambdaEditor_Inner = observer(
|
|
410
411
|
<button
|
411
412
|
className="lambda-editor__action"
|
412
413
|
onClick={openInPopUp}
|
413
|
-
disabled={Boolean(parserError)}
|
414
414
|
tabIndex={-1}
|
415
415
|
title="Open in a popup..."
|
416
416
|
>
|
@@ -569,12 +569,14 @@ const LambdaEditor_PopUp = observer(
|
|
569
569
|
}
|
570
570
|
|
571
571
|
useEffect(() => {
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
572
|
+
if (!lambdaEditorState.parserError) {
|
573
|
+
flowResult(
|
574
|
+
lambdaEditorState.convertLambdaObjectToGrammarString({
|
575
|
+
pretty: true,
|
576
|
+
preserveCompilationError: true,
|
577
|
+
}),
|
578
|
+
).catch(applicationStore.alertUnhandledError);
|
579
|
+
}
|
578
580
|
}, [applicationStore, lambdaEditorState]);
|
579
581
|
|
580
582
|
// dispose editor
|
@@ -18,6 +18,7 @@ import {
|
|
18
18
|
ShareBoxIcon,
|
19
19
|
type TooltipPlacement,
|
20
20
|
Tooltip,
|
21
|
+
ClickAwayListener,
|
21
22
|
} from '@finos/legend-art';
|
22
23
|
import {
|
23
24
|
type AbstractProperty,
|
@@ -137,73 +138,88 @@ export const QueryBuilderPropertyInfoTooltip: React.FC<{
|
|
137
138
|
explorerState,
|
138
139
|
} = props;
|
139
140
|
|
141
|
+
const [open, setIsOpen] = useState(false);
|
142
|
+
|
140
143
|
return (
|
141
|
-
<
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
onClick={() => explorerState.highlightTreeNode(path)}
|
171
|
-
title="Show in tree"
|
172
|
-
>
|
173
|
-
<ShareBoxIcon color="white" />
|
174
|
-
</button>
|
144
|
+
<ClickAwayListener onClickAway={() => setIsOpen(false)}>
|
145
|
+
<div>
|
146
|
+
<Tooltip
|
147
|
+
arrow={true}
|
148
|
+
{...(placement !== undefined ? { placement } : {})}
|
149
|
+
classes={{
|
150
|
+
tooltip: 'query-builder__tooltip',
|
151
|
+
arrow: 'query-builder__tooltip__arrow',
|
152
|
+
tooltipPlacementRight: 'query-builder__tooltip--right',
|
153
|
+
}}
|
154
|
+
open={open}
|
155
|
+
onClose={() => setIsOpen(false)}
|
156
|
+
TransitionProps={{
|
157
|
+
// disable transition
|
158
|
+
// NOTE: somehow, this is the only workaround we have, if for example
|
159
|
+
// we set `appear = true`, the tooltip will jump out of position
|
160
|
+
timeout: 0,
|
161
|
+
}}
|
162
|
+
disableFocusListener={true}
|
163
|
+
disableHoverListener={true}
|
164
|
+
disableTouchListener={true}
|
165
|
+
title={
|
166
|
+
<div className="query-builder__tooltip__content">
|
167
|
+
<div className="query-builder__tooltip__header">{title}</div>
|
168
|
+
<div className="query-builder__tooltip__item">
|
169
|
+
<div className="query-builder__tooltip__item__label">Type</div>
|
170
|
+
<div className="query-builder__tooltip__item__value">
|
171
|
+
{type?.path ?? property.genericType.value.rawType.path}
|
172
|
+
</div>
|
175
173
|
</div>
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
174
|
+
<div className="query-builder__tooltip__item">
|
175
|
+
<div className="query-builder__tooltip__item__label">Path</div>
|
176
|
+
<div className="query-builder__tooltip__item__value">
|
177
|
+
{path}
|
178
|
+
</div>
|
179
|
+
{explorerState && (
|
180
|
+
<div className="query-builder__tooltip__item__action">
|
181
|
+
<button
|
182
|
+
onClick={() => explorerState.highlightTreeNode(path)}
|
183
|
+
title="Show in tree"
|
184
|
+
>
|
185
|
+
<ShareBoxIcon color="white" />
|
186
|
+
</button>
|
187
|
+
</div>
|
188
|
+
)}
|
189
|
+
</div>
|
190
|
+
<div className="query-builder__tooltip__item">
|
191
|
+
<div className="query-builder__tooltip__item__label">
|
192
|
+
Multiplicity
|
193
|
+
</div>
|
194
|
+
<div className="query-builder__tooltip__item__value">
|
195
|
+
{getMultiplicityDescription(property.multiplicity)}
|
196
|
+
</div>
|
197
|
+
</div>
|
198
|
+
<div className="query-builder__tooltip__item">
|
199
|
+
<div className="query-builder__tooltip__item__label">
|
200
|
+
Derived Property
|
201
|
+
</div>
|
202
|
+
<div className="query-builder__tooltip__item__value">
|
203
|
+
{property instanceof DerivedProperty ? 'Yes' : 'No'}
|
204
|
+
</div>
|
205
|
+
</div>
|
206
|
+
<div className="query-builder__tooltip__item">
|
207
|
+
<div className="query-builder__tooltip__item__label">
|
208
|
+
Mapped
|
209
|
+
</div>
|
210
|
+
<div className="query-builder__tooltip__item__value">
|
211
|
+
{isMapped ? 'Yes' : 'No'}
|
212
|
+
</div>
|
213
|
+
</div>
|
214
|
+
<QueryBuilderTaggedValueInfoTooltip
|
215
|
+
taggedValues={property.taggedValues}
|
216
|
+
/>
|
198
217
|
</div>
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
>
|
206
|
-
{children}
|
207
|
-
</Tooltip>
|
218
|
+
}
|
219
|
+
>
|
220
|
+
<div onClick={() => setIsOpen(true)}>{children}</div>
|
221
|
+
</Tooltip>
|
222
|
+
</div>
|
223
|
+
</ClickAwayListener>
|
208
224
|
);
|
209
225
|
};
|
@@ -16,12 +16,7 @@
|
|
16
16
|
|
17
17
|
import { action, computed, makeObservable, observable } from 'mobx';
|
18
18
|
import type { QueryBuilderTDSState } from './QueryBuilderTDSState.js';
|
19
|
-
import {
|
20
|
-
addUniqueEntry,
|
21
|
-
deleteEntry,
|
22
|
-
type Hashable,
|
23
|
-
hashArray,
|
24
|
-
} from '@finos/legend-shared';
|
19
|
+
import { addUniqueEntry, type Hashable, hashArray } from '@finos/legend-shared';
|
25
20
|
import { QUERY_BUILDER_STATE_HASH_STRUCTURE } from '../../QueryBuilderStateHashUtils.js';
|
26
21
|
import type { QueryBuilderTDSColumnState } from './QueryBuilderTDSColumnState.js';
|
27
22
|
import { COLUMN_SORT_TYPE } from '../../../graph/QueryBuilderMetaModelConst.js';
|
@@ -76,8 +71,8 @@ export class QueryResultSetModifierState implements Hashable {
|
|
76
71
|
slice: observable.ref,
|
77
72
|
setShowModal: action,
|
78
73
|
setLimit: action,
|
79
|
-
|
80
|
-
|
74
|
+
setDistinct: action,
|
75
|
+
setSortColumns: action,
|
81
76
|
addSortColumn: action,
|
82
77
|
updateSortColumns: action,
|
83
78
|
setSlice: action,
|
@@ -96,12 +91,12 @@ export class QueryResultSetModifierState implements Hashable {
|
|
96
91
|
this.limit = val === undefined || val <= 0 ? undefined : val;
|
97
92
|
}
|
98
93
|
|
99
|
-
|
100
|
-
this.distinct =
|
94
|
+
setDistinct(val: boolean): void {
|
95
|
+
this.distinct = val;
|
101
96
|
}
|
102
97
|
|
103
|
-
|
104
|
-
|
98
|
+
setSortColumns(val: SortColumnState[]): void {
|
99
|
+
this.sortColumns = val;
|
105
100
|
}
|
106
101
|
|
107
102
|
addSortColumn(val: SortColumnState): void {
|