@finos/legend-application-query 13.0.11 → 13.0.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos/legend-application-query",
3
- "version": "13.0.11",
3
+ "version": "13.0.13",
4
4
  "description": "Legend Query application core",
5
5
  "keywords": [
6
6
  "legend",
@@ -43,14 +43,14 @@
43
43
  "test:watch": "jest --watch"
44
44
  },
45
45
  "dependencies": {
46
- "@finos/legend-application": "15.0.14",
47
- "@finos/legend-art": "7.0.15",
48
- "@finos/legend-graph": "29.0.2",
49
- "@finos/legend-lego": "1.0.9",
50
- "@finos/legend-query-builder": "3.2.11",
51
- "@finos/legend-server-depot": "6.0.7",
52
- "@finos/legend-shared": "10.0.6",
53
- "@finos/legend-storage": "3.0.56",
46
+ "@finos/legend-application": "15.0.15",
47
+ "@finos/legend-art": "7.0.16",
48
+ "@finos/legend-graph": "29.0.4",
49
+ "@finos/legend-lego": "1.0.11",
50
+ "@finos/legend-query-builder": "4.0.1",
51
+ "@finos/legend-server-depot": "6.0.8",
52
+ "@finos/legend-shared": "10.0.7",
53
+ "@finos/legend-storage": "3.0.57",
54
54
  "@testing-library/react": "14.0.0",
55
55
  "@types/react": "18.2.6",
56
56
  "@types/react-dom": "18.2.4",
@@ -61,13 +61,13 @@
61
61
  "serializr": "3.0.2"
62
62
  },
63
63
  "devDependencies": {
64
- "@finos/legend-dev-utils": "2.0.61",
64
+ "@finos/legend-dev-utils": "2.0.62",
65
65
  "@jest/globals": "29.5.0",
66
66
  "cross-env": "7.0.3",
67
67
  "eslint": "8.40.0",
68
68
  "jest": "29.5.0",
69
69
  "npm-run-all": "4.1.5",
70
- "rimraf": "5.0.0",
70
+ "rimraf": "5.0.1",
71
71
  "sass": "1.62.1",
72
72
  "typescript": "5.0.4"
73
73
  },
@@ -53,11 +53,9 @@ import {
53
53
  generateQuerySetupRoute,
54
54
  } from '../__lib__/LegendQueryNavigation.js';
55
55
  import {
56
- QuerySaveAsState,
57
56
  createViewProjectHandler,
58
57
  createViewSDLCProjectHandler,
59
- QuerySaveState,
60
- QueryRenameState,
58
+ ExistingQueryEditorStore,
61
59
  } from '../stores/QueryEditorStore.js';
