@finos/legend-extension-dsl-data-space-studio 0.1.152 → 0.1.154

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 (75) hide show
  1. package/lib/components/DSL_DataProduct_ElementDriver.d.ts +27 -0
  2. package/lib/components/DSL_DataProduct_ElementDriver.d.ts.map +1 -0
  3. package/lib/components/DSL_DataProduct_ElementDriver.js +60 -0
  4. package/lib/components/DSL_DataProduct_ElementDriver.js.map +1 -0
  5. package/lib/components/DSL_DataSpace_LegendStudioApplicationPlugin.d.ts +3 -1
  6. package/lib/components/DSL_DataSpace_LegendStudioApplicationPlugin.d.ts.map +1 -1
  7. package/lib/components/DSL_DataSpace_LegendStudioApplicationPlugin.js +30 -15
  8. package/lib/components/DSL_DataSpace_LegendStudioApplicationPlugin.js.map +1 -1
  9. package/lib/components/DSL_NewDataProductEditor.d.ts +19 -0
  10. package/lib/components/DSL_NewDataProductEditor.d.ts.map +1 -0
  11. package/lib/components/DSL_NewDataProductEditor.js +32 -0
  12. package/lib/components/DSL_NewDataProductEditor.js.map +1 -0
  13. package/lib/components/DataSpaceEditor.d.ts +1 -1
  14. package/lib/components/DataSpaceEditor.d.ts.map +1 -1
  15. package/lib/components/DataSpaceEditor.js +18 -9
  16. package/lib/components/DataSpaceEditor.js.map +1 -1
  17. package/lib/components/DataSpaceExecutionContextEditor.d.ts +37 -0
  18. package/lib/components/DataSpaceExecutionContextEditor.d.ts.map +1 -0
  19. package/lib/components/DataSpaceExecutionContextEditor.js +170 -0
  20. package/lib/components/DataSpaceExecutionContextEditor.js.map +1 -0
  21. package/lib/components/DataSpaceGeneralEditor/DataSpaceDefaultExecutionContextSection.d.ts +19 -0
  22. package/lib/components/DataSpaceGeneralEditor/DataSpaceDefaultExecutionContextSection.d.ts.map +1 -0
  23. package/lib/components/DataSpaceGeneralEditor/DataSpaceDefaultExecutionContextSection.js +138 -0
  24. package/lib/components/DataSpaceGeneralEditor/DataSpaceDefaultExecutionContextSection.js.map +1 -0
  25. package/lib/components/DataSpaceGeneralEditor/DataSpaceDiagramsSection.d.ts +19 -0
  26. package/lib/components/DataSpaceGeneralEditor/DataSpaceDiagramsSection.d.ts.map +1 -0
  27. package/lib/components/DataSpaceGeneralEditor/DataSpaceDiagramsSection.js +63 -0
  28. package/lib/components/DataSpaceGeneralEditor/DataSpaceDiagramsSection.js.map +1 -0
  29. package/lib/components/DataSpaceGeneralEditor/DataSpaceElementsSection.d.ts +19 -0
  30. package/lib/components/DataSpaceGeneralEditor/DataSpaceElementsSection.d.ts.map +1 -0
  31. package/lib/components/DataSpaceGeneralEditor/DataSpaceElementsSection.js +58 -0
  32. package/lib/components/DataSpaceGeneralEditor/DataSpaceElementsSection.js.map +1 -0
  33. package/lib/components/DataSpaceGeneralEditor/DataSpaceExecutablesSection.d.ts +19 -0
  34. package/lib/components/DataSpaceGeneralEditor/DataSpaceExecutablesSection.d.ts.map +1 -0
  35. package/lib/components/DataSpaceGeneralEditor/DataSpaceExecutablesSection.js +56 -0
  36. package/lib/components/DataSpaceGeneralEditor/DataSpaceExecutablesSection.js.map +1 -0
  37. package/lib/components/DataSpaceGeneralEditor/DataSpaceGeneralEditor.d.ts +19 -0
  38. package/lib/components/DataSpaceGeneralEditor/DataSpaceGeneralEditor.d.ts.map +1 -0
  39. package/lib/components/DataSpaceGeneralEditor/DataSpaceGeneralEditor.js +40 -0
  40. package/lib/components/DataSpaceGeneralEditor/DataSpaceGeneralEditor.js.map +1 -0
  41. package/lib/components/DataSpaceGeneralEditor/DataSpaceSupportInfoSection.d.ts +19 -0
  42. package/lib/components/DataSpaceGeneralEditor/DataSpaceSupportInfoSection.d.ts.map +1 -0
  43. package/lib/components/DataSpaceGeneralEditor/DataSpaceSupportInfoSection.js +120 -0
  44. package/lib/components/DataSpaceGeneralEditor/DataSpaceSupportInfoSection.js.map +1 -0
  45. package/lib/index.css +2 -2
  46. package/lib/index.css.map +1 -1
  47. package/lib/package.json +3 -2
  48. package/lib/stores/DataSpaceEditorState.d.ts +18 -2
  49. package/lib/stores/DataSpaceEditorState.d.ts.map +1 -1
  50. package/lib/stores/DataSpaceEditorState.js +55 -3
  51. package/lib/stores/DataSpaceEditorState.js.map +1 -1
  52. package/lib/stores/DataSpaceExecutionContextState.d.ts +37 -0
  53. package/lib/stores/DataSpaceExecutionContextState.d.ts.map +1 -0
  54. package/lib/stores/DataSpaceExecutionContextState.js +87 -0
  55. package/lib/stores/DataSpaceExecutionContextState.js.map +1 -0
  56. package/lib/stores/studio/DSL_DataSpace_GraphModifierHelper.d.ts +34 -3
  57. package/lib/stores/studio/DSL_DataSpace_GraphModifierHelper.d.ts.map +1 -1
  58. package/lib/stores/studio/DSL_DataSpace_GraphModifierHelper.js +128 -2
  59. package/lib/stores/studio/DSL_DataSpace_GraphModifierHelper.js.map +1 -1
  60. package/package.json +10 -9
  61. package/src/components/DSL_DataProduct_ElementDriver.tsx +82 -0
  62. package/src/components/DSL_DataSpace_LegendStudioApplicationPlugin.tsx +35 -19
  63. package/src/components/DSL_NewDataProductEditor.tsx +57 -0
  64. package/src/components/DataSpaceEditor.tsx +54 -33
  65. package/src/components/DataSpaceExecutionContextEditor.tsx +692 -0
  66. package/src/components/DataSpaceGeneralEditor/DataSpaceDefaultExecutionContextSection.tsx +393 -0
  67. package/src/components/DataSpaceGeneralEditor/DataSpaceDiagramsSection.tsx +144 -0
  68. package/src/components/DataSpaceGeneralEditor/DataSpaceElementsSection.tsx +128 -0
  69. package/src/components/DataSpaceGeneralEditor/DataSpaceExecutablesSection.tsx +108 -0
  70. package/src/components/DataSpaceGeneralEditor/DataSpaceGeneralEditor.tsx +76 -0
  71. package/src/components/DataSpaceGeneralEditor/DataSpaceSupportInfoSection.tsx +276 -0
  72. package/src/stores/DataSpaceEditorState.ts +92 -4
  73. package/src/stores/DataSpaceExecutionContextState.ts +135 -0
  74. package/src/stores/studio/DSL_DataSpace_GraphModifierHelper.ts +278 -3
  75. package/tsconfig.json +11 -1
