@finos/legend-query-builder 3.1.0 → 3.2.1

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