@jsonforms/material-renderers
Version:
Material Renderer Set for JSON Forms
779 lines (746 loc) • 101 kB
JavaScript
import React, { useState, useCallback, useMemo, Fragment, useEffect } from 'react';
import { rankWith, isAllOfControl, findMatchingUISchema, createCombinatorRenderInfos, Generate, isLayout, isAnyOfControl, createDefaultValue, Resolve, encode, Paths, formatErrorMessage, errorAt, or, isObjectArrayControl, isPrimitiveArrayControl, isDescriptionHidden, getAjv, and, uiTypeIs, schemaMatches, hasType, schemaSubPathMatches, resolveSchema, showAsRequired, isObjectControl, findUISchema, isOneOfControl, isObjectArray, computeLabel, composePaths, isBooleanControl, optionIs, isDateControl, defaultDateFormat, isDateTimeControl, defaultDateTimeFormat, isEnumControl, isIntegerControl, isTimeControl, isNumberControl, isOneOfEnumControl, isRangeControl, isStringControl, defaultTimeFormat, createId, removeId, update, moveUp, moveDown, computeChildLabel, withIncreasedRank, isVisible, deriveLabelForUISchemaElement, isObjectArrayWithNesting, isNumberFormatControl, categorizationHasCategory } from '@jsonforms/core';
import { withJsonFormsAllOfProps, JsonFormsDispatch, withJsonFormsAnyOfProps, DispatchCell, useJsonForms, withJsonFormsArrayLayoutProps, withTranslateProps, withArrayTranslationProps, withJsonFormsMultiEnumProps, withJsonFormsDetailProps, withJsonFormsOneOfProps, withJsonFormsLabelProps, withJsonFormsMasterListItemProps, withJsonFormsControlProps, Control, withJsonFormsEnumProps, withJsonFormsOneOfEnumProps, withJsonFormsContext, withJsonFormsLayoutProps, withJsonFormsCellProps, withJsonFormsEnumCellProps, withJsonFormsOneOfEnumCellProps } from '@jsonforms/react';
import { Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, Button, Tabs, Tab, TableCell, styled as styled$1, Badge, Tooltip, TableRow, Stack, Grid, Typography, FormHelperText, IconButton, Table, TableHead, TableBody, Autocomplete, TextField, Checkbox, useThemeProps, Input, FilledInput, OutlinedInput, useTheme, InputAdornment, Select, MenuItem, Switch, FormControl, FormLabel, FormGroup, FormControlLabel, Toolbar, ListItemButton, ListItemAvatar, Avatar, ListItemText, ListItemSecondaryAction, List, InputLabel, RadioGroup, Radio, Slider, Accordion, AccordionSummary, AccordionDetails, Card, CardHeader, CardContent, AppBar, Stepper, Step, StepButton } from '@mui/material';
import omit from 'lodash/omit';
import isEmpty from 'lodash/isEmpty';
import union from 'lodash/union';
import startCase from 'lodash/startCase';
import range from 'lodash/range';
import DeleteIcon from '@mui/icons-material/Delete';
import ArrowDownward from '@mui/icons-material/ArrowDownward';
import ArrowUpward from '@mui/icons-material/ArrowUpward';
import { styled } from '@mui/material/styles';
import AddIcon from '@mui/icons-material/Add';
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
import merge from 'lodash/merge';
import dayjs from 'dayjs';
import customParsing from 'dayjs/plugin/customParseFormat';
import debounce from 'lodash/debounce';
import Close from '@mui/icons-material/Close';
import map from 'lodash/map';
import { LocalizationProvider, DatePicker, DateTimePicker, TimePicker } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
const MaterialAllOfRenderer = ({ schema, rootSchema, visible, renderers, cells, path, uischemas, uischema, }) => {
const delegateUISchema = findMatchingUISchema(uischemas)(schema, uischema.scope, path);
if (!visible) {
return null;
}
if (delegateUISchema) {
return (React.createElement(JsonFormsDispatch, { schema: schema, uischema: delegateUISchema, path: path, renderers: renderers, cells: cells }));
}
const allOfRenderInfos = createCombinatorRenderInfos(schema.allOf, rootSchema, 'allOf', uischema, path, uischemas);
return (React.createElement(React.Fragment, null, allOfRenderInfos.map((allOfRenderInfo, allOfIndex) => (React.createElement(JsonFormsDispatch, { key: allOfIndex, schema: allOfRenderInfo.schema, uischema: allOfRenderInfo.uischema, path: path, renderers: renderers, cells: cells })))));
};
const materialAllOfControlTester = rankWith(3, isAllOfControl);
var MaterialAllOfRenderer$1 = withJsonFormsAllOfProps(MaterialAllOfRenderer);
class CombinatorProperties extends React.Component {
render() {
const { schema, combinatorKeyword, path, rootSchema } = this.props;
const otherProps = omit(schema, combinatorKeyword);
const foundUISchema = Generate.uiSchema(otherProps, 'VerticalLayout', undefined, rootSchema);
let isLayoutWithElements = false;
if (foundUISchema !== null && isLayout(foundUISchema)) {
isLayoutWithElements = foundUISchema.elements.length > 0;
}
if (isLayoutWithElements) {
return (React.createElement(JsonFormsDispatch, { schema: otherProps, path: path, uischema: foundUISchema }));
}
return null;
}
}
const TabSwitchConfirmDialog = ({ open, handleClose, confirm, cancel, id, }) => {
return (React.createElement(Dialog, { open: open, onClose: handleClose, "aria-labelledby": 'alert-dialog-title', "aria-describedby": 'alert-dialog-description' },
React.createElement(DialogTitle, { id: 'alert-dialog-title' }, 'Clear form?'),
React.createElement(DialogContent, null,
React.createElement(DialogContentText, { id: 'alert-dialog-description' }, "Your data will be cleared if you navigate away from this tab. Do you want to proceed?")),
React.createElement(DialogActions, null,
React.createElement(Button, { onClick: cancel, color: 'primary' }, "No"),
React.createElement(Button, { onClick: confirm, color: 'primary', autoFocus: true, id: `${id}-confirm-yes` }, "Yes"))));
};
const MaterialAnyOfRenderer = ({ handleChange, schema, rootSchema, indexOfFittingSchema, visible, path, renderers, cells, uischema, uischemas, id, data, }) => {
const [selectedAnyOf, setSelectedAnyOf] = useState(indexOfFittingSchema || 0);
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
const [newSelectedIndex, setNewSelectedIndex] = useState(0);
const handleClose = useCallback(() => setConfirmDialogOpen(false), [setConfirmDialogOpen]);
const handleTabChange = useCallback((_event, newIndex) => {
if (isEmpty(data) ||
typeof data ===
typeof createDefaultValue(anyOfRenderInfos[newIndex].schema, rootSchema)) {
setSelectedAnyOf(newIndex);
}
else {
setNewSelectedIndex(newIndex);
setConfirmDialogOpen(true);
}
}, [setConfirmDialogOpen, setSelectedAnyOf, data]);
const openNewTab = (newIndex) => {
handleChange(path, createDefaultValue(anyOfRenderInfos[newIndex].schema, rootSchema));
setSelectedAnyOf(newIndex);
};
const confirm = useCallback(() => {
openNewTab(newSelectedIndex);
setConfirmDialogOpen(false);
}, [handleChange, createDefaultValue, newSelectedIndex]);
const anyOf = 'anyOf';
const anyOfRenderInfos = createCombinatorRenderInfos(schema.anyOf, rootSchema, anyOf, uischema, path, uischemas);
if (!visible) {
return null;
}
return (React.createElement(React.Fragment, null,
React.createElement(CombinatorProperties, { schema: schema, combinatorKeyword: anyOf, path: path, rootSchema: rootSchema }),
React.createElement(Tabs, { value: selectedAnyOf, onChange: handleTabChange }, anyOfRenderInfos.map((anyOfRenderInfo) => (React.createElement(Tab, { key: anyOfRenderInfo.label, label: anyOfRenderInfo.label })))),
anyOfRenderInfos.map((anyOfRenderInfo, anyOfIndex) => selectedAnyOf === anyOfIndex && (React.createElement(JsonFormsDispatch, { key: anyOfIndex, schema: anyOfRenderInfo.schema, uischema: anyOfRenderInfo.uischema, path: path, renderers: renderers, cells: cells }))),
React.createElement(TabSwitchConfirmDialog, { cancel: handleClose, confirm: confirm, id: 'anyOf-' + id, open: confirmDialogOpen, handleClose: handleClose })));
};
const materialAnyOfControlTester = rankWith(3, isAnyOfControl);
var MaterialAnyOfRenderer$1 = withJsonFormsAnyOfProps(MaterialAnyOfRenderer);
const StyledTableCell = styled(TableCell)({
borderBottom: 'none',
});
const NoBorderTableCell = ({ children, ...otherProps }) => (React.createElement(StyledTableCell, { ...otherProps }, children));
const StyledBadge = styled$1(Badge)(({ theme }) => ({
color: theme.palette.error.main,
}));
const ValidationIcon = ({ errorMessages, id }) => {
return (React.createElement(Tooltip, { id: id, title: errorMessages },
React.createElement(StyledBadge, { badgeContent: errorMessages.split('\n').length },
React.createElement(ErrorOutlineIcon, { color: 'inherit' }))));
};
const fixedCellSmall = {
paddingLeft: 0,
paddingRight: 0,
};
const TableToolbar = React.memo(function TableToolbar({ numColumns, errors, label, description, path, addItem, schema, enabled, translations, rootSchema, disableAdd, }) {
return (React.createElement(TableRow, null,
React.createElement(NoBorderTableCell, { colSpan: numColumns },
React.createElement(Stack, null,
React.createElement(Grid, { container: true, justifyContent: 'flex-start', alignItems: 'center', spacing: 2 },
React.createElement(Grid, { item: true },
React.createElement(Typography, { variant: 'h6' }, label)),
React.createElement(Grid, { item: true }, errors.length !== 0 && (React.createElement(Grid, { item: true },
React.createElement(ValidationIcon, { id: 'tooltip-validation', errorMessages: errors }))))),
description && React.createElement(FormHelperText, null, description))),
enabled && !disableAdd ? (React.createElement(NoBorderTableCell, { align: 'right', style: fixedCellSmall },
React.createElement(Tooltip, { id: 'tooltip-add', title: translations.addTooltip, placement: 'bottom' },
React.createElement(IconButton, { "aria-label": translations.addAriaLabel, onClick: addItem(path, createDefaultValue(schema, rootSchema)), size: 'large' },
React.createElement(AddIcon, null))))) : null));
});
const styles = {
fixedCell: {
width: '150px',
height: '50px',
paddingLeft: 0,
paddingRight: 0,
textAlign: 'center',
},
fixedCellSmall: {
width: '50px',
height: '50px',
paddingLeft: 0,
paddingRight: 0,
textAlign: 'center',
},
};
const generateCells = (Cell, schema, rowPath, enabled, cells) => {
if (schema.type === 'object') {
return getValidColumnProps(schema).map((prop) => {
const cellPath = Paths.compose(rowPath, prop);
const props = {
propName: prop,
schema,
title: schema.properties?.[prop]?.title ?? startCase(prop),
rowPath,
cellPath,
enabled,
cells,
};
return React.createElement(Cell, { key: cellPath, ...props });
});
}
else {
const props = {
schema,
rowPath,
cellPath: rowPath,
enabled,
};
return React.createElement(Cell, { key: rowPath, ...props });
}
};
const getValidColumnProps = (scopedSchema) => {
if (scopedSchema.type === 'object' &&
typeof scopedSchema.properties === 'object') {
return Object.keys(scopedSchema.properties).filter((prop) => scopedSchema.properties[prop].type !== 'array');
}
return [''];
};
const EmptyTable = ({ numColumns, translations }) => (React.createElement(TableRow, null,
React.createElement(NoBorderTableCell, { colSpan: numColumns },
React.createElement(Typography, { align: 'center' }, translations.noDataMessage))));
const TableHeaderCell = React.memo(function TableHeaderCell({ title, }) {
return React.createElement(TableCell, null, title);
});
const ctxToNonEmptyCellProps = (ctx, ownProps) => {
const path = ownProps.rowPath +
(ownProps.schema.type === 'object' ? '.' + ownProps.propName : '');
const errors = formatErrorMessage(union(errorAt(path, ownProps.schema)(ctx.core).map((error) => error.message)));
return {
rowPath: ownProps.rowPath,
propName: ownProps.propName,
schema: ownProps.schema,
rootSchema: ctx.core.schema,
errors,
path,
enabled: ownProps.enabled,
cells: ownProps.cells || ctx.cells,
renderers: ownProps.renderers || ctx.renderers,
};
};
const controlWithoutLabel = (scope) => ({
type: 'Control',
scope: scope,
label: false,
});
const NonEmptyCellComponent = React.memo(function NonEmptyCellComponent({ path, propName, schema, rootSchema, errors, enabled, renderers, cells, isValid, }) {
return (React.createElement(NoBorderTableCell, null,
schema.properties ? (React.createElement(DispatchCell, { schema: Resolve.schema(schema, `#/properties/${encode(propName)}`, rootSchema), uischema: controlWithoutLabel(`#/properties/${encode(propName)}`), path: path, enabled: enabled, renderers: renderers, cells: cells })) : (React.createElement(DispatchCell, { schema: schema, uischema: controlWithoutLabel('#'), path: path, enabled: enabled, renderers: renderers, cells: cells })),
React.createElement(FormHelperText, { error: !isValid }, !isValid && errors)));
});
const NonEmptyCell = (ownProps) => {
const ctx = useJsonForms();
const emptyCellProps = ctxToNonEmptyCellProps(ctx, ownProps);
const isValid = isEmpty(emptyCellProps.errors);
return React.createElement(NonEmptyCellComponent, { ...emptyCellProps, isValid: isValid });
};
const NonEmptyRowComponent = ({ childPath, schema, rowIndex, openDeleteDialog, moveUpCreator, moveDownCreator, enableUp, enableDown, showSortButtons, enabled, cells, path, translations, disableRemove, }) => {
const moveUp = useMemo(() => moveUpCreator(path, rowIndex), [moveUpCreator, path, rowIndex]);
const moveDown = useMemo(() => moveDownCreator(path, rowIndex), [moveDownCreator, path, rowIndex]);
return (React.createElement(TableRow, { key: childPath, hover: true },
generateCells(NonEmptyCell, schema, childPath, enabled, cells),
enabled ? (React.createElement(NoBorderTableCell, { style: showSortButtons ? styles.fixedCell : styles.fixedCellSmall },
React.createElement(Grid, { container: true, direction: 'row', justifyContent: 'flex-end', alignItems: 'center' },
showSortButtons ? (React.createElement(Fragment, null,
React.createElement(Grid, { item: true },
React.createElement(Tooltip, { id: 'tooltip-up', title: translations.up, placement: 'bottom', open: enableUp ? undefined : false },
React.createElement(IconButton, { "aria-label": translations.upAriaLabel, onClick: moveUp, disabled: !enableUp, size: 'large' },
React.createElement(ArrowUpward, null)))),
React.createElement(Grid, { item: true },
React.createElement(Tooltip, { id: 'tooltip-down', title: translations.down, placement: 'bottom', open: enableDown ? undefined : false },
React.createElement(IconButton, { "aria-label": translations.downAriaLabel, onClick: moveDown, disabled: !enableDown, size: 'large' },
React.createElement(ArrowDownward, null)))))) : null,
!disableRemove ? (React.createElement(Grid, { item: true },
React.createElement(Tooltip, { id: 'tooltip-remove', title: translations.removeTooltip, placement: 'bottom' },
React.createElement(IconButton, { "aria-label": translations.removeAriaLabel, onClick: () => openDeleteDialog(childPath, rowIndex), size: 'large' },
React.createElement(DeleteIcon, null))))) : null))) : null));
};
const NonEmptyRow = React.memo(NonEmptyRowComponent);
const TableRows = ({ data, path, schema, openDeleteDialog, moveUp, moveDown, uischema, config, enabled, cells, translations, disableRemove, }) => {
const isEmptyTable = data === 0;
if (isEmptyTable) {
return (React.createElement(EmptyTable, { numColumns: getValidColumnProps(schema).length + 1, translations: translations }));
}
const appliedUiSchemaOptions = merge({}, config, uischema.options);
return (React.createElement(React.Fragment, null, range(data).map((index) => {
const childPath = Paths.compose(path, `${index}`);
return (React.createElement(NonEmptyRow, { key: childPath, childPath: childPath, rowIndex: index, schema: schema, openDeleteDialog: openDeleteDialog, moveUpCreator: moveUp, moveDownCreator: moveDown, enableUp: index !== 0, enableDown: index !== data - 1, showSortButtons: appliedUiSchemaOptions.showSortButtons ||
appliedUiSchemaOptions.showArrayTableSortButtons, enabled: enabled, cells: cells, path: path, translations: translations, disableRemove: disableRemove }));
})));
};
class MaterialTableControl extends React.Component {
constructor() {
super(...arguments);
this.addItem = (path, value) => this.props.addItem(path, value);
}
render() {
const { label, description, path, schema, rootSchema, uischema, errors, openDeleteDialog, visible, enabled, cells, translations, disableAdd, disableRemove, config, } = this.props;
const appliedUiSchemaOptions = merge({}, config, uischema.options);
const doDisableAdd = disableAdd || appliedUiSchemaOptions.disableAdd;
const doDisableRemove = disableRemove || appliedUiSchemaOptions.disableRemove;
const controlElement = uischema;
const isObjectSchema = schema.type === 'object';
const headerCells = isObjectSchema
? generateCells(TableHeaderCell, schema, path, enabled, cells)
: undefined;
if (!visible) {
return null;
}
return (React.createElement(Table, null,
React.createElement(TableHead, null,
React.createElement(TableToolbar, { errors: errors, label: label, description: description, addItem: this.addItem, numColumns: isObjectSchema ? headerCells.length : 1, path: path, uischema: controlElement, schema: schema, rootSchema: rootSchema, enabled: enabled, translations: translations, disableAdd: doDisableAdd }),
isObjectSchema && (React.createElement(TableRow, null,
headerCells,
enabled ? React.createElement(TableCell, null) : null))),
React.createElement(TableBody, null,
React.createElement(TableRows, { openDeleteDialog: openDeleteDialog, translations: translations, ...this.props, disableRemove: doDisableRemove }))));
}
}
const DeleteDialog = React.memo(function DeleteDialog({ open, onClose, onConfirm, onCancel, title, message, acceptText, declineText, }) {
return (React.createElement(Dialog, { open: open, keepMounted: true, onClose: onClose, "aria-labelledby": 'alert-dialog-confirmdelete-title', "aria-describedby": 'alert-dialog-confirmdelete-description' },
React.createElement(DialogTitle, { id: 'alert-dialog-confirmdelete-title' }, title),
React.createElement(DialogContent, null,
React.createElement(DialogContentText, { id: 'alert-dialog-confirmdelete-description' }, message)),
React.createElement(DialogActions, null,
React.createElement(Button, { onClick: onCancel, color: 'primary' }, declineText),
React.createElement(Button, { onClick: onConfirm, color: 'primary' }, acceptText))));
});
const MaterialArrayControlRenderer = (props) => {
const [open, setOpen] = useState(false);
const [path, setPath] = useState(undefined);
const [rowData, setRowData] = useState(undefined);
const { removeItems, visible, translations } = props;
const openDeleteDialog = useCallback((p, rowIndex) => {
setOpen(true);
setPath(p);
setRowData(rowIndex);
}, [setOpen, setPath, setRowData]);
const deleteCancel = useCallback(() => setOpen(false), [setOpen]);
const deleteConfirm = useCallback(() => {
const p = path.substring(0, path.lastIndexOf('.'));
removeItems(p, [rowData])();
setOpen(false);
}, [setOpen, path, rowData]);
const deleteClose = useCallback(() => setOpen(false), [setOpen]);
if (!visible) {
return null;
}
return (React.createElement(React.Fragment, null,
React.createElement(MaterialTableControl, { ...props, openDeleteDialog: openDeleteDialog, translations: translations }),
React.createElement(DeleteDialog, { open: open, onCancel: deleteCancel, onConfirm: deleteConfirm, onClose: deleteClose, acceptText: translations.deleteDialogAccept, declineText: translations.deleteDialogDecline, title: translations.deleteDialogTitle, message: translations.deleteDialogMessage })));
};
const materialArrayControlTester = rankWith(3, or(isObjectArrayControl, isPrimitiveArrayControl));
var MaterialArrayControlRenderer$1 = withJsonFormsArrayLayoutProps(withTranslateProps(withArrayTranslationProps(MaterialArrayControlRenderer)));
const useFocus = () => {
const [focused, setFocused] = useState(false);
const onFocus = useCallback(() => setFocused(true), []);
const onBlur = useCallback(() => setFocused(false), []);
return [focused, onFocus, onBlur];
};
const MuiAutocomplete = (props) => {
const { description, errors, visible, required, label, data, className, id, enabled, uischema, path, handleChange, options, config, getOptionLabel, renderOption, filterOptions, isValid, } = props;
const appliedUiSchemaOptions = merge({}, config, uischema.options);
const [inputValue, setInputValue] = React.useState(data ?? '');
const [focused, onFocus, onBlur] = useFocus();
const findOption = options.find((o) => o.value === data) ?? null;
const showDescription = !isDescriptionHidden(visible, description, focused, appliedUiSchemaOptions.showUnfocusedDescription);
const firstFormHelperText = showDescription
? description
: !isValid
? errors
: null;
const secondFormHelperText = showDescription && !isValid ? errors : null;
if (!visible) {
return null;
}
return (React.createElement(React.Fragment, null,
React.createElement(Autocomplete, { className: className, id: id, disabled: !enabled, value: findOption, onChange: (_event, newValue) => {
handleChange(path, newValue?.value);
}, inputValue: inputValue, onInputChange: (_event, newInputValue) => {
setInputValue(newInputValue);
}, autoHighlight: true, autoComplete: true, fullWidth: true, options: options, getOptionLabel: getOptionLabel || ((option) => option?.label), freeSolo: false, renderInput: (params) => {
return (React.createElement(TextField, { label: label, type: 'text', inputProps: params.inputProps, inputRef: params.InputProps.ref, autoFocus: appliedUiSchemaOptions.focus, disabled: !enabled, ...params, id: id, required: required && !appliedUiSchemaOptions.hideRequiredAsterisk, error: !isValid, fullWidth: !appliedUiSchemaOptions.trim, InputLabelProps: data ? { shrink: true } : undefined, onFocus: onFocus, onBlur: onBlur, focused: focused }));
}, renderOption: renderOption, filterOptions: filterOptions }),
React.createElement(FormHelperText, { error: !isValid && !showDescription }, firstFormHelperText),
React.createElement(FormHelperText, { error: !isValid }, secondFormHelperText)));
};
const MuiCheckbox = React.memo(function MuiCheckbox(props) {
const { data, className, id, enabled, uischema, path, handleChange, config, inputProps, } = props;
const appliedUiSchemaOptions = merge({}, config, uischema.options);
const inputPropsMerged = merge({}, inputProps, {
autoFocus: !!appliedUiSchemaOptions.focus,
});
const checked = !!data;
return (React.createElement(Checkbox, { checked: checked, onChange: (_ev, isChecked) => handleChange(path, isChecked), className: className, id: id, disabled: !enabled, inputProps: inputPropsMerged }));
});
dayjs.extend(customParsing);
const createOnChangeHandler = (path, handleChange, saveFormat) => (value) => {
if (!value) {
handleChange(path, undefined);
}
else if (value.toString() !== 'Invalid Date') {
const formatedDate = formatDate(value, saveFormat);
handleChange(path, formatedDate);
}
};
const createOnBlurHandler = (path, handleChange, format, saveFormat, rerenderChild, onBlur) => (e) => {
const date = dayjs(e.target.value, format);
const formatedDate = formatDate(date, saveFormat);
if (formatedDate.toString() === 'Invalid Date') {
handleChange(path, undefined);
rerenderChild();
}
else {
handleChange(path, formatedDate);
}
onBlur();
};
const formatDate = (date, saveFormat) => {
let formatedDate = date.format(saveFormat);
const indexOfYear = saveFormat.indexOf('YYYY');
if (date.year() < 1000 && indexOfYear !== -1) {
const stringUpToYear = formatedDate.slice(0, indexOfYear);
const stringFromYear = formatedDate.slice(indexOfYear);
if (date.year() >= 100) {
formatedDate = [stringUpToYear, 0, stringFromYear].join('');
}
else if (date.year() >= 10) {
formatedDate = [stringUpToYear, 0, 0, stringFromYear].join('');
}
else if (date.year() >= 1) {
formatedDate = [stringUpToYear, 0, 0, 0, stringFromYear].join('');
}
}
return formatedDate;
};
const getData = (data, saveFormat) => {
if (!data) {
return null;
}
const dayjsData = dayjs(data, saveFormat);
if (dayjsData.toString() === 'Invalid Date') {
return null;
}
return dayjsData;
};
const renderLayoutElements = (elements, schema, path, enabled, renderers, cells) => {
return elements.map((child, index) => (React.createElement(Grid, { item: true, key: `${path}-${index}`, xs: true },
React.createElement(JsonFormsDispatch, { uischema: child, schema: schema, path: path, enabled: enabled, renderers: renderers, cells: cells }))));
};
const MaterialLayoutRendererComponent = ({ visible, elements, schema, path, enabled, direction, renderers, cells, }) => {
if (isEmpty(elements) || !visible) {
return null;
}
else {
return (React.createElement(Grid, { container: true, direction: direction, spacing: direction === 'row' ? 2 : 0 }, renderLayoutElements(elements, schema, path, enabled, renderers, cells)));
}
};
const MaterialLayoutRenderer = React.memo(MaterialLayoutRendererComponent);
const withAjvProps = (Component) => function WithAjvProps(props) {
const ctx = useJsonForms();
const ajv = getAjv({ jsonforms: { ...ctx } });
return React.createElement(Component, { ...props, ajv: ajv });
};
const variantToInput = {
standard: Input,
filled: FilledInput,
outlined: OutlinedInput,
};
const defaultInputVariant = 'outlined';
function useInputVariant() {
const { variant = defaultInputVariant } = useThemeProps({
props: {},
name: 'MuiTextField',
});
return variant;
}
function useInputComponent() {
const variant = useInputVariant();
return variantToInput[variant] ?? variantToInput[defaultInputVariant];
}
const eventToValue$3 = (ev) => ev.target.value;
const useDebouncedChange = (handleChange, defaultValue, data, path, eventToValueFunction = eventToValue$3, timeout = 300, flushOnBlur = false, focused = false) => {
const [input, setInput] = useState(data ?? defaultValue);
useEffect(() => {
setInput(data ?? defaultValue);
}, [data]);
const debouncedUpdate = useCallback(debounce((newValue) => handleChange(path, newValue), timeout), [handleChange, path, timeout]);
useEffect(() => {
if (!focused && flushOnBlur) {
debouncedUpdate.flush();
}
}, [focused, flushOnBlur, debouncedUpdate]);
const onChange = useCallback((ev) => {
const newValue = eventToValueFunction(ev);
setInput(newValue ?? defaultValue);
debouncedUpdate(newValue);
}, [debouncedUpdate, eventToValueFunction]);
const onClear = useCallback(() => {
setInput(defaultValue);
handleChange(path, undefined);
}, [defaultValue, handleChange, path]);
return [input, onChange, onClear];
};
const i18nDefaults = {
'enum.none': 'None',
};
const toNumber$1 = (value) => value === '' ? undefined : parseInt(value, 10);
const eventToValue$2 = (ev) => toNumber$1(ev.target.value);
const MuiInputInteger = React.memo(function MuiInputInteger(props) {
const [focused, onFocus, onBlur] = useFocus();
const { data, className, id, enabled, uischema, isValid, path, handleChange, config, label, } = props;
const InputComponent = useInputComponent();
const inputProps = { step: '1' };
const appliedUiSchemaOptions = merge({}, config, uischema.options);
const [inputValue, onChange] = useDebouncedChange(handleChange, '', data, path, eventToValue$2, undefined, true, focused);
return (React.createElement(InputComponent, { label: label, type: 'number', value: inputValue, onFocus: onFocus, onBlur: onBlur, onChange: onChange, className: className, id: id, disabled: !enabled, autoFocus: appliedUiSchemaOptions.focus, inputProps: inputProps, fullWidth: true, error: !isValid }));
});
const toNumber = (value) => value === '' ? undefined : parseFloat(value);
const eventToValue$1 = (ev) => toNumber(ev.target.value);
const MuiInputNumber = React.memo(function MuiInputNumber(props) {
const [focused, onFocus, onBlur] = useFocus();
const { data, className, id, enabled, uischema, isValid, path, handleChange, config, label, } = props;
const InputComponent = useInputComponent();
const inputProps = { step: '0.1' };
const appliedUiSchemaOptions = merge({}, config, uischema.options);
const [inputValue, onChange] = useDebouncedChange(handleChange, '', data, path, eventToValue$1, undefined, true, focused);
return (React.createElement(InputComponent, { type: 'number', label: label, value: inputValue, onChange: onChange, onFocus: onFocus, onBlur: onBlur, className: className, id: id, disabled: !enabled, autoFocus: appliedUiSchemaOptions.focus, inputProps: inputProps, fullWidth: true, error: !isValid }));
});
const MuiInputNumberFormat = React.memo(function MuiInputNumberFormat(props) {
const [focused, onFocus, onBlur] = useFocus();
const { className, id, enabled, uischema, isValid, path, handleChange, schema, config, label, } = props;
const InputComponent = useInputComponent();
const maxLength = schema.maxLength;
const appliedUiSchemaOptions = merge({}, config, uischema.options);
let inputProps;
if (appliedUiSchemaOptions.restrict) {
inputProps = { maxLength: maxLength };
}
else {
inputProps = {};
}
const formattedNumber = props.toFormatted(props.data);
const validStringNumber = useCallback((ev) => props.fromFormatted(ev.currentTarget.value), [props.fromFormatted]);
const [inputValue, onChange] = useDebouncedChange(handleChange, '', formattedNumber, path, validStringNumber, undefined, true, focused);
return (React.createElement(InputComponent, { type: 'text', value: inputValue, onChange: onChange, onFocus: onFocus, onBlur: onBlur, className: className, id: id, label: label, disabled: !enabled, autoFocus: appliedUiSchemaOptions.focus, multiline: appliedUiSchemaOptions.multi, fullWidth: !appliedUiSchemaOptions.trim || maxLength === undefined, inputProps: inputProps, error: !isValid }));
});
const eventToValue = (ev) => ev.target.value === '' ? undefined : ev.target.value;
const MuiInputText = React.memo(function MuiInputText(props) {
const [focused, onFocus, onBlur] = useFocus();
const [showAdornment, setShowAdornment] = useState(false);
const { data, config, className, id, enabled, uischema, isValid, path, handleChange, schema, muiInputProps, label, inputComponent, } = props;
const InputComponent = useInputComponent();
const maxLength = schema.maxLength;
const appliedUiSchemaOptions = merge({}, config, uischema.options);
let inputProps;
if (appliedUiSchemaOptions.restrict) {
inputProps = { maxLength: maxLength };
}
else {
inputProps = {};
}
inputProps = merge(inputProps, muiInputProps);
if (appliedUiSchemaOptions.trim && maxLength !== undefined) {
inputProps.size = maxLength;
}
const [inputText, onChange, onClear] = useDebouncedChange(handleChange, '', data, path, eventToValue, undefined, true, focused);
const onPointerEnter = () => setShowAdornment(true);
const onPointerLeave = () => setShowAdornment(false);
const theme = useTheme();
const closeStyle = {
background: theme.jsonforms?.input?.delete?.background ||
theme.palette.background.default,
borderRadius: '50%',
};
return (React.createElement(InputComponent, { label: label, type: appliedUiSchemaOptions.format === 'password' ? 'password' : 'text', value: inputText, onChange: onChange, className: className, onBlur: onBlur, onFocus: onFocus, id: id, disabled: !enabled, autoFocus: appliedUiSchemaOptions.focus, multiline: appliedUiSchemaOptions.multi, fullWidth: !appliedUiSchemaOptions.trim || maxLength === undefined, inputProps: inputProps, error: !isValid, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, endAdornment: React.createElement(InputAdornment, { position: 'end', style: {
display: !showAdornment || !enabled || data === undefined
? 'none'
: 'flex',
position: 'absolute',
right: 0,
} },
React.createElement(IconButton, { "aria-label": 'Clear input field', onClick: onClear, size: 'large' },
React.createElement(Close, { style: closeStyle }))), inputComponent: inputComponent }));
});
const MuiInputTime = React.memo(function MuiInputTime(props) {
const { data, className, id, enabled, uischema, isValid, path, handleChange, config, label, } = props;
const InputComponent = useInputComponent();
const appliedUiSchemaOptions = merge({}, config, uischema.options);
const [inputValue, onChange] = useDebouncedChange(handleChange, '', data, path);
return (React.createElement(InputComponent, { type: 'time', value: inputValue, onChange: onChange, className: className, id: id, label: label, disabled: !enabled, autoFocus: appliedUiSchemaOptions.focus, fullWidth: true, error: !isValid }));
});
const MuiSelect = React.memo(function MuiSelect(props) {
const { data, className, id, enabled, schema, uischema, isValid, path, handleChange, options, config, label, t, multiple, } = props;
const appliedUiSchemaOptions = merge({}, config, uischema.options);
const noneOptionLabel = useMemo(() => t('enum.none', i18nDefaults['enum.none'], { schema, uischema, path }), [t, schema, uischema, path]);
return (React.createElement(Select, { className: className, id: id, label: label, disabled: !enabled, autoFocus: appliedUiSchemaOptions.focus, value: data !== undefined ? data : '', onChange: (ev) => handleChange(path, ev.target.value || undefined), fullWidth: true, multiple: multiple || false, error: !isValid }, [
React.createElement(MenuItem, { value: '', key: 'jsonforms.enum.none' },
React.createElement("em", null, noneOptionLabel)),
].concat(options.map((optionValue) => (React.createElement(MenuItem, { value: optionValue.value, key: optionValue.value }, optionValue.label))))));
});
const MuiToggle = React.memo(function MuiToggle(props) {
const { data, className, id, enabled, uischema, path, handleChange, config, inputProps, } = props;
const appliedUiSchemaOptions = merge({}, config, uischema.options);
const inputPropsMerged = merge({}, inputProps, {
autoFocus: !!appliedUiSchemaOptions.focus,
});
const checked = !!data;
return (React.createElement(Switch, { checked: checked, onChange: (_ev, isChecked) => handleChange(path, isChecked), className: className, id: id, disabled: !enabled, inputProps: inputPropsMerged }));
});
const MaterialEnumArrayRenderer = ({ config, id, schema, visible, errors, description, label, required, path, options, data, addItem, removeItem, handleChange: _handleChange, ...otherProps }) => {
const [focused, onFocus, onBlur] = useFocus();
const isValid = errors.length === 0;
const appliedUiSchemaOptions = merge({}, config, otherProps.uischema.options);
const showDescription = !isDescriptionHidden(visible, description, focused, appliedUiSchemaOptions.showUnfocusedDescription);
if (!visible) {
return null;
}
return (React.createElement(FormControl, { component: 'fieldset', fullWidth: !appliedUiSchemaOptions.trim, onFocus: onFocus, onBlur: onBlur },
React.createElement(FormLabel, { error: !isValid, component: 'legend', required: showAsRequired(required, appliedUiSchemaOptions.hideRequiredAsterisk) }, label),
React.createElement(FormGroup, { row: true }, options.map((option, index) => {
const optionPath = Paths.compose(path, `${index}`);
const checkboxValue = data?.includes(option.value)
? option.value
: undefined;
return (React.createElement(FormControlLabel, { id: id + '-label-' + option.value, key: option.value, control: React.createElement(MuiCheckbox, { id: id + '-' + option.value, key: 'checkbox-' + option.value, isValid: isEmpty(errors), path: optionPath, handleChange: (_childPath, newValue) => newValue
? addItem(path, option.value)
: removeItem(path, option.value), data: checkboxValue, errors: errors, schema: schema, visible: visible, ...otherProps }), label: option.label }));
})),
React.createElement(FormHelperText, { error: !isValid }, !isValid ? errors : showDescription ? description : null)));
};
const hasOneOfItems = (schema) => schema.oneOf !== undefined &&
schema.oneOf.length > 0 &&
schema.oneOf.every((entry) => {
return entry.const !== undefined;
});
const hasEnumItems = (schema) => schema.type === 'string' && schema.enum !== undefined;
const materialEnumArrayRendererTester = rankWith(5, and(uiTypeIs('Control'), and(schemaMatches((schema) => hasType(schema, 'array') &&
!Array.isArray(schema.items) &&
schema.uniqueItems === true), schemaSubPathMatches('items', (schema, rootSchema) => {
const resolvedSchema = schema.$ref
? resolveSchema(rootSchema, schema.$ref, rootSchema)
: schema;
return hasOneOfItems(resolvedSchema) || hasEnumItems(resolvedSchema);
}))));
var MaterialEnumArrayRenderer$1 = withJsonFormsMultiEnumProps(MaterialEnumArrayRenderer);
const MaterialObjectRenderer = ({ renderers, cells, uischemas, schema, label, path, visible, enabled, uischema, rootSchema, }) => {
const detailUiSchema = useMemo(() => findUISchema(uischemas, schema, uischema.scope, path, () => isEmpty(path)
? Generate.uiSchema(schema, 'VerticalLayout', undefined, rootSchema)
: {
...Generate.uiSchema(schema, 'Group', undefined, rootSchema),
label,
}, uischema, rootSchema), [uischemas, schema, uischema.scope, path, label, uischema, rootSchema]);
if (!visible) {
return null;
}
return (React.createElement(JsonFormsDispatch, { visible: visible, enabled: enabled, schema: schema, uischema: detailUiSchema, path: path, renderers: renderers, cells: cells }));
};
const materialObjectControlTester = rankWith(2, isObjectControl);
var MaterialObjectRenderer$1 = withJsonFormsDetailProps(MaterialObjectRenderer);
const MaterialOneOfRenderer = ({ handleChange, schema, path, renderers, cells, rootSchema, id, visible, indexOfFittingSchema, uischema, uischemas, data, }) => {
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(indexOfFittingSchema || 0);
const [newSelectedIndex, setNewSelectedIndex] = useState(0);
const handleClose = useCallback(() => setConfirmDialogOpen(false), [setConfirmDialogOpen]);
const cancel = useCallback(() => {
setConfirmDialogOpen(false);
}, [setConfirmDialogOpen]);
const oneOfRenderInfos = useMemo(() => createCombinatorRenderInfos(schema.oneOf, rootSchema, 'oneOf', uischema, path, uischemas), [schema, rootSchema, uischema, path, uischemas]);
const openNewTab = (newIndex) => {
handleChange(path, createDefaultValue(oneOfRenderInfos[newIndex].schema, rootSchema));
setSelectedIndex(newIndex);
};
const confirm = useCallback(() => {
openNewTab(newSelectedIndex);
setConfirmDialogOpen(false);
}, [handleChange, createDefaultValue, newSelectedIndex]);
const handleTabChange = useCallback((_event, newOneOfIndex) => {
setNewSelectedIndex(newOneOfIndex);
if (isEmpty(data)) {
openNewTab(newOneOfIndex);
}
else {
setConfirmDialogOpen(true);
}
}, [setConfirmDialogOpen, setSelectedIndex, data]);
if (!visible) {
return null;
}
return (React.createElement(React.Fragment, null,
React.createElement(CombinatorProperties, { schema: schema, combinatorKeyword: 'oneOf', path: path, rootSchema: rootSchema }),
React.createElement(Tabs, { value: selectedIndex, onChange: handleTabChange }, oneOfRenderInfos.map((oneOfRenderInfo) => (React.createElement(Tab, { key: oneOfRenderInfo.label, label: oneOfRenderInfo.label })))),
oneOfRenderInfos.map((oneOfRenderInfo, oneOfIndex) => selectedIndex === oneOfIndex && (React.createElement(JsonFormsDispatch, { key: oneOfIndex, schema: oneOfRenderInfo.schema, uischema: oneOfRenderInfo.uischema, path: path, renderers: renderers, cells: cells }))),
React.createElement(TabSwitchConfirmDialog, { cancel: cancel, confirm: confirm, id: 'oneOf-' + id, open: confirmDialogOpen, handleClose: handleClose })));
};
const materialOneOfControlTester = rankWith(3, isOneOfControl);
var MaterialOneOfRenderer$1 = withJsonFormsOneOfProps(MaterialOneOfRenderer);
const materialLabelRendererTester = rankWith(1, uiTypeIs('Label'));
const MaterialLabelRenderer = ({ text, visible }) => {
if (!visible) {
return null;
}
return React.createElement(Typography, { variant: 'h6' }, text);
};
var MaterialLabelRenderer$1 = withJsonFormsLabelProps(MaterialLabelRenderer);
const ArrayLayoutToolbar = React.memo(function ArrayLayoutToolbar({ label, description, errors, addItem, path, enabled, createDefault, translations, disableAdd, }) {
return (React.createElement(Toolbar, { disableGutters: true },
React.createElement(Stack, null,
React.createElement(Grid, { container: true, alignItems: 'center', justifyContent: 'space-between' },
React.createElement(Grid, { item: true },
React.createElement(Grid, { container: true, justifyContent: 'flex-start', alignItems: 'center', spacing: 2 },
React.createElement(Grid, { item: true },
React.createElement(Typography, { variant: 'h6' }, label)),
React.createElement(Grid, { item: true }, errors.length !== 0 && (React.createElement(Grid, { item: true },
React.createElement(ValidationIcon, { id: 'tooltip-validation', errorMessages: errors })))))),
enabled && !disableAdd && (React.createElement(Grid, { item: true },
React.createElement(Grid, { container: true },
React.createElement(Grid, { item: true },
React.createElement(Tooltip, { id: 'tooltip-add', title: translations.addTooltip, placement: 'bottom' },
React.createElement(IconButton, { "aria-label": translations.addTooltip, onClick: addItem(path, createDefault()), size: 'large' },
React.createElement(AddIcon, null)))))))),
description && React.createElement(FormHelperText, null, description))));
});
const ListWithDetailMasterItem = ({ index, childLabel, selected, enabled, handleSelect, removeItem, path, translations, disableRemove, }) => {
return (React.createElement(ListItemButton, { selected: selected, onClick: handleSelect(index) },
React.createElement(ListItemAvatar, null,
React.createElement(Avatar, { "aria-label": 'Index' }, index + 1)),
React.createElement(ListItemText, { primary: childLabel }),
enabled && !disableRemove && (React.createElement(ListItemSecondaryAction, null,
React.createElement(Tooltip, { id: 'tooltip-remove', title: translations.removeTooltip, placement: 'bottom' },
React.createElement(IconButton, { "aria-label": translations.removeAriaLabel, onClick: removeItem(path, index), size: 'large' },
React.createElement(DeleteIcon, null)))))));
};
var ListWithDetailMasterItem$1 = withJsonFormsMasterListItemProps(ListWithDetailMasterItem);
const MaterialListWithDetailRenderer = ({ uischemas, schema, uischema, path, enabled, errors, visible, label, required, removeItems, addItem, data, renderers, cells, config, rootSchema, description, disableAdd, disableRemove, translations, }) => {
const [selectedIndex, setSelectedIndex] = useState(undefined);
const handleRemoveItem = useCallback((p, value) => () => {
removeItems(p, [value])();
if (selectedIndex === value) {
setSelectedIndex(undefined);
}
else if (selectedIndex > value) {
setSelectedIndex(selectedIndex - 1);
}
}, [removeItems, setSelectedIndex]);
const handleListItemClick = useCallback((index) => () => setSelectedIndex(index), [setSelectedIndex]);
const handleCreateDefaultValue = useCallback(() => createDefaultValue(schema, rootSchema), [createDefaultValue]);
const foundUISchema = useMemo(() => findUISchema(uischemas, schema, uischema.scope, path, undefined, uischema, rootSchema), [uischemas, schema, uischema.scope, path, uischema, rootSchema]);
const appliedUiSchemaOptions = merge({}, config, uischema.options);
const doDisableAdd = disableAdd || appliedUiSchemaOptions.disableAdd;
const doDisableRemove = disableRemove || appliedUiSchemaOptions.disableRemove;
React.useEffect(() => {
setSelectedIndex(undefined);
}, [schema]);
if (!visible) {
return null;
}
return (React.createElement(React.Fragment, null,
React.createElement(ArrayLayoutToolbar, { translations: translations, label: computeLabel(label, required, appliedUiSchemaOptions.hideRequiredAsterisk), description: description, errors: errors, path: path, enabled: enabled, addItem: addItem, createDefault: handleCreateDefaultValue, disableAdd: doDisableAdd }),
React.createElement(Grid, { container: true, direction: 'row', spacing: 2 },
React.createElement(Grid, { item: true, xs: 3 },
React.createElement(List, null, data > 0 ? (map(range(data), (index) => (React.createElement(ListWithDetailMasterItem$1, { index: index, path: path, schema: schema, enabled: enabled, handleSelect: handleListItemClick, removeItem: handleRemoveItem, selected: selectedIndex === index, key: index, uischema: foundUISchema, childLabelProp: appliedUiSchemaOptions.elementLabelProp, translations: translations, disableRemove: doDisableRemove })))) : (React.createElement("p", null, translations.noDataMessage)))),
React.createElement(Grid, { item: true, xs: true }, selectedIndex !== undefined ? (React.createElement(JsonFormsDispatch, { renderers: renderers, cells: cells, visible: visible, schema: schema, uischema: foundUISchema, path: composePaths(path, `${selectedIndex}`) })) : (React.createElement(Typography, { variant: 'h6' }, translations.noSelection))))));
};
const materialListWithDetailTester = rankWith(4, and(uiTypeIs('ListWithDetail'), isObjectArray));
var MaterialListWithDetailRenderer$1 = withJsonFormsArrayLayoutProps(withTranslateProps(withArrayTranslationProps(MaterialListWithDetailRenderer)));
const MaterialInputControl = (props) => {
const [focused, onFocus, onBlur] = useFocus();
const { id, description, errors, label, uischema, visible, required, config, input, } = props;
const variant = useInputVariant();
const isValid = errors.length === 0;
const appliedUiSchemaOptions = merge({}, config, uischema.options);
const showDescription = !isDescriptionHidden(visible, description, focused, appliedUiSchemaOptions.showUnfocusedDescription);
const firstFormHelperText = showDescription
? description
: !isValid
? errors
: null;
const secondFormHelperText = showDescription && !isValid ? errors : null;
const InnerComponent = input;
if (!visible) {
return null;
}
return (React.createElement(FormControl, { fullWidth: !appliedUiSchemaOptions.trim, onFocus: onFocus, onBlur: onBlur, variant: variant, id: id },
React.createElement(InputLabel, { htmlFor: id + '-input', error: !isValid, required: showAsRequired(required, appliedUiSchemaOptions.hideRequiredAsterisk) }, label),
React.createElement(InnerComponent, { ...props, id: id + '-input', isValid: isValid, visible: visible }),
React.createElement(FormHelperText, { error: !isValid && !showDescription }, firstFormHelp