@@ -0,0 +1,692 @@
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 {
18
+ type ElementDragSource,
19
+ type UMLEditorElementDropTarget,
20
+ CORE_DND_TYPE,
21
+ useEditorStore,
22
+ } from '@finos/legend-application-studio';
23
+ import { observer } from 'mobx-react-lite';
24
+ import { useDrop } from 'react-dnd';
25
+ import { DataSpaceEditorState } from '../stores/DataSpaceEditorState.js';
26
+ import {
27
+ BlankPanelPlaceholder,
28
+ clsx,
29
+ ContextMenu,
30
+ CustomSelectorInput,
31
+ Dialog,
32
+ ErrorIcon,
33
+ ExclamationTriangleIcon,
34
+ InputWithInlineValidation,
35
+ LongArrowRightIcon,
36
+ MenuContent,
37
+ MenuContentItem,
38
+ ModalTitle,
39
+ PanelContent,
40
+ PanelDropZone,
41
+ PanelHeader,
42
+ PanelHeaderActionItem,
43
+ PanelHeaderActions,
44
+ PlusIcon,
45
+ PURE_MappingIcon,
46
+ PURE_RuntimeIcon,
47
+ ResizablePanel,
48
+ ResizablePanelGroup,
49
+ ResizablePanelSplitter,
50
+ ResizablePanelSplitterLine,
51
+ } from '@finos/legend-art';
52
+ import {
53
+ Mapping,
54
+ PackageableElementExplicitReference,
55
+ PackageableRuntime,
56
+ validate_PureExecutionMapping,
57
+ } from '@finos/legend-graph';
58
+ import {
59
+ dataSpace_setExecutionContextDefaultRuntime,
60
+ dataSpace_setExecutionContextMapping,
61
+ dataSpace_setExecutionContextTitle,
62
+ dataSpace_setExecutionContextDescription,
63
+ } from '../stores/studio/DSL_DataSpace_GraphModifierHelper.js';
64
+ import { guaranteeNonNullable } from '@finos/legend-shared';
65
+ import { forwardRef, useCallback, useState } from 'react';
66
+ import type { DataSpaceExecutionContextState } from '../stores/DataSpaceExecutionContextState.js';
67
+ import type { DataSpaceExecutionContext } from '@finos/legend-extension-dsl-data-space/graph';
68
+ import {
69
+ buildElementOption,
70
+ type PackageableElementOption,
71
+ } from '@finos/legend-lego/graph-editor';
72
+ import type { PropsValue } from 'react-select';
73
+
74
+ const DataSpaceExecutionContextConfigurationEditor = observer(
75
+ (props: {
76
+ executionContextState: DataSpaceExecutionContextState;
77
+ executionContext: DataSpaceExecutionContext;
78
+ }) => {
79
+ const { executionContextState, executionContext } = props;
80
+ const isReadOnly = executionContextState.dataSpaceEditorState.isReadOnly;
81
+ const editorStore = executionContextState.editorStore;
82
+ const applicationStore = editorStore.applicationStore;
83
+
84
+ // Mapping
85
+ // TODO: this is not generic error handling, as there could be other problems
86
+ // with mapping, we need to genericize this
87
+ const isMappingEmpty = validate_PureExecutionMapping(
88
+ executionContext.mapping.value,
89
+ );
90
+ const mapping = executionContext.mapping.value;
91
+ const mappingOptions =
92
+ editorStore.graphManagerState.usableMappings.map(buildElementOption);
93
+ const noMappingLabel = (
94
+ <div
95
+ className="service-execution-editor__configuration__mapping-option--empty"
96
+ title={isMappingEmpty?.messages.join('\n') ?? ''}
97
+ >
98
+ <div className="service-execution-editor__configuration__mapping-option--empty__label">
99
+ (none)
100
+ </div>
101
+ <ErrorIcon />
102
+ </div>
103
+ );
104
+ const selectedMappingOption = {
105
+ value: mapping,
106
+ label: isMappingEmpty ? noMappingLabel : mapping.path,
107
+ } as PackageableElementOption<Mapping>;
108
+ const onMappingSelectionChange = (
109
+ val: PackageableElementOption<Mapping>,
110
+ ): void => {
111
+ if (val.value !== mapping) {
112
+ dataSpace_setExecutionContextMapping(
113
+ executionContext,
114
+ PackageableElementExplicitReference.create(val.value),
115
+ );
116
+ executionContextState.autoSelectRuntimeOnMappingChange(val.value);
117
+ }
118
+ };
119
+ const visitMapping = (): void =>
120
+ editorStore.graphEditorMode.openElement(mapping);
121
+
122
+ // Runtime
123
+ const defaultRuntime = executionContext.defaultRuntime;
124
+ // NOTE: for now, only include runtime associated with the mapping
125
+ // TODO?: Should we bring the runtime compatibility check from query to here?
126
+ const runtimes = editorStore.graphManagerState.graph.runtimes.filter((rt) =>
127
+ rt.runtimeValue.mappings.map((m) => m.value).includes(mapping),
128
+ );
129
+ const runtimeOptions = runtimes.map((rt) => ({
130
+ label: rt.path,
131
+ value: PackageableElementExplicitReference.create(rt),
132
+ }));
133
+ const runtimePointerWarning = !runtimes.includes(defaultRuntime.value) // if the runtime does not belong to the chosen mapping
134
+ ? `runtime is not associated with specified mapping '${mapping.path}'`
135
+ : undefined;
136
+ const selectedRuntimeOption = {
137
+ value: defaultRuntime,
138
+ label: (
139
+ <div
140
+ className="service-execution-editor__configuration__runtime-option__pointer"
141
+ title={undefined}
142
+ >
143
+ <div
144
+ className={clsx(
145
+ 'service-execution-editor__configuration__runtime-option__pointer__label',
146
+ {
147
+ 'service-execution-editor__configuration__runtime-option__pointer__label--with-warning':
148
+ Boolean(runtimePointerWarning),
149
+ },
150
+ )}
151
+ >
152
+ {defaultRuntime.value.path}
153
+ </div>
154
+ {runtimePointerWarning && (
155
+ <div
156
+ className="service-execution-editor__configuration__runtime-option__pointer__warning"
157
+ title={runtimePointerWarning}
158
+ >
159
+ <ExclamationTriangleIcon />
160
+ </div>
161
+ )}
162
+ </div>
163
+ ),
164
+ } as unknown as PropsValue<{
165
+ label: string;
166
+ value: PackageableElementExplicitReference<PackageableRuntime>;
167
+ }>;
168
+ const onRuntimeSelectionChange = (val: {
169
+ label: string | React.ReactNode;
170
+ value:
171
+ | PackageableElementExplicitReference<PackageableRuntime>
172
+ | undefined;
173
+ }): void => {
174
+ if (
175
+ val.value?.value !== defaultRuntime.value &&
176
+ val.value !== undefined
177
+ ) {
178
+ dataSpace_setExecutionContextDefaultRuntime(
179
+ executionContext,
180
+ val.value,
181
+ );
182
+ }
183
+ };
184
+ const visitRuntime = (): void => {
185
+ editorStore.graphEditorMode.openElement(defaultRuntime.value);
186
+ };
187
+
188
+ // DnD
189
+ const handleMappingOrRuntimeDrop = useCallback(
190
+ (item: UMLEditorElementDropTarget): void => {
191
+ const element = item.data.packageableElement;
192
+ if (!isReadOnly) {
193
+ if (element instanceof Mapping) {
194
+ dataSpace_setExecutionContextMapping(
195
+ executionContext,
196
+ PackageableElementExplicitReference.create(element),
197
+ );
198
+ executionContextState.autoSelectRuntimeOnMappingChange(element);
199
+ } else if (
200
+ element instanceof PackageableRuntime &&
201
+ element.runtimeValue.mappings.map((m) => m.value).includes(mapping)
202
+ ) {
203
+ dataSpace_setExecutionContextDefaultRuntime(
204
+ executionContext,
205
+ PackageableElementExplicitReference.create(element),
206
+ );
207
+ }
208
+ }
209
+ },
210
+ [isReadOnly, mapping, executionContextState, executionContext],
211
+ );
212
+ const [{ isMappingOrRuntimeDragOver }, dropConnector] = useDrop<
213
+ ElementDragSource,
214
+ void,
215
+ { isMappingOrRuntimeDragOver: boolean }
216
+ >(
217
+ () => ({
218
+ accept: [
219
+ CORE_DND_TYPE.PROJECT_EXPLORER_MAPPING,
220
+ CORE_DND_TYPE.PROJECT_EXPLORER_RUNTIME,
221
+ ],
222
+ drop: (item) => handleMappingOrRuntimeDrop(item),
223
+ collect: (monitor) => ({
224
+ isMappingOrRuntimeDragOver: monitor.isOver({ shallow: true }),
225
+ }),
226
+ }),
227
+ [handleMappingOrRuntimeDrop],
228
+ );
229
+
230
+ return (
231
+ <PanelContent>
232
+ <PanelDropZone
233
+ dropTargetConnector={dropConnector}
234
+ isDragOver={isMappingOrRuntimeDragOver && !isReadOnly}
235
+ >
236
+ <div className="service-execution-editor__configuration__items">
237
+ <div className="service-execution-editor__configuration__item">
238
+ <div className="btn--sm service-execution-editor__configuration__item__label">
239
+ <PURE_MappingIcon />
240
+ </div>
241
+ <CustomSelectorInput
242
+ className="panel__content__form__section__dropdown service-execution-editor__configuration__item__dropdown"
243
+ disabled={isReadOnly}
244
+ options={mappingOptions}
245
+ onChange={onMappingSelectionChange}
246
+ value={selectedMappingOption}
247
+ darkMode={
248
+ !applicationStore.layoutService
249
+ .TEMPORARY__isLightColorThemeEnabled
250
+ }
251
+ hasError={Boolean(isMappingEmpty)}
252
+ />
253
+ <button
254
+ className="btn--dark btn--sm service-execution-editor__configuration__item__btn"
255
+ onClick={visitMapping}
256
+ tabIndex={-1}
257
+ title="See mapping"
258
+ disabled={Boolean(isMappingEmpty)}
259
+ >
260
+ <LongArrowRightIcon />
261
+ </button>
262
+ </div>
263
+ <div className="service-execution-editor__configuration__item">
264
+ <div className="btn--sm service-execution-editor__configuration__item__label">
265
+ <PURE_RuntimeIcon />
266
+ </div>
267
+ <CustomSelectorInput
268
+ className="panel__content__form__section__dropdown service-execution-editor__configuration__item__dropdown"
269
+ disabled={isReadOnly}
270
+ options={runtimeOptions}
271
+ onChange={onRuntimeSelectionChange}
272
+ value={selectedRuntimeOption}
273
+ darkMode={
274
+ !applicationStore.layoutService
275
+ .TEMPORARY__isLightColorThemeEnabled
276
+ }
277
+ />
278
+ <button
279
+ className="btn--sm btn--dark service-execution-editor__configuration__item__btn"
280
+ onClick={visitRuntime}
281
+ tabIndex={-1}
282
+ title="See runtime"
283
+ >
284
+ <LongArrowRightIcon />
285
+ </button>
286
+ </div>
287
+
288
+ {/* Title input */}
289
+ <div className="execution-context-editor__form-section">
290
+ <div className="execution-context-editor__form-section__label">
291
+ Title
292
+ </div>
293
+ <div className="execution-context-editor__form-section__content">
294
+ <input
295
+ className="execution-context-editor__form-section__content__input panel__content__form__section__input"
296
+ spellCheck={false}
297
+ disabled={isReadOnly}
298
+ value={executionContext.title ?? ''}
299
+ onChange={(event): void =>
300
+ dataSpace_setExecutionContextTitle(
301
+ executionContext,
302
+ event.target.value,
303
+ )
304
+ }
305
+ />
306
+ </div>
307
+ </div>
308
+
309
+ {/* Description input */}
310
+ <div className="execution-context-editor__form-section">
311
+ <div className="execution-context-editor__form-section__label">
312
+ Description
313
+ </div>
314
+ <div className="execution-context-editor__form-section__content">
315
+ <textarea
316
+ className="execution-context-editor__form-section__content__textarea panel__content__form__section__input"
317
+ spellCheck={false}
318
+ disabled={isReadOnly}
319
+ value={executionContext.description ?? ''}
320
+ onChange={(event): void =>
321
+ dataSpace_setExecutionContextDescription(
322
+ executionContext,
323
+ event.target.value,
324
+ )
325
+ }
326
+ rows={4}
327
+ />
328
+ </div>
329
+ </div>
330
+
331
+ {/* Test Data input removed as it's not used */}
332
+ </div>
333
+ </PanelDropZone>
334
+ </PanelContent>
335
+ );
336
+ },
337
+ );
338
+
339
+ export const NewExecutionContextModal = observer(
340
+ (props: {
341
+ executionState: DataSpaceExecutionContextState;
342
+ isReadOnly: boolean;
343
+ }) => {
344
+ const { executionState, isReadOnly } = props;
345
+ const [name, setName] = useState('');
346
+ const validationMessage =
347
+ name === ''
348
+ ? `Execution context name can't be empty`
349
+ : executionState.executionContexts.find((e) => e.name === name)
350
+ ? 'Execution context name already exists'
351
+ : undefined;
352
+
353
+ const closeModal = (): void =>
354
+ executionState.setNewExecutionContextModal(false);
355
+ const onChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
356
+ setName(event.target.value);
357
+ };
358
+ return (
359
+ <Dialog
360
+ open={executionState.newExecutionContextModal}
361
+ onClose={closeModal}
362
+ classes={{ container: 'search-modal__container' }}
363
+ PaperProps={{ classes: { root: 'search-modal__inner-container' } }}
364
+ >
365
+ <form
366
+ onSubmit={(event) => {
367
+ event.preventDefault();
368
+ executionState.addExecutionContext(name);
369
+ setName('');
370
+ closeModal();
371
+ }}
372
+ className="modal modal--dark search-modal"
373
+ >
374
+ <ModalTitle title="New Execution Context" />
375
+ <div className="service-execution-editor__change__modal">
376
+ <InputWithInlineValidation
377
+ className="service-execution-editor__input input-group__input"
378
+ spellCheck={false}
379
+ value={name}
380
+ onChange={onChange}
381
+ placeholder="Key execution name"
382
+ error={validationMessage}
383
+ />
384
+ </div>
385
+ <div className="search-modal__actions">
386
+ <button
387
+ className="btn btn--dark"
388
+ disabled={isReadOnly || Boolean(validationMessage)}
389
+ >
390
+ Add
391
+ </button>
392
+ </div>
393
+ </form>
394
+ </Dialog>
395
+ );
396
+ },
397
+ );
398
+
399
+ export const RenameModal = observer(
400
+ (props: {
401
+ val: string;
402
+ isReadOnly: boolean;
403
+ setValue: (val: string) => void;
404
+ showModal: boolean;
405
+ closeModal: () => void;
406
+ executionContext: DataSpaceExecutionContext;
407
+ }) => {
408
+ const {
409
+ val,
410
+ isReadOnly,
411
+ showModal,
412
+ closeModal,
413
+ setValue,
414
+ executionContext,
415
+ } = props;
416
+ const [inputValue, setInputValue] = useState(val);
417
+ const changeValue: React.ChangeEventHandler<HTMLInputElement> = (event) => {
418
+ setInputValue(event.target.value);
419
+ };
420
+ return (
421
+ <Dialog
422
+ open={showModal}
423
+ onClose={closeModal}
424
+ classes={{ container: 'search-modal__container' }}
425
+ PaperProps={{ classes: { root: 'search-modal__inner-container' } }}
426
+ >
427
+ <form
428
+ onSubmit={(event) => {
429
+ event.preventDefault();
430
+ setValue(inputValue);
431
+ closeModal();
432
+ }}
433
+ className="modal modal--dark search-modal"
434
+ >
435
+ <ModalTitle title="Edit Execution Context" />
436
+ <div className="execution-context-editor__form-section">
437
+ <div className="execution-context-editor__form-section__label">
438
+ Key
439
+ </div>
440
+ <div className="execution-context-editor__form-section__content">
441
+ <input
442
+ className="execution-context-editor__form-section__content__input panel__content__form__section__input"
443
+ spellCheck={false}
444
+ disabled={isReadOnly}
445
+ value={inputValue}
446
+ onChange={changeValue}
447
+ />
448
+ </div>
449
+ </div>
450
+
451
+ {/* Title input */}
452
+ <div className="execution-context-editor__form-section">
453
+ <div className="execution-context-editor__form-section__label">
454
+ Title
455
+ </div>
456
+ <div className="execution-context-editor__form-section__content">
457
+ <input
458
+ className="execution-context-editor__form-section__content__input panel__content__form__section__input"
459
+ spellCheck={false}
460
+ disabled={isReadOnly}
461
+ value={executionContext.title ?? ''}
462
+ onChange={(event): void =>
463
+ dataSpace_setExecutionContextTitle(
464
+ executionContext,
465
+ event.target.value,
466
+ )
467
+ }
468
+ />
469
+ </div>
470
+ </div>
471
+
472
+ {/* Description input */}
473
+ <div className="execution-context-editor__form-section">
474
+ <div className="execution-context-editor__form-section__label">
475
+ Description
476
+ </div>
477
+ <div className="execution-context-editor__form-section__content">
478
+ <textarea
479
+ className="execution-context-editor__form-section__content__textarea panel__content__form__section__input"
480
+ spellCheck={false}
481
+ disabled={isReadOnly}
482
+ value={executionContext.description ?? ''}
483
+ onChange={(event): void =>
484
+ dataSpace_setExecutionContextDescription(
485
+ executionContext,
486
+ event.target.value,
487
+ )
488
+ }
489
+ rows={4}
490
+ />
491
+ </div>
492
+ </div>
493
+
494
+ <div className="search-modal__actions">
495
+ <button className="btn btn--dark" disabled={isReadOnly}>
496
+ Done
497
+ </button>
498
+ </div>
499
+ </form>
500
+ </Dialog>
501
+ );
502
+ },
503
+ );
504
+
505
+ const ExecutionContextMenu = observer(
506
+ forwardRef<
507
+ HTMLDivElement,
508
+ {
509
+ dataSpaceExecutionContextState: DataSpaceExecutionContextState;
510
+ dataSpaceExecutionContext: DataSpaceExecutionContext;
511
+ isReadOnly: boolean;
512
+ }
513
+ >(function TestContainerContextMenu(props, ref) {
514
+ const { dataSpaceExecutionContextState, dataSpaceExecutionContext } = props;
515
+ const rename = (): void => {
516
+ dataSpaceExecutionContextState.setExecutionContextToRename(
517
+ dataSpaceExecutionContext,
518
+ );
519
+ };
520
+ const remove = (): void => {
521
+ dataSpaceExecutionContextState.removeExecutionContext(
522
+ dataSpaceExecutionContext,
523
+ );
524
+ };
525
+ const add = (): void => {
526
+ dataSpaceExecutionContextState.setNewExecutionContextModal(true);
527
+ };
528
+ return (
529
+ <MenuContent ref={ref}>
530
+ <MenuContentItem onClick={rename}>Rename</MenuContentItem>
531
+ <MenuContentItem onClick={remove}>Delete</MenuContentItem>
532
+ <MenuContentItem onClick={add}>Create a new key</MenuContentItem>
533
+ </MenuContent>
534
+ );
535
+ }),
536
+ );
537
+
538
+ const ExecutionContextItem = observer(
539
+ (props: {
540
+ dataSpaceExecutionContextState: DataSpaceExecutionContextState;
541
+ dataSpaceExecutionContext: DataSpaceExecutionContext;
542
+ isReadOnly: boolean;
543
+ }) => {
544
+ const {
545
+ dataSpaceExecutionContextState,
546
+ dataSpaceExecutionContext,
547
+ isReadOnly,
548
+ } = props;
549
+ const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] =
550
+ useState(false);
551
+ const isActive =
552
+ dataSpaceExecutionContextState.selectedExecutionContext ===
553
+ dataSpaceExecutionContext;
554
+
555
+ const openKeyedExecution = (): void =>
556
+ dataSpaceExecutionContextState.setSelectedExecutionContext(
557
+ dataSpaceExecutionContext,
558
+ );
559
+ const onContextMenuOpen = (): void => setIsSelectedFromContextMenu(true);
560
+ const onContextMenuClose = (): void => setIsSelectedFromContextMenu(false);
561
+ return (
562
+ <ContextMenu
563
+ className={clsx(
564
+ 'service-multi-execution-editor__item',
565
+ {
566
+ 'service-multi-execution-editor__item--selected-from-context-menu':
567
+ !isActive && isSelectedFromContextMenu,
568
+ },
569
+ { 'service-multi-execution-editor__item--active': isActive },
570
+ )}
571
+ disabled={isReadOnly}
572
+ content={
573
+ <ExecutionContextMenu
574
+ dataSpaceExecutionContextState={dataSpaceExecutionContextState}
575
+ dataSpaceExecutionContext={dataSpaceExecutionContext}
576
+ isReadOnly={isReadOnly}
577
+ />
578
+ }
579
+ menuProps={{ elevation: 7 }}
580
+ onOpen={onContextMenuOpen}
581
+ onClose={onContextMenuClose}
582
+ >
583
+ <button
584
+ className={clsx('service-multi-execution-editor__item__label')}
585
+ onClick={openKeyedExecution}
586
+ tabIndex={-1}
587
+ >
588
+ {dataSpaceExecutionContext.name}
589
+ </button>
590
+ </ContextMenu>
591
+ );
592
+ },
593
+ );
594
+
595
+ export const DataSpaceExecutionContextEditor = observer(() => {
596
+ const editorStore = useEditorStore();
597
+
598
+ const dataSpaceState =
599
+ editorStore.tabManagerState.getCurrentEditorState(DataSpaceEditorState);
600
+ const executionContextState = dataSpaceState.executionContextState;
601
+
602
+ const addExecutionKey = (): void => {
603
+ executionContextState.setNewExecutionContextModal(true);
604
+ };
605
+
606
+ return (
607
+ <div className="service-execution-editor__execution">
608
+ <ResizablePanelGroup orientation="vertical">
609
+ <ResizablePanel size={300} minSize={200}>
610
+ <div className="service-multi-execution-editor__panel">
611
+ <PanelHeader>
612
+ <div className="panel__header__title">
613
+ <div className="panel__header__title__content">
614
+ Execution Contexts
615
+ </div>
616
+ </div>
617
+ <PanelHeaderActions>
618
+ <PanelHeaderActionItem
619
+ disabled={dataSpaceState.isReadOnly}
620
+ onClick={addExecutionKey}
621
+ title="Add an execution context"
622
+ >
623
+ <PlusIcon />
624
+ </PanelHeaderActionItem>
625
+ </PanelHeaderActions>
626
+ </PanelHeader>
627
+
628
+ {executionContextState.executionContexts.map((executionContext) => (
629
+ <ExecutionContextItem
630
+ key={executionContext.name}
631
+ dataSpaceExecutionContextState={executionContextState}
632
+ dataSpaceExecutionContext={executionContext}
633
+ isReadOnly={dataSpaceState.isReadOnly}
634
+ />
635
+ ))}
636
+ {!executionContextState.executionContexts.length && (
637
+ <BlankPanelPlaceholder
638
+ text="Add an execution context"
639
+ onClick={addExecutionKey}
640
+ clickActionType="add"
641
+ tooltipText="Click to add an execution context"
642
+ />
643
+ )}
644
+ </div>
645
+ {executionContextState.newExecutionContextModal && (
646
+ <NewExecutionContextModal
647
+ executionState={executionContextState}
648
+ isReadOnly={dataSpaceState.isReadOnly}
649
+ />
650
+ )}
651
+ {executionContextState.executionContextToRename && (
652
+ <RenameModal
653
+ val={executionContextState.executionContextToRename.name}
654
+ isReadOnly={dataSpaceState.isReadOnly}
655
+ showModal={true}
656
+ closeModal={(): void =>
657
+ executionContextState.setExecutionContextToRename(undefined)
658
+ }
659
+ setValue={(val: string): void =>
660
+ executionContextState.renameExecutionContext(
661
+ guaranteeNonNullable(
662
+ executionContextState.executionContextToRename,
663
+ ),
664
+ val,
665
+ )
666
+ }
667
+ executionContext={executionContextState.executionContextToRename}
668
+ />
669
+ )}
670
+ </ResizablePanel>
671
+ <ResizablePanelSplitter>
672
+ <ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
673
+ </ResizablePanelSplitter>
674
+ <ResizablePanel minSize={56}>
675
+ {executionContextState.selectedExecutionContext ? (
676
+ <DataSpaceExecutionContextConfigurationEditor
677
+ executionContextState={executionContextState}
678
+ executionContext={executionContextState.selectedExecutionContext}
679
+ />
680
+ ) : (
681
+ <BlankPanelPlaceholder
682
+ text="Add an execution context"
683
+ onClick={addExecutionKey}
684
+ clickActionType="add"
685
+ tooltipText="Click to add an execution context"
686
+ />
687
+ )}
688
+ </ResizablePanel>
689
+ </ResizablePanelGroup>
690
+ </div>
691
+ );
692
+ });