@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.
Files changed (176) hide show
  1. package/lib/application/LegendApplication.d.ts +2 -2
  2. package/lib/application/LegendApplication.d.ts.map +1 -1
  3. package/lib/application/LegendApplication.js +4 -4
  4. package/lib/application/LegendApplication.js.map +1 -1
  5. package/lib/application/LegendApplicationPluginManager.d.ts +3 -3
  6. package/lib/application/LegendApplicationPluginManager.d.ts.map +1 -1
  7. package/lib/components/ActionAlert.d.ts +1 -0
  8. package/lib/components/ActionAlert.d.ts.map +1 -1
  9. package/lib/components/ActionAlert.js +2 -2
  10. package/lib/components/ActionAlert.js.map +1 -1
  11. package/lib/components/ApplicationStoreProvider.d.ts +3 -3
  12. package/lib/components/ApplicationStoreProvider.d.ts.map +1 -1
  13. package/lib/components/ApplicationStoreProvider.js +2 -2
  14. package/lib/components/ApplicationStoreProvider.js.map +1 -1
  15. package/lib/components/ApplicationStoreProviderTestUtils.d.ts +5 -5
  16. package/lib/components/ApplicationStoreProviderTestUtils.d.ts.map +1 -1
  17. package/lib/components/ApplicationStoreProviderTestUtils.js +5 -4
  18. package/lib/components/ApplicationStoreProviderTestUtils.js.map +1 -1
  19. package/lib/components/ApplicationTestID.js +1 -1
  20. package/lib/components/ApplicationTestID.js.map +1 -1
  21. package/lib/components/BasicValueSpecificationEditor.d.ts +44 -0
  22. package/lib/components/BasicValueSpecificationEditor.d.ts.map +1 -0
  23. package/lib/components/BasicValueSpecificationEditor.js +276 -0
  24. package/lib/components/BasicValueSpecificationEditor.js.map +1 -0
  25. package/lib/components/BlockingAlert.d.ts +1 -0
  26. package/lib/components/BlockingAlert.d.ts.map +1 -1
  27. package/lib/components/BlockingAlert.js +1 -1
  28. package/lib/components/BlockingAlert.js.map +1 -1
  29. package/lib/components/CustomDatePicker.d.ts +38 -0
  30. package/lib/components/CustomDatePicker.d.ts.map +1 -0
  31. package/lib/components/CustomDatePicker.js +592 -0
  32. package/lib/components/CustomDatePicker.js.map +1 -0
  33. package/lib/components/DocumentationLink.d.ts +1 -1
  34. package/lib/components/DocumentationLink.js +2 -2
  35. package/lib/components/DocumentationLink.js.map +1 -1
  36. package/lib/components/LambdaEditor.d.ts +2 -1
  37. package/lib/components/LambdaEditor.d.ts.map +1 -1
  38. package/lib/components/LambdaEditor.js +3 -3
  39. package/lib/components/LambdaEditor.js.map +1 -1
  40. package/lib/components/LambdaParameterValuesEditor.d.ts +25 -0
  41. package/lib/components/LambdaParameterValuesEditor.d.ts.map +1 -0
  42. package/lib/components/LambdaParameterValuesEditor.js +52 -0
  43. package/lib/components/LambdaParameterValuesEditor.js.map +1 -0
  44. package/lib/components/LegendApplicationComponentFrameworkProvider.d.ts +1 -1
  45. package/lib/components/LegendApplicationComponentFrameworkProvider.js +3 -3
  46. package/lib/components/LegendApplicationComponentFrameworkProvider.js.map +1 -1
  47. package/lib/components/LegendApplicationNavigationContextServiceUtils.d.ts +32 -0
  48. package/lib/components/LegendApplicationNavigationContextServiceUtils.d.ts.map +1 -0
  49. package/lib/components/LegendApplicationNavigationContextServiceUtils.js +57 -0
  50. package/lib/components/LegendApplicationNavigationContextServiceUtils.js.map +1 -0
  51. package/lib/components/NotificationManager.d.ts +1 -0
  52. package/lib/components/NotificationManager.d.ts.map +1 -1
  53. package/lib/components/NotificationManager.js +2 -2
  54. package/lib/components/NotificationManager.js.map +1 -1
  55. package/lib/components/TextInputEditor.d.ts +2 -2
  56. package/lib/components/TextInputEditor.d.ts.map +1 -1
  57. package/lib/components/TextInputEditor.js +2 -2
  58. package/lib/components/TextInputEditor.js.map +1 -1
  59. package/lib/components/{AppHeader.d.ts → VirtualAssistant.d.ts} +5 -5
  60. package/lib/components/VirtualAssistant.d.ts.map +1 -0
  61. package/lib/components/VirtualAssistant.js +171 -0
  62. package/lib/components/VirtualAssistant.js.map +1 -0
  63. package/lib/components/WebApplicationNavigatorProvider.d.ts +2 -2
  64. package/lib/components/WebApplicationNavigatorProvider.d.ts.map +1 -1
  65. package/lib/components/WebApplicationNavigatorProvider.js +1 -1
  66. package/lib/components/WebApplicationNavigatorProvider.js.map +1 -1
  67. package/lib/components/WebApplicationNavigatorProviderTestUtils.d.ts +2 -2
  68. package/lib/components/WebApplicationNavigatorProviderTestUtils.d.ts.map +1 -1
  69. package/lib/components/WebApplicationNavigatorProviderTestUtils.js +3 -2
  70. package/lib/components/WebApplicationNavigatorProviderTestUtils.js.map +1 -1
  71. package/lib/const.js +2 -2
  72. package/lib/const.js.map +1 -1
  73. package/lib/index.css +2 -2
  74. package/lib/index.css.map +1 -1
  75. package/lib/index.d.ts +30 -22
  76. package/lib/index.d.ts.map +1 -1
  77. package/lib/index.js +32 -22
  78. package/lib/index.js.map +1 -1
  79. package/lib/stores/ApplicationEvent.d.ts +9 -7
  80. package/lib/stores/ApplicationEvent.d.ts.map +1 -1
  81. package/lib/stores/ApplicationEvent.js +10 -8
  82. package/lib/stores/ApplicationEvent.js.map +1 -1
  83. package/lib/stores/ApplicationStore.d.ts +15 -9
  84. package/lib/stores/ApplicationStore.d.ts.map +1 -1
  85. package/lib/stores/ApplicationStore.js +26 -21
  86. package/lib/stores/ApplicationStore.js.map +1 -1
  87. package/lib/stores/ApplicationStoreTestUtils.d.ts +3 -3
  88. package/lib/stores/ApplicationStoreTestUtils.d.ts.map +1 -1
  89. package/lib/stores/ApplicationStoreTestUtils.js +2 -2
  90. package/lib/stores/ApplicationStoreTestUtils.js.map +1 -1
  91. package/lib/stores/ApplicationTelemetry.d.ts +1 -1
  92. package/lib/stores/ApplicationTelemetry.d.ts.map +1 -1
  93. package/lib/stores/ApplicationTelemetry.js +2 -2
  94. package/lib/stores/ApplicationTelemetry.js.map +1 -1
  95. package/lib/stores/CJS__Fuse.cjs +35 -0
  96. package/lib/stores/CJS__Fuse.cjs.map +1 -0
  97. package/lib/stores/CJS__Fuse.d.cts +28 -0
  98. package/lib/stores/CJS__Fuse.d.cts.map +1 -0
  99. package/lib/stores/LambdaParameterState.d.ts +59 -0
  100. package/lib/stores/LambdaParameterState.d.ts.map +1 -0
  101. package/lib/stores/LambdaParameterState.js +184 -0
  102. package/lib/stores/LambdaParameterState.js.map +1 -0
  103. package/lib/stores/LegendApplicationAssistantService.d.ts +63 -0
  104. package/lib/stores/LegendApplicationAssistantService.d.ts.map +1 -0
  105. package/lib/stores/LegendApplicationAssistantService.js +167 -0
  106. package/lib/stores/LegendApplicationAssistantService.js.map +1 -0
  107. package/lib/stores/LegendApplicationConfig.d.ts +5 -4
  108. package/lib/stores/LegendApplicationConfig.d.ts.map +1 -1
  109. package/lib/stores/LegendApplicationConfig.js +8 -3
  110. package/lib/stores/LegendApplicationConfig.js.map +1 -1
  111. package/lib/stores/LegendApplicationDocumentationService.d.ts +70 -0
  112. package/lib/stores/LegendApplicationDocumentationService.d.ts.map +1 -0
  113. package/lib/stores/LegendApplicationDocumentationService.js +152 -0
  114. package/lib/stores/LegendApplicationDocumentationService.js.map +1 -0
  115. package/lib/stores/LegendApplicationEventService.d.ts +22 -0
  116. package/lib/stores/LegendApplicationEventService.d.ts.map +1 -0
  117. package/lib/stores/LegendApplicationEventService.js +25 -0
  118. package/lib/stores/LegendApplicationEventService.js.map +1 -0
  119. package/lib/stores/LegendApplicationNavigationContextService.d.ts +74 -0
  120. package/lib/stores/LegendApplicationNavigationContextService.d.ts.map +1 -0
  121. package/lib/stores/LegendApplicationNavigationContextService.js +118 -0
  122. package/lib/stores/LegendApplicationNavigationContextService.js.map +1 -0
  123. package/lib/stores/LegendApplicationPlugin.d.ts +6 -2
  124. package/lib/stores/LegendApplicationPlugin.d.ts.map +1 -1
  125. package/lib/stores/LegendApplicationPlugin.js.map +1 -1
  126. package/lib/stores/PureLanguageSupport.d.ts.map +1 -1
  127. package/lib/stores/PureLanguageSupport.js +8 -2
  128. package/lib/stores/PureLanguageSupport.js.map +1 -1
  129. package/lib/stores/ValueSpecificationModifierHelper.d.ts +27 -0
  130. package/lib/stores/ValueSpecificationModifierHelper.d.ts.map +1 -0
  131. package/lib/stores/ValueSpecificationModifierHelper.js +49 -0
  132. package/lib/stores/ValueSpecificationModifierHelper.js.map +1 -0
  133. package/package.json +24 -18
  134. package/src/application/LegendApplication.tsx +6 -6
  135. package/src/application/LegendApplicationPluginManager.tsx +3 -3
  136. package/src/components/ActionAlert.tsx +2 -2
  137. package/src/components/ApplicationStoreProvider.tsx +4 -4
  138. package/src/components/ApplicationStoreProviderTestUtils.tsx +7 -6
  139. package/src/components/BasicValueSpecificationEditor.tsx +703 -0
  140. package/src/components/BlockingAlert.tsx +1 -1
  141. package/src/components/CustomDatePicker.tsx +1235 -0
  142. package/src/components/DocumentationLink.tsx +2 -2
  143. package/src/components/LambdaEditor.tsx +4 -4
  144. package/src/components/LambdaParameterValuesEditor.tsx +114 -0
  145. package/src/components/LegendApplicationComponentFrameworkProvider.tsx +3 -3
  146. package/src/components/LegendApplicationNavigationContextServiceUtils.tsx +63 -0
  147. package/src/components/NotificationManager.tsx +2 -2
  148. package/src/components/TextInputEditor.tsx +2 -2
  149. package/src/components/VirtualAssistant.tsx +600 -0
  150. package/src/components/WebApplicationNavigatorProvider.tsx +1 -1
  151. package/src/components/WebApplicationNavigatorProviderTestUtils.tsx +3 -2
  152. package/src/index.ts +39 -28
  153. package/src/stores/ApplicationEvent.ts +11 -7
  154. package/src/stores/ApplicationStore.ts +29 -27
  155. package/src/stores/ApplicationStoreTestUtils.ts +4 -4
  156. package/src/stores/ApplicationTelemetry.ts +2 -2
  157. package/src/stores/CJS__Fuse.cts +28 -0
  158. package/src/stores/LambdaParameterState.ts +314 -0
  159. package/src/stores/LegendApplicationAssistantService.ts +218 -0
  160. package/src/stores/LegendApplicationConfig.ts +20 -6
  161. package/src/stores/LegendApplicationDocumentationService.ts +276 -0
  162. package/src/stores/LegendApplicationEventService.ts +32 -0
  163. package/src/stores/LegendApplicationNavigationContextService.ts +131 -0
  164. package/src/stores/LegendApplicationPlugin.ts +10 -2
  165. package/src/stores/PureLanguageSupport.ts +8 -2
  166. package/src/stores/ValueSpecificationModifierHelper.ts +104 -0
  167. package/tsconfig.json +18 -12
  168. package/lib/components/AppHeader.d.ts.map +0 -1
  169. package/lib/components/AppHeader.js +0 -26
  170. package/lib/components/AppHeader.js.map +0 -1
  171. package/lib/stores/LegendApplicationDocumentationRegistry.d.ts +0 -36
  172. package/lib/stores/LegendApplicationDocumentationRegistry.d.ts.map +0 -1
  173. package/lib/stores/LegendApplicationDocumentationRegistry.js +0 -47
  174. package/lib/stores/LegendApplicationDocumentationRegistry.js.map +0 -1
  175. package/src/components/AppHeader.tsx +0 -49
  176. 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 { WebApplicationNavigator } from '../stores/WebApplicationNavigator';
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,