@finos/legend-application 3.0.3 → 4.0.2
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/application/LegendApplication.d.ts +2 -2
- package/lib/application/LegendApplication.d.ts.map +1 -1
- package/lib/application/LegendApplication.js +4 -4
- package/lib/application/LegendApplication.js.map +1 -1
- package/lib/application/LegendApplicationPluginManager.d.ts +3 -3
- package/lib/application/LegendApplicationPluginManager.d.ts.map +1 -1
- package/lib/components/ActionAlert.d.ts +1 -0
- package/lib/components/ActionAlert.d.ts.map +1 -1
- package/lib/components/ActionAlert.js +2 -2
- package/lib/components/ActionAlert.js.map +1 -1
- package/lib/components/ApplicationStoreProvider.d.ts +3 -3
- package/lib/components/ApplicationStoreProvider.d.ts.map +1 -1
- package/lib/components/ApplicationStoreProvider.js +2 -2
- package/lib/components/ApplicationStoreProvider.js.map +1 -1
- package/lib/components/ApplicationStoreProviderTestUtils.d.ts +5 -5
- package/lib/components/ApplicationStoreProviderTestUtils.d.ts.map +1 -1
- package/lib/components/ApplicationStoreProviderTestUtils.js +5 -4
- package/lib/components/ApplicationStoreProviderTestUtils.js.map +1 -1
- package/lib/components/ApplicationTestID.js +1 -1
- package/lib/components/ApplicationTestID.js.map +1 -1
- package/lib/components/BasicValueSpecificationEditor.d.ts +44 -0
- package/lib/components/BasicValueSpecificationEditor.d.ts.map +1 -0
- package/lib/components/BasicValueSpecificationEditor.js +276 -0
- package/lib/components/BasicValueSpecificationEditor.js.map +1 -0
- package/lib/components/BlockingAlert.d.ts +1 -0
- package/lib/components/BlockingAlert.d.ts.map +1 -1
- package/lib/components/BlockingAlert.js +1 -1
- package/lib/components/BlockingAlert.js.map +1 -1
- package/lib/components/CustomDatePicker.d.ts +38 -0
- package/lib/components/CustomDatePicker.d.ts.map +1 -0
- package/lib/components/CustomDatePicker.js +592 -0
- package/lib/components/CustomDatePicker.js.map +1 -0
- package/lib/components/DocumentationLink.d.ts +1 -1
- package/lib/components/DocumentationLink.js +2 -2
- package/lib/components/DocumentationLink.js.map +1 -1
- package/lib/components/LambdaEditor.d.ts +2 -1
- package/lib/components/LambdaEditor.d.ts.map +1 -1
- package/lib/components/LambdaEditor.js +3 -3
- package/lib/components/LambdaEditor.js.map +1 -1
- package/lib/components/LambdaParameterValuesEditor.d.ts +25 -0
- package/lib/components/LambdaParameterValuesEditor.d.ts.map +1 -0
- package/lib/components/LambdaParameterValuesEditor.js +52 -0
- package/lib/components/LambdaParameterValuesEditor.js.map +1 -0
- package/lib/components/LegendApplicationComponentFrameworkProvider.d.ts +1 -1
- package/lib/components/LegendApplicationComponentFrameworkProvider.js +3 -3
- package/lib/components/LegendApplicationComponentFrameworkProvider.js.map +1 -1
- package/lib/components/LegendApplicationNavigationContextServiceUtils.d.ts +32 -0
- package/lib/components/LegendApplicationNavigationContextServiceUtils.d.ts.map +1 -0
- package/lib/components/LegendApplicationNavigationContextServiceUtils.js +57 -0
- package/lib/components/LegendApplicationNavigationContextServiceUtils.js.map +1 -0
- package/lib/components/NotificationManager.d.ts +1 -0
- package/lib/components/NotificationManager.d.ts.map +1 -1
- package/lib/components/NotificationManager.js +2 -2
- package/lib/components/NotificationManager.js.map +1 -1
- package/lib/components/TextInputEditor.d.ts +2 -2
- package/lib/components/TextInputEditor.d.ts.map +1 -1
- package/lib/components/TextInputEditor.js +2 -2
- package/lib/components/TextInputEditor.js.map +1 -1
- package/lib/components/{AppHeader.d.ts → VirtualAssistant.d.ts} +5 -5
- package/lib/components/VirtualAssistant.d.ts.map +1 -0
- package/lib/components/VirtualAssistant.js +171 -0
- package/lib/components/VirtualAssistant.js.map +1 -0
- package/lib/components/WebApplicationNavigatorProvider.d.ts +2 -2
- package/lib/components/WebApplicationNavigatorProvider.d.ts.map +1 -1
- package/lib/components/WebApplicationNavigatorProvider.js +1 -1
- package/lib/components/WebApplicationNavigatorProvider.js.map +1 -1
- package/lib/components/WebApplicationNavigatorProviderTestUtils.d.ts +2 -2
- package/lib/components/WebApplicationNavigatorProviderTestUtils.d.ts.map +1 -1
- package/lib/components/WebApplicationNavigatorProviderTestUtils.js +3 -2
- package/lib/components/WebApplicationNavigatorProviderTestUtils.js.map +1 -1
- package/lib/const.js +2 -2
- package/lib/const.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/index.d.ts +30 -22
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +32 -22
- package/lib/index.js.map +1 -1
- package/lib/stores/ApplicationEvent.d.ts +9 -7
- package/lib/stores/ApplicationEvent.d.ts.map +1 -1
- package/lib/stores/ApplicationEvent.js +10 -8
- package/lib/stores/ApplicationEvent.js.map +1 -1
- package/lib/stores/ApplicationStore.d.ts +15 -9
- package/lib/stores/ApplicationStore.d.ts.map +1 -1
- package/lib/stores/ApplicationStore.js +26 -21
- package/lib/stores/ApplicationStore.js.map +1 -1
- package/lib/stores/ApplicationStoreTestUtils.d.ts +3 -3
- package/lib/stores/ApplicationStoreTestUtils.d.ts.map +1 -1
- package/lib/stores/ApplicationStoreTestUtils.js +2 -2
- package/lib/stores/ApplicationStoreTestUtils.js.map +1 -1
- package/lib/stores/ApplicationTelemetry.d.ts +1 -1
- package/lib/stores/ApplicationTelemetry.d.ts.map +1 -1
- package/lib/stores/ApplicationTelemetry.js +2 -2
- package/lib/stores/ApplicationTelemetry.js.map +1 -1
- package/lib/stores/CJS__Fuse.cjs +35 -0
- package/lib/stores/CJS__Fuse.cjs.map +1 -0
- package/lib/stores/CJS__Fuse.d.cts +28 -0
- package/lib/stores/CJS__Fuse.d.cts.map +1 -0
- package/lib/stores/LambdaParameterState.d.ts +59 -0
- package/lib/stores/LambdaParameterState.d.ts.map +1 -0
- package/lib/stores/LambdaParameterState.js +184 -0
- package/lib/stores/LambdaParameterState.js.map +1 -0
- package/lib/stores/LegendApplicationAssistantService.d.ts +63 -0
- package/lib/stores/LegendApplicationAssistantService.d.ts.map +1 -0
- package/lib/stores/LegendApplicationAssistantService.js +167 -0
- package/lib/stores/LegendApplicationAssistantService.js.map +1 -0
- package/lib/stores/LegendApplicationConfig.d.ts +5 -4
- package/lib/stores/LegendApplicationConfig.d.ts.map +1 -1
- package/lib/stores/LegendApplicationConfig.js +8 -3
- package/lib/stores/LegendApplicationConfig.js.map +1 -1
- package/lib/stores/LegendApplicationDocumentationService.d.ts +70 -0
- package/lib/stores/LegendApplicationDocumentationService.d.ts.map +1 -0
- package/lib/stores/LegendApplicationDocumentationService.js +152 -0
- package/lib/stores/LegendApplicationDocumentationService.js.map +1 -0
- package/lib/stores/LegendApplicationEventService.d.ts +22 -0
- package/lib/stores/LegendApplicationEventService.d.ts.map +1 -0
- package/lib/stores/LegendApplicationEventService.js +25 -0
- package/lib/stores/LegendApplicationEventService.js.map +1 -0
- package/lib/stores/LegendApplicationNavigationContextService.d.ts +74 -0
- package/lib/stores/LegendApplicationNavigationContextService.d.ts.map +1 -0
- package/lib/stores/LegendApplicationNavigationContextService.js +118 -0
- package/lib/stores/LegendApplicationNavigationContextService.js.map +1 -0
- package/lib/stores/LegendApplicationPlugin.d.ts +6 -2
- package/lib/stores/LegendApplicationPlugin.d.ts.map +1 -1
- package/lib/stores/LegendApplicationPlugin.js.map +1 -1
- package/lib/stores/PureLanguageSupport.d.ts.map +1 -1
- package/lib/stores/PureLanguageSupport.js +8 -2
- package/lib/stores/PureLanguageSupport.js.map +1 -1
- package/lib/stores/ValueSpecificationModifierHelper.d.ts +27 -0
- package/lib/stores/ValueSpecificationModifierHelper.d.ts.map +1 -0
- package/lib/stores/ValueSpecificationModifierHelper.js +49 -0
- package/lib/stores/ValueSpecificationModifierHelper.js.map +1 -0
- package/package.json +24 -18
- package/src/application/LegendApplication.tsx +6 -6
- package/src/application/LegendApplicationPluginManager.tsx +3 -3
- package/src/components/ActionAlert.tsx +2 -2
- package/src/components/ApplicationStoreProvider.tsx +4 -4
- package/src/components/ApplicationStoreProviderTestUtils.tsx +7 -6
- package/src/components/BasicValueSpecificationEditor.tsx +703 -0
- package/src/components/BlockingAlert.tsx +1 -1
- package/src/components/CustomDatePicker.tsx +1235 -0
- package/src/components/DocumentationLink.tsx +2 -2
- package/src/components/LambdaEditor.tsx +4 -4
- package/src/components/LambdaParameterValuesEditor.tsx +114 -0
- package/src/components/LegendApplicationComponentFrameworkProvider.tsx +3 -3
- package/src/components/LegendApplicationNavigationContextServiceUtils.tsx +63 -0
- package/src/components/NotificationManager.tsx +2 -2
- package/src/components/TextInputEditor.tsx +2 -2
- package/src/components/VirtualAssistant.tsx +600 -0
- package/src/components/WebApplicationNavigatorProvider.tsx +1 -1
- package/src/components/WebApplicationNavigatorProviderTestUtils.tsx +3 -2
- package/src/index.ts +39 -28
- package/src/stores/ApplicationEvent.ts +11 -7
- package/src/stores/ApplicationStore.ts +29 -27
- package/src/stores/ApplicationStoreTestUtils.ts +4 -4
- package/src/stores/ApplicationTelemetry.ts +2 -2
- package/src/stores/CJS__Fuse.cts +28 -0
- package/src/stores/LambdaParameterState.ts +314 -0
- package/src/stores/LegendApplicationAssistantService.ts +218 -0
- package/src/stores/LegendApplicationConfig.ts +20 -6
- package/src/stores/LegendApplicationDocumentationService.ts +276 -0
- package/src/stores/LegendApplicationEventService.ts +32 -0
- package/src/stores/LegendApplicationNavigationContextService.ts +131 -0
- package/src/stores/LegendApplicationPlugin.ts +10 -2
- package/src/stores/PureLanguageSupport.ts +8 -2
- package/src/stores/ValueSpecificationModifierHelper.ts +104 -0
- package/tsconfig.json +18 -12
- package/lib/components/AppHeader.d.ts.map +0 -1
- package/lib/components/AppHeader.js +0 -26
- package/lib/components/AppHeader.js.map +0 -1
- package/lib/stores/LegendApplicationDocumentationRegistry.d.ts +0 -36
- package/lib/stores/LegendApplicationDocumentationRegistry.d.ts.map +0 -1
- package/lib/stores/LegendApplicationDocumentationRegistry.js +0 -47
- package/lib/stores/LegendApplicationDocumentationRegistry.js.map +0 -1
- package/src/components/AppHeader.tsx +0 -49
- package/src/stores/LegendApplicationDocumentationRegistry.ts +0 -81
|
@@ -0,0 +1,600 @@
|
|
|
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
|
+
clsx,
|
|
19
|
+
BasePopper,
|
|
20
|
+
TimesIcon,
|
|
21
|
+
SearchIcon,
|
|
22
|
+
MapMarkerIcon,
|
|
23
|
+
CloseIcon,
|
|
24
|
+
ContextMenu,
|
|
25
|
+
MenuContent,
|
|
26
|
+
MenuContentItem,
|
|
27
|
+
MarkdownTextViewer,
|
|
28
|
+
ChevronDownIcon,
|
|
29
|
+
ChevronRightIcon,
|
|
30
|
+
BlankPanelContent,
|
|
31
|
+
BeardIcon,
|
|
32
|
+
SunglassesIcon,
|
|
33
|
+
WizardHatIcon,
|
|
34
|
+
FaceLaughWinkIcon,
|
|
35
|
+
VerticalDragHandleThinIcon,
|
|
36
|
+
CircleIcon,
|
|
37
|
+
PanelLoadingIndicator,
|
|
38
|
+
} from '@finos/legend-art';
|
|
39
|
+
import {
|
|
40
|
+
ContentType,
|
|
41
|
+
debounce,
|
|
42
|
+
downloadFileUsingDataURI,
|
|
43
|
+
isString,
|
|
44
|
+
uuid,
|
|
45
|
+
} from '@finos/legend-shared';
|
|
46
|
+
import { format } from 'date-fns';
|
|
47
|
+
import { observer } from 'mobx-react-lite';
|
|
48
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
49
|
+
import { DATE_TIME_FORMAT, TAB_SIZE } from '../const.js';
|
|
50
|
+
import {
|
|
51
|
+
type VirtualAssistantDocumentationEntry,
|
|
52
|
+
VIRTUAL_ASSISTANT_TAB,
|
|
53
|
+
} from '../stores/LegendApplicationAssistantService.js';
|
|
54
|
+
import { useApplicationStore } from './ApplicationStoreProvider.js';
|
|
55
|
+
import Draggable from 'react-draggable';
|
|
56
|
+
|
|
57
|
+
const WIZARD_GREETING = `Bonjour, It's Pierre!`;
|
|
58
|
+
|
|
59
|
+
const VirtualAssistantDocumentationEntryViewer = observer(
|
|
60
|
+
(props: { entry: VirtualAssistantDocumentationEntry }) => {
|
|
61
|
+
const { entry } = props;
|
|
62
|
+
const applicationStore = useApplicationStore();
|
|
63
|
+
const toggleExpand = (): void => entry.setIsOpen(!entry.isOpen);
|
|
64
|
+
const copyDocumentationKey = applicationStore.guardUnhandledError(() =>
|
|
65
|
+
applicationStore.copyTextToClipboard(entry.documentationKey),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<ContextMenu
|
|
70
|
+
className="virtual-assistant__doc-entry"
|
|
71
|
+
menuProps={{
|
|
72
|
+
elevation: 7,
|
|
73
|
+
classes: {
|
|
74
|
+
root: 'virtual-assistant__context-menu',
|
|
75
|
+
},
|
|
76
|
+
}}
|
|
77
|
+
content={
|
|
78
|
+
<MenuContent>
|
|
79
|
+
<MenuContentItem onClick={copyDocumentationKey}>
|
|
80
|
+
Copy Documentation Key
|
|
81
|
+
</MenuContentItem>
|
|
82
|
+
</MenuContent>
|
|
83
|
+
}
|
|
84
|
+
>
|
|
85
|
+
<div className="virtual-assistant__doc-entry">
|
|
86
|
+
<div className="virtual-assistant__doc-entry__header">
|
|
87
|
+
<button
|
|
88
|
+
className={clsx('virtual-assistant__doc-entry__expand-icon', {
|
|
89
|
+
'virtual-assistant__doc-entry__expand-icon--disabled':
|
|
90
|
+
!entry.content,
|
|
91
|
+
})}
|
|
92
|
+
disabled={!entry.content}
|
|
93
|
+
tabIndex={-1}
|
|
94
|
+
onClick={toggleExpand}
|
|
95
|
+
>
|
|
96
|
+
{entry.isOpen ? <ChevronDownIcon /> : <ChevronRightIcon />}
|
|
97
|
+
</button>
|
|
98
|
+
{entry.url ? (
|
|
99
|
+
<a
|
|
100
|
+
className="virtual-assistant__doc-entry__title virtual-assistant__doc-entry__title--link"
|
|
101
|
+
rel="noopener noreferrer"
|
|
102
|
+
target="_blank"
|
|
103
|
+
href={entry.url}
|
|
104
|
+
title="Click to see external documentation"
|
|
105
|
+
>
|
|
106
|
+
{entry.title}
|
|
107
|
+
</a>
|
|
108
|
+
) : (
|
|
109
|
+
<div
|
|
110
|
+
className="virtual-assistant__doc-entry__title"
|
|
111
|
+
onClick={toggleExpand}
|
|
112
|
+
>
|
|
113
|
+
{entry.title}
|
|
114
|
+
</div>
|
|
115
|
+
)}
|
|
116
|
+
</div>
|
|
117
|
+
{entry.isOpen && entry.content && (
|
|
118
|
+
<div className="virtual-assistant__doc-entry__content">
|
|
119
|
+
{isString(entry.content) ? (
|
|
120
|
+
<div className="virtual-assistant__doc-entry__content__text">
|
|
121
|
+
{entry.content}
|
|
122
|
+
</div>
|
|
123
|
+
) : (
|
|
124
|
+
<MarkdownTextViewer
|
|
125
|
+
className="virtual-assistant__doc-entry__content__markdown-text"
|
|
126
|
+
value={entry.content}
|
|
127
|
+
/>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
)}
|
|
131
|
+
</div>
|
|
132
|
+
</ContextMenu>
|
|
133
|
+
);
|
|
134
|
+
},
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const VirtualAssistantContextualSupportPanel = observer(() => {
|
|
138
|
+
const applicationStore = useApplicationStore();
|
|
139
|
+
const assistantService = applicationStore.assistantService;
|
|
140
|
+
const contextualEntry = assistantService.currentContextualDocumentationEntry;
|
|
141
|
+
const copyContextIDToClipboard = applicationStore.guardUnhandledError(() =>
|
|
142
|
+
applicationStore.copyTextToClipboard(contextualEntry?.context ?? ''),
|
|
143
|
+
);
|
|
144
|
+
const copyCurrentContextIDToClipboard = applicationStore.guardUnhandledError(
|
|
145
|
+
() =>
|
|
146
|
+
applicationStore.copyTextToClipboard(
|
|
147
|
+
applicationStore.navigationContextService.currentContext?.value ?? '',
|
|
148
|
+
),
|
|
149
|
+
);
|
|
150
|
+
const copyContextStackToClipboard = applicationStore.guardUnhandledError(() =>
|
|
151
|
+
applicationStore.copyTextToClipboard(
|
|
152
|
+
applicationStore.navigationContextService.contextStack
|
|
153
|
+
.map((context) => context.value)
|
|
154
|
+
.join(' > '),
|
|
155
|
+
),
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<ContextMenu
|
|
160
|
+
className="virtual-assistant__contextual-support"
|
|
161
|
+
disabled={!contextualEntry}
|
|
162
|
+
menuProps={{
|
|
163
|
+
elevation: 7,
|
|
164
|
+
classes: {
|
|
165
|
+
root: 'virtual-assistant__context-menu',
|
|
166
|
+
},
|
|
167
|
+
}}
|
|
168
|
+
content={
|
|
169
|
+
<MenuContent>
|
|
170
|
+
<MenuContentItem onClick={copyContextIDToClipboard}>
|
|
171
|
+
Copy Context ID
|
|
172
|
+
</MenuContentItem>
|
|
173
|
+
<MenuContentItem onClick={copyCurrentContextIDToClipboard}>
|
|
174
|
+
Copy Current Context ID
|
|
175
|
+
</MenuContentItem>
|
|
176
|
+
<MenuContentItem onClick={copyContextStackToClipboard}>
|
|
177
|
+
Copy Context Stack
|
|
178
|
+
</MenuContentItem>
|
|
179
|
+
</MenuContent>
|
|
180
|
+
}
|
|
181
|
+
>
|
|
182
|
+
{contextualEntry && (
|
|
183
|
+
<div className="virtual-assistant__contextual-support__content">
|
|
184
|
+
{contextualEntry.title && (
|
|
185
|
+
<div className="virtual-assistant__contextual-support__title">
|
|
186
|
+
{contextualEntry.title}
|
|
187
|
+
</div>
|
|
188
|
+
)}
|
|
189
|
+
{contextualEntry.content && (
|
|
190
|
+
<>
|
|
191
|
+
{isString(contextualEntry.content) ? (
|
|
192
|
+
<div className="virtual-assistant__contextual-support__text">
|
|
193
|
+
{contextualEntry.content}
|
|
194
|
+
</div>
|
|
195
|
+
) : (
|
|
196
|
+
<MarkdownTextViewer
|
|
197
|
+
className="virtual-assistant__contextual-support__markdown-text"
|
|
198
|
+
value={contextualEntry.content}
|
|
199
|
+
/>
|
|
200
|
+
)}
|
|
201
|
+
</>
|
|
202
|
+
)}
|
|
203
|
+
{contextualEntry.related.length && (
|
|
204
|
+
<div className="virtual-assistant__contextual-support__relevant-entries">
|
|
205
|
+
<div className="virtual-assistant__contextual-support__relevant-entries__title">
|
|
206
|
+
Related entries ({contextualEntry.related.length})
|
|
207
|
+
</div>
|
|
208
|
+
{contextualEntry.related.map((entry) => (
|
|
209
|
+
<VirtualAssistantDocumentationEntryViewer
|
|
210
|
+
key={entry.uuid}
|
|
211
|
+
entry={entry}
|
|
212
|
+
/>
|
|
213
|
+
))}
|
|
214
|
+
</div>
|
|
215
|
+
)}
|
|
216
|
+
</div>
|
|
217
|
+
)}
|
|
218
|
+
{!contextualEntry && (
|
|
219
|
+
<BlankPanelContent>
|
|
220
|
+
<div className="virtual-assistant__contextual-support__placeholder">
|
|
221
|
+
<FaceLaughWinkIcon className="virtual-assistant__contextual-support__placeholder__icon" />
|
|
222
|
+
<div className="virtual-assistant__contextual-support__placeholder__message">
|
|
223
|
+
No contextual documentation found!
|
|
224
|
+
</div>
|
|
225
|
+
<div className="virtual-assistant__contextual-support__placeholder__instruction">
|
|
226
|
+
Keep using the app, when contextual doc available, we will let you
|
|
227
|
+
know!
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
</BlankPanelContent>
|
|
231
|
+
)}
|
|
232
|
+
</ContextMenu>
|
|
233
|
+
);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const VirtualAssistantSearchPanel = observer(() => {
|
|
237
|
+
const applicationStore = useApplicationStore();
|
|
238
|
+
const searchInputRef = useRef<HTMLInputElement>(null);
|
|
239
|
+
const assistantService = applicationStore.assistantService;
|
|
240
|
+
const searchText = assistantService.searchText;
|
|
241
|
+
const debouncedSearch = useMemo(
|
|
242
|
+
() => debounce(() => assistantService.search(), 100),
|
|
243
|
+
[assistantService],
|
|
244
|
+
);
|
|
245
|
+
const onSearchTextChange: React.ChangeEventHandler<HTMLInputElement> = (
|
|
246
|
+
event,
|
|
247
|
+
) => {
|
|
248
|
+
assistantService.setSearchText(event.target.value);
|
|
249
|
+
debouncedSearch();
|
|
250
|
+
};
|
|
251
|
+
const clearSearchText = (): void => {
|
|
252
|
+
assistantService.resetSearch();
|
|
253
|
+
searchInputRef.current?.focus();
|
|
254
|
+
};
|
|
255
|
+
const results = assistantService.searchResults;
|
|
256
|
+
const resultCount =
|
|
257
|
+
assistantService.searchResults.length > 99
|
|
258
|
+
? '99+'
|
|
259
|
+
: assistantService.searchResults.length;
|
|
260
|
+
const downloadDocRegistry = (): void => {
|
|
261
|
+
downloadFileUsingDataURI(
|
|
262
|
+
`documentation-registry_${format(
|
|
263
|
+
new Date(Date.now()),
|
|
264
|
+
DATE_TIME_FORMAT,
|
|
265
|
+
)}.json`,
|
|
266
|
+
JSON.stringify(
|
|
267
|
+
applicationStore.documentationService.publishDocRegistry(),
|
|
268
|
+
undefined,
|
|
269
|
+
TAB_SIZE,
|
|
270
|
+
),
|
|
271
|
+
ContentType.APPLICATION_JSON,
|
|
272
|
+
);
|
|
273
|
+
};
|
|
274
|
+
const downloadContextualDocRegistry = (): void => {
|
|
275
|
+
downloadFileUsingDataURI(
|
|
276
|
+
`documentation-registry_${format(
|
|
277
|
+
new Date(Date.now()),
|
|
278
|
+
DATE_TIME_FORMAT,
|
|
279
|
+
)}.json`,
|
|
280
|
+
JSON.stringify(
|
|
281
|
+
applicationStore.documentationService.publishContextualDocRegistry(),
|
|
282
|
+
undefined,
|
|
283
|
+
TAB_SIZE,
|
|
284
|
+
),
|
|
285
|
+
ContentType.APPLICATION_JSON,
|
|
286
|
+
);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
useEffect(() => {
|
|
290
|
+
searchInputRef.current?.focus();
|
|
291
|
+
}, []);
|
|
292
|
+
|
|
293
|
+
return (
|
|
294
|
+
<div className="virtual-assistant__search">
|
|
295
|
+
<div className="virtual-assistant__search__header">
|
|
296
|
+
<input
|
|
297
|
+
ref={searchInputRef}
|
|
298
|
+
className={clsx('virtual-assistant__search__input input--dark', {
|
|
299
|
+
'virtual-assistant__search__input--searching': searchText,
|
|
300
|
+
})}
|
|
301
|
+
onChange={onSearchTextChange}
|
|
302
|
+
value={searchText}
|
|
303
|
+
placeholder="Ask me a question"
|
|
304
|
+
/>
|
|
305
|
+
{!searchText ? (
|
|
306
|
+
<div className="virtual-assistant__search__input__search__icon">
|
|
307
|
+
<SearchIcon />
|
|
308
|
+
</div>
|
|
309
|
+
) : (
|
|
310
|
+
<>
|
|
311
|
+
<div className="virtual-assistant__search__input__search__count">
|
|
312
|
+
{resultCount}
|
|
313
|
+
</div>
|
|
314
|
+
<button
|
|
315
|
+
className="virtual-assistant__search__input__clear-btn"
|
|
316
|
+
tabIndex={-1}
|
|
317
|
+
onClick={clearSearchText}
|
|
318
|
+
title="Clear"
|
|
319
|
+
>
|
|
320
|
+
<TimesIcon />
|
|
321
|
+
</button>
|
|
322
|
+
</>
|
|
323
|
+
)}
|
|
324
|
+
</div>
|
|
325
|
+
<div className="virtual-assistant__search__content">
|
|
326
|
+
<PanelLoadingIndicator
|
|
327
|
+
isLoading={assistantService.searchState.isInProgress}
|
|
328
|
+
/>
|
|
329
|
+
{Boolean(results.length) && (
|
|
330
|
+
<div className="virtual-assistant__search__results">
|
|
331
|
+
{results.map((result) => (
|
|
332
|
+
<VirtualAssistantDocumentationEntryViewer
|
|
333
|
+
key={result.uuid}
|
|
334
|
+
entry={result}
|
|
335
|
+
/>
|
|
336
|
+
))}
|
|
337
|
+
</div>
|
|
338
|
+
)}
|
|
339
|
+
{searchText && !results.length && (
|
|
340
|
+
<BlankPanelContent>
|
|
341
|
+
<div className="virtual-assistant__search__results__placeholder">
|
|
342
|
+
no result
|
|
343
|
+
</div>
|
|
344
|
+
</BlankPanelContent>
|
|
345
|
+
)}
|
|
346
|
+
{/*
|
|
347
|
+
NOTE: technically, we don't need to check for the result size here.
|
|
348
|
+
However, since the search results update is slightly delayed compared to
|
|
349
|
+
the search text update, we do this to avoid showing the placeholder too
|
|
350
|
+
early, i.e. when the search results are not yet cleaned
|
|
351
|
+
*/}
|
|
352
|
+
{!searchText && !results.length && (
|
|
353
|
+
<ContextMenu
|
|
354
|
+
className="virtual-assistant__character__container"
|
|
355
|
+
menuProps={{
|
|
356
|
+
elevation: 7,
|
|
357
|
+
classes: {
|
|
358
|
+
root: 'virtual-assistant__context-menu',
|
|
359
|
+
},
|
|
360
|
+
}}
|
|
361
|
+
content={
|
|
362
|
+
<MenuContent>
|
|
363
|
+
<MenuContentItem onClick={downloadDocRegistry}>
|
|
364
|
+
Download documentation registry
|
|
365
|
+
</MenuContentItem>
|
|
366
|
+
<MenuContentItem onClick={downloadContextualDocRegistry}>
|
|
367
|
+
Download contextual documentation registry
|
|
368
|
+
</MenuContentItem>
|
|
369
|
+
</MenuContent>
|
|
370
|
+
}
|
|
371
|
+
>
|
|
372
|
+
<div className="virtual-assistant__character">
|
|
373
|
+
<div className="virtual-assistant__character__figure">
|
|
374
|
+
<WizardHatIcon className="virtual-assistant__character__hat" />
|
|
375
|
+
<SunglassesIcon className="virtual-assistant__character__glasses" />
|
|
376
|
+
<BeardIcon className="virtual-assistant__character__beard" />
|
|
377
|
+
</div>
|
|
378
|
+
<div className="virtual-assistant__character__greeting">
|
|
379
|
+
{WIZARD_GREETING}
|
|
380
|
+
</div>
|
|
381
|
+
<div className="virtual-assistant__character__question">
|
|
382
|
+
How can I help today?
|
|
383
|
+
</div>
|
|
384
|
+
</div>
|
|
385
|
+
</ContextMenu>
|
|
386
|
+
)}
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
const VirtualAssistantPanel = observer(
|
|
393
|
+
(props: { triggerElement: HTMLElement | null }) => {
|
|
394
|
+
const { triggerElement } = props;
|
|
395
|
+
const applicationStore = useApplicationStore();
|
|
396
|
+
const assistantService = applicationStore.assistantService;
|
|
397
|
+
const currentContextualDocumentationEntry =
|
|
398
|
+
assistantService.currentContextualDocumentationEntry;
|
|
399
|
+
const selectedTab = assistantService.selectedTab;
|
|
400
|
+
|
|
401
|
+
const selectSearch = (): void =>
|
|
402
|
+
assistantService.setSelectedTab(VIRTUAL_ASSISTANT_TAB.SEARCH);
|
|
403
|
+
const selectContextualDoc = (): void =>
|
|
404
|
+
assistantService.setSelectedTab(VIRTUAL_ASSISTANT_TAB.CONTEXTUAL_SUPPORT);
|
|
405
|
+
const closeAssistantPanel = (): void => assistantService.setIsOpen(false);
|
|
406
|
+
|
|
407
|
+
return (
|
|
408
|
+
<BasePopper
|
|
409
|
+
open={assistantService.isOpen}
|
|
410
|
+
className="virtual-assistant__panel__container"
|
|
411
|
+
anchorEl={triggerElement}
|
|
412
|
+
// NOTE: make sure the assistant is always fully displayed (not cropped)
|
|
413
|
+
placement="auto-start"
|
|
414
|
+
>
|
|
415
|
+
<div className="virtual-assistant__panel">
|
|
416
|
+
<div className="virtual-assistant__panel__header">
|
|
417
|
+
<div className="virtual-assistant__panel__header__tabs">
|
|
418
|
+
<div
|
|
419
|
+
className={clsx('virtual-assistant__panel__header__tab', {
|
|
420
|
+
'virtual-assistant__panel__header__tab--active':
|
|
421
|
+
selectedTab === VIRTUAL_ASSISTANT_TAB.SEARCH,
|
|
422
|
+
})}
|
|
423
|
+
onClick={selectSearch}
|
|
424
|
+
title="Search"
|
|
425
|
+
>
|
|
426
|
+
<div className="virtual-assistant__panel__header__tab__content">
|
|
427
|
+
<SearchIcon />
|
|
428
|
+
</div>
|
|
429
|
+
</div>
|
|
430
|
+
<div
|
|
431
|
+
className={clsx('virtual-assistant__panel__header__tab', {
|
|
432
|
+
'virtual-assistant__panel__header__tab--active':
|
|
433
|
+
selectedTab === VIRTUAL_ASSISTANT_TAB.CONTEXTUAL_SUPPORT,
|
|
434
|
+
})}
|
|
435
|
+
onClick={selectContextualDoc}
|
|
436
|
+
title="Contextual Support"
|
|
437
|
+
>
|
|
438
|
+
<div className="virtual-assistant__panel__header__tab__content">
|
|
439
|
+
<MapMarkerIcon />
|
|
440
|
+
{currentContextualDocumentationEntry && (
|
|
441
|
+
<div
|
|
442
|
+
className="virtual-assistant__panel__header__tab__indicator"
|
|
443
|
+
title="Contextual support available"
|
|
444
|
+
/>
|
|
445
|
+
)}
|
|
446
|
+
</div>
|
|
447
|
+
</div>
|
|
448
|
+
</div>
|
|
449
|
+
<div className="virtual-assistant__panel__header__actions">
|
|
450
|
+
<button
|
|
451
|
+
className="virtual-assistant__panel__header__action"
|
|
452
|
+
tabIndex={-1}
|
|
453
|
+
onClick={closeAssistantPanel}
|
|
454
|
+
title="Close panel"
|
|
455
|
+
>
|
|
456
|
+
<CloseIcon className="virtual-assistant__panel__icon__close" />
|
|
457
|
+
</button>
|
|
458
|
+
</div>
|
|
459
|
+
</div>
|
|
460
|
+
<div className="virtual-assistant__panel__content">
|
|
461
|
+
{selectedTab === VIRTUAL_ASSISTANT_TAB.SEARCH && (
|
|
462
|
+
<VirtualAssistantSearchPanel />
|
|
463
|
+
)}
|
|
464
|
+
{selectedTab === VIRTUAL_ASSISTANT_TAB.CONTEXTUAL_SUPPORT && (
|
|
465
|
+
<VirtualAssistantContextualSupportPanel />
|
|
466
|
+
)}
|
|
467
|
+
</div>
|
|
468
|
+
</div>
|
|
469
|
+
</BasePopper>
|
|
470
|
+
);
|
|
471
|
+
},
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
export const VirtualAssistant = observer(() => {
|
|
475
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
476
|
+
const [_key, _setKey] = useState(uuid());
|
|
477
|
+
const applicationStore = useApplicationStore();
|
|
478
|
+
const assistantRef = useRef<HTMLDivElement>(null);
|
|
479
|
+
const assistantService = applicationStore.assistantService;
|
|
480
|
+
const currentContextualDocumentationEntry =
|
|
481
|
+
assistantService.currentContextualDocumentationEntry;
|
|
482
|
+
const toggleAssistantPanel = (): void => {
|
|
483
|
+
const newVal = !assistantService.isOpen;
|
|
484
|
+
// open the contextual help tab when contextual help is available
|
|
485
|
+
if (newVal && currentContextualDocumentationEntry) {
|
|
486
|
+
assistantService.setSelectedTab(VIRTUAL_ASSISTANT_TAB.CONTEXTUAL_SUPPORT);
|
|
487
|
+
}
|
|
488
|
+
assistantService.setIsOpen(!assistantService.isOpen);
|
|
489
|
+
};
|
|
490
|
+
const hideAssistant = (): void =>
|
|
491
|
+
applicationStore.assistantService.hideAssistant();
|
|
492
|
+
const resetPosition = (): void => {
|
|
493
|
+
// close the panel since
|
|
494
|
+
assistantService.setIsOpen(false);
|
|
495
|
+
_setKey(uuid());
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// drag and drop
|
|
499
|
+
const onDragEnd = (): void => setIsDragging(false);
|
|
500
|
+
const onDragStart = (): void => setIsDragging(true);
|
|
501
|
+
|
|
502
|
+
useEffect(() => {
|
|
503
|
+
if (assistantService.isHidden) {
|
|
504
|
+
// reset to default position when we hide the assistant
|
|
505
|
+
// so that when we open it the position is reset
|
|
506
|
+
_setKey(uuid());
|
|
507
|
+
}
|
|
508
|
+
}, [assistantService.isHidden]);
|
|
509
|
+
|
|
510
|
+
return (
|
|
511
|
+
<Draggable
|
|
512
|
+
// this is a trick so we could reset the default position of the assistant
|
|
513
|
+
// See https://github.com/react-grid-layout/react-draggable/issues/214#issuecomment-270021423
|
|
514
|
+
key={_key}
|
|
515
|
+
// make sure we cannot drag and drop outside of the screen
|
|
516
|
+
bounds="parent"
|
|
517
|
+
onStart={onDragStart}
|
|
518
|
+
onStop={onDragEnd}
|
|
519
|
+
// limit the dnd trigger to only the drag handle
|
|
520
|
+
handle=".virtual-assistant__station__drag-handle"
|
|
521
|
+
>
|
|
522
|
+
<div
|
|
523
|
+
className="virtual-assistant"
|
|
524
|
+
// NOTE: we have to set the `ref` at this level so even when the assistant is hidden
|
|
525
|
+
// the element is still in the DOM so when we programmatically show the assistant panel
|
|
526
|
+
// the anchor is available in time
|
|
527
|
+
ref={assistantRef}
|
|
528
|
+
>
|
|
529
|
+
<div
|
|
530
|
+
// NOTE: make sure when we change the documentation entry, the flashing animation
|
|
531
|
+
// is replayed
|
|
532
|
+
key={currentContextualDocumentationEntry?.uuid ?? ''}
|
|
533
|
+
className={clsx('virtual-assistant__station', {
|
|
534
|
+
'virtual-assistant__station--hidden': assistantService.isHidden,
|
|
535
|
+
'virtual-assistant__station--active': Boolean(
|
|
536
|
+
currentContextualDocumentationEntry,
|
|
537
|
+
),
|
|
538
|
+
})}
|
|
539
|
+
>
|
|
540
|
+
<button
|
|
541
|
+
className="virtual-assistant__station__trigger"
|
|
542
|
+
tabIndex={-1}
|
|
543
|
+
onClick={toggleAssistantPanel}
|
|
544
|
+
title={
|
|
545
|
+
assistantService.isOpen
|
|
546
|
+
? `Click to close assistant panel`
|
|
547
|
+
: `${
|
|
548
|
+
currentContextualDocumentationEntry
|
|
549
|
+
? 'Contextual support available.\n'
|
|
550
|
+
: ''
|
|
551
|
+
}Click to open assistant panel...`
|
|
552
|
+
}
|
|
553
|
+
>
|
|
554
|
+
{assistantService.isOpen ? (
|
|
555
|
+
<CloseIcon className="virtual-assistant__station__trigger__close" />
|
|
556
|
+
) : currentContextualDocumentationEntry ? (
|
|
557
|
+
<CircleIcon className="virtual-assistant__station__trigger__circle" />
|
|
558
|
+
) : null}
|
|
559
|
+
</button>
|
|
560
|
+
{/* NOTE: temporarily hide the assistant panel while dragging so the position is re-calculated */}
|
|
561
|
+
{!isDragging &&
|
|
562
|
+
assistantService.isOpen &&
|
|
563
|
+
!assistantService.isHidden &&
|
|
564
|
+
assistantRef.current && (
|
|
565
|
+
<VirtualAssistantPanel triggerElement={assistantRef.current} />
|
|
566
|
+
)}
|
|
567
|
+
|
|
568
|
+
<ContextMenu
|
|
569
|
+
className={clsx('virtual-assistant__station__drag-handle', {
|
|
570
|
+
'virtual-assistant__station__drag-handle--dragging': isDragging,
|
|
571
|
+
})}
|
|
572
|
+
menuProps={{
|
|
573
|
+
elevation: 7,
|
|
574
|
+
classes: {
|
|
575
|
+
root: 'virtual-assistant__context-menu',
|
|
576
|
+
},
|
|
577
|
+
}}
|
|
578
|
+
content={
|
|
579
|
+
<MenuContent>
|
|
580
|
+
<MenuContentItem onClick={resetPosition}>
|
|
581
|
+
Reset Position
|
|
582
|
+
</MenuContentItem>
|
|
583
|
+
<MenuContentItem onClick={hideAssistant}>
|
|
584
|
+
Hide Assistant
|
|
585
|
+
</MenuContentItem>
|
|
586
|
+
</MenuContent>
|
|
587
|
+
}
|
|
588
|
+
>
|
|
589
|
+
<div
|
|
590
|
+
className="virtual-assistant__station__drag-handle__content"
|
|
591
|
+
title={isDragging ? undefined : 'Grab to drag assistant'}
|
|
592
|
+
>
|
|
593
|
+
<VerticalDragHandleThinIcon />
|
|
594
|
+
</div>
|
|
595
|
+
</ContextMenu>
|
|
596
|
+
</div>
|
|
597
|
+
</div>
|
|
598
|
+
</Draggable>
|
|
599
|
+
);
|
|
600
|
+
});
|
|
@@ -19,7 +19,7 @@ import { useLocalObservable } from 'mobx-react-lite';
|
|
|
19
19
|
import { createContext, useContext } from 'react';
|
|
20
20
|
import { useHistory } from 'react-router';
|
|
21
21
|
import type { History } from 'history';
|
|
22
|
-
import { WebApplicationNavigator } from '../stores/WebApplicationNavigator';
|
|
22
|
+
import { WebApplicationNavigator } from '../stores/WebApplicationNavigator.js';
|
|
23
23
|
|
|
24
24
|
const WebApplicationNavigatorContext = createContext<
|
|
25
25
|
WebApplicationNavigator | undefined
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { jest } from '@jest/globals';
|
|
18
|
+
import { WebApplicationNavigator } from '../stores/WebApplicationNavigator.js';
|
|
18
19
|
import { createMemoryHistory, type History } from 'history';
|
|
19
20
|
|
|
20
21
|
export const TEST__provideMockedWebApplicationNavigator = (customization?: {
|
|
@@ -26,7 +27,7 @@ export const TEST__provideMockedWebApplicationNavigator = (customization?: {
|
|
|
26
27
|
new WebApplicationNavigator(
|
|
27
28
|
customization?.history ?? createMemoryHistory(),
|
|
28
29
|
);
|
|
29
|
-
const MockWebApplicationNavigatorProvider = require('./WebApplicationNavigatorProvider'); // eslint-disable-line @typescript-eslint/no-unsafe-assignment
|
|
30
|
+
const MockWebApplicationNavigatorProvider = require('./WebApplicationNavigatorProvider.js'); // eslint-disable-line @typescript-eslint/no-unsafe-assignment
|
|
30
31
|
MockWebApplicationNavigatorProvider.useWebApplicationNavigator = jest.fn();
|
|
31
32
|
MockWebApplicationNavigatorProvider.useWebApplicationNavigator.mockReturnValue(
|
|
32
33
|
value,
|