mirror of
https://github.com/qlik-oss/nebula.js.git
synced 2025-12-23 11:44:10 -05:00
225 lines
5.3 KiB
JavaScript
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>
|
|
);
|
|
}
|