@finos/legend-query-builder 3.1.0 → 3.2.0
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__/QueryBuilderTelemetryHelper.d.ts +20 -20
- package/lib/__lib__/QueryBuilderTelemetryHelper.d.ts.map +1 -1
- package/lib/__lib__/QueryBuilderTelemetryHelper.js +40 -40
- package/lib/__lib__/QueryBuilderTelemetryHelper.js.map +1 -1
- package/lib/components/QueryLoader.d.ts +30 -0
- package/lib/components/QueryLoader.d.ts.map +1 -0
- package/lib/components/QueryLoader.js +160 -0
- package/lib/components/QueryLoader.js.map +1 -0
- package/lib/components/shared/QueryBuilderPanelIssueCountBadge.d.ts.map +1 -1
- package/lib/components/shared/QueryBuilderPanelIssueCountBadge.js +2 -1
- package/lib/components/shared/QueryBuilderPanelIssueCountBadge.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -0
- package/lib/index.js.map +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.d.ts +30 -0
- package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.d.ts.map +1 -0
- package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.js +17 -0
- package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.js.map +1 -0
- package/lib/stores/QueryLoaderState.d.ts +67 -0
- package/lib/stores/QueryLoaderState.d.ts.map +1 -0
- package/lib/stores/QueryLoaderState.js +205 -0
- package/lib/stores/QueryLoaderState.js.map +1 -0
- package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts +3 -1
- package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts.map +1 -1
- package/lib/stores/shared/ValueSpecificationEditorHelper.js +7 -1
- package/lib/stores/shared/ValueSpecificationEditorHelper.js.map +1 -1
- package/package.json +8 -8
- package/src/__lib__/QueryBuilderTelemetryHelper.ts +40 -59
- package/src/components/QueryLoader.tsx +501 -0
- package/src/components/shared/QueryBuilderPanelIssueCountBadge.tsx +2 -1
- package/src/index.ts +3 -0
- package/src/stores/QueryBuilder_LegendApplicationPlugin_Extension.ts +36 -0
- package/src/stores/QueryLoaderState.ts +298 -0
- package/src/stores/shared/ValueSpecificationEditorHelper.ts +39 -2
- package/tsconfig.json +3 -0
@@ -0,0 +1,501 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2020-present, Goldman Sachs
|
3
|
+
*
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
* you may not use this file except in compliance with the License.
|
6
|
+
* You may obtain a copy of the License at
|
7
|
+
*
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
*
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
* See the License for the specific language governing permissions and
|
14
|
+
* limitations under the License.
|
15
|
+
*/
|
16
|
+
|
17
|
+
import { useApplicationStore } from '@finos/legend-application';
|
18
|
+
import {
|
19
|
+
CODE_EDITOR_LANGUAGE,
|
20
|
+
CodeEditor,
|
21
|
+
} from '@finos/legend-lego/code-editor';
|
22
|
+
import {
|
23
|
+
Dialog,
|
24
|
+
Modal,
|
25
|
+
ModalTitle,
|
26
|
+
clsx,
|
27
|
+
SearchIcon,
|
28
|
+
TimesIcon,
|
29
|
+
DropdownMenu,
|
30
|
+
MenuContent,
|
31
|
+
MenuContentItem,
|
32
|
+
BlankPanelContent,
|
33
|
+
PanelLoadingIndicator,
|
34
|
+
ModalHeader,
|
35
|
+
ModalBody,
|
36
|
+
ModalFooter,
|
37
|
+
ModalFooterButton,
|
38
|
+
UserIcon,
|
39
|
+
LastModifiedIcon,
|
40
|
+
MoreVerticalIcon,
|
41
|
+
ThinChevronRightIcon,
|
42
|
+
InfoCircleIcon,
|
43
|
+
} from '@finos/legend-art';
|
44
|
+
import type { LightQuery } from '@finos/legend-graph';
|
45
|
+
import {
|
46
|
+
debounce,
|
47
|
+
formatDistanceToNow,
|
48
|
+
guaranteeNonNullable,
|
49
|
+
quantifyList,
|
50
|
+
} from '@finos/legend-shared';
|
51
|
+
import { flowResult } from 'mobx';
|
52
|
+
import { observer } from 'mobx-react-lite';
|
53
|
+
import { useRef, useState, useMemo, useEffect } from 'react';
|
54
|
+
import {
|
55
|
+
QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT,
|
56
|
+
type QueryLoaderState,
|
57
|
+
} from '../stores/QueryLoaderState.js';
|
58
|
+
|
59
|
+
const QueryPreviewViewer = observer(
|
60
|
+
(props: { queryLoaderState: QueryLoaderState }) => {
|
61
|
+
const { queryLoaderState } = props;
|
62
|
+
const close = (): void => {
|
63
|
+
queryLoaderState.setShowPreviewViewer(false);
|
64
|
+
};
|
65
|
+
return (
|
66
|
+
<Dialog
|
67
|
+
open={queryLoaderState.showPreviewViewer}
|
68
|
+
onClose={close}
|
69
|
+
classes={{
|
70
|
+
root: 'editor-modal__root-container',
|
71
|
+
container: 'editor-modal__container',
|
72
|
+
paper: 'editor-modal__content',
|
73
|
+
}}
|
74
|
+
>
|
75
|
+
<Modal className="editor-modal" darkMode={true}>
|
76
|
+
<ModalHeader
|
77
|
+
title={
|
78
|
+
guaranteeNonNullable(queryLoaderState.queryPreviewContent).name
|
79
|
+
}
|
80
|
+
/>
|
81
|
+
<ModalBody>
|
82
|
+
<CodeEditor
|
83
|
+
inputValue={
|
84
|
+
guaranteeNonNullable(queryLoaderState.queryPreviewContent)
|
85
|
+
.content
|
86
|
+
}
|
87
|
+
isReadOnly={true}
|
88
|
+
language={CODE_EDITOR_LANGUAGE.PURE}
|
89
|
+
showMiniMap={true}
|
90
|
+
/>
|
91
|
+
</ModalBody>
|
92
|
+
<ModalFooter>
|
93
|
+
<ModalFooterButton onClick={close} text="Close" />
|
94
|
+
</ModalFooter>
|
95
|
+
</Modal>
|
96
|
+
</Dialog>
|
97
|
+
);
|
98
|
+
},
|
99
|
+
);
|
100
|
+
|
101
|
+
export const QueryLoader = observer(
|
102
|
+
(props: { queryLoaderState: QueryLoaderState; loadActionLabel: string }) => {
|
103
|
+
const { queryLoaderState, loadActionLabel } = props;
|
104
|
+
const applicationStore = useApplicationStore();
|
105
|
+
const searchInputRef = useRef<HTMLInputElement>(null);
|
106
|
+
const queryRenameInputRef = useRef<HTMLInputElement>(null);
|
107
|
+
const results = queryLoaderState.queries;
|
108
|
+
const [isMineOnly, setIsMineOnly] = useState(false);
|
109
|
+
const [showQueryNameEditInput, setShowQueryNameEditInput] = useState<
|
110
|
+
number | undefined
|
111
|
+
>();
|
112
|
+
useEffect(() => {
|
113
|
+
queryRenameInputRef.current?.focus();
|
114
|
+
queryRenameInputRef.current?.select();
|
115
|
+
}, [showQueryNameEditInput]);
|
116
|
+
const [queryNameInputValue, setQueryNameInputValue] = useState<string>('');
|
117
|
+
const showEditQueryNameInput =
|
118
|
+
(value: string, idx: number): (() => void) =>
|
119
|
+
(): void => {
|
120
|
+
setQueryNameInputValue(value);
|
121
|
+
setShowQueryNameEditInput(idx);
|
122
|
+
};
|
123
|
+
const hideEditQueryNameInput = (): void => {
|
124
|
+
setShowQueryNameEditInput(undefined);
|
125
|
+
setQueryNameInputValue('');
|
126
|
+
};
|
127
|
+
const changeQueryNameInputValue: React.ChangeEventHandler<
|
128
|
+
HTMLInputElement
|
129
|
+
> = (event) => setQueryNameInputValue(event.target.value);
|
130
|
+
|
131
|
+
// search text
|
132
|
+
const debouncedLoadQueries = useMemo(
|
133
|
+
() =>
|
134
|
+
debounce((input: string): void => {
|
135
|
+
flowResult(queryLoaderState.searchQueries(input)).catch(
|
136
|
+
applicationStore.alertUnhandledError,
|
137
|
+
);
|
138
|
+
}, 500),
|
139
|
+
[applicationStore.alertUnhandledError, queryLoaderState],
|
140
|
+
);
|
141
|
+
const onSearchTextChange: React.ChangeEventHandler<HTMLInputElement> = (
|
142
|
+
event,
|
143
|
+
) => {
|
144
|
+
if (event.target.value !== queryLoaderState.searchText) {
|
145
|
+
queryLoaderState.setSearchText(event.target.value);
|
146
|
+
debouncedLoadQueries.cancel();
|
147
|
+
debouncedLoadQueries(event.target.value);
|
148
|
+
}
|
149
|
+
};
|
150
|
+
const clearQuerySearching = (): void => {
|
151
|
+
queryLoaderState.setSearchText('');
|
152
|
+
debouncedLoadQueries.cancel();
|
153
|
+
debouncedLoadQueries('');
|
154
|
+
};
|
155
|
+
const toggleShowCurrentUserQueriesOnly = (): void => {
|
156
|
+
queryLoaderState.setShowCurrentUserQueriesOnly(
|
157
|
+
!queryLoaderState.showCurrentUserQueriesOnly,
|
158
|
+
);
|
159
|
+
setIsMineOnly(!isMineOnly);
|
160
|
+
debouncedLoadQueries.cancel();
|
161
|
+
debouncedLoadQueries(queryLoaderState.searchText);
|
162
|
+
};
|
163
|
+
const toggleExtraFilters = (key: string): void => {
|
164
|
+
queryLoaderState.extraFilters.set(
|
165
|
+
key,
|
166
|
+
!queryLoaderState.extraFilters.get(key),
|
167
|
+
);
|
168
|
+
debouncedLoadQueries.cancel();
|
169
|
+
debouncedLoadQueries(queryLoaderState.searchText);
|
170
|
+
};
|
171
|
+
|
172
|
+
useEffect(() => {
|
173
|
+
flowResult(queryLoaderState.searchQueries('')).catch(
|
174
|
+
applicationStore.alertUnhandledError,
|
175
|
+
);
|
176
|
+
}, [applicationStore, queryLoaderState]);
|
177
|
+
|
178
|
+
useEffect(() => {
|
179
|
+
searchInputRef.current?.focus();
|
180
|
+
}, [queryLoaderState]);
|
181
|
+
|
182
|
+
// actions
|
183
|
+
const renameQuery =
|
184
|
+
(query: LightQuery): (() => void) =>
|
185
|
+
(): void => {
|
186
|
+
if (!queryLoaderState.isReadOnly) {
|
187
|
+
flowResult(
|
188
|
+
queryLoaderState.renameQuery(query.id, queryNameInputValue),
|
189
|
+
)
|
190
|
+
.catch(applicationStore.alertUnhandledError)
|
191
|
+
.finally(() => hideEditQueryNameInput());
|
192
|
+
}
|
193
|
+
};
|
194
|
+
|
195
|
+
const deleteQuery =
|
196
|
+
(query: LightQuery): (() => void) =>
|
197
|
+
(): void => {
|
198
|
+
if (!queryLoaderState.isReadOnly) {
|
199
|
+
flowResult(queryLoaderState.deleteQuery(query.id)).catch(
|
200
|
+
applicationStore.alertUnhandledError,
|
201
|
+
);
|
202
|
+
}
|
203
|
+
};
|
204
|
+
|
205
|
+
const showPreview = (queryId: string): void => {
|
206
|
+
flowResult(queryLoaderState.getPreviewQueryContent(queryId)).catch(
|
207
|
+
applicationStore.alertUnhandledError,
|
208
|
+
);
|
209
|
+
queryLoaderState.setShowPreviewViewer(true);
|
210
|
+
};
|
211
|
+
|
212
|
+
return (
|
213
|
+
<div className="query-loader">
|
214
|
+
<div className="query-loader__header">
|
215
|
+
<div className="query-loader__search">
|
216
|
+
<div className="query-loader__search__input__container">
|
217
|
+
<input
|
218
|
+
ref={searchInputRef}
|
219
|
+
className={clsx('query-loader__search__input input--dark', {
|
220
|
+
'query-loader__search__input--searching':
|
221
|
+
queryLoaderState.searchText,
|
222
|
+
})}
|
223
|
+
onChange={onSearchTextChange}
|
224
|
+
value={queryLoaderState.searchText}
|
225
|
+
placeholder="Search for queries by name or ID"
|
226
|
+
/>
|
227
|
+
{!queryLoaderState.searchText ? (
|
228
|
+
<div className="query-loader__search__input__search__icon">
|
229
|
+
<SearchIcon />
|
230
|
+
</div>
|
231
|
+
) : (
|
232
|
+
<>
|
233
|
+
<button
|
234
|
+
className="query-loader__search__input__clear-btn"
|
235
|
+
tabIndex={-1}
|
236
|
+
onClick={clearQuerySearching}
|
237
|
+
title="Clear"
|
238
|
+
>
|
239
|
+
<TimesIcon />
|
240
|
+
</button>
|
241
|
+
</>
|
242
|
+
)}
|
243
|
+
</div>
|
244
|
+
</div>
|
245
|
+
<div className="query-loader__filter">
|
246
|
+
<div className="query-loader__filter__toggler">
|
247
|
+
<button
|
248
|
+
className={clsx('query-loader__filter__toggler__btn', {
|
249
|
+
'query-loader__filter__toggler__btn--toggled': isMineOnly,
|
250
|
+
})}
|
251
|
+
onClick={toggleShowCurrentUserQueriesOnly}
|
252
|
+
tabIndex={-1}
|
253
|
+
>
|
254
|
+
Mine Only
|
255
|
+
</button>
|
256
|
+
{queryLoaderState.extraFilterOptions.length > 0 && (
|
257
|
+
<div className="query-loader__filter__extra__filters">
|
258
|
+
{Array.from(queryLoaderState.extraFilters.entries()).map(
|
259
|
+
([key, value]) => (
|
260
|
+
<button
|
261
|
+
key={key}
|
262
|
+
className={clsx('query-loader__filter__toggler__btn', {
|
263
|
+
'query-loader__filter__toggler__btn--toggled': value,
|
264
|
+
})}
|
265
|
+
onClick={(): void => toggleExtraFilters(key)}
|
266
|
+
tabIndex={-1}
|
267
|
+
>
|
268
|
+
{key}
|
269
|
+
</button>
|
270
|
+
),
|
271
|
+
)}
|
272
|
+
</div>
|
273
|
+
)}
|
274
|
+
</div>
|
275
|
+
</div>
|
276
|
+
</div>
|
277
|
+
<div className="query-loader__content">
|
278
|
+
<PanelLoadingIndicator
|
279
|
+
isLoading={
|
280
|
+
queryLoaderState.searchQueriesState.isInProgress ||
|
281
|
+
queryLoaderState.renameQueryState.isInProgress ||
|
282
|
+
queryLoaderState.deleteQueryState.isInProgress ||
|
283
|
+
queryLoaderState.previewQueryState.isInProgress
|
284
|
+
}
|
285
|
+
/>
|
286
|
+
|
287
|
+
<div className="query-loader__results">
|
288
|
+
{queryLoaderState.searchQueriesState.hasCompleted && (
|
289
|
+
<>
|
290
|
+
<div className="query-loader__results__summary">
|
291
|
+
{queryLoaderState.showingDefaultQueries ? (
|
292
|
+
queryLoaderState.generateDefaultQueriesSummaryText?.(
|
293
|
+
results,
|
294
|
+
) ?? 'Refine your search to get better matches'
|
295
|
+
) : results.length >= QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT ? (
|
296
|
+
<>
|
297
|
+
{`Found ${QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT}+ matches`}{' '}
|
298
|
+
<InfoCircleIcon
|
299
|
+
title="Some queries are not listed, refine your search to get better matches"
|
300
|
+
className="query-loader__results__summary__info"
|
301
|
+
/>
|
302
|
+
</>
|
303
|
+
) : (
|
304
|
+
`Found ${quantifyList(results, 'match', 'matches')}`
|
305
|
+
)}
|
306
|
+
</div>
|
307
|
+
{results
|
308
|
+
.slice(0, QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT)
|
309
|
+
.map((query, idx) => (
|
310
|
+
<div
|
311
|
+
className="query-loader__result"
|
312
|
+
title={`Click to ${loadActionLabel}...`}
|
313
|
+
key={query.id}
|
314
|
+
onClick={() => queryLoaderState.loadQuery(query)}
|
315
|
+
>
|
316
|
+
<div className="query-loader__result__content">
|
317
|
+
{showQueryNameEditInput === idx ? (
|
318
|
+
<div className="query-loader__result__title__editor">
|
319
|
+
<input
|
320
|
+
className="query-loader__result__title__editor__input input--dark"
|
321
|
+
spellCheck={false}
|
322
|
+
ref={queryRenameInputRef}
|
323
|
+
value={queryNameInputValue}
|
324
|
+
onChange={changeQueryNameInputValue}
|
325
|
+
onKeyDown={(event) => {
|
326
|
+
if (event.code === 'Enter') {
|
327
|
+
renameQuery(query)();
|
328
|
+
} else if (event.code === 'Escape') {
|
329
|
+
hideEditQueryNameInput();
|
330
|
+
}
|
331
|
+
}}
|
332
|
+
onBlur={() => hideEditQueryNameInput()}
|
333
|
+
/>
|
334
|
+
</div>
|
335
|
+
) : (
|
336
|
+
<div
|
337
|
+
className="query-loader__result__title"
|
338
|
+
title={query.name}
|
339
|
+
>
|
340
|
+
{query.name}
|
341
|
+
</div>
|
342
|
+
)}
|
343
|
+
<div className="query-loader__result__description">
|
344
|
+
<div className="query-loader__result__description__date__icon">
|
345
|
+
<LastModifiedIcon />
|
346
|
+
</div>
|
347
|
+
<div className="query-loader__result__description__date">
|
348
|
+
{query.lastUpdatedAt
|
349
|
+
? formatDistanceToNow(
|
350
|
+
new Date(query.lastUpdatedAt),
|
351
|
+
{
|
352
|
+
includeSeconds: true,
|
353
|
+
addSuffix: true,
|
354
|
+
},
|
355
|
+
)
|
356
|
+
: '(unknown)'}
|
357
|
+
</div>
|
358
|
+
<div
|
359
|
+
className={clsx(
|
360
|
+
'query-loader__result__description__author__icon',
|
361
|
+
{
|
362
|
+
'query-loader__result__description__author__icon--owner':
|
363
|
+
query.isCurrentUserQuery,
|
364
|
+
},
|
365
|
+
)}
|
366
|
+
>
|
367
|
+
<UserIcon />
|
368
|
+
</div>
|
369
|
+
<div className="query-loader__result__description__author__name">
|
370
|
+
{query.isCurrentUserQuery ? (
|
371
|
+
<div
|
372
|
+
title={query.owner}
|
373
|
+
className="query-loader__result__description__owner"
|
374
|
+
>
|
375
|
+
Me
|
376
|
+
</div>
|
377
|
+
) : (
|
378
|
+
query.owner
|
379
|
+
)}
|
380
|
+
</div>
|
381
|
+
</div>
|
382
|
+
</div>
|
383
|
+
<DropdownMenu
|
384
|
+
className="query-loader__result__actions-menu"
|
385
|
+
title="More Actions..."
|
386
|
+
content={
|
387
|
+
<MenuContent>
|
388
|
+
<MenuContentItem
|
389
|
+
onClick={(): void => showPreview(query.id)}
|
390
|
+
>
|
391
|
+
Show Query Preview
|
392
|
+
</MenuContentItem>
|
393
|
+
{!queryLoaderState.isReadOnly && (
|
394
|
+
<MenuContentItem
|
395
|
+
disabled={!query.isCurrentUserQuery}
|
396
|
+
onClick={deleteQuery(query)}
|
397
|
+
>
|
398
|
+
Delete
|
399
|
+
</MenuContentItem>
|
400
|
+
)}
|
401
|
+
{!queryLoaderState.isReadOnly && (
|
402
|
+
<MenuContentItem
|
403
|
+
disabled={!query.isCurrentUserQuery}
|
404
|
+
onClick={showEditQueryNameInput(
|
405
|
+
query.name,
|
406
|
+
idx,
|
407
|
+
)}
|
408
|
+
>
|
409
|
+
Rename
|
410
|
+
</MenuContentItem>
|
411
|
+
)}
|
412
|
+
</MenuContent>
|
413
|
+
}
|
414
|
+
menuProps={{
|
415
|
+
anchorOrigin: {
|
416
|
+
vertical: 'bottom',
|
417
|
+
horizontal: 'left',
|
418
|
+
},
|
419
|
+
transformOrigin: {
|
420
|
+
vertical: 'top',
|
421
|
+
horizontal: 'left',
|
422
|
+
},
|
423
|
+
elevation: 7,
|
424
|
+
}}
|
425
|
+
>
|
426
|
+
<MoreVerticalIcon />
|
427
|
+
</DropdownMenu>
|
428
|
+
<div className="query-loader__result__arrow">
|
429
|
+
<ThinChevronRightIcon />
|
430
|
+
</div>
|
431
|
+
</div>
|
432
|
+
))}
|
433
|
+
</>
|
434
|
+
)}
|
435
|
+
{!queryLoaderState.searchQueriesState.hasCompleted && (
|
436
|
+
<BlankPanelContent>Loading queries...</BlankPanelContent>
|
437
|
+
)}
|
438
|
+
</div>
|
439
|
+
{queryLoaderState.showPreviewViewer &&
|
440
|
+
queryLoaderState.queryPreviewContent && (
|
441
|
+
<QueryPreviewViewer queryLoaderState={queryLoaderState} />
|
442
|
+
)}
|
443
|
+
</div>
|
444
|
+
</div>
|
445
|
+
);
|
446
|
+
},
|
447
|
+
);
|
448
|
+
|
449
|
+
export const QueryLoaderDialog = observer(
|
450
|
+
(props: {
|
451
|
+
queryLoaderState: QueryLoaderState;
|
452
|
+
title: string;
|
453
|
+
loadActionLabel?: string | undefined;
|
454
|
+
}) => {
|
455
|
+
const { queryLoaderState, title, loadActionLabel } = props;
|
456
|
+
|
457
|
+
const close = (): void => {
|
458
|
+
queryLoaderState.setQueryLoaderDialogOpen(false);
|
459
|
+
queryLoaderState.reset();
|
460
|
+
};
|
461
|
+
|
462
|
+
return (
|
463
|
+
<Dialog
|
464
|
+
open={queryLoaderState.isQueryLoaderDialogOpen}
|
465
|
+
onClose={close}
|
466
|
+
classes={{
|
467
|
+
root: 'query-loader__dialog',
|
468
|
+
container: 'query-loader__dialog__container',
|
469
|
+
}}
|
470
|
+
PaperProps={{
|
471
|
+
classes: { root: 'query-loader__dialog__body' },
|
472
|
+
}}
|
473
|
+
>
|
474
|
+
<Modal
|
475
|
+
darkMode={true}
|
476
|
+
className="modal query-loader__dialog__body__content"
|
477
|
+
>
|
478
|
+
<div className="modal query-loader__dialog__header">
|
479
|
+
<ModalTitle
|
480
|
+
className="query-loader__dialog__header__title"
|
481
|
+
title={title}
|
482
|
+
/>
|
483
|
+
<button
|
484
|
+
className="query-loader__dialog__header__close-btn"
|
485
|
+
title="Close"
|
486
|
+
onClick={close}
|
487
|
+
>
|
488
|
+
<TimesIcon />
|
489
|
+
</button>
|
490
|
+
</div>
|
491
|
+
<div className="modal query-loader__dialog__content">
|
492
|
+
<QueryLoader
|
493
|
+
queryLoaderState={queryLoaderState}
|
494
|
+
loadActionLabel={loadActionLabel ?? title.toLowerCase()}
|
495
|
+
/>
|
496
|
+
</div>
|
497
|
+
</Modal>
|
498
|
+
</Dialog>
|
499
|
+
);
|
500
|
+
},
|
501
|
+
);
|
@@ -15,6 +15,7 @@
|
|
15
15
|
*/
|
16
16
|
|
17
17
|
import { TimesCircleIcon } from '@finos/legend-art';
|
18
|
+
import { quantifyList } from '@finos/legend-shared';
|
18
19
|
|
19
20
|
export const QueryBuilderPanelIssueCountBadge: React.FC<{
|
20
21
|
issues: string[] | undefined;
|
@@ -23,7 +24,7 @@ export const QueryBuilderPanelIssueCountBadge: React.FC<{
|
|
23
24
|
if (!issues) {
|
24
25
|
return null;
|
25
26
|
}
|
26
|
-
const labelText =
|
27
|
+
const labelText = quantifyList(issues, 'issue');
|
27
28
|
return (
|
28
29
|
<div
|
29
30
|
className="query-builder-panel-issue-count-badge"
|
package/src/index.ts
CHANGED
@@ -70,3 +70,6 @@ export * from './stores/shared/ValueSpecificationEditorHelper.js';
|
|
70
70
|
|
71
71
|
export * from './components/execution-plan/ExecutionPlanViewer.js';
|
72
72
|
export * from './stores/execution-plan/ExecutionPlanState.js';
|
73
|
+
export * from './components/QueryLoader.js';
|
74
|
+
export * from './stores/QueryLoaderState.js';
|
75
|
+
export * from './stores/QueryBuilder_LegendApplicationPlugin_Extension.js';
|
@@ -0,0 +1,36 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2020-present, Goldman Sachs
|
3
|
+
*
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
* you may not use this file except in compliance with the License.
|
6
|
+
* You may obtain a copy of the License at
|
7
|
+
*
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
*
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
* See the License for the specific language governing permissions and
|
14
|
+
* limitations under the License.
|
15
|
+
*/
|
16
|
+
|
17
|
+
import type { LegendApplicationPlugin } from '@finos/legend-application';
|
18
|
+
import type { QueryBuilderState } from './QueryBuilderState.js';
|
19
|
+
import type { QuerySearchSpecification } from '@finos/legend-graph';
|
20
|
+
|
21
|
+
export type LoadQueryFilterOption = {
|
22
|
+
key: string;
|
23
|
+
label: (queryBuilderState: QueryBuilderState) => string | undefined;
|
24
|
+
filterFunction: (
|
25
|
+
searchSpecification: QuerySearchSpecification,
|
26
|
+
queryBuilderState: QueryBuilderState,
|
27
|
+
) => QuerySearchSpecification;
|
28
|
+
};
|
29
|
+
|
30
|
+
export interface QueryBuilder_LegendApplicationPlugin_Extension
|
31
|
+
extends LegendApplicationPlugin {
|
32
|
+
/**
|
33
|
+
* Get the list of filter options for query loader.
|
34
|
+
*/
|
35
|
+
getExtraLoadQueryFilterOptions?(): LoadQueryFilterOption[];
|
36
|
+
}
|