@finos/legend-application 0.0.13 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/lib/components/ActionAlert.d.ts.map +1 -1
- package/lib/components/ActionAlert.js +5 -8
- package/lib/components/ActionAlert.js.map +1 -1
- package/lib/components/AppHeader.d.ts +20 -0
- package/lib/components/AppHeader.d.ts.map +1 -0
- package/lib/components/AppHeader.js +26 -0
- package/lib/components/AppHeader.js.map +1 -0
- package/lib/components/ApplicationStoreProvider.js +1 -1
- package/lib/components/ApplicationStoreProvider.js.map +1 -1
- package/lib/components/ApplicationStoreProviderTestUtils.js +1 -1
- package/lib/components/ApplicationStoreProviderTestUtils.js.map +1 -1
- package/lib/components/BlockingAlert.js +2 -2
- package/lib/components/BlockingAlert.js.map +1 -1
- package/lib/components/LambdaEditor.d.ts.map +1 -1
- package/lib/components/LambdaEditor.js +18 -19
- package/lib/components/LambdaEditor.js.map +1 -1
- package/lib/components/NotificationSnackbar.d.ts.map +1 -1
- package/lib/components/NotificationSnackbar.js +22 -12
- package/lib/components/NotificationSnackbar.js.map +1 -1
- package/lib/components/TextInputEditor.d.ts.map +1 -1
- package/lib/components/TextInputEditor.js +16 -7
- package/lib/components/TextInputEditor.js.map +1 -1
- 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/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/stores/ApplicationStore.d.ts +1 -1
- package/lib/stores/ApplicationStore.d.ts.map +1 -1
- package/lib/stores/ApplicationStore.js +9 -12
- package/lib/stores/ApplicationStore.js.map +1 -1
- package/lib/stores/WebApplicationNavigator.d.ts +18 -1
- package/lib/stores/WebApplicationNavigator.d.ts.map +1 -1
- package/lib/stores/WebApplicationNavigator.js +5 -2
- package/lib/stores/WebApplicationNavigator.js.map +1 -1
- package/package.json +20 -20
- package/src/components/ActionAlert.tsx +1 -4
- package/src/components/AppHeader.tsx +47 -0
- package/src/components/LambdaEditor.tsx +11 -10
- package/src/components/NotificationSnackbar.tsx +28 -3
- package/src/components/TextInputEditor.tsx +17 -5
- package/src/components/WebApplicationNavigatorProvider.tsx +2 -1
- package/src/index.ts +1 -0
- package/src/stores/ApplicationStore.ts +8 -20
- package/src/stores/WebApplicationNavigator.ts +22 -2
- package/tsconfig.json +2 -5
- package/tsconfig.package.json +0 -40
|
@@ -31,14 +31,16 @@ import {
|
|
|
31
31
|
FaBug,
|
|
32
32
|
} from 'react-icons/fa';
|
|
33
33
|
import { useApplicationStore } from './ApplicationStoreProvider';
|
|
34
|
+
import { ChevronDownIcon, ChevronUpIcon, clsx } from '@finos/legend-art';
|
|
35
|
+
import { useState } from 'react';
|
|
34
36
|
|
|
35
37
|
export const NotificationSnackbar = observer(() => {
|
|
36
38
|
const applicationStore = useApplicationStore();
|
|
37
39
|
const notification = applicationStore.notification;
|
|
38
40
|
const isOpen = Boolean(notification);
|
|
39
|
-
// TODO: have a better way to truncate message in snackbar
|
|
40
41
|
const message = notification?.message ?? '';
|
|
41
42
|
const severity = notification?.severity ?? NOTIFCATION_SEVERITY.INFO;
|
|
43
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
42
44
|
let notificationIcon = (
|
|
43
45
|
<div className="notification__message__content__icon notification__message__content__icon--info">
|
|
44
46
|
<FaInfoCircle />
|
|
@@ -76,7 +78,14 @@ export const NotificationSnackbar = observer(() => {
|
|
|
76
78
|
default:
|
|
77
79
|
break;
|
|
78
80
|
}
|
|
79
|
-
const handleClose = (): void =>
|
|
81
|
+
const handleClose = (): void => {
|
|
82
|
+
applicationStore.setNotification(undefined);
|
|
83
|
+
setIsExpanded(false);
|
|
84
|
+
};
|
|
85
|
+
const handleCopy = (): Promise<void> =>
|
|
86
|
+
applicationStore.copyTextToClipboard(message);
|
|
87
|
+
const toggleExpansion = (): void => setIsExpanded(!isExpanded);
|
|
88
|
+
|
|
80
89
|
const onSnackbarAutoHideOrClickAway = (
|
|
81
90
|
event: React.SyntheticEvent<unknown>,
|
|
82
91
|
reason: SnackbarCloseReason,
|
|
@@ -125,12 +134,28 @@ export const NotificationSnackbar = observer(() => {
|
|
|
125
134
|
message={
|
|
126
135
|
<div className="notification__message__content">
|
|
127
136
|
{notificationIcon}
|
|
128
|
-
<div
|
|
137
|
+
<div
|
|
138
|
+
className={clsx('notification__message__content__text', {
|
|
139
|
+
'notification__message__content__text--expanded': isExpanded,
|
|
140
|
+
})}
|
|
141
|
+
onClick={handleCopy}
|
|
142
|
+
title="Click to Copy"
|
|
143
|
+
>
|
|
129
144
|
{message}
|
|
130
145
|
</div>
|
|
131
146
|
</div>
|
|
132
147
|
}
|
|
133
148
|
action={[
|
|
149
|
+
<button
|
|
150
|
+
className="notification__action"
|
|
151
|
+
id="expand_button"
|
|
152
|
+
key="expand"
|
|
153
|
+
onClick={toggleExpansion}
|
|
154
|
+
tabIndex={-1}
|
|
155
|
+
title={isExpanded ? 'Collapse' : 'Expand'}
|
|
156
|
+
>
|
|
157
|
+
{isExpanded ? <ChevronDownIcon /> : <ChevronUpIcon />}
|
|
158
|
+
</button>,
|
|
134
159
|
<button
|
|
135
160
|
className="notification__action"
|
|
136
161
|
key="close"
|
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
disableEditorHotKeys,
|
|
24
24
|
baseTextEditorSettings,
|
|
25
25
|
resetLineNumberGutterWidth,
|
|
26
|
+
getEditorValue,
|
|
27
|
+
normalizeLineEnding,
|
|
26
28
|
} from '@finos/legend-art';
|
|
27
29
|
import type { EDITOR_LANGUAGE } from '../const';
|
|
28
30
|
import { EDITOR_THEME, TAB_SIZE } from '../const';
|
|
@@ -63,6 +65,16 @@ export const TextInputEditor: React.FC<{
|
|
|
63
65
|
const onDidChangeModelContentEventDisposer = useRef<IDisposable | undefined>(
|
|
64
66
|
undefined,
|
|
65
67
|
);
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* NOTE: we want to normalize line ending here since if the original
|
|
71
|
+
* input value includes CR '\r' character, it will get normalized, calling
|
|
72
|
+
* the updateInput method and cause a rerender. With the way we setup
|
|
73
|
+
* `onChange` method, React will warn about `setState` being called in
|
|
74
|
+
* `render` method.
|
|
75
|
+
* See https://github.com/finos/legend-studio/issues/608
|
|
76
|
+
*/
|
|
77
|
+
const value = normalizeLineEnding(inputValue);
|
|
66
78
|
const textInputRef = useRef<HTMLDivElement>(null);
|
|
67
79
|
|
|
68
80
|
const { ref, width, height } = useResizeDetector<HTMLDivElement>();
|
|
@@ -103,8 +115,8 @@ export const TextInputEditor: React.FC<{
|
|
|
103
115
|
onDidChangeModelContentEventDisposer.current?.dispose();
|
|
104
116
|
onDidChangeModelContentEventDisposer.current =
|
|
105
117
|
editor.onDidChangeModelContent(() => {
|
|
106
|
-
const currentVal = editor
|
|
107
|
-
if (currentVal !==
|
|
118
|
+
const currentVal = getEditorValue(editor);
|
|
119
|
+
if (currentVal !== value) {
|
|
108
120
|
updateInput?.(currentVal);
|
|
109
121
|
}
|
|
110
122
|
});
|
|
@@ -123,9 +135,9 @@ export const TextInputEditor: React.FC<{
|
|
|
123
135
|
});
|
|
124
136
|
|
|
125
137
|
// Set the text value and editor options
|
|
126
|
-
const currentValue = editor
|
|
127
|
-
if (currentValue !==
|
|
128
|
-
editor.setValue(
|
|
138
|
+
const currentValue = getEditorValue(editor);
|
|
139
|
+
if (currentValue !== value) {
|
|
140
|
+
editor.setValue(value);
|
|
129
141
|
}
|
|
130
142
|
editor.updateOptions({
|
|
131
143
|
readOnly: Boolean(isReadOnly),
|
|
@@ -18,6 +18,7 @@ import { guaranteeNonNullable } from '@finos/legend-shared';
|
|
|
18
18
|
import { useLocalObservable } from 'mobx-react-lite';
|
|
19
19
|
import { createContext, useContext } from 'react';
|
|
20
20
|
import { useHistory } from 'react-router';
|
|
21
|
+
import type { History } from 'history';
|
|
21
22
|
import { WebApplicationNavigator } from '../stores/WebApplicationNavigator';
|
|
22
23
|
|
|
23
24
|
const WebApplicationNavigatorContext = createContext<
|
|
@@ -29,7 +30,7 @@ export const WebApplicationNavigatorProvider = ({
|
|
|
29
30
|
}: {
|
|
30
31
|
children: React.ReactNode;
|
|
31
32
|
}): React.ReactElement => {
|
|
32
|
-
const history = useHistory();
|
|
33
|
+
const history = useHistory() as History;
|
|
33
34
|
const navigator = useLocalObservable(
|
|
34
35
|
() => new WebApplicationNavigator(history),
|
|
35
36
|
);
|
package/src/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ export * from './components/WebApplicationNavigatorProvider';
|
|
|
23
23
|
|
|
24
24
|
export * from './components/ApplicationStoreProviderTestUtils';
|
|
25
25
|
export * from './components/WebApplicationNavigatorProviderTestUtils';
|
|
26
|
+
export { AppHeader } from './components/AppHeader';
|
|
26
27
|
export { BlockingAlert } from './components/BlockingAlert';
|
|
27
28
|
export { ActionAlert } from './components/ActionAlert';
|
|
28
29
|
export { NotificationSnackbar } from './components/NotificationSnackbar';
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import type { Log, SuperGenericFunction } from '@finos/legend-shared';
|
|
18
18
|
import {
|
|
19
|
+
assertTrue,
|
|
19
20
|
LogEvent,
|
|
20
21
|
assertErrorThrown,
|
|
21
22
|
isString,
|
|
@@ -204,26 +205,15 @@ export class ApplicationStore<T extends LegendApplicationConfig> {
|
|
|
204
205
|
);
|
|
205
206
|
}
|
|
206
207
|
|
|
207
|
-
notifyError(
|
|
208
|
-
content: unknown,
|
|
209
|
-
actions?: NotificationAction[],
|
|
210
|
-
autoHideDuration?: number | null,
|
|
211
|
-
): void {
|
|
208
|
+
notifyError(content: Error | string, actions?: NotificationAction[]): void {
|
|
212
209
|
let message: string | undefined;
|
|
213
|
-
if (content instanceof
|
|
210
|
+
if (content instanceof ApplicationError) {
|
|
211
|
+
message = content.getFullErrorMessage();
|
|
212
|
+
} else if (content instanceof Error) {
|
|
214
213
|
message = content.message;
|
|
215
|
-
} else if (isString(content)) {
|
|
216
|
-
message = content;
|
|
217
214
|
} else {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
LogEvent.create(
|
|
221
|
-
APPLICATION_LOG_EVENT.ILLEGAL_APPLICATION_STATE_OCCURRED,
|
|
222
|
-
),
|
|
223
|
-
'Unable to display error in notification',
|
|
224
|
-
message,
|
|
225
|
-
);
|
|
226
|
-
this.notifyIllegalState('Unable to display error');
|
|
215
|
+
assertTrue(isString(content), `Can't display error`);
|
|
216
|
+
message = content;
|
|
227
217
|
}
|
|
228
218
|
if (message) {
|
|
229
219
|
this.setNotification(
|
|
@@ -231,9 +221,7 @@ export class ApplicationStore<T extends LegendApplicationConfig> {
|
|
|
231
221
|
NOTIFCATION_SEVERITY.ERROR,
|
|
232
222
|
message,
|
|
233
223
|
actions ?? [],
|
|
234
|
-
|
|
235
|
-
? undefined
|
|
236
|
-
: autoHideDuration ?? DEFAULT_NOTIFICATION_HIDE_TIME,
|
|
224
|
+
undefined,
|
|
237
225
|
),
|
|
238
226
|
);
|
|
239
227
|
}
|
|
@@ -20,6 +20,20 @@ import { guaranteeNonNullable } from '@finos/legend-shared';
|
|
|
20
20
|
/**
|
|
21
21
|
* This is an initial attempt to try to generalize the application
|
|
22
22
|
* to other platforms. But regardless, this is more convenient for testing.
|
|
23
|
+
*
|
|
24
|
+
* FIXME: this is not the right way to do this. Our intention here is to make
|
|
25
|
+
* app navigator something generic enough so we are somewhat platform-agnostic
|
|
26
|
+
* i.e. browser, electron, PC, UNIX, etc.
|
|
27
|
+
*
|
|
28
|
+
* Parameterize on the type of the location might not be the best thing to do.
|
|
29
|
+
* Because typing wise, this forces us to also parameterize consumers of this,
|
|
30
|
+
* which is `ApplicationStore`. It means that we must dictate in the source
|
|
31
|
+
* code the platform the app depends on, clearly for web browser, `string` is the
|
|
32
|
+
* easy option, but if we do so, it defeats the purpose of this abstraction in the
|
|
33
|
+
* first place.
|
|
34
|
+
*
|
|
35
|
+
* As such, instead, we should design a more generic concept `Location` to pass around.
|
|
36
|
+
* We would need to flesh out the details, but this is the rough idea.
|
|
23
37
|
*/
|
|
24
38
|
interface ApplicationNavigator<T> {
|
|
25
39
|
reload(): void;
|
|
@@ -27,6 +41,8 @@ interface ApplicationNavigator<T> {
|
|
|
27
41
|
jumpTo(location: T): void;
|
|
28
42
|
openNewWindow(location: T): void;
|
|
29
43
|
getCurrentLocation(): T;
|
|
44
|
+
getCurrentLocationPath(): T;
|
|
45
|
+
generateLocation(locationPath: T): T;
|
|
30
46
|
}
|
|
31
47
|
|
|
32
48
|
export class WebApplicationNavigator implements ApplicationNavigator<string> {
|
|
@@ -63,10 +79,14 @@ export class WebApplicationNavigator implements ApplicationNavigator<string> {
|
|
|
63
79
|
return this.window.location.href;
|
|
64
80
|
}
|
|
65
81
|
|
|
66
|
-
|
|
82
|
+
getCurrentLocationPath(): string {
|
|
83
|
+
return this.historyAPI.location.pathname;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
generateLocation(locationPath: string): string {
|
|
67
87
|
return (
|
|
68
88
|
window.location.origin +
|
|
69
|
-
this.historyAPI.createHref({ pathname:
|
|
89
|
+
this.historyAPI.createHref({ pathname: locationPath })
|
|
70
90
|
);
|
|
71
91
|
}
|
|
72
92
|
}
|
package/tsconfig.json
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"allowSyntheticDefaultImports": true,
|
|
23
23
|
"strict": true,
|
|
24
24
|
"noImplicitOverride": true,
|
|
25
|
+
"noUncheckedIndexedAccess": true,
|
|
25
26
|
"exactOptionalPropertyTypes": true,
|
|
26
27
|
"forceConsistentCasingInFileNames": true,
|
|
27
28
|
"outDir": "./lib",
|
|
@@ -29,11 +30,6 @@
|
|
|
29
30
|
"rootDir": "./src",
|
|
30
31
|
"jsx": "react-jsx"
|
|
31
32
|
},
|
|
32
|
-
"references": [
|
|
33
|
-
{
|
|
34
|
-
"path": "./tsconfig.package.json"
|
|
35
|
-
}
|
|
36
|
-
],
|
|
37
33
|
"files": [
|
|
38
34
|
"./src/const.ts",
|
|
39
35
|
"./src/index.ts",
|
|
@@ -49,6 +45,7 @@
|
|
|
49
45
|
"./src/stores/WebApplicationNavigator.ts",
|
|
50
46
|
"./src/application/LegendApplication.tsx",
|
|
51
47
|
"./src/components/ActionAlert.tsx",
|
|
48
|
+
"./src/components/AppHeader.tsx",
|
|
52
49
|
"./src/components/ApplicationBackdrop.tsx",
|
|
53
50
|
"./src/components/ApplicationStoreProvider.tsx",
|
|
54
51
|
"./src/components/ApplicationStoreProviderTestUtils.tsx",
|
package/tsconfig.package.json
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"lib": [
|
|
4
|
-
"dom",
|
|
5
|
-
"dom.iterable",
|
|
6
|
-
"esnext",
|
|
7
|
-
"webworker",
|
|
8
|
-
"scripthost"
|
|
9
|
-
],
|
|
10
|
-
"composite": true,
|
|
11
|
-
"declaration": true,
|
|
12
|
-
"declarationMap": true,
|
|
13
|
-
"sourceMap": true,
|
|
14
|
-
"target": "esnext",
|
|
15
|
-
"module": "esnext",
|
|
16
|
-
"skipLibCheck": true,
|
|
17
|
-
"moduleResolution": "node",
|
|
18
|
-
"resolveJsonModule": true,
|
|
19
|
-
"isolatedModules": true,
|
|
20
|
-
"importsNotUsedAsValues": "error",
|
|
21
|
-
"esModuleInterop": true,
|
|
22
|
-
"allowSyntheticDefaultImports": true,
|
|
23
|
-
"strict": true,
|
|
24
|
-
"noImplicitOverride": true,
|
|
25
|
-
"exactOptionalPropertyTypes": true,
|
|
26
|
-
"forceConsistentCasingInFileNames": true,
|
|
27
|
-
"outDir": "./lib",
|
|
28
|
-
"tsBuildInfoFile": "./build/package.tsbuildinfo",
|
|
29
|
-
"rootDir": "./"
|
|
30
|
-
},
|
|
31
|
-
"files": [
|
|
32
|
-
"./package.json"
|
|
33
|
-
],
|
|
34
|
-
"include": [
|
|
35
|
-
"package.json"
|
|
36
|
-
],
|
|
37
|
-
"exclude": [
|
|
38
|
-
"lib"
|
|
39
|
-
]
|
|
40
|
-
}
|