mirror of
https://github.com/qlik-oss/nebula.js.git
synced 2025-12-19 17:58:43 -05:00
fix: nebula serve with new model cache (#1455)
* fix: use correct context in serve * chore: always include sourcemaps in serve * chore: add missing file * chore: rename file
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
import useRpc from './useRpc';
|
||||
import { useModelStore } from './useModelStore';
|
||||
|
||||
export default function usePropertiesById(id) {
|
||||
const [modelStore] = useModelStore();
|
||||
const model = modelStore.get(id);
|
||||
const [properties, ...rest] = useRpc(model, 'getProperties');
|
||||
return [properties, (p) => model.setProperties(p), ...rest];
|
||||
}
|
||||
@@ -23,7 +23,7 @@ const cfg = ({ srcDir, distDir, dev = false, serveConfig = {} }) => {
|
||||
eHub: [path.resolve(srcDir, 'eHub')],
|
||||
fixtures: [path.resolve(__dirname, './fixtures.js')],
|
||||
},
|
||||
devtool: dev ? 'source-map' : false,
|
||||
devtool: 'source-map',
|
||||
output: {
|
||||
path: distDir,
|
||||
filename: '[name].js',
|
||||
|
||||
@@ -6,6 +6,7 @@ import { styled } from '@mui/material/styles';
|
||||
import { Grid, Toolbar, IconButton, CircularProgress } from '@mui/material';
|
||||
|
||||
import SvgIcon from '@nebula.js/ui/icons/SvgIcon';
|
||||
import { useModelStore } from '@nebula.js/nucleus/src/hooks/useModelStore';
|
||||
|
||||
import PropsDialog from './PropertiesDialog';
|
||||
|
||||
@@ -35,6 +36,7 @@ const StyledGrid = styled(Grid)(({ theme }) => ({
|
||||
export default function ({ id, expandable, minHeight }) {
|
||||
const language = 'en-US'; // TODO - useLocale
|
||||
const app = useContext(AppContext);
|
||||
const [modelStore] = useModelStore();
|
||||
const [model, setModel] = useState(null);
|
||||
const [exporting, setExporting] = useState(false);
|
||||
const [localViz, setLocalViz] = useState(null);
|
||||
@@ -47,6 +49,7 @@ export default function ({ id, expandable, minHeight }) {
|
||||
useEffect(() => {
|
||||
const v = app.getObject(id).then((m) => {
|
||||
setModel(m);
|
||||
modelStore.set(`${id}`, m);
|
||||
return m;
|
||||
});
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@ import React, { useCallback, useState, useContext } from 'react';
|
||||
|
||||
import { Divider, Grid, Checkbox, FormControlLabel } from '@mui/material';
|
||||
|
||||
import usePropertiesById from '@nebula.js/nucleus/src/hooks/usePropertiesById';
|
||||
import useProperties from './useProperties';
|
||||
|
||||
import AppContext from '../../contexts/AppContext';
|
||||
import Data from './property-panel/Data';
|
||||
import generateComponents from './AutoComponents';
|
||||
|
||||
export default function Properties({ viz, sn, isTemp, storage, flags }) {
|
||||
const [properties, setProperties] = usePropertiesById(viz.id);
|
||||
const [properties, setProperties] = useProperties(viz?.model);
|
||||
const app = useContext(AppContext);
|
||||
|
||||
const [isReadCacheEnabled, setReadCacheEnabled] = useState(storage.get('readFromCache') !== false);
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useEffect, useContext } from 'react';
|
||||
|
||||
import { Grid } from '@mui/material';
|
||||
|
||||
import usePropertiesById from '@nebula.js/nucleus/src/hooks/usePropertiesById';
|
||||
import useProperties from './useProperties';
|
||||
|
||||
import Cell from './Cell';
|
||||
import NebulaContext from '../../contexts/NebulaContext';
|
||||
@@ -11,7 +11,7 @@ import VizContext from '../../contexts/VizContext';
|
||||
export default function Stage({ info, storage, uid }) {
|
||||
const nebbie = useContext(NebulaContext);
|
||||
const { activeViz, setActiveViz } = useContext(VizContext);
|
||||
const [properties] = usePropertiesById(uid);
|
||||
const [properties] = useProperties(activeViz?.model);
|
||||
|
||||
useEffect(() => {
|
||||
if (!uid) {
|
||||
|
||||
@@ -4,6 +4,9 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { embed } from '@nebula.js/stardust';
|
||||
import { createTheme, ThemeProvider, StyledEngineProvider } from '@nebula.js/ui/theme';
|
||||
import { WbSunny, Brightness3, ColorLens, Language, Home } from '@nebula.js/ui/icons';
|
||||
import InstanceContext from '@nebula.js/nucleus/src/contexts/InstanceContext';
|
||||
import initModelStore from '@nebula.js/nucleus/src/stores/new-model-store';
|
||||
import initSelectionStore from '@nebula.js/nucleus/src/stores/new-selections-store';
|
||||
|
||||
import {
|
||||
Grid,
|
||||
@@ -84,6 +87,7 @@ export default function Visualize() {
|
||||
const [initialized, setInit] = useState(false);
|
||||
const [nebbie, setNebbie] = useState(null);
|
||||
const customThemes = Array.isArray(info?.themes) && info.themes.length ? ['light', 'dark', ...info.themes] : [];
|
||||
const [context, setContext] = useState();
|
||||
|
||||
const theme = useMemo(() => createTheme(currentMuiThemeName), [currentMuiThemeName]);
|
||||
|
||||
@@ -183,6 +187,17 @@ export default function Visualize() {
|
||||
if (app) app.destroySessionObject(uid.current);
|
||||
};
|
||||
window.addEventListener('beforeunload', unload);
|
||||
|
||||
if (app) {
|
||||
const modelStore = initModelStore(app.id);
|
||||
const selectionStore = initSelectionStore(app.id);
|
||||
setContext({
|
||||
modelStore,
|
||||
selectionStore,
|
||||
myKey: 'this is the key here',
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', unload);
|
||||
};
|
||||
@@ -221,183 +236,189 @@ export default function Visualize() {
|
||||
}
|
||||
setObjectListMode(listMode);
|
||||
};
|
||||
|
||||
// For another time: refactor away the usage of internal Nebula hooks so we don't need the InstanceContext here.
|
||||
return (
|
||||
<AppContext.Provider value={app}>
|
||||
<StyledEngineProvider injectFirst>
|
||||
<ThemeProvider theme={theme}>
|
||||
<NebulaContext.Provider value={nebbie}>
|
||||
<Grid
|
||||
container
|
||||
wrap="nowrap"
|
||||
direction="column"
|
||||
style={{ background: theme.palette.background.darkest, height: '100%' }}
|
||||
gap={0}
|
||||
>
|
||||
<Grid item>
|
||||
<Toolbar
|
||||
variant="dense"
|
||||
style={{ background: theme.palette.background.paper, boxShadow: theme.shadows[1] }}
|
||||
>
|
||||
<Grid container gap={1}>
|
||||
<Grid item container alignItems="center" style={{ width: 'auto' }}>
|
||||
<Grid item>
|
||||
<a href="https://github.com/qlik-oss/nebula.js" target="_blank" rel="noopener noreferrer">
|
||||
<img
|
||||
src="/assets/logo.svg"
|
||||
alt="nebula.js logo"
|
||||
href="https://github.com/qlik-oss/nebula.js"
|
||||
style={{ height: '24px', position: 'relative', top: '2px' }}
|
||||
/>
|
||||
</a>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item container alignItems="center" style={{ width: 'auto' }}>
|
||||
<IconButton title="Home" onClick={() => navigate('/')} size="large">
|
||||
<Home style={{ verticalAlign: 'middle' }} />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Tabs value={objectListMode ? 1 : 0} onChange={handleCreateEditChange} aria-label="Navigation">
|
||||
<Tab label={<Typography>Create</Typography>} value={0} />
|
||||
<Tab label={<Typography>Edit</Typography>} value={1} />
|
||||
</Tabs>
|
||||
</Grid>
|
||||
<Grid item container alignItems="center" style={{ width: 'auto' }}>
|
||||
<Grid item gap={1}>
|
||||
<Typography>State:</Typography>
|
||||
</Grid>
|
||||
<Grid item gap={2}>
|
||||
<ToggleButtonGroup
|
||||
size="small"
|
||||
value={currentInteractions}
|
||||
onChange={handleInteractionsChange}
|
||||
color="primary"
|
||||
>
|
||||
<ToggleButton value="select">Select</ToggleButton>
|
||||
<ToggleButton value="active">Active</ToggleButton>
|
||||
<ToggleButton value="passive">Passive</ToggleButton>
|
||||
<ToggleButton value="edit">Edit</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
{customThemes.length ? (
|
||||
<>
|
||||
<IconButton
|
||||
title="Select theme"
|
||||
onClick={(e) => setThemeChooserAnchorEl(e.currentTarget)}
|
||||
size="large"
|
||||
>
|
||||
<ColorLens fontSize="small" />
|
||||
</IconButton>
|
||||
<Menu
|
||||
anchorEl={themeChooserAnchorEl}
|
||||
open={!!themeChooserAnchorEl}
|
||||
keepMounted
|
||||
onClose={() => setThemeChooserAnchorEl(null)}
|
||||
>
|
||||
{customThemes.map((t) => (
|
||||
<MenuItem
|
||||
key={t}
|
||||
selected={t === currentThemeName}
|
||||
onClick={() => handleThemeChange(t)}
|
||||
>
|
||||
{t}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</>
|
||||
) : (
|
||||
<IconButton title="Toggle light/dark mode" onClick={toggleDarkMode} size="large">
|
||||
{currentThemeName === 'dark' ? (
|
||||
<WbSunny fontSize="small" />
|
||||
) : (
|
||||
<Brightness3 fontSize="small" />
|
||||
)}
|
||||
</IconButton>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button
|
||||
startIcon={<Language />}
|
||||
title="Select language"
|
||||
onClick={(e) => setLanguageChooserAnchorEl(e.currentTarget)}
|
||||
>
|
||||
{currentLanguage}
|
||||
</Button>
|
||||
<Menu
|
||||
anchorEl={languageChooserAnchorEl}
|
||||
open={!!languageChooserAnchorEl}
|
||||
keepMounted
|
||||
onClose={() => setLanguageChooserAnchorEl(null)}
|
||||
>
|
||||
{languages.map((t) => (
|
||||
<MenuItem key={t} selected={t === currentLanguage} onClick={() => handleLanguageChange(t)}>
|
||||
{t}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Toolbar>
|
||||
</Grid>
|
||||
<Grid item style={{ padding: theme.spacing(SPACING / 2, SPACING / 2, 0, SPACING / 2) }}>
|
||||
<div ref={currentSelectionsRef} style={{ flex: '0 0 auto', boxShadow: theme.shadows[1] }} />
|
||||
</Grid>
|
||||
<InstanceContext.Provider value={context}>
|
||||
<AppContext.Provider value={app}>
|
||||
<StyledEngineProvider injectFirst>
|
||||
<ThemeProvider theme={theme}>
|
||||
<NebulaContext.Provider value={nebbie}>
|
||||
<Grid
|
||||
item
|
||||
xs
|
||||
style={{
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
padding: theme.spacing(0, SPACING / 2, SPACING / 2, SPACING / 2),
|
||||
}}
|
||||
container
|
||||
wrap="nowrap"
|
||||
direction="column"
|
||||
style={{ background: theme.palette.background.darkest, height: '100%' }}
|
||||
gap={0}
|
||||
>
|
||||
<VizContext.Provider value={vizContext}>
|
||||
{sn ? (
|
||||
<Grid container wrap="nowrap" style={{ height: '100%' }} gap={SPACING / 2}>
|
||||
<Grid item>
|
||||
<Toolbar
|
||||
variant="dense"
|
||||
style={{ background: theme.palette.background.paper, boxShadow: theme.shadows[1] }}
|
||||
>
|
||||
<Grid container gap={1}>
|
||||
<Grid item container alignItems="center" style={{ width: 'auto' }}>
|
||||
<Grid item>
|
||||
<a href="https://github.com/qlik-oss/nebula.js" target="_blank" rel="noopener noreferrer">
|
||||
<img
|
||||
src="/assets/logo.svg"
|
||||
alt="nebula.js logo"
|
||||
href="https://github.com/qlik-oss/nebula.js"
|
||||
style={{ height: '24px', position: 'relative', top: '2px' }}
|
||||
/>
|
||||
</a>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item container alignItems="center" style={{ width: 'auto' }}>
|
||||
<IconButton title="Home" onClick={() => navigate('/')} size="large">
|
||||
<Home style={{ verticalAlign: 'middle' }} />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
<Grid item xs zeroMinWidth>
|
||||
{objectListMode ? (
|
||||
<Collection cache={currentId} types={[info?.supernova.name]} />
|
||||
) : (
|
||||
<Stage info={info} storage={storage} uid={currentId} />
|
||||
<Tabs value={objectListMode ? 1 : 0} onChange={handleCreateEditChange} aria-label="Navigation">
|
||||
<Tab label={<Typography>Create</Typography>} value={0} />
|
||||
<Tab label={<Typography>Edit</Typography>} value={1} />
|
||||
</Tabs>
|
||||
</Grid>
|
||||
<Grid item container alignItems="center" style={{ width: 'auto' }}>
|
||||
<Grid item gap={1}>
|
||||
<Typography>State:</Typography>
|
||||
</Grid>
|
||||
<Grid item gap={2}>
|
||||
<ToggleButtonGroup
|
||||
size="small"
|
||||
value={currentInteractions}
|
||||
onChange={handleInteractionsChange}
|
||||
color="primary"
|
||||
>
|
||||
<ToggleButton value="select">Select</ToggleButton>
|
||||
<ToggleButton value="active">Active</ToggleButton>
|
||||
<ToggleButton value="passive">Passive</ToggleButton>
|
||||
<ToggleButton value="edit">Edit</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
{customThemes.length ? (
|
||||
<>
|
||||
<IconButton
|
||||
title="Select theme"
|
||||
onClick={(e) => setThemeChooserAnchorEl(e.currentTarget)}
|
||||
size="large"
|
||||
>
|
||||
<ColorLens fontSize="small" />
|
||||
</IconButton>
|
||||
<Menu
|
||||
anchorEl={themeChooserAnchorEl}
|
||||
open={!!themeChooserAnchorEl}
|
||||
keepMounted
|
||||
onClose={() => setThemeChooserAnchorEl(null)}
|
||||
>
|
||||
{customThemes.map((t) => (
|
||||
<MenuItem
|
||||
key={t}
|
||||
selected={t === currentThemeName}
|
||||
onClick={() => handleThemeChange(t)}
|
||||
>
|
||||
{t}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</>
|
||||
) : (
|
||||
<IconButton title="Toggle light/dark mode" onClick={toggleDarkMode} size="large">
|
||||
{currentThemeName === 'dark' ? (
|
||||
<WbSunny fontSize="small" />
|
||||
) : (
|
||||
<Brightness3 fontSize="small" />
|
||||
)}
|
||||
</IconButton>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button
|
||||
startIcon={<Language />}
|
||||
title="Select language"
|
||||
onClick={(e) => setLanguageChooserAnchorEl(e.currentTarget)}
|
||||
>
|
||||
{currentLanguage}
|
||||
</Button>
|
||||
<Menu
|
||||
anchorEl={languageChooserAnchorEl}
|
||||
open={!!languageChooserAnchorEl}
|
||||
keepMounted
|
||||
onClose={() => setLanguageChooserAnchorEl(null)}
|
||||
>
|
||||
{languages.map((t) => (
|
||||
<MenuItem
|
||||
key={t}
|
||||
selected={t === currentLanguage}
|
||||
onClick={() => handleLanguageChange(t)}
|
||||
>
|
||||
{t}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Toolbar>
|
||||
</Grid>
|
||||
<Grid item style={{ padding: theme.spacing(SPACING / 2, SPACING / 2, 0, SPACING / 2) }}>
|
||||
<div ref={currentSelectionsRef} style={{ flex: '0 0 auto', boxShadow: theme.shadows[1] }} />
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs
|
||||
style={{
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
padding: theme.spacing(0, SPACING / 2, SPACING / 2, SPACING / 2),
|
||||
}}
|
||||
>
|
||||
<VizContext.Provider value={vizContext}>
|
||||
{sn ? (
|
||||
<Grid container wrap="nowrap" style={{ height: '100%' }} gap={SPACING / 2}>
|
||||
<Grid item xs zeroMinWidth>
|
||||
{objectListMode ? (
|
||||
<Collection cache={currentId} types={[info?.supernova.name]} />
|
||||
) : (
|
||||
<Stage info={info} storage={storage} uid={currentId} />
|
||||
)}
|
||||
</Grid>
|
||||
{activeViz && (
|
||||
<Grid
|
||||
item
|
||||
style={{
|
||||
background: theme.palette.background.paper,
|
||||
overflow: 'hidden auto',
|
||||
marginTop: 48,
|
||||
boxShadow: theme.shadows[1],
|
||||
padding: 0,
|
||||
}}
|
||||
>
|
||||
<Properties
|
||||
sn={sn}
|
||||
viz={activeViz}
|
||||
isTemp={!objectListMode}
|
||||
storage={storage}
|
||||
flags={info?.flags}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
{activeViz && (
|
||||
<Grid
|
||||
item
|
||||
style={{
|
||||
background: theme.palette.background.paper,
|
||||
overflow: 'hidden auto',
|
||||
marginTop: 48,
|
||||
boxShadow: theme.shadows[1],
|
||||
padding: 0,
|
||||
}}
|
||||
>
|
||||
<Properties
|
||||
sn={sn}
|
||||
viz={activeViz}
|
||||
isTemp={!objectListMode}
|
||||
storage={storage}
|
||||
flags={info?.flags}
|
||||
/>
|
||||
) : (
|
||||
<Grid container wrap="nowrap" style={{ paddingTop: '48px' }} justifyContent="center">
|
||||
<Grid item>
|
||||
<CircularProgress />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
) : (
|
||||
<Grid container wrap="nowrap" style={{ paddingTop: '48px' }} justifyContent="center">
|
||||
<Grid item>
|
||||
<CircularProgress />
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</VizContext.Provider>
|
||||
)}
|
||||
</VizContext.Provider>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</NebulaContext.Provider>
|
||||
</ThemeProvider>
|
||||
</StyledEngineProvider>
|
||||
</AppContext.Provider>
|
||||
</NebulaContext.Provider>
|
||||
</ThemeProvider>
|
||||
</StyledEngineProvider>
|
||||
</AppContext.Provider>
|
||||
</InstanceContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
22
commands/serve/web/components/Visualize/useProperties.js
Normal file
22
commands/serve/web/components/Visualize/useProperties.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function useProperties(model) {
|
||||
const [properties, setProps] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
if (model) {
|
||||
const onChanged = () => {
|
||||
model.getProperties().then((props) => {
|
||||
setProps(props);
|
||||
});
|
||||
};
|
||||
model.on('changed', onChanged);
|
||||
onChanged();
|
||||
return () => {
|
||||
model.removeListener('changed', onChanged);
|
||||
};
|
||||
}
|
||||
return () => {};
|
||||
}, [model]);
|
||||
return [properties, (p) => model.setProperties(p)];
|
||||
}
|
||||
Reference in New Issue
Block a user