Files
nebula.js/commands/serve/web/components/Visualize/AutoComponents.jsx
Ahmad Mirzaei 7001a47232 feat: introducing react router to nebula web (#973)
* feat: nebula web react-router

* chore: `<HubLayout />`
2022-10-26 16:09:51 +02:00

225 lines
5.3 KiB
JavaScript

/* eslint no-param-reassign:0 */
/* eslint no-use-before-define:0 */
import React, { useState } from 'react';
import { styled } from '@mui/material/styles';
import {
Typography,
Grid,
Checkbox,
FormControlLabel,
TextField,
Accordion,
AccordionSummary,
AccordionDetails,
InputAdornment,
} from '@mui/material';
import { ExpandMore } from '@nebula.js/ui/icons';
import Variable from './property-panel/Variable';
const PREFIX = 'AutoComponents';
const classes = {
summary: `${PREFIX}-summary`,
details: `${PREFIX}-details`,
root: `${PREFIX}-root`,
};
const StyledGrid = styled(Grid)(({ theme }) => ({
[`& .${classes.summary}`]: {
padding: theme.spacing(0, 1),
backgroundColor: theme.palette.background.lighter,
borderBottom: `1px solid ${theme.palette.divider}`,
},
[`& .${classes.details}`]: {
padding: theme.spacing(1),
},
}));
const StyledAccordion = styled(Accordion)(({ theme }) => ({
[`& .${classes.root}`]: {
boxShadow: 'none',
marginLeft: -theme.spacing(1),
marginRight: -theme.spacing(1),
'&$expanded': {
marginLeft: -theme.spacing(1),
marginRight: -theme.spacing(1),
},
},
}));
const getType = (value, key) => {
if (Array.isArray(value)) {
return 'array';
}
if (enableExpressions && key === 'variable' && typeof value === 'object' && 'name' in value && 'value' in value) {
return 'variable';
}
if (enableExpressions && value && typeof value === 'object' && 'qStringExpression' in value) {
return 'expression';
}
if (typeof value === 'boolean') {
return 'boolean';
}
if (typeof value === 'string') {
return 'string';
}
if (typeof value === 'number') {
return 'number';
}
if (value && typeof value === 'object') {
return 'object';
}
return 'unknown';
};
function Bool({ property, value, target, changed }) {
const handleChange = (e) => {
target[property] = e.target.checked;
changed();
};
return (
<FormControlLabel
control={<Checkbox checked={value} onChange={handleChange} />}
label={property}
labelPlacement="end"
/>
);
}
function Str({ property, value, target, changed }) {
const [s, setS] = useState(value);
const handleChange = (e) => {
setS(e.target.value);
};
const onBlur = () => {
if (s !== value) {
target[property] = s;
changed();
}
};
return <TextField variant="standard" fullWidth onChange={handleChange} onBlur={onBlur} label={property} value={s} />;
}
function Num({ property, value, target, changed }) {
const [s, setS] = useState(+value);
const handleChange = (e) => {
setS(e.target.value);
};
const onBlur = () => {
if (s !== value) {
target[property] = +s;
changed();
}
};
return <TextField variant="standard" fullWidth onChange={handleChange} onBlur={onBlur} label={property} value={s} />;
}
function Expression({ property, value, target, changed }) {
const initial = value.qStringExpression && value.qStringExpression.qExpr ? value.qStringExpression.qExpr : '';
const [s, setS] = useState(initial);
const handleChange = (e) => {
setS(e.target.value);
};
const onBlur = () => {
if (s !== value) {
target[property].qStringExpression = { qExpr: `=${s}` };
changed();
}
};
return (
<TextField
variant="standard"
InputProps={{
startAdornment: <InputAdornment position="start">=</InputAdornment>,
}}
fullWidth
onChange={handleChange}
onBlur={onBlur}
label={property}
value={s}
/>
);
}
function Obj({ property, value, changed, app }) {
return (
<StyledAccordion square className={classes.root}>
<AccordionSummary expandIcon={<ExpandMore />} className={classes.summary}>
<Typography>{property}</Typography>
</AccordionSummary>
<AccordionDetails className={classes.details}>{generateComponents(value, changed, app)}</AccordionDetails>
</StyledAccordion>
);
}
const registeredComponents = {
boolean: Bool,
string: Str,
object: Obj,
number: Num,
expression: Expression,
variable: Variable,
};
const QRX = /^q[A-Z]/;
let enableExpressions = false;
export default function generateComponents(properties, changed, app, flags) {
if (flags && flags.PP_EXPRESSIONS) {
enableExpressions = true;
}
const components = Object.keys(properties)
.map((key) => {
if (['visualization', 'version'].indexOf(key) !== -1) {
return false;
}
if (QRX.test(key)) {
// skip q properties for now, but allow qStringExpression
return false;
}
const type = getType(properties[key], key);
if (!registeredComponents[type]) {
return false;
}
return {
Component: registeredComponents[type],
property: key,
target: properties,
value: properties[key],
key: `${key}:${properties[key]}`,
};
})
.filter(Boolean);
return (
<StyledGrid container direction="column" gap={0} alignItems="stretch">
{components.map((c) => (
<Grid item xs key={c.key} style={{ width: '100%' }}>
<c.Component
key={c.key}
app={app}
property={c.property}
value={c.value}
target={properties}
changed={changed}
/>
</Grid>
))}
</StyledGrid>
);
}