@unisphere/nx 1.0.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/README.md +32 -0
- package/dist/generators/add-application/add-application.d.ts +5 -0
- package/dist/generators/add-application/add-application.d.ts.map +1 -0
- package/dist/generators/add-application/add-application.js +98 -0
- package/dist/generators/add-application/schema.d.ts +6 -0
- package/dist/generators/add-application/schema.json +92 -0
- package/dist/generators/add-application/templates/default/.babelrc +11 -0
- package/dist/generators/add-application/templates/default/.eslintrc.json +18 -0
- package/dist/generators/add-application/templates/default/jest.config.d.ts +14 -0
- package/dist/generators/add-application/templates/default/jest.config.d.ts.map +1 -0
- package/dist/generators/add-application/templates/default/jest.config.js +13 -0
- package/dist/generators/add-application/templates/default/package.json.template +7 -0
- package/dist/generators/add-application/templates/default/project.json.template +9 -0
- package/dist/generators/add-application/templates/default/src/app/app.tsx.template +76 -0
- package/dist/generators/add-application/templates/default/src/favicon.ico +0 -0
- package/dist/generators/add-application/templates/default/src/index.html.template +17 -0
- package/dist/generators/add-application/templates/default/src/main.tsx +13 -0
- package/dist/generators/add-application/templates/default/src/styles.css +0 -0
- package/dist/generators/add-application/templates/default/tsconfig.app.json +24 -0
- package/dist/generators/add-application/templates/default/tsconfig.json +20 -0
- package/dist/generators/add-application/templates/default/tsconfig.spec.json +25 -0
- package/dist/generators/add-application/templates/default/webpack.config.js.template +33 -0
- package/dist/generators/add-application/templates/interactive-playground/.babelrc +11 -0
- package/dist/generators/add-application/templates/interactive-playground/.eslintrc.json +18 -0
- package/dist/generators/add-application/templates/interactive-playground/jest.config.d.ts +14 -0
- package/dist/generators/add-application/templates/interactive-playground/jest.config.d.ts.map +1 -0
- package/dist/generators/add-application/templates/interactive-playground/jest.config.js +13 -0
- package/dist/generators/add-application/templates/interactive-playground/package.json +7 -0
- package/dist/generators/add-application/templates/interactive-playground/project.json +9 -0
- package/dist/generators/add-application/templates/interactive-playground/src/app/app.tsx.template +122 -0
- package/dist/generators/add-application/templates/interactive-playground/src/app/components/header.tsx.template +123 -0
- package/dist/generators/add-application/templates/interactive-playground/src/app/components/settings-buttons.tsx +59 -0
- package/dist/generators/add-application/templates/interactive-playground/src/app/components/settings-form.tsx +104 -0
- package/dist/generators/add-application/templates/interactive-playground/src/app/components/settings.tsx +74 -0
- package/dist/generators/add-application/templates/interactive-playground/src/app/configuration-provider.tsx +163 -0
- package/dist/generators/add-application/templates/interactive-playground/src/app/definitions.d.ts +11 -0
- package/dist/generators/add-application/templates/interactive-playground/src/app/definitions.d.ts.map +1 -0
- package/dist/generators/add-application/templates/interactive-playground/src/app/definitions.js +19 -0
- package/dist/generators/add-application/templates/interactive-playground/src/app/utils/merge-deep.d.ts +13 -0
- package/dist/generators/add-application/templates/interactive-playground/src/app/utils/merge-deep.d.ts.map +1 -0
- package/dist/generators/add-application/templates/interactive-playground/src/app/utils/merge-deep.js +35 -0
- package/dist/generators/add-application/templates/interactive-playground/src/favicon.ico +0 -0
- package/dist/generators/add-application/templates/interactive-playground/src/index.html +17 -0
- package/dist/generators/add-application/templates/interactive-playground/src/main.tsx +13 -0
- package/dist/generators/add-application/templates/interactive-playground/src/styles.css +0 -0
- package/dist/generators/add-application/templates/interactive-playground/tsconfig.app.json +24 -0
- package/dist/generators/add-application/templates/interactive-playground/tsconfig.json +20 -0
- package/dist/generators/add-application/templates/interactive-playground/tsconfig.spec.json +25 -0
- package/dist/generators/add-application/templates/interactive-playground/webpack.config.js +33 -0
- package/dist/generators/add-package/README.md +70 -0
- package/dist/generators/add-package/add-package.d.ts +5 -0
- package/dist/generators/add-package/add-package.d.ts.map +1 -0
- package/dist/generators/add-package/add-package.js +85 -0
- package/dist/generators/add-package/files/.babelrc +12 -0
- package/dist/generators/add-package/files/.eslintrc.json +18 -0
- package/dist/generators/add-package/files/README.md +1 -0
- package/dist/generators/add-package/files/package.json.template +22 -0
- package/dist/generators/add-package/files/project.json.template +22 -0
- package/dist/generators/add-package/files/rollup.config.js +6 -0
- package/dist/generators/add-package/files/src/index.ts +1 -0
- package/dist/generators/add-package/files/tsconfig.json +20 -0
- package/dist/generators/add-package/files/tsconfig.lib.json +23 -0
- package/dist/generators/add-package/files/tsconfig.spec.json +26 -0
- package/dist/generators/add-package/files/vite.config.ts +30 -0
- package/dist/generators/add-package/schema.d.ts +4 -0
- package/dist/generators/add-package/schema.json +46 -0
- package/dist/generators/add-runtime/add-runtime.d.ts +5 -0
- package/dist/generators/add-runtime/add-runtime.d.ts.map +1 -0
- package/dist/generators/add-runtime/add-runtime.js +112 -0
- package/dist/generators/add-runtime/core-templates/__runtimeName__-runtime/runtime-types.ts.template +21 -0
- package/dist/generators/add-runtime/files/.babelrc +12 -0
- package/dist/generators/add-runtime/files/.eslintrc.json +18 -0
- package/dist/generators/add-runtime/files/README.md.template +1 -0
- package/dist/generators/add-runtime/files/package.json.template +12 -0
- package/dist/generators/add-runtime/files/project.json.template +22 -0
- package/dist/generators/add-runtime/files/rollup.config.js +6 -0
- package/dist/generators/add-runtime/files/src/index.ts +1 -0
- package/dist/generators/add-runtime/files/src/lib/create-factory.tsx.template +21 -0
- package/dist/generators/add-runtime/files/src/lib/runtime.tsx.template +66 -0
- package/dist/generators/add-runtime/files/tsconfig.json +20 -0
- package/dist/generators/add-runtime/files/tsconfig.lib.json +23 -0
- package/dist/generators/add-runtime/files/tsconfig.spec.json +26 -0
- package/dist/generators/add-runtime/files/vite.config.ts +30 -0
- package/dist/generators/add-runtime/schema.d.ts +4 -0
- package/dist/generators/add-runtime/schema.json +44 -0
- package/dist/generators/add-visual/add-visual.d.ts +8 -0
- package/dist/generators/add-visual/add-visual.d.ts.map +1 -0
- package/dist/generators/add-visual/add-visual.js +236 -0
- package/dist/generators/add-visual/files/render-method.template +31 -0
- package/dist/generators/add-visual/schema.d.ts +5 -0
- package/dist/generators/add-visual/schema.json +30 -0
- package/dist/generators/add-visual/visual-utils.d.ts +42 -0
- package/dist/generators/add-visual/visual-utils.d.ts.map +1 -0
- package/dist/generators/add-visual/visual-utils.js +99 -0
- package/dist/generators/utils.d.ts +18 -0
- package/dist/generators/utils.d.ts.map +1 -0
- package/dist/generators/utils.js +198 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/generators.json +24 -0
- package/package.json +81 -0
package/dist/generators/add-application/templates/interactive-playground/src/app/app.tsx.template
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { useContext, useState } from 'react';
|
|
2
|
+
import { HostThemeProvider } from '@kaltura/ds-react-theme';
|
|
3
|
+
import { Box } from '@mui/material';
|
|
4
|
+
import { ConfigurationContext } from './configuration-provider';
|
|
5
|
+
import { isConfigurationValid } from './definitions';
|
|
6
|
+
import { Header } from './components/header';
|
|
7
|
+
import { ConfigurationProvider } from './configuration-provider';
|
|
8
|
+
import { UnisphereProvider } from '@unisphere/runtime-react';
|
|
9
|
+
import { UnisphereWorkspaceConfig } from '@unisphere/runtime';
|
|
10
|
+
import { UnisphereWorkspaceType } from '@unisphere/runtime';
|
|
11
|
+
import { <%= runtimeName__pascalCase %>Runtime } from '@kaltura/unisphere-<%= widgetName__lowerDashCase %>-core';
|
|
12
|
+
|
|
13
|
+
export function AppContent() {
|
|
14
|
+
const { configuration } = useContext(ConfigurationContext);
|
|
15
|
+
|
|
16
|
+
const [workspace, setWorkspace] = useState<UnisphereWorkspaceType | null>(
|
|
17
|
+
null
|
|
18
|
+
);
|
|
19
|
+
const [runtime, setRuntime] = useState<<%= runtimeName__pascalCase %>Runtime | null>(null);
|
|
20
|
+
|
|
21
|
+
const [initialConfiguration] = useState<UnisphereWorkspaceConfig | null>(
|
|
22
|
+
() => {
|
|
23
|
+
/*
|
|
24
|
+
Developer Note:
|
|
25
|
+
• You don’t need to touch the code to enable runtime configuration.
|
|
26
|
+
|
|
27
|
+
• If this runtime is already active in an app:
|
|
28
|
+
1. Open Unisphere, press Cmd + K, then choose Manage Workspace Runtimes.
|
|
29
|
+
2. Find the runtime you want, click … (Action), and select Copy Settings.
|
|
30
|
+
3. Paste into NVQ2 with a valid host—settings will be ready to go.
|
|
31
|
+
|
|
32
|
+
• If the runtime isn’t yet in use:
|
|
33
|
+
1. Manually create the JSON payload matching the runtime’s schema.
|
|
34
|
+
|
|
35
|
+
Once you run the application you will have a button where you can save the configurations.
|
|
36
|
+
The application will save it in the local storage and will load it on the next run.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
if (!isConfigurationValid(configuration)) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
appId: '<%= runtimeName__lowerDashCase %>-expo',
|
|
46
|
+
appVersion: '1.0.0',
|
|
47
|
+
serverUrl: configuration.unisphereServerUrl,
|
|
48
|
+
ui: {
|
|
49
|
+
theme: configuration.theme,
|
|
50
|
+
language: configuration.language,
|
|
51
|
+
},
|
|
52
|
+
runtimes: [
|
|
53
|
+
{
|
|
54
|
+
widgetName: 'unisphere.widget.<%= widgetName__lowerDashCase %>',
|
|
55
|
+
runtimeName: '<%= runtimeName__lowerDashCase %>',
|
|
56
|
+
settings: JSON.parse(configuration.settings),
|
|
57
|
+
visuals: [],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
} catch (e) {
|
|
62
|
+
console.error('Failed to parse settings', e);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const onWorkspaceLoaded = (workspace: UnisphereWorkspaceType) => {
|
|
69
|
+
setWorkspace(workspace);
|
|
70
|
+
const runtime = workspace.getRuntime<<%= runtimeName__pascalCase %>Runtime>(
|
|
71
|
+
'unisphere.widget.<%= widgetName__lowerDashCase %>',
|
|
72
|
+
'<%= runtimeName__lowerDashCase %>'
|
|
73
|
+
);
|
|
74
|
+
setRuntime(runtime);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Box
|
|
79
|
+
sx={{
|
|
80
|
+
width: '100vw',
|
|
81
|
+
height: '100vh',
|
|
82
|
+
overflow: 'hidden',
|
|
83
|
+
padding: '12px 24px',
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
<HostThemeProvider
|
|
87
|
+
overrides={{
|
|
88
|
+
mode: configuration.theme,
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
<Box sx={{ height: '100vh', width: '100vw', pt: '64px' }}>
|
|
92
|
+
<Header />
|
|
93
|
+
{initialConfiguration && (
|
|
94
|
+
<UnisphereProvider
|
|
95
|
+
onWorkspaceLoaded={onWorkspaceLoaded}
|
|
96
|
+
initialConfiguration={initialConfiguration}
|
|
97
|
+
>
|
|
98
|
+
{/* <UnisphereRuntimeVisual
|
|
99
|
+
widgetName="unisphere.widget.<%= widgetName__lowerDashCase %>"
|
|
100
|
+
runtimeName="<%= runtimeName__lowerDashCase %>"
|
|
101
|
+
visualType="-- provide here the visual name --"
|
|
102
|
+
visualSettings={{
|
|
103
|
+
-- provide here the visual settings --
|
|
104
|
+
}}
|
|
105
|
+
/> */}
|
|
106
|
+
</UnisphereProvider>
|
|
107
|
+
)}
|
|
108
|
+
</Box>
|
|
109
|
+
</HostThemeProvider>
|
|
110
|
+
</Box>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const App = () => {
|
|
115
|
+
return (
|
|
116
|
+
<ConfigurationProvider configurationKey="unisphere-<%= widgetName__lowerDashCase %>-<%= runtimeName__lowerDashCase %>-expo">
|
|
117
|
+
<AppContent />
|
|
118
|
+
</ConfigurationProvider>
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export default App;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React, { useContext, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
AppBar,
|
|
4
|
+
Box,
|
|
5
|
+
IconButton,
|
|
6
|
+
ListItemIcon,
|
|
7
|
+
Menu,
|
|
8
|
+
MenuItem,
|
|
9
|
+
Toolbar,
|
|
10
|
+
Tooltip,
|
|
11
|
+
Typography,
|
|
12
|
+
} from '@mui/material';
|
|
13
|
+
import { ConfigurationContext } from '../configuration-provider';
|
|
14
|
+
import { isConfigurationValid } from '../definitions';
|
|
15
|
+
import {
|
|
16
|
+
DeveloperBoard as DeveloperBoardIcon,
|
|
17
|
+
Settings as SettingsIcon,
|
|
18
|
+
Storage as StorageIcon,
|
|
19
|
+
} from '@mui/icons-material';
|
|
20
|
+
import { Settings } from './settings';
|
|
21
|
+
|
|
22
|
+
export const Header: React.FC = () => {
|
|
23
|
+
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
|
24
|
+
const { configuration: Configuration, createSharedUrl } =
|
|
25
|
+
useContext(ConfigurationContext);
|
|
26
|
+
|
|
27
|
+
const open = Boolean(anchorEl);
|
|
28
|
+
const replaceBaseUrlInNewTab = () => {
|
|
29
|
+
const baseUrl = prompt('please provide the new base url');
|
|
30
|
+
if (!baseUrl) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const filteredUrl = baseUrl.replace(/\/configuration$/, '');
|
|
35
|
+
window.open(
|
|
36
|
+
createSharedUrl({
|
|
37
|
+
baseUrl: `${filteredUrl}`,
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
return;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
|
44
|
+
setAnchorEl(event.currentTarget);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const handleClose = () => {
|
|
48
|
+
setAnchorEl(null);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const [settingsOpen, setSettingsOpen] = useState(() => {
|
|
52
|
+
return !isConfigurationValid(Configuration);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const handleSettingsClosed = () => {
|
|
56
|
+
setSettingsOpen(false);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<AppBar position="fixed" sx={{ zIndex: 1000 }}>
|
|
61
|
+
<Toolbar style={{ justifyContent: 'space-between' }}>
|
|
62
|
+
<Box
|
|
63
|
+
sx={{
|
|
64
|
+
display: 'flex',
|
|
65
|
+
alignItems: 'center',
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
<Typography variant="h6" sx={{ flexGrow: 1 }}>
|
|
69
|
+
<%= widgetName__pascalCase %> - <%= runtimeName__pascalCase %> Expo
|
|
70
|
+
</Typography>
|
|
71
|
+
</Box>
|
|
72
|
+
|
|
73
|
+
<Box
|
|
74
|
+
sx={{
|
|
75
|
+
display: 'flex',
|
|
76
|
+
alignItems: 'center',
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
<Tooltip title="Menu">
|
|
80
|
+
<IconButton onClick={handleMenu} color="inherit" aria-label="menu">
|
|
81
|
+
<SettingsIcon />
|
|
82
|
+
</IconButton>
|
|
83
|
+
</Tooltip>
|
|
84
|
+
|
|
85
|
+
<Menu
|
|
86
|
+
id="menu-appbar"
|
|
87
|
+
anchorEl={anchorEl}
|
|
88
|
+
anchorOrigin={{
|
|
89
|
+
vertical: 'top',
|
|
90
|
+
horizontal: 'right',
|
|
91
|
+
}}
|
|
92
|
+
keepMounted
|
|
93
|
+
transformOrigin={{
|
|
94
|
+
vertical: 'top',
|
|
95
|
+
horizontal: 'right',
|
|
96
|
+
}}
|
|
97
|
+
open={open}
|
|
98
|
+
onClose={handleClose}
|
|
99
|
+
>
|
|
100
|
+
<MenuItem
|
|
101
|
+
onClick={() => {
|
|
102
|
+
handleClose();
|
|
103
|
+
setSettingsOpen(true);
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
<ListItemIcon>
|
|
107
|
+
<StorageIcon fontSize="small" />
|
|
108
|
+
</ListItemIcon>
|
|
109
|
+
<Typography variant="inherit">Configuration</Typography>
|
|
110
|
+
</MenuItem>
|
|
111
|
+
<MenuItem onClick={replaceBaseUrlInNewTab}>
|
|
112
|
+
<ListItemIcon>
|
|
113
|
+
<DeveloperBoardIcon fontSize="small" />
|
|
114
|
+
</ListItemIcon>
|
|
115
|
+
<Typography variant="inherit">Change Base URL</Typography>
|
|
116
|
+
</MenuItem>
|
|
117
|
+
</Menu>
|
|
118
|
+
</Box>
|
|
119
|
+
</Toolbar>
|
|
120
|
+
{settingsOpen && <Settings onClose={handleSettingsClosed} />}
|
|
121
|
+
</AppBar>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import { Box, Button } from '@mui/material';
|
|
3
|
+
import {
|
|
4
|
+
ConfigurationContext,
|
|
5
|
+
ConfigurationTypes,
|
|
6
|
+
} from '../configuration-provider';
|
|
7
|
+
|
|
8
|
+
export interface SettingsButtonsProps {
|
|
9
|
+
onReset: () => void;
|
|
10
|
+
customSave?: React.ReactElement;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const ButtonsContainer: React.FC<{
|
|
14
|
+
children?: React.ReactChild;
|
|
15
|
+
}> = ({ children }) => {
|
|
16
|
+
return (
|
|
17
|
+
<Box
|
|
18
|
+
sx={{
|
|
19
|
+
display: 'flex',
|
|
20
|
+
justifyContent: 'space-between',
|
|
21
|
+
alignItems: 'center',
|
|
22
|
+
marginTop: '20px',
|
|
23
|
+
}}
|
|
24
|
+
>
|
|
25
|
+
{children}
|
|
26
|
+
</Box>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const SettingsButtons: React.FC<SettingsButtonsProps> = ({
|
|
31
|
+
customSave,
|
|
32
|
+
onReset,
|
|
33
|
+
}) => {
|
|
34
|
+
const { ConfigurationType } = useContext(ConfigurationContext);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<ButtonsContainer>
|
|
38
|
+
<>
|
|
39
|
+
{ConfigurationType === ConfigurationTypes.Localstorage && (
|
|
40
|
+
<Button
|
|
41
|
+
onClick={onReset}
|
|
42
|
+
variant="contained"
|
|
43
|
+
color="secondary"
|
|
44
|
+
style={{ marginRight: '8px' }}
|
|
45
|
+
>
|
|
46
|
+
Clear All
|
|
47
|
+
</Button>
|
|
48
|
+
)}
|
|
49
|
+
{customSave ? (
|
|
50
|
+
customSave
|
|
51
|
+
) : (
|
|
52
|
+
<Button variant="contained" color="primary" type={'submit'}>
|
|
53
|
+
Save & Close
|
|
54
|
+
</Button>
|
|
55
|
+
)}
|
|
56
|
+
</>
|
|
57
|
+
</ButtonsContainer>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Controller, useFormContext } from 'react-hook-form';
|
|
2
|
+
import React, { MouseEvent } from 'react';
|
|
3
|
+
import { MenuItem, Select, Stack, TextField, Typography } from '@mui/material';
|
|
4
|
+
import { Configuration } from '../definitions';
|
|
5
|
+
import { UseFormReturn } from 'react-hook-form/dist/types';
|
|
6
|
+
|
|
7
|
+
const FormCard: React.FC<React.PropsWithChildren<{ title: string }>> = ({
|
|
8
|
+
title,
|
|
9
|
+
children,
|
|
10
|
+
}) => {
|
|
11
|
+
return (
|
|
12
|
+
<Stack
|
|
13
|
+
sx={{
|
|
14
|
+
padding: '8px',
|
|
15
|
+
border: '1px solid grey',
|
|
16
|
+
borderRadius: '8px',
|
|
17
|
+
gap: '16px',
|
|
18
|
+
}}
|
|
19
|
+
>
|
|
20
|
+
<Typography variant="body1" sx={{ fontWeight: 700 }}>
|
|
21
|
+
{title}
|
|
22
|
+
</Typography>
|
|
23
|
+
{children}
|
|
24
|
+
</Stack>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const FormItem: React.FC<React.PropsWithChildren<{ title: string }>> = ({
|
|
29
|
+
title,
|
|
30
|
+
children,
|
|
31
|
+
}) => {
|
|
32
|
+
return (
|
|
33
|
+
<Stack
|
|
34
|
+
sx={{
|
|
35
|
+
padding: '4px',
|
|
36
|
+
gap: '8px',
|
|
37
|
+
}}
|
|
38
|
+
>
|
|
39
|
+
<Typography variant="body1">{title}</Typography>
|
|
40
|
+
{children}
|
|
41
|
+
</Stack>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const SettingsForm: React.FC = () => {
|
|
46
|
+
const context = useFormContext<Configuration>();
|
|
47
|
+
|
|
48
|
+
const {
|
|
49
|
+
register,
|
|
50
|
+
reset,
|
|
51
|
+
formState: { errors },
|
|
52
|
+
control,
|
|
53
|
+
watch,
|
|
54
|
+
} = context;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Stack gap="16px">
|
|
58
|
+
<FormCard title="Host Settings">
|
|
59
|
+
<TextField
|
|
60
|
+
name={'unisphereServerUrl'}
|
|
61
|
+
fullWidth
|
|
62
|
+
inputProps={{
|
|
63
|
+
...register('unisphereServerUrl', { required: true }),
|
|
64
|
+
}}
|
|
65
|
+
label={'Service URL'}
|
|
66
|
+
helperText={errors?.unisphereServerUrl ? 'required' : ''}
|
|
67
|
+
error={!!errors?.unisphereServerUrl}
|
|
68
|
+
/>
|
|
69
|
+
|
|
70
|
+
<TextField
|
|
71
|
+
name={'language'}
|
|
72
|
+
fullWidth
|
|
73
|
+
inputProps={{ ...register('language') }}
|
|
74
|
+
label={'Language code'}
|
|
75
|
+
/>
|
|
76
|
+
|
|
77
|
+
<FormItem title="Theme">
|
|
78
|
+
<Controller
|
|
79
|
+
control={control}
|
|
80
|
+
name="theme"
|
|
81
|
+
render={({ field }) => (
|
|
82
|
+
<Select {...field}>
|
|
83
|
+
<MenuItem value="light">Light</MenuItem>
|
|
84
|
+
<MenuItem value="dark">Dark</MenuItem>
|
|
85
|
+
</Select>
|
|
86
|
+
)}
|
|
87
|
+
/>
|
|
88
|
+
</FormItem>
|
|
89
|
+
</FormCard>
|
|
90
|
+
<FormCard title="Unisphere Settings">
|
|
91
|
+
<TextField
|
|
92
|
+
name={'settings'}
|
|
93
|
+
fullWidth
|
|
94
|
+
inputProps={{
|
|
95
|
+
...register('settings', { required: true }),
|
|
96
|
+
}}
|
|
97
|
+
label={'Unisphere Settings'}
|
|
98
|
+
helperText={`Easily copy settings from Unisphere by entering a workable host in NVQ2 and then do: Unisphere cmd+k > Manage Workspace Runtimes > show ... (actions) of desired runtime > copy settings`}
|
|
99
|
+
error={!!errors?.settings}
|
|
100
|
+
/>
|
|
101
|
+
</FormCard>
|
|
102
|
+
</Stack>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import { Dialog, DialogActions, DialogContent } from '@mui/material';
|
|
3
|
+
import { FormProvider, useForm } from 'react-hook-form';
|
|
4
|
+
import { ConfigurationContext } from '../configuration-provider';
|
|
5
|
+
import { getDefaultConfiguration, Configuration } from '../definitions';
|
|
6
|
+
import { SettingsButtons } from './settings-buttons';
|
|
7
|
+
import { SettingsForm } from './settings-form';
|
|
8
|
+
|
|
9
|
+
export const Settings: React.FC<{ onClose: () => void }> = ({ onClose }) => {
|
|
10
|
+
const {
|
|
11
|
+
clearConfiguration,
|
|
12
|
+
configuration: Configuration,
|
|
13
|
+
updateConfiguration,
|
|
14
|
+
} = useContext(ConfigurationContext);
|
|
15
|
+
|
|
16
|
+
const formMethods = useForm<Configuration>({
|
|
17
|
+
defaultValues: Configuration,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const onSubmit = (data: any) => {
|
|
21
|
+
const updatedData = {
|
|
22
|
+
...data,
|
|
23
|
+
};
|
|
24
|
+
updateConfiguration(updatedData);
|
|
25
|
+
|
|
26
|
+
onClose();
|
|
27
|
+
|
|
28
|
+
window.location.reload();
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const handleClose = (event: any, reason: string) => {
|
|
32
|
+
if (reason === 'backdropClick') {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
onClose();
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const handleReset = () => {
|
|
39
|
+
// eslint-disable-next-line no-restricted-globals
|
|
40
|
+
const accepted = confirm('Do you want to clear all values?');
|
|
41
|
+
if (!accepted) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
clearConfiguration();
|
|
45
|
+
formMethods.reset(getDefaultConfiguration());
|
|
46
|
+
};
|
|
47
|
+
return (
|
|
48
|
+
<div>
|
|
49
|
+
<Dialog open={true} onClose={handleClose} disableEscapeKeyDown={true}>
|
|
50
|
+
<FormProvider {...formMethods}>
|
|
51
|
+
<form
|
|
52
|
+
noValidate
|
|
53
|
+
autoComplete="off"
|
|
54
|
+
onSubmit={formMethods.handleSubmit(onSubmit)}
|
|
55
|
+
>
|
|
56
|
+
<DialogContent
|
|
57
|
+
sx={{
|
|
58
|
+
display: 'flex',
|
|
59
|
+
width: '500px',
|
|
60
|
+
flexDirection: 'column',
|
|
61
|
+
overflow: 'hidden',
|
|
62
|
+
}}
|
|
63
|
+
>
|
|
64
|
+
<SettingsForm />
|
|
65
|
+
<DialogActions sx={{ padding: 0 }}>
|
|
66
|
+
<SettingsButtons onReset={handleReset} />
|
|
67
|
+
</DialogActions>
|
|
68
|
+
</DialogContent>
|
|
69
|
+
</form>
|
|
70
|
+
</FormProvider>
|
|
71
|
+
</Dialog>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import React, { useMemo, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
getDefaultConfiguration,
|
|
4
|
+
isConfigurationValid,
|
|
5
|
+
Configuration,
|
|
6
|
+
} from './definitions';
|
|
7
|
+
import { mergeDeep } from './utils/merge-deep';
|
|
8
|
+
|
|
9
|
+
export const ConfigurationContext = React.createContext<{
|
|
10
|
+
configuration: Configuration;
|
|
11
|
+
sharedUrl: string;
|
|
12
|
+
clearConfiguration: () => void;
|
|
13
|
+
updateConfiguration: (configuration: Configuration) => void;
|
|
14
|
+
createSharedUrl: (options?: {
|
|
15
|
+
baseUrl?: string;
|
|
16
|
+
customConfiguration?: Configuration;
|
|
17
|
+
}) => string;
|
|
18
|
+
ConfigurationType: ConfigurationTypes;
|
|
19
|
+
refreshConfiguration: () => void;
|
|
20
|
+
}>(null as any);
|
|
21
|
+
|
|
22
|
+
export const getConfigurationFromQueryParams = (
|
|
23
|
+
configurationKey: string
|
|
24
|
+
): Configuration | null => {
|
|
25
|
+
const search = window.location.search;
|
|
26
|
+
const params = new URLSearchParams(search);
|
|
27
|
+
|
|
28
|
+
if (!params || !configurationKey || !params.get(configurationKey))
|
|
29
|
+
return null;
|
|
30
|
+
|
|
31
|
+
const configuration = window.atob(params.get(configurationKey) || '');
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(configuration);
|
|
34
|
+
} catch (e: any) {
|
|
35
|
+
console.warn(`Failed to parse login query string ${e.message} ${e.stack}`);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export enum ConfigurationTypes {
|
|
41
|
+
None,
|
|
42
|
+
Querystring,
|
|
43
|
+
Localstorage,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const { hostname, port, protocol } = window.location;
|
|
47
|
+
export const hosterAppUrl = `${protocol}//${hostname}${port ? `:${port}` : ''}`;
|
|
48
|
+
|
|
49
|
+
const getConfigurationFromLocalStorage = (
|
|
50
|
+
configurationKey: string
|
|
51
|
+
): Configuration | null => {
|
|
52
|
+
const data = localStorage.getItem(configurationKey);
|
|
53
|
+
return data ? JSON.parse(data) : null;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const loginParamsToBase64 = (configuration: Configuration | null): string => {
|
|
57
|
+
if (!configuration) {
|
|
58
|
+
return '';
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
return window.btoa(JSON.stringify(configuration));
|
|
62
|
+
} catch (e) {
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const ConfigurationProvider: React.FC<
|
|
68
|
+
React.PropsWithChildren<{
|
|
69
|
+
configurationKey: string;
|
|
70
|
+
}>
|
|
71
|
+
> = (props) => {
|
|
72
|
+
const { configurationKey } = props;
|
|
73
|
+
const [ConfigurationType] = useState<ConfigurationTypes>(() => {
|
|
74
|
+
return getConfigurationFromQueryParams(configurationKey)
|
|
75
|
+
? ConfigurationTypes.Querystring
|
|
76
|
+
: ConfigurationTypes.Localstorage;
|
|
77
|
+
});
|
|
78
|
+
const [configuration, setConfiguration] = useState<Configuration | null>(
|
|
79
|
+
() => {
|
|
80
|
+
const configuration = mergeDeep(
|
|
81
|
+
{},
|
|
82
|
+
getDefaultConfiguration(),
|
|
83
|
+
ConfigurationType === ConfigurationTypes.Querystring
|
|
84
|
+
? getConfigurationFromQueryParams(configurationKey)
|
|
85
|
+
: getConfigurationFromLocalStorage(configurationKey)
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return configuration && isConfigurationValid(configuration)
|
|
89
|
+
? configuration
|
|
90
|
+
: getDefaultConfiguration();
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const providerValue = useMemo(() => {
|
|
95
|
+
const createSharedUrl = (options?: {
|
|
96
|
+
baseUrl?: string;
|
|
97
|
+
customConfiguration?: Configuration;
|
|
98
|
+
}) => {
|
|
99
|
+
const { baseUrl = '', customConfiguration } = options || {};
|
|
100
|
+
|
|
101
|
+
if (!options?.customConfiguration && !configuration) {
|
|
102
|
+
return '';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return `${
|
|
106
|
+
baseUrl || hosterAppUrl
|
|
107
|
+
}/configuration?${configurationKey}=${loginParamsToBase64(
|
|
108
|
+
customConfiguration || configuration
|
|
109
|
+
)}`;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const saveToLocalStorage = (params: Configuration) => {
|
|
113
|
+
if (ConfigurationType === ConfigurationTypes.Querystring) {
|
|
114
|
+
const sharedUrl = createSharedUrl({
|
|
115
|
+
customConfiguration: params,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (sharedUrl) {
|
|
119
|
+
window.location.href = sharedUrl;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
localStorage.setItem(
|
|
126
|
+
configurationKey,
|
|
127
|
+
params ? JSON.stringify(params) : ''
|
|
128
|
+
);
|
|
129
|
+
setConfiguration(params || null);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const clearLocalStorage = () => {
|
|
133
|
+
localStorage.removeItem(configurationKey);
|
|
134
|
+
setConfiguration(null);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const refreshConfiguration = () => {
|
|
138
|
+
setConfiguration((prev) => {
|
|
139
|
+
if (!prev) {
|
|
140
|
+
return prev;
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
...prev,
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
return {
|
|
148
|
+
configuration: configuration || getDefaultConfiguration(),
|
|
149
|
+
ConfigurationType,
|
|
150
|
+
refreshConfiguration,
|
|
151
|
+
updateConfiguration: saveToLocalStorage,
|
|
152
|
+
clearConfiguration: clearLocalStorage,
|
|
153
|
+
createSharedUrl,
|
|
154
|
+
sharedUrl: createSharedUrl(),
|
|
155
|
+
};
|
|
156
|
+
}, [configuration]);
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<ConfigurationContext.Provider value={providerValue}>
|
|
160
|
+
{props.children}
|
|
161
|
+
</ConfigurationContext.Provider>
|
|
162
|
+
);
|
|
163
|
+
};
|
package/dist/generators/add-application/templates/interactive-playground/src/app/definitions.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const inProduction: boolean;
|
|
2
|
+
export interface Configuration {
|
|
3
|
+
_schemaVersion: string;
|
|
4
|
+
unisphereServerUrl: string;
|
|
5
|
+
language: string;
|
|
6
|
+
theme: 'light' | 'dark';
|
|
7
|
+
settings: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const getDefaultConfiguration: () => Configuration;
|
|
10
|
+
export declare const isConfigurationValid: (config: any) => config is Configuration;
|
|
11
|
+
//# sourceMappingURL=definitions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../../../../../../src/generators/add-application/templates/interactive-playground/src/app/definitions.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,SAAwC,CAAC;AAGlE,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,uBAAuB,QAAO,aAMzC,CAAC;AAGH,eAAO,MAAM,oBAAoB,GAAI,QAAQ,GAAG,KAAG,MAAM,IAAI,aAE5D,CAAC"}
|
package/dist/generators/add-application/templates/interactive-playground/src/app/definitions.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isConfigurationValid = exports.getDefaultConfiguration = exports.inProduction = void 0;
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
exports.inProduction = process.env.NODE_ENV === 'production';
|
|
7
|
+
const getDefaultConfiguration = () => ({
|
|
8
|
+
unisphereServerUrl: 'https://unisphere.nvq2.ovp.kaltura.com/v1',
|
|
9
|
+
language: 'en',
|
|
10
|
+
_schemaVersion: '1',
|
|
11
|
+
theme: 'light',
|
|
12
|
+
settings: '',
|
|
13
|
+
});
|
|
14
|
+
exports.getDefaultConfiguration = getDefaultConfiguration;
|
|
15
|
+
// todo set here the logic needed to validate configuration
|
|
16
|
+
const isConfigurationValid = (config) => {
|
|
17
|
+
return config?.unisphereServerUrl && config?.settings;
|
|
18
|
+
};
|
|
19
|
+
exports.isConfigurationValid = isConfigurationValid;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple object check.
|
|
3
|
+
* @param item
|
|
4
|
+
* @returns {boolean}
|
|
5
|
+
*/
|
|
6
|
+
export declare function isObject(item: any): any;
|
|
7
|
+
/**
|
|
8
|
+
* Deep merge two objects.
|
|
9
|
+
* @param target
|
|
10
|
+
* @param ...sources
|
|
11
|
+
*/
|
|
12
|
+
export declare function mergeDeep(target: any, ...sources: any[]): any;
|
|
13
|
+
//# sourceMappingURL=merge-deep.d.ts.map
|