62
60
  import {
63
61
  LEGEND_APPLICATION_COLOR_THEME,
@@ -82,54 +80,64 @@ import { QUERY_DOCUMENTATION_KEY } from '../application/LegendQueryDocumentation
82
80
  import { debounce } from '@finos/legend-shared';
83
81
  import { LegendQueryTelemetryHelper } from '../__lib__/LegendQueryTelemetryHelper.js';
84
82
 
85
- const QuerySaveAsDialogContent = observer(
86
- (props: { saveAsState: QuerySaveAsState }) => {
87
- const { saveAsState } = props;
88
- const applicationStore = useApplicationStore();
89
- const create = (): void => {
90
- flowResult(saveAsState.createQuery(true)).catch(
91
- applicationStore.alertUnhandledError,
92
- );
93
- };
94
-
95
- const isExistingQueryName = saveAsState.editorStore.existingQueryName;
96
-
97
- // name
98
- const nameInputRef = useRef<HTMLInputElement>(null);
99
-
100
- const debouncedLoadQueries = useMemo(
101
- () =>
102
- debounce((input: string): void => {
103
- flowResult(
104
- saveAsState.editorStore.searchExistingQueryName(input),
105
- ).catch(applicationStore.alertUnhandledError);
106
- }, 500),
107
- [applicationStore, saveAsState.editorStore],
83
+ const CreateQueryDialog = observer(() => {
84
+ const editorStore = useQueryEditorStore();
85
+ const createQueryState = editorStore.queryCreatorState;
86
+ const close = (): void => {
87
+ createQueryState.setShowCreateModal(false);
88
+ createQueryState.editorStore.setExistingQueryName(undefined);
89
+ };
90
+ const applicationStore = useApplicationStore();
91
+ const create = (): void => {
92
+ flowResult(createQueryState.createQuery()).catch(
93
+ applicationStore.alertUnhandledError,
108
94
  );
95
+ };
96
+ const isExistingQueryName = createQueryState.editorStore.existingQueryName;
97
+ // name
98
+ const nameInputRef = useRef<HTMLInputElement>(null);
99
+ const debouncedLoadQueries = useMemo(
100
+ () =>
101
+ debounce((input: string): void => {
102
+ flowResult(
103
+ createQueryState.editorStore.searchExistingQueryName(input),
104
+ ).catch(applicationStore.alertUnhandledError);
105
+ }, 500),
106
+ [applicationStore, createQueryState.editorStore],
107
+ );
109
108
 
110
- const changeName: React.ChangeEventHandler<HTMLInputElement> = (event) => {
111
- saveAsState.setQueryName(event.target.value);
112
- };
113
-
114
- useEffect(() => {
115
- nameInputRef.current?.focus();
116
- }, []);
109
+ const changeName: React.ChangeEventHandler<HTMLInputElement> = (event) => {
110
+ createQueryState.setQueryName(event.target.value);
111
+ };
117
112
 
118
- useEffect(() => {
119
- const searchText = saveAsState.queryName;
120
- debouncedLoadQueries.cancel();
121
- debouncedLoadQueries(searchText);
122
- }, [
123
- saveAsState.queryName,
124
- debouncedLoadQueries,
125
- saveAsState.editorStore.queryLoaderState.queries,
126
- ]);
113
+ useEffect(() => {
114
+ nameInputRef.current?.focus();
115
+ }, []);
127
116
 
128
- return (
129
- <>
117
+ useEffect(() => {
118
+ const searchText = createQueryState.queryName;
119
+ debouncedLoadQueries.cancel();
120
+ debouncedLoadQueries(searchText);
121
+ }, [
122
+ createQueryState.queryName,
123
+ debouncedLoadQueries,
124
+ createQueryState.editorStore.queryLoaderState.queries,
125
+ ]);
126
+ return (
127
+ <Dialog
128
+ open={createQueryState.showCreateModal}
129
+ onClose={close}
130
+ classes={{
131
+ root: 'editor-modal__root-container',
132
+ container: 'editor-modal__container',
133
+ paper: 'editor-modal__content',
134
+ }}
135
+ >
136
+ <Modal darkMode={true} className="query-export">
137
+ <ModalHeader title="Create New Query" />
130
138
  <ModalBody>
131
139
  <PanelLoadingIndicator
132
- isLoading={saveAsState.createQueryState.isInProgress}
140
+ isLoading={createQueryState.createQueryState.isInProgress}
133
141
  />
134
142
  <PanelListItem>
135
143
  <div className="input--with-validation">
@@ -139,7 +147,7 @@ const QuerySaveAsDialogContent = observer(
139
147
  'input--caution': isExistingQueryName,
140
148
  })}
141
149
  spellCheck={false}
142
- value={saveAsState.queryName}
150
+ value={createQueryState.queryName}
143
151
  onChange={changeName}
144
152
  />
145
153
  {isExistingQueryName && (
@@ -156,58 +164,132 @@ const QuerySaveAsDialogContent = observer(
156
164
  <ModalFooter>
157
165
  <ModalFooterButton
158
166
  text="Create Query"
159
- title={
160
- !saveAsState.allowPersist
161
- ? `Cannot create new query`
162
- : 'Create new query'
163
- }
167
+ title={'Create new query'}
168
+ disabled={Boolean(
169
+ createQueryState.editorStore.isPerformingBlockingAction ||
170
+ isExistingQueryName,
171
+ )}
164
172
  onClick={create}
165
173
  />
166
174
  </ModalFooter>
167
- </>
168
- );
169
- },
170
- );
171
-
172
- const QuerySaveDialog = observer(() => {
173
- const editorStore = useQueryEditorStore();
174
- const saveAsState = editorStore.saveAsState;
175
- const close = (): void => editorStore.setSaveAsState(undefined);
176
-
177
- return (
178
- <Dialog
179
- open={Boolean(saveAsState)}
180
- onClose={close}
181
- classes={{
182
- root: 'editor-modal__root-container',
183
- container: 'editor-modal__container',
184
- paper: 'editor-modal__content',
185
- }}
186
- >
187
- <Modal darkMode={true} className="query-export">
188
- <ModalHeader title="Create New Query" />
189
- {saveAsState && <QuerySaveAsDialogContent saveAsState={saveAsState} />}
190
175
  </Modal>
191
176
  </Dialog>
192
177
  );
193
178
  });
194
179
 
180
+ const QueryEditorExistingQueryHeader = observer(
181
+ (props: {
182
+ queryBuilderState: QueryBuilderState;
183
+ existingEditorStore: ExistingQueryEditorStore;
184
+ }) => {
185
+ const { existingEditorStore } = props;
186
+ const updateState = existingEditorStore.updateState;
187
+ const isRenaming = updateState.queryRenamer;
188
+ const applicationStore = existingEditorStore.applicationStore;
189
+ const renameRef = useRef<HTMLInputElement>(null);
190
+ const [queryRenameName, setQueryRenameName] = useState<string>(
191
+ existingEditorStore.lightQuery.name,
192
+ );
193
+
194
+ const enableRename = (): void => {
195
+ setQueryRenameName(existingEditorStore.lightQuery.name);
196
+ existingEditorStore.updateState.setQueryRenamer(true);
197
+ };
198
+ const renameQuery = (val: string): void => {
199
+ if (!(queryRenameName === existingEditorStore.lightQuery.name)) {
200
+ flowResult(updateState.updateQuery(val)).catch(
201
+ applicationStore.alertUnhandledError,
202
+ );
203
+ }
204
+ };
205
+
206
+ const changeQueryName: React.ChangeEventHandler<HTMLInputElement> = (
207
+ event,
208
+ ) => setQueryRenameName(event.target.value);
209
+
210
+ const debouncedLoadQueries = useMemo(
211
+ () =>
212
+ debounce((input: string): void => {
213
+ flowResult(existingEditorStore.searchExistingQueryName(input)).catch(
214
+ applicationStore.alertUnhandledError,
215
+ );
216
+ }, 500),
217
+ [applicationStore.alertUnhandledError, existingEditorStore],
218
+ );
219
+
220
+ useEffect(() => {
221
+ const searchText = queryRenameName;
222
+ debouncedLoadQueries.cancel();
223
+ debouncedLoadQueries(searchText);
224
+ }, [queryRenameName, debouncedLoadQueries]);
225
+
226
+ const isExistingQueryName =
227
+ existingEditorStore.existingQueryName &&
228
+ queryRenameName !== existingEditorStore.lightQuery.name;
229
+
230
+ return (
231
+ <>
232
+ {isRenaming ? (
233
+ <div className="query-editor__header__content__main query-editor__header__content__title">
234
+ <PanelListItem>
235
+ <div className="input--with-validation">
236
+ <input
237
+ ref={renameRef}
238
+ className={clsx('input input--dark', {
239
+ 'input--caution': isExistingQueryName,
240
+ })}
241
+ onChange={changeQueryName}
242
+ onKeyDown={(event) => {
243
+ if (event.code === 'Enter') {
244
+ event.stopPropagation();
245
+ updateState.setQueryRenamer(false);
246
+ existingEditorStore.setExistingQueryName(undefined);
247
+ renameQuery(queryRenameName);
248
+ } else if (event.code === 'Escape') {
249
+ event.stopPropagation();
250
+ updateState.setQueryRenamer(false);
251
+ existingEditorStore.setExistingQueryName(undefined);
252
+ }
253
+ }}
254
+ value={queryRenameName}
255
+ />
256
+ {isExistingQueryName && (
257
+ <div
258
+ className="input--with-validation__caution"
259
+ title={`Query with name '${isExistingQueryName}' already exists`}
260
+ >
261
+ <ExclamationTriangleIcon className="input--with-validation__caution__indicator" />
262
+ </div>
263
+ )}
264
+ </div>
265
+ </PanelListItem>
266
+ </div>
267
+ ) : (
268
+ <div
269
+ onDoubleClick={enableRename}
270
+ className="query-editor__header__content__main query-editor__header__content__title"
271
+ title="Double-click to rename query"
272
+ >
273
+ {existingEditorStore.lightQuery.name}
274
+ </div>
275
+ )}
276
+ </>
277
+ );
278
+ },
279
+ );
280
+
195
281
  const QueryEditorHeaderContent = observer(
196
282
  (props: { queryBuilderState: QueryBuilderState }) => {
197
283
  const { queryBuilderState } = props;
198
284
  const editorStore = useQueryEditorStore();
199
- const renameState = editorStore.renameState;
200
285
  const applicationStore = useLegendQueryApplicationStore();
286
+ const isExistingQuery = editorStore instanceof ExistingQueryEditorStore;
201
287
 
202
- const renameRef = useRef<HTMLInputElement>(null);
203
- const [showQueryNameEditInput, setShowQueryNameEditInput] =
204
- useState<boolean>(false);
205
-
206
- useEffect(() => {
207
- renameRef.current?.focus();
208
- renameRef.current?.select();
209
- }, [showQueryNameEditInput]);
210
-
288
+ const renameQuery = (): void => {
289
+ if (editorStore instanceof ExistingQueryEditorStore) {
290
+ editorStore.updateState.setQueryRenamer(true);
291
+ }
292
+ };
211
293
  // actions
212
294
  const openQueryLoader = (): void => {
213
295
  editorStore.queryLoaderState.setQueryLoaderDialogOpen(true);
@@ -245,66 +327,12 @@ const QueryEditorHeaderContent = observer(
245
327
  );
246
328
  };
247
329
 
248
- const updateQuery = applicationStore.guardUnhandledError(
249
- async (): Promise<void> => {
250
- try {
251
- const lambda = queryBuilderState.buildQuery();
252
- editorStore.setRenameState(
253
- new QueryRenameState(
254
- editorStore,
255
- queryBuilderState,
256
- lambda,
257
- await editorStore.getPersistConfiguration(lambda, {
258
- update: true,
259
- }),
260
- ),
261
- );
262
- setShowQueryNameEditInput(true);
263
- } catch {
264
- // do nothing
265
- }
266
- },
267
- );
268
-
269
330
  const saveQuery = applicationStore.guardUnhandledError(
270
331
  async (): Promise<void> => {
271
- try {
272
- const lambda = queryBuilderState.buildQuery();
273
- editorStore.setSaveState(
274
- new QuerySaveState(
275
- editorStore,
276
- queryBuilderState,
277
- lambda,
278
- await editorStore.getPersistConfiguration(lambda, {
279
- update: true,
280
- }),
281
- ),
282
- );
283
- await flowResult(editorStore.saveState?.saveQuery()).catch(
332
+ if (editorStore instanceof ExistingQueryEditorStore) {
333
+ flowResult(editorStore.updateState.updateQuery(undefined)).catch(
284
334
  applicationStore.alertUnhandledError,
285
335
  );
286
- } catch {
287
- // do nothing
288
- }
289
- },
290
- );
291
-
292
- const saveAsQuery = applicationStore.guardUnhandledError(
293
- async (): Promise<void> => {
294
- try {
295
- const lambda = queryBuilderState.buildQuery();
296
- editorStore.setSaveAsState(
297
- new QuerySaveAsState(
298
- editorStore,
299
- queryBuilderState,
300
- lambda,
301
- await editorStore.getPersistConfiguration(lambda, {
302
- update: true,
303
- }),
304
- ),
305
- );
306
- } catch {
307
- // do nothing
308
336
  }
309
337
  },
310
338
  );
@@ -324,49 +352,27 @@ const QueryEditorHeaderContent = observer(
324
352
  }
325
353
  };
326
354
 
327
- const renameQuery = (): void => {
328
- if (renameState) {
329
- flowResult(renameState.renameQuery()).catch(
330
- applicationStore.alertUnhandledError,
331
- );
332
- }
355
+ const handleQuerySaveAs = (): void => {
356
+ editorStore.queryCreatorState.setShowCreateModal(true);
333
357
  };
334
358
 
335
- const changeQueryName: React.ChangeEventHandler<HTMLInputElement> = (
336
- event,
337
- ) => renameState?.setQueryName(event.target.value);
338
-
339
- const onEnter: React.KeyboardEventHandler<HTMLInputElement> = (event) => {
340
- if (event.code === 'Enter') {
341
- renameQuery();
359
+ const renderQueryTitle = (): React.ReactNode => {
360
+ if (editorStore instanceof ExistingQueryEditorStore) {
361
+ return (
362
+ <QueryEditorExistingQueryHeader
363
+ queryBuilderState={queryBuilderState}
364
+ existingEditorStore={editorStore}
365
+ />
366
+ );
342
367
  }
368
+ return (
369
+ <div className="query-editor__header__content__main query-editor__header__content__title">
370
+ {/* TODO: Fix styling when label not an existing query name */}
371
+ {editorStore.label ?? ''}
372
+ </div>
373
+ );
343
374
  };
344
375
 
345
- const debouncedLoadQueries = useMemo(
346
- () =>
347
- debounce((input: string): void => {
348
- flowResult(
349
- renameState?.editorStore.searchExistingQueryName(input),
350
- ).catch(applicationStore.alertUnhandledError);
351
- }, 500),
352
- [applicationStore, renameState?.editorStore],
353
- );
354
-
355
- useEffect(() => {
356
- if (renameState && renameState.queryName !== editorStore.title) {
357
- const searchText = renameState.queryName;
358
- debouncedLoadQueries.cancel();
359
- debouncedLoadQueries(searchText);
360
- }
361
- }, [
362
- renameState,
363
- debouncedLoadQueries,
364
- editorStore.title,
365
- renameState?.queryName,
366
- ]);
367
-
368
- const isExistingQueryName = renameState?.editorStore.existingQueryName;
369
-
370
376
  const extraHelpMenuContentItems = applicationStore.pluginManager
371
377
  .getApplicationPlugins()
372
378
  .flatMap(
@@ -386,50 +392,7 @@ const QueryEditorHeaderContent = observer(
386
392
 
387
393
  return (
388
394
  <div className="query-editor__header__content">
389
- {renameState ? (
390
- <div className="query-editor__header__content__main query-editor__header__content__title">
391
- <PanelListItem>
392
- <div className="input--with-validation">
393
- <input
394
- ref={renameRef}
395
- className={clsx('input input--dark', {
396
- 'input--caution': isExistingQueryName,
397
- })}
398
- onChange={changeQueryName}
399
- onKeyDown={(event) => {
400
- if (event.code === 'Enter') {
401
- event.stopPropagation();
402
- setShowQueryNameEditInput(false);
403
- onEnter(event);
404
- } else if (event.code === 'Escape') {
405
- setShowQueryNameEditInput(false);
406
- event.stopPropagation();
407
- renameState.renameQueryState.reset();
408
- renameState.editorStore.setRenameState(undefined);
409
- }
410
- }}
411
- value={renameState.queryName}
412
- />
413
- {isExistingQueryName && (
414
- <div
415
- className="input--with-validation__caution"
416
- title={`Query with name '${isExistingQueryName}' already exists`}
417
- >
418
- <ExclamationTriangleIcon className="input--with-validation__caution__indicator" />
419
- </div>
420
- )}
421
- </div>
422
- </PanelListItem>
423
- </div>
424
- ) : (
425
- <div
426
- onDoubleClick={updateQuery}
427
- className="query-editor__header__content__main query-editor__header__content__title"
428
- title="Double-click to rename query"
429
- >
430
- {editorStore.title}
431
- </div>
432
- )}
395
+ {renderQueryTitle()}
433
396
 
434
397
  <div className="query-editor__header__actions">
435
398
  {applicationStore.pluginManager
@@ -447,6 +410,7 @@ const QueryEditorHeaderContent = observer(
447
410
 
448
411
  <Button
449
412
  className="query-editor__header__action btn--dark"
413
+ disabled={editorStore.isPerformingBlockingAction}
450
414
  onClick={openQueryLoader}
451
415
  title="Load query..."
452
416
  >
@@ -459,9 +423,7 @@ const QueryEditorHeaderContent = observer(
459
423
  <Button
460
424
  className="query-editor__header__action btn--dark"
461
425
  disabled={
462
- editorStore.isSaveActionDisabled ||
463
- !editorStore.title ||
464
- Boolean(editorStore.saveState?.saveQueryState.isInProgress)
426
+ !isExistingQuery || editorStore.isPerformingBlockingAction
465
427
  }
466
428
  onClick={saveQuery}
467
429
  title="Save query"
@@ -471,11 +433,8 @@ const QueryEditorHeaderContent = observer(
471
433
  </Button>
472
434
  <Button
473
435
  className="query-editor__header__action btn--dark"
474
- disabled={
475
- editorStore.isSaveActionDisabled ||
476
- Boolean(editorStore.saveAsState?.createQueryState.isInProgress)
477
- }
478
- onClick={saveAsQuery}
436
+ disabled={editorStore.isPerformingBlockingAction}
437
+ onClick={handleQuerySaveAs}
479
438
  title="Save as new query"
480
439
  >
481
440
  <SaveAsIcon />
@@ -483,7 +442,6 @@ const QueryEditorHeaderContent = observer(
483
442
  Save As...
484
443
  </div>
485
444
  </Button>
486
-
487
445
  <DropdownMenu
488
446
  className="query-editor__header__action btn--dark"
489
447
  disabled={editorStore.isViewProjectActionDisabled}
@@ -548,13 +506,15 @@ const QueryEditorHeaderContent = observer(
548
506
  disabled={editorStore.isViewProjectActionDisabled}
549
507
  content={
550
508
  <MenuContent>
551
- <MenuContentItem
552
- className="query-editor__header__action__options"
553
- onClick={updateQuery}
554
- disabled={!editorStore.title}
555
- >
556
- Rename Query
557
- </MenuContentItem>
509
+ {isExistingQuery && (
510
+ <MenuContentItem
511
+ className="query-editor__header__action__options"
512
+ onClick={renameQuery}
513
+ disabled={!isExistingQuery}
514
+ >
515
+ Rename Query
516
+ </MenuContentItem>
517
+ )}
558
518
  <MenuContentItem
559
519
  className="query-editor__header__action__options"
560
520
  disabled={editorStore.isViewProjectActionDisabled}
@@ -581,7 +541,9 @@ const QueryEditorHeaderContent = observer(
581
541
  <CaretDownIcon className="query-editor__header__action__dropdown-trigger" />
582
542
  </DropdownMenu>
583
543
 
584
- {editorStore.saveAsState && <QuerySaveDialog />}
544
+ {editorStore.queryCreatorState.showCreateModal && (
545
+ <CreateQueryDialog />
546
+ )}
585
547
  </div>
586
548
  </div>
587
549
  );