@@ -460,6 +497,9 @@ DataGridView.propTypes = {
canAdd: PropTypes.bool,
canDelete: PropTypes.bool,
visible: PropTypes.bool,
+ canAddRow: PropTypes.oneOfType([
+ PropTypes.bool, PropTypes.func,
+ ]),
canEditRow: PropTypes.oneOfType([
PropTypes.bool, PropTypes.func,
]),
diff --git a/web/pgadmin/static/js/SchemaView/FieldSetView.jsx b/web/pgadmin/static/js/SchemaView/FieldSetView.jsx
index 5953c397d..7596ec86b 100644
--- a/web/pgadmin/static/js/SchemaView/FieldSetView.jsx
+++ b/web/pgadmin/static/js/SchemaView/FieldSetView.jsx
@@ -25,7 +25,7 @@ export default function FieldSetView({
useEffect(()=>{
/* Calculate the fields which depends on the current field */
- if(!isDataGridForm) {
+ if(!isDataGridForm && depListener) {
schema.fields.forEach((field)=>{
/* Self change is also dep change */
if(field.depChange || field.deferredDepChange) {
diff --git a/web/pgadmin/static/js/SchemaView/FormView.jsx b/web/pgadmin/static/js/SchemaView/FormView.jsx
index b3d51ef08..c85eb4742 100644
--- a/web/pgadmin/static/js/SchemaView/FormView.jsx
+++ b/web/pgadmin/static/js/SchemaView/FormView.jsx
@@ -122,7 +122,7 @@ export function getFieldMetaData(field, schema, value, viewHelperProps) {
/* The first component of schema view form */
export default function FormView({
value, formErr, schema={}, viewHelperProps, isNested=false, accessPath, dataDispatch, hasSQLTab,
- getSQLValue, onTabChange, firstEleRef, className, isDataGridForm=false, visible}) {
+ getSQLValue, onTabChange, firstEleRef, className, isDataGridForm=false, isTabView=true, visible}) {
let defaultTab = 'General';
let tabs = {};
let tabsClassname = {};
@@ -164,6 +164,10 @@ export default function FormView({
}
});
});
+ return ()=>{
+ /* Cleanup the listeners when unmounting */
+ depListener.removeDepListener(accessPath);
+ };
}
}, []);
@@ -175,7 +179,7 @@ export default function FormView({
getFieldMetaData(field, schema, value, viewHelperProps);
if(modeSupported) {
- let {group} = field;
+ let {group, CustomControl} = field;
group = groupLabels[group] || group || defaultTab;
if(!tabs[group]) tabs[group] = [];
@@ -190,7 +194,7 @@ export default function FormView({
}
tabs[group].push(
);
} else if(field.type === 'nested-fieldset') {
@@ -223,11 +227,18 @@ export default function FormView({
canDelete = false;
}
- tabs[group].push(
-
- );
+ const props = {
+ key: field.id, value: value[field.id], viewHelperProps: viewHelperProps, formErr: formErr,
+ schema: field.schema, accessPath: accessPath.concat(field.id), dataDispatch: dataDispatch,
+ containerClassName: classes.controlRow, ...field, canAdd: canAdd, canEdit: canEdit, canDelete: canDelete,
+ visible: visible,
+ };
+
+ if(CustomControl) {
+ tabs[group].push();
+ } else {
+ tabs[group].push();
+ }
} else if(field.type === 'group') {
groupLabels[field.id] = field.label;
if(!visible) {
@@ -311,35 +322,48 @@ export default function FormView({
return <>>;
}
- return (
- <>
-
-
- {
- setTabValue(selTabValue);
- }}
- // indicatorColor="primary"
- variant="scrollable"
- scrollButtons="auto"
- action={(ref)=>ref && ref.updateIndicator()}
- >
- {Object.keys(tabs).map((tabName)=>{
- return ;
- })}
-
+ if(isTabView) {
+ return (
+ <>
+
+
+ {
+ setTabValue(selTabValue);
+ }}
+ // indicatorColor="primary"
+ variant="scrollable"
+ scrollButtons="auto"
+ action={(ref)=>ref && ref.updateIndicator()}
+ >
+ {Object.keys(tabs).map((tabName)=>{
+ return ;
+ })}
+
+
+ {Object.keys(tabs).map((tabName, i)=>{
+ return (
+
+ {tabs[tabName]}
+
+ );
+ })}
- {Object.keys(tabs).map((tabName, i)=>{
- return (
-
- {tabs[tabName]}
-
- );
- })}
-
- >);
+ >);
+ } else {
+ return (
+ <>
+
+ {Object.keys(tabs).map((tabName)=>{
+ return (
+ <>{tabs[tabName]}>
+ );
+ })}
+
+ >);
+ }
}
FormView.propTypes = {
diff --git a/web/pgadmin/static/js/SchemaView/base_schema.ui.js b/web/pgadmin/static/js/SchemaView/base_schema.ui.js
index 4521b6329..ebff0853e 100644
--- a/web/pgadmin/static/js/SchemaView/base_schema.ui.js
+++ b/web/pgadmin/static/js/SchemaView/base_schema.ui.js
@@ -126,4 +126,9 @@ export default class BaseUISchema {
});
return newRow;
}
+
+ /* Used in header schema */
+ addDisabled() {
+ return false;
+ }
}
diff --git a/web/pgadmin/static/js/SchemaView/index.jsx b/web/pgadmin/static/js/SchemaView/index.jsx
index f125998b1..8887d93c0 100644
--- a/web/pgadmin/static/js/SchemaView/index.jsx
+++ b/web/pgadmin/static/js/SchemaView/index.jsx
@@ -28,7 +28,7 @@ import { minMaxValidator, numberValidator, integerValidator, emptyValidator, che
import { MappedFormControl } from './MappedControl';
import gettext from 'sources/gettext';
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
-import FormView from './FormView';
+import FormView, { getFieldMetaData } from './FormView';
import { pgAlertify } from '../helpers/legacyConnector';
import { evalFunc } from 'sources/utils';
import PropTypes from 'prop-types';
@@ -113,9 +113,9 @@ const diffArrayOptions = {
compareFunction: objectComparator,
};
-function getChangedData(topSchema, mode, sessData, stringify=false) {
+function getChangedData(topSchema, viewHelperProps, sessData, stringify=false) {
let changedData = {};
- let isEdit = mode === 'edit';
+ let isEdit = viewHelperProps.mode === 'edit';
/* The comparator and setter */
const attrChanged = (currPath, change, force=false)=>{
@@ -136,6 +136,10 @@ function getChangedData(topSchema, mode, sessData, stringify=false) {
/* Will be called recursively as data can be nested */
const parseChanges = (schema, accessPath, changedData)=>{
schema.fields.forEach((field)=>{
+ let {modeSupported} = getFieldMetaData(field, schema, {}, viewHelperProps);
+ if(!modeSupported) {
+ return;
+ }
if(typeof(field.type) == 'string' && field.type.startsWith('nested-')) {
/* its nested */
parseChanges(field.schema, accessPath, changedData);
@@ -324,7 +328,6 @@ const sessDataReducer = (state, action)=>{
/* If there is any dep listeners get the changes */
data = getDepChange(action.path, data, state, action);
deferredList = getDeferredDepChange(action.path, data, state, action);
- // let deferredInfo = getDeferredDepChange(action.path, data, state, action);
data.__deferred__ = deferredList || [];
break;
case SCHEMA_STATE_ACTIONS.ADD_ROW:
@@ -387,7 +390,7 @@ function prepareData(val) {
/* If its the dialog */
function SchemaDialogView({
- getInitData, viewHelperProps, schema={}, ...props}) {
+ getInitData, viewHelperProps, schema={}, showFooter=true, isTabView=true, ...props}) {
const classes = useDialogStyles();
/* Some useful states */
const [dirty, setDirty] = useState(false);
@@ -421,11 +424,12 @@ function SchemaDialogView({
if(!isNotValid) setFormErr({});
/* check if anything changed */
- let dataChanged = Object.keys(getChangedData(schema, viewHelperProps.mode, sessData)).length > 0;
- setDirty(dataChanged);
+ let changedData = getChangedData(schema, viewHelperProps, sessData);
+ let isDataChanged = Object.keys(changedData).length > 0;
+ setDirty(isDataChanged);
/* tell the callbacks the data has changed */
- props.onDataChange && props.onDataChange(dataChanged);
+ props.onDataChange && props.onDataChange(isDataChanged, changedData);
}, [sessData]);
useEffect(()=>{
@@ -518,7 +522,7 @@ function SchemaDialogView({
setSaving(true);
setLoaderText('Saving...');
/* Get the changed data */
- let changeData = getChangedData(schema, viewHelperProps.mode, sessData);
+ let changeData = getChangedData(schema, viewHelperProps, sessData);
/* Add the id when in edit mode */
if(viewHelperProps.mode !== 'edit') {
@@ -577,7 +581,7 @@ function SchemaDialogView({
/* Called when SQL tab is active */
if(dirty) {
if(!formErr.name) {
- let changeData = getChangedData(schema, viewHelperProps.mode, sessData);
+ let changeData = getChangedData(schema, viewHelperProps, sessData);
if(viewHelperProps.mode !== 'edit') {
/* If new then merge the changed data with origData */
changeData = _.assign({}, schema.origData, changeData);
@@ -626,11 +630,11 @@ function SchemaDialogView({
+ hasSQLTab={props.hasSQL} getSQLValue={getSQLValue} firstEleRef={firstEleRef} isTabView={isTabView} />
-
+ {showFooter &&
{useMemo(()=>
props.onHelp(true, isNew)} icon={}
disabled={props.disableSqlHelp} className={classes.buttonMargin} title="SQL help for this object type."/>
@@ -648,7 +652,7 @@ function SchemaDialogView({
{gettext('Save')}
-
+ }
@@ -671,10 +675,12 @@ SchemaDialogView.propTypes = {
onHelp: PropTypes.func,
onDataChange: PropTypes.func,
confirmOnCloseReset: PropTypes.bool,
+ isTabView: PropTypes.bool,
hasSQL: PropTypes.bool,
getSQLValue: PropTypes.func,
disableSqlHelp: PropTypes.bool,
disableDialogHelp: PropTypes.bool,
+ showFooter: PropTypes.bool,
};
const usePropsStyles = makeStyles((theme)=>({
diff --git a/web/pgadmin/static/js/helpers/DataGridViewWithHeaderForm.jsx b/web/pgadmin/static/js/helpers/DataGridViewWithHeaderForm.jsx
new file mode 100644
index 000000000..547cb3f8b
--- /dev/null
+++ b/web/pgadmin/static/js/helpers/DataGridViewWithHeaderForm.jsx
@@ -0,0 +1,92 @@
+import React, { useCallback, useEffect, useRef, useState } from 'react';
+import { Box, makeStyles } from '@material-ui/core';
+import DataGridView, { DataGridHeader } from '../SchemaView/DataGridView';
+import SchemaView, { SCHEMA_STATE_ACTIONS } from '../SchemaView';
+import { DefaultButton } from '../components/Buttons';
+import { evalFunc } from '../utils';
+import PropTypes from 'prop-types';
+import CustomPropTypes from '../custom_prop_types';
+
+const useStyles = makeStyles((theme)=>({
+ formBorder: {
+ ...theme.mixins.panelBorder,
+ borderBottom: 0,
+ },
+ form: {
+ padding: '0.25rem',
+ },
+ addBtn: {
+ marginLeft: 'auto',
+ }
+}));
+
+export default function DataGridViewWithHeaderForm(props) {
+ let {containerClassName, headerSchema, headerVisible, ...otherProps} = props;
+ const classes = useStyles();
+ const headerFormData = useRef({});
+ const schemaRef = useRef(otherProps.schema);
+ const [isAddDisabled, setAddDisabled] = useState(true);
+
+ const onAddClick = useCallback(()=>{
+ if(otherProps.canAddRow) {
+ let state = schemaRef.current.top ? schemaRef.current.top.sessData : schemaRef.current.sessData;
+ let canAddRow = evalFunc(schemaRef.current, otherProps.canAddRow, state || {});
+ if(!canAddRow) {
+ return;
+ }
+ }
+
+ let newRow = headerSchema.getNewData(headerFormData.current);
+ otherProps.dataDispatch({
+ type: SCHEMA_STATE_ACTIONS.ADD_ROW,
+ path: otherProps.accessPath,
+ value: newRow,
+ });
+ }, []);
+
+ useEffect(()=>{
+ headerSchema.top = schemaRef.current.top;
+ }, []);
+
+ let state = schemaRef.current.top ? schemaRef.current.top.origData : schemaRef.current.origData;
+ headerVisible = headerVisible && evalFunc(null, headerVisible, state);
+ return (
+
+
+
+ {headerVisible &&
+ Promise.resolve({})}
+ schema={headerSchema}
+ viewHelperProps={props.viewHelperProps}
+ showFooter={false}
+ onDataChange={(isDataChanged, dataChanged)=>{
+ headerFormData.current = dataChanged;
+ setAddDisabled(headerSchema.addDisabled(headerFormData.current));
+ }}
+ hasSQL={false}
+ isTabView={false}
+ />
+
+ Add
+
+ }
+
+
+
+ );
+}
+
+DataGridViewWithHeaderForm.propTypes = {
+ label: PropTypes.string,
+ value: PropTypes.array,
+ viewHelperProps: PropTypes.object,
+ formErr: PropTypes.object,
+ headerSchema: CustomPropTypes.schemaUI.isRequired,
+ headerVisible: PropTypes.func,
+ schema: CustomPropTypes.schemaUI,
+ accessPath: PropTypes.array.isRequired,
+ dataDispatch: PropTypes.func.isRequired,
+ containerClassName: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
+};