From c4d510bb5464606830ac50a3999f50ab14936ea0 Mon Sep 17 00:00:00 2001 From: Matt Oestreich Date: Wed, 13 Jan 2021 17:34:46 -0600 Subject: [PATCH] working on refactor from class to functional --- __tests__/demo/demo.js | 87 +++- src/components/MTableEditCell/index.js | 36 +- .../MTableEditField/BooleanField.js | 30 ++ .../MTableEditField/CurrencyField.js | 31 ++ src/components/MTableEditField/DateField.js | 51 +++ .../MTableEditField/DateTimeField.js | 25 ++ src/components/MTableEditField/LookupField.js | 24 ++ src/components/MTableEditField/TextField.js | 32 ++ src/components/MTableEditField/TimeField.js | 24 ++ src/components/MTableEditField/index.js | 43 ++ src/components/MTableEditRow/index.js | 394 ++++++++++++++++++ src/components/index.js | 49 ++- src/components/m-table-edit-cell.js | 159 +++++++ src/components/m-table-edit-field.js | 118 +++--- src/components/m-table-edit-row.js | 184 ++++---- src/utils/data-manager.js | 21 +- 16 files changed, 1090 insertions(+), 218 deletions(-) create mode 100644 src/components/MTableEditField/BooleanField.js create mode 100644 src/components/MTableEditField/CurrencyField.js create mode 100644 src/components/MTableEditField/DateField.js create mode 100644 src/components/MTableEditField/DateTimeField.js create mode 100644 src/components/MTableEditField/LookupField.js create mode 100644 src/components/MTableEditField/TextField.js create mode 100644 src/components/MTableEditField/TimeField.js create mode 100644 src/components/MTableEditField/index.js create mode 100644 src/components/MTableEditRow/index.js create mode 100644 src/components/m-table-edit-cell.js diff --git a/__tests__/demo/demo.js b/__tests__/demo/demo.js index ff96f975..c6c09ad9 100644 --- a/__tests__/demo/demo.js +++ b/__tests__/demo/demo.js @@ -1,33 +1,70 @@ -import React from 'react'; +import React, { useState } from 'react'; import { render } from 'react-dom'; import MaterialTable from '../../src'; import { ExportPdf, ExportCsv } from '../../exporters'; +const default_data = [ + { + name: 'Bar', + sirname: 'Zab', + age: 44, + date: new Date('December 1, 1999') + }, + { name: 'Baz', sirname: 'Oof', age: 34, date: new Date('1/1/1970') }, + { name: 'Foo', sirname: 'Rab', age: 24, date: new Date(Date.now()) } +]; + +const default_cols = [ + { + title: 'Given Name', + field: 'name', + customFilterAndSearch: (term, rowData) => term == rowData.name.length + }, + { title: 'Sirname', field: 'sirname' }, + { title: 'Age', field: 'age' }, + { title: 'Date', field: 'date', type: 'date' } +]; + const App = () => { const ref = React.useRef(); + const [data, setData] = useState(default_data); return ( term == rowData.name.length - }, - { title: 'Sirname', field: 'sirname' }, - { title: 'Age', field: 'age' }, - { title: 'Date', field: 'date', type: 'date' } - ]} + data={data} + columns={default_cols} + editable={{ + onRowAdd: (newData) => + new Promise((resolve, reject) => { + setTimeout(() => { + setData([...data, newData]); + + resolve(); + }, 1000); + }), + onRowUpdate: (newData, oldData) => + new Promise((resolve, reject) => { + setTimeout(() => { + const dataUpdate = [...data]; + const index = oldData.tableData.id; + dataUpdate[index] = newData; + setData([...dataUpdate]); + + resolve(); + }, 1000); + }), + onRowDelete: (oldData) => + new Promise((resolve, reject) => { + setTimeout(() => { + const dataDelete = [...data]; + const index = oldData.tableData.id; + dataDelete.splice(index, 1); + setData([...dataDelete]); + + resolve(); + }, 1000); + }) + }} options={{ filtering: true, exportMenu: [ @@ -44,8 +81,12 @@ const App = () => { cellEditable={{ onCellEditApproved: (newValue, oldValue, rowData, columnDef) => { return new Promise((resolve, reject) => { - console.log('newValue: ' + newValue); - setTimeout(resolve, 1000); + const datacopy = [...data]; + const row = rowData.tableData.id; + const field = columnDef.field; + datacopy[row][field] = newValue; + setData(datacopy); + resolve(); }); } }} diff --git a/src/components/MTableEditCell/index.js b/src/components/MTableEditCell/index.js index 6b63b9f1..1ea60f33 100644 --- a/src/components/MTableEditCell/index.js +++ b/src/components/MTableEditCell/index.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import { TableCell, CircularProgress } from '@material-ui/core'; import { withTheme } from '@material-ui/core/styles'; @@ -9,6 +9,23 @@ function MTableEditCell(props) { value: props.rowData[props.columnDef.field] })); + useEffect(() => { + props.cellEditable + .onCellEditApproved( + state.value, // newValue + props.rowData[props.columnDef.field], // oldValue + props.rowData, // rowData with old value + props.columnDef // columnDef + ) + .then(() => { + setState({ ...state, isLoading: false }); + props.onCellEditFinished(props.rowData, props.columnDef); + }) + .catch(() => { + setState({ ...state, isLoading: false }); + }); + }, []); + const getStyle = () => { let cellStyle = { boxShadow: '2px 0px 15px rgba(125,147,178,.25)', @@ -55,22 +72,7 @@ function MTableEditCell(props) { }; const onApprove = () => { - setState({ ...state, isLoading: true }, () => { - props.cellEditable - .onCellEditApproved( - state.value, // newValue - props.rowData[props.columnDef.field], // oldValue - props.rowData, // rowData with old value - props.columnDef // columnDef - ) - .then(() => { - setState({ ...state, isLoading: false }); - props.onCellEditFinished(props.rowData, props.columnDef); - }) - .catch(() => { - setState({ ...state, isLoading: false }); - }); - }); + setState({ ...state, isLoading: true }); }; const onCancel = () => { diff --git a/src/components/MTableEditField/BooleanField.js b/src/components/MTableEditField/BooleanField.js new file mode 100644 index 00000000..619fb44f --- /dev/null +++ b/src/components/MTableEditField/BooleanField.js @@ -0,0 +1,30 @@ +import React from 'react'; + +export default function BooleanField(props) { + return ( + + + props.onChange(event.target.checked)} + style={{ + padding: 0, + width: 24, + marginLeft: 9 + }} + inputProps={{ + 'aria-label': props.columnDef.title + }} + /> + } + /> + + {props.helperText} + + ); +} diff --git a/src/components/MTableEditField/CurrencyField.js b/src/components/MTableEditField/CurrencyField.js new file mode 100644 index 00000000..afd2585a --- /dev/null +++ b/src/components/MTableEditField/CurrencyField.js @@ -0,0 +1,31 @@ +import React from 'react'; + +export default function CurrencyField(props) { + return ( + { + let value = event.target.valueAsNumber; + if (!value && value !== 0) { + value = undefined; + } + return props.onChange(value); + }} + InputProps={{ + style: { + fontSize: 13, + textAlign: 'right' + } + }} + inputProps={{ + 'aria-label': props.columnDef.title + }} + onKeyDown={props.onKeyDown} + autoFocus={props.autoFocus} + /> + ); +} diff --git a/src/components/MTableEditField/DateField.js b/src/components/MTableEditField/DateField.js new file mode 100644 index 00000000..d13a2d13 --- /dev/null +++ b/src/components/MTableEditField/DateField.js @@ -0,0 +1,51 @@ +import React from 'react'; +import DateFnsUtils from '@date-io/date-fns'; +import { MuiPickersUtilsProvider, DatePicker } from '@material-ui/pickers'; + +export default function DateField({ + columnDef, + value, + onChange, + locale, + ...rest +}) { + const getProps = () => { + const { + columnDef, + rowData, + onRowDataChange, + errorState, + onBulkEditRowChanged, + scrollWidth, + ...remaining + } = rest; + return remaining; + }; + + const dateFormat = + columnDef.dateSetting && columnDef.dateSetting.format + ? columnDef.dateSetting.format + : 'dd.MM.yyyy'; + + const datePickerProps = getProps(); + + return ( + + + + ); +} diff --git a/src/components/MTableEditField/DateTimeField.js b/src/components/MTableEditField/DateTimeField.js new file mode 100644 index 00000000..75a9eb1b --- /dev/null +++ b/src/components/MTableEditField/DateTimeField.js @@ -0,0 +1,25 @@ +import React from 'react'; +import DateFnsUtils from '@date-io/date-fns'; +import { MuiPickersUtilsProvider, DateTimePicker } from '@material-ui/pickers'; + +export default function DateTimeField(props) { + return ( + + + + ); +} diff --git a/src/components/MTableEditField/LookupField.js b/src/components/MTableEditField/LookupField.js new file mode 100644 index 00000000..f98e15f8 --- /dev/null +++ b/src/components/MTableEditField/LookupField.js @@ -0,0 +1,24 @@ +import React from 'react'; + +export default function LookupField(props) { + return ( + + + {Boolean(helperText) && {helperText}} + + ); +} diff --git a/src/components/MTableEditField/TextField.js b/src/components/MTableEditField/TextField.js new file mode 100644 index 00000000..fa64fb9d --- /dev/null +++ b/src/components/MTableEditField/TextField.js @@ -0,0 +1,32 @@ +import React from 'react'; + +let renders = 0; + +export default function TextField(props) { + return ( + + props.onChange( + props.columnDef.type === 'numeric' + ? event.target.valueAsNumber + : event.target.value + ) + } + InputProps={{ + style: { + minWidth: 50, + fontSize: 13 + } + }} + inputProps={{ + 'aria-label': props.columnDef.title + }} + /> + ); +} diff --git a/src/components/MTableEditField/TimeField.js b/src/components/MTableEditField/TimeField.js new file mode 100644 index 00000000..acce21ec --- /dev/null +++ b/src/components/MTableEditField/TimeField.js @@ -0,0 +1,24 @@ +import React from 'react'; +import DateFnsUtils from '@date-io/date-fns'; + +export default function TimeField(props) { + return ( + + + + ); +} diff --git a/src/components/MTableEditField/index.js b/src/components/MTableEditField/index.js new file mode 100644 index 00000000..ef506e9f --- /dev/null +++ b/src/components/MTableEditField/index.js @@ -0,0 +1,43 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import LookupField from './LookupField'; +import BooleanField from './BooleanField'; +import DateField from './DateField'; +import TimeField from './TimeField'; +import TextField from './TextField'; +import DateTimeField from './DateTimeField'; +import CurrencyField from './CurrencyField'; + +function MTableEditField(props) { + function render() { + let component = 'ok'; + if (props.columnDef.editComponent) { + component = props.columnDef.editComponent(props); + } else if (props.columnDef.lookup) { + component = ; + } else if (props.columnDef.type === 'boolean') { + component = ; + } else if (props.columnDef.type === 'date') { + component = ; + } else if (props.columnDef.type === 'time') { + component = ; + } else if (props.columnDef.type === 'datetime') { + component = ; + } else if (props.columnDef.type === 'currency') { + component = ; + } else { + component = ; + } + return component; + } + return render(); +} + +MTableEditField.propTypes = { + value: PropTypes.any, + onChange: PropTypes.func.isRequired, + columnDef: PropTypes.object.isRequired, + locale: PropTypes.object +}; + +export default MTableEditField; diff --git a/src/components/MTableEditRow/index.js b/src/components/MTableEditRow/index.js new file mode 100644 index 00000000..5fdcfe07 --- /dev/null +++ b/src/components/MTableEditRow/index.js @@ -0,0 +1,394 @@ +import TableCell from '@material-ui/core/TableCell'; +import TableRow from '@material-ui/core/TableRow'; +import Typography from '@material-ui/core/Typography'; +import PropTypes from 'prop-types'; +import * as React from 'react'; +import { byString, setByString } from '../../utils'; +import * as CommonValues from '../../utils/common-values'; + +export default function MTableEditRow(props) { + const [state, setState] = React.useState(() => ({ + data: props.data ? JSON.parse(JSON.stringify(props.data)) : createRowData() + })); + + function createRowData() { + return props.columns + .filter((column) => 'initialEditValue' in column && column.field) + .reduce((prev, column) => { + prev[column.field] = column.initialEditValue; + return prev; + }, {}); + } + + function renderColumns() { + const size = CommonValues.elementSize(props); + const mapArr = props.columns + .filter( + (columnDef) => + !columnDef.hidden && !(columnDef.tableData.groupOrder > -1) + ) + .sort((a, b) => a.tableData.columnOrder - b.tableData.columnOrder) + .map((columnDef, index) => { + const value = + typeof state.data[columnDef.field] !== 'undefined' + ? state.data[columnDef.field] + : byString(state.data, columnDef.field); + const getCellStyle = (columnDef, value) => { + let cellStyle = { + color: 'inherit' + }; + if (typeof columnDef.cellStyle === 'function') { + cellStyle = { + ...cellStyle, + ...columnDef.cellStyle(value, props.data) + }; + } else { + cellStyle = { ...cellStyle, ...columnDef.cellStyle }; + } + if (columnDef.disableClick) { + cellStyle.cursor = 'default'; + } + + return { ...cellStyle }; + }; + + const style = {}; + if (index === 0) { + style.paddingLeft = 24 + props.level * 20; + } + + let allowEditing = false; + + if (columnDef.editable === undefined) { + allowEditing = true; + } + if (columnDef.editable === 'always') { + allowEditing = true; + } + if (columnDef.editable === 'onAdd' && props.mode === 'add') { + allowEditing = true; + } + if (columnDef.editable === 'onUpdate' && props.mode === 'update') { + allowEditing = true; + } + if (typeof columnDef.editable === 'function') { + allowEditing = columnDef.editable(columnDef, props.data); + } + if (!columnDef.field || !allowEditing) { + const readonlyValue = props.getFieldValue(state.data, columnDef); + return ( + + ); + } else { + const { editComponent, ...cellProps } = columnDef; + const EditComponent = editComponent || props.components.EditField; + let error = { isValid: true, helperText: '' }; + if (columnDef.validate) { + const validateResponse = columnDef.validate(state.data); + switch (typeof validateResponse) { + case 'object': + error = { ...validateResponse }; + break; + case 'boolean': + error = { isValid: validateResponse, helperText: '' }; + break; + case 'string': + error = { isValid: false, helperText: validateResponse }; + break; + } + } + return ( + + { + const data = { ...state.data }; + setByString(data, columnDef.field, value); + // data[columnDef.field] = value; + setState({ data }, () => { + if (props.onBulkEditRowChanged) { + props.onBulkEditRowChanged(props.data, data); + } + }); + }} + onRowDataChange={(data) => { + setState({ data }, () => { + if (props.onBulkEditRowChanged) { + props.onBulkEditRowChanged(props.data, data); + } + }); + }} + /> + + ); + } + }); + return mapArr; + } + + const handleSave = () => { + const newData = state.data; + delete newData.tableData; + props.onEditingApproved(props.mode, state.data, props.data); + }; + + function renderActions() { + if (props.mode === 'bulk') { + return ; + } + + const size = CommonValues.elementSize(props); + const localization = { + ...MTableEditRow.defaultProps.localization, + ...props.localization + }; + const isValid = props.columns.every((column) => { + if (column.validate) { + const response = column.validate(state.data); + switch (typeof response) { + case 'object': + return response.isValid; + case 'string': + return response.length === 0; + case 'boolean': + return response; + } + } else { + return true; + } + }); + const actions = [ + { + icon: props.icons.Check, + tooltip: localization.saveTooltip, + disabled: !isValid, + onClick: handleSave + }, + { + icon: props.icons.Clear, + tooltip: localization.cancelTooltip, + onClick: () => { + props.onEditingCanceled(props.mode, props.data); + } + } + ]; + return ( + +
+ +
+
+ ); + } + + function getStyle() { + const style = { + // boxShadow: '1px 1px 1px 1px rgba(0,0,0,0.2)', + borderBottom: '1px solid red' + }; + + return style; + } + + const handleKeyDown = (e) => { + if (e.keyCode === 13 && e.target.type !== 'textarea') { + handleSave(); + } else if (e.keyCode === 13 && e.target.type === 'textarea' && e.shiftKey) { + handleSave(); + } else if (e.keyCode === 27) { + props.onEditingCanceled(props.mode, props.data); + } + }; + + function render() { + const size = CommonValues.elementSize(props); + const localization = { + ...MTableEditRow.defaultProps.localization, + ...props.localization + }; + let columns; + if ( + props.mode === 'add' || + props.mode === 'update' || + props.mode === 'bulk' + ) { + columns = renderColumns(); + } else { + const colSpan = props.columns.filter( + (columnDef) => + !columnDef.hidden && !(columnDef.tableData.groupOrder > -1) + ).length; + columns = [ + + {localization.deleteText} + + ]; + } + + if (props.options.selection) { + columns.splice( + 0, + 0, + + ); + } + if (props.isTreeData) { + columns.splice( + 0, + 0, + + ); + } + + if (props.options.actionsColumnIndex === -1) { + columns.push(renderActions()); + } else if (props.options.actionsColumnIndex >= 0) { + let endPos = 0; + if (props.options.selection) { + endPos = 1; + } + if (props.isTreeData) { + endPos = 1; + if (props.options.selection) { + columns.splice(1, 1); + } + } + columns.splice( + props.options.actionsColumnIndex + endPos, + 0, + renderActions() + ); + } + + // Lastly we add detail panel icon + if (props.detailPanel) { + const aligment = props.options.detailPanelColumnAlignment; + const index = aligment === 'left' ? 0 : columns.length; + columns.splice( + index, + 0, + + ); + } + + props.columns + .filter((columnDef) => columnDef.tableData.groupOrder > -1) + .forEach((columnDef) => { + columns.splice( + 0, + 0, + + ); + }); + + const { + detailPanel, + isTreeData, + onRowClick, + onRowSelected, + onTreeExpandChanged, + onToggleDetailPanel, + onEditingApproved, + onEditingCanceled, + getFieldValue, + components, + icons, + columns: columnsProp, // renamed to not conflict with definition above + localization: localizationProp, // renamed to not conflict with definition above + options, + actions, + errorState, + onBulkEditRowChanged, + scrollWidth, + ...rowProps + } = props; + + return ( + <> + + {columns} + + + ); + } + + return render(); +} + +MTableEditRow.defaultProps = { + actions: [], + index: 0, + options: {}, + path: [], + localization: { + saveTooltip: 'Save', + cancelTooltip: 'Cancel', + deleteText: 'Are you sure you want to delete this row?' + }, + onBulkEditRowChanged: () => {} +}; + +MTableEditRow.propTypes = { + actions: PropTypes.array, + icons: PropTypes.any.isRequired, + index: PropTypes.number.isRequired, + data: PropTypes.object, + detailPanel: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.object, PropTypes.func])) + ]), + options: PropTypes.object.isRequired, + onRowSelected: PropTypes.func, + path: PropTypes.arrayOf(PropTypes.number), + columns: PropTypes.array, + onRowClick: PropTypes.func, + onEditingApproved: PropTypes.func, + onEditingCanceled: PropTypes.func, + localization: PropTypes.object, + getFieldValue: PropTypes.func, + errorState: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), + onBulkEditRowChanged: PropTypes.func +}; diff --git a/src/components/index.js b/src/components/index.js index 35dd5607..f7d91874 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,30 +1,33 @@ -import MTableBody from './m-table-body'; -import MTableBodyRow from './m-table-body-row'; -import MTableGroupbar from './m-table-groupbar'; -import MTableGroupRow from './m-table-group-row'; -import MTableEditRow from './m-table-edit-row'; -import MTableEditField from './m-table-edit-field'; -import MTableHeader from './m-table-header'; -import MTablePagination from './m-table-pagination'; -import MTableSteppedPagination from './m-table-stepped-pagination'; -import MTableToolbar from './m-table-toolbar'; +/** --------------------------- + * Class based components + * (aka original) + --------------------------- */ + +export { default as MTableBody } from './m-table-body'; +export { default as MTableBodyRow } from './m-table-body-row'; +export { default as MTableGroupbar } from './m-table-groupbar'; +export { default as MTableGroupRow } from './m-table-group-row'; +export { default as MTableHeader } from './m-table-header'; +export { default as MTablePagination } from './m-table-pagination'; +export { default as MTableSteppedPagination } from './m-table-stepped-pagination'; +export { default as MTableToolbar } from './m-table-toolbar'; + +/** HAVING ISSUES WITH THE REFACTORED VERSIONS OF: */ +export { default as MTableEditField } from './m-table-edit-field'; +export { default as MTableEditCell } from './m-table-edit-cell'; + +/** --------------------------- + * Functional components + * (aka refactor) + --------------------------- */ // Trying to keep these in alphabetical order export { default as MTableAction } from './MTableAction'; export { default as MTableActions } from './MTableActions'; export { default as MTableCell } from './MTableCell'; -export { default as MTableEditCell } from './MTableEditCell'; +export { default as MTableEditRow } from './MTableEditRow'; export { default as MTableFilterRow } from './MTableFilterRow'; -export { - MTableBody, - MTableBodyRow, - MTableGroupbar, - MTableGroupRow, - MTableEditRow, - MTableEditField, - MTableHeader, - MTablePagination, - MTableSteppedPagination, - MTableToolbar -}; +/** THESE REFACTORS ARE HAVING ISSUES */ +// export { default as MTableEditCell } from './MTableEditCell'; +// export { default as MTableEditField } from './MTableEditField'; diff --git a/src/components/m-table-edit-cell.js b/src/components/m-table-edit-cell.js new file mode 100644 index 00000000..e0209810 --- /dev/null +++ b/src/components/m-table-edit-cell.js @@ -0,0 +1,159 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import TableCell from '@material-ui/core/TableCell'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import withTheme from '@material-ui/core/styles/withTheme'; + +class MTableEditCell extends React.Component { + constructor(props) { + super(props); + + this.state = { + isLoading: false, + value: this.props.rowData[this.props.columnDef.field] + }; + } + + getStyle = () => { + let cellStyle = { + boxShadow: '2px 0px 15px rgba(125,147,178,.25)', + color: 'inherit', + width: this.props.columnDef.tableData.width, + boxSizing: 'border-box', + fontSize: 'inherit', + fontFamily: 'inherit', + fontWeight: 'inherit', + padding: '0 16px' + }; + + if (typeof this.props.columnDef.cellStyle === 'function') { + cellStyle = { + ...cellStyle, + ...this.props.columnDef.cellStyle(this.state.value, this.props.rowData) + }; + } else { + cellStyle = { ...cellStyle, ...this.props.columnDef.cellStyle }; + } + + if (typeof this.props.cellEditable.cellStyle === 'function') { + cellStyle = { + ...cellStyle, + ...this.props.cellEditable.cellStyle( + this.state.value, + this.props.rowData, + this.props.columnDef + ) + }; + } else { + cellStyle = { ...cellStyle, ...this.props.cellEditable.cellStyle }; + } + + return cellStyle; + }; + + handleKeyDown = (e) => { + if (e.keyCode === 13) { + this.onApprove(); + } else if (e.keyCode === 27) { + this.onCancel(); + } + }; + + onApprove = () => { + this.setState({ isLoading: true }, () => { + this.props.cellEditable + .onCellEditApproved( + this.state.value, // newValue + this.props.rowData[this.props.columnDef.field], // oldValue + this.props.rowData, // rowData with old value + this.props.columnDef // columnDef + ) + .then(() => { + this.setState({ isLoading: false }); + this.props.onCellEditFinished( + this.props.rowData, + this.props.columnDef + ); + }) + .catch((error) => { + this.setState({ isLoading: false }); + }); + }); + }; + + onCancel = () => { + this.props.onCellEditFinished(this.props.rowData, this.props.columnDef); + }; + + renderActions() { + if (this.state.isLoading) { + return ( +
+ +
+ ); + } + + const actions = [ + { + icon: this.props.icons.Check, + tooltip: this.props.localization.saveTooltip, + onClick: this.onApprove, + disabled: this.state.isLoading + }, + { + icon: this.props.icons.Clear, + tooltip: this.props.localization.cancelTooltip, + onClick: this.onCancel, + disabled: this.state.isLoading + } + ]; + + return ( + + ); + } + + render() { + return ( + +
+
+ this.setState({ value })} + onKeyDown={this.handleKeyDown} + disabled={this.state.isLoading} + rowData={this.props.rowData} + autoFocus + /> +
+ {this.renderActions()} +
+
+ ); + } +} + +MTableEditCell.defaultProps = { + columnDef: {} +}; + +MTableEditCell.propTypes = { + cellEditable: PropTypes.object.isRequired, + columnDef: PropTypes.object.isRequired, + components: PropTypes.object.isRequired, + errorState: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), + icons: PropTypes.object.isRequired, + localization: PropTypes.object.isRequired, + onCellEditFinished: PropTypes.func.isRequired, + rowData: PropTypes.object.isRequired, + size: PropTypes.string +}; + +export default withTheme(MTableEditCell); diff --git a/src/components/m-table-edit-field.js b/src/components/m-table-edit-field.js index d5c60252..2631e32c 100644 --- a/src/components/m-table-edit-field.js +++ b/src/components/m-table-edit-field.js @@ -16,8 +16,8 @@ import { } from '@material-ui/pickers'; import PropTypes from 'prop-types'; -function MTableEditField() { - function getProps() { +class MTableEditField extends React.Component { + getProps() { const { columnDef, rowData, @@ -26,26 +26,26 @@ function MTableEditField() { onBulkEditRowChanged, scrollWidth, ...props - } = props; + } = this.props; return props; } - function renderLookupField() { - const { helperText, error, ...props } = getProps(); + renderLookupField() { + const { helperText, error, ...props } = this.getProps(); return ( @@ -54,8 +54,8 @@ function MTableEditField() { ); } - function renderBooleanField() { - const { helperText, error, ...props } = getProps(); + renderBooleanField() { + const { helperText, error, ...props } = this.getProps(); return ( @@ -65,16 +65,16 @@ function MTableEditField() { control={ props.onChange(event.target.checked)} + value={String(this.props.value)} + checked={Boolean(this.props.value)} + onChange={(event) => this.props.onChange(event.target.checked)} style={{ padding: 0, width: 24, marginLeft: 9 }} inputProps={{ - 'aria-label': props.columnDef.title, + 'aria-label': this.props.columnDef.title }} /> } @@ -85,18 +85,19 @@ function MTableEditField() { ); } - function renderDateField() { + renderDateField() { const dateFormat = - props.columnDef.dateSetting && props.columnDef.dateSetting.format - ? props.columnDef.dateSetting.format + this.props.columnDef.dateSetting && + this.props.columnDef.dateSetting.format + ? this.props.columnDef.dateSetting.format : 'dd.MM.yyyy'; return ( - + ); } - function renderTimeField() { + renderTimeField() { return ( - + ); } - function renderDateTimeField() { + renderDateTimeField() { return ( - + ); } - function renderTextField() { + renderTextField() { return ( - props.onChange( - props.columnDef.type === 'numeric' + this.props.onChange( + this.props.columnDef.type === 'numeric' ? event.target.valueAsNumber : event.target.value ) @@ -178,26 +183,28 @@ function MTableEditField() { } }} inputProps={{ - 'aria-label': props.columnDef.title, + 'aria-label': this.props.columnDef.title }} /> ); } - function renderCurrencyField() { + renderCurrencyField() { return ( { let value = event.target.valueAsNumber; if (!value && value !== 0) { value = undefined; } - return props.onChange(value); + return this.props.onChange(value); }} InputProps={{ style: { @@ -206,15 +213,15 @@ function MTableEditField() { } }} inputProps={{ - 'aria-label': props.columnDef.title, + 'aria-label': this.props.columnDef.title }} - onKeyDown={props.onKeyDown} - autoFocus={props.autoFocus} + onKeyDown={this.props.onKeyDown} + autoFocus={this.props.autoFocus} /> ); } - function render() { + render() { let component = 'ok'; if (this.props.columnDef.editComponent) { @@ -237,9 +244,6 @@ function MTableEditField() { return component; } - - //________________________ - return render(); } MTableEditField.propTypes = { diff --git a/src/components/m-table-edit-row.js b/src/components/m-table-edit-row.js index 14c5f01a..dcd5d2ed 100644 --- a/src/components/m-table-edit-row.js +++ b/src/components/m-table-edit-row.js @@ -8,13 +8,19 @@ import { byString, setByString } from '../utils'; import * as CommonValues from '../utils/common-values'; /* eslint-enable no-unused-vars */ -export default function MTableEditRow(props) { - const [state, setState] = React.useState(() => ({ - data: props.data ? JSON.parse(JSON.stringify(props.data)) : createRowData(), - })); - - function createRowData() { - return props.columns +export default class MTableEditRow extends React.Component { + constructor(props) { + super(props); + + this.state = { + data: props.data + ? JSON.parse(JSON.stringify(props.data)) + : this.createRowData() + }; + } + + createRowData() { + return this.props.columns .filter((column) => 'initialEditValue' in column && column.field) .reduce((prev, column) => { prev[column.field] = column.initialEditValue; @@ -22,9 +28,9 @@ export default function MTableEditRow(props) { }, {}); } - function renderColumns() { - const size = CommonValues.elementSize(props); - const mapArr = props.columns + renderColumns() { + const size = CommonValues.elementSize(this.props); + const mapArr = this.props.columns .filter( (columnDef) => !columnDef.hidden && !(columnDef.tableData.groupOrder > -1) @@ -32,9 +38,9 @@ export default function MTableEditRow(props) { .sort((a, b) => a.tableData.columnOrder - b.tableData.columnOrder) .map((columnDef, index) => { const value = - typeof state.data[columnDef.field] !== 'undefined' - ? state.data[columnDef.field] - : byString(state.data, columnDef.field); + typeof this.state.data[columnDef.field] !== 'undefined' + ? this.state.data[columnDef.field] + : byString(this.state.data, columnDef.field); const getCellStyle = (columnDef, value) => { let cellStyle = { color: 'inherit' @@ -42,7 +48,7 @@ export default function MTableEditRow(props) { if (typeof columnDef.cellStyle === 'function') { cellStyle = { ...cellStyle, - ...columnDef.cellStyle(value, props.data), + ...columnDef.cellStyle(value, this.props.data) }; } else { cellStyle = { ...cellStyle, ...columnDef.cellStyle }; @@ -56,7 +62,7 @@ export default function MTableEditRow(props) { const style = {}; if (index === 0) { - style.paddingLeft = 24 + props.level * 20; + style.paddingLeft = 24 + this.props.level * 20; } let allowEditing = false; @@ -67,34 +73,38 @@ export default function MTableEditRow(props) { if (columnDef.editable === 'always') { allowEditing = true; } - if (columnDef.editable === 'onAdd' && props.mode === 'add') { + if (columnDef.editable === 'onAdd' && this.props.mode === 'add') { allowEditing = true; } - if (columnDef.editable === 'onUpdate' && props.mode === 'update') { + if (columnDef.editable === 'onUpdate' && this.props.mode === 'update') { allowEditing = true; } if (typeof columnDef.editable === 'function') { - allowEditing = columnDef.editable(columnDef, props.data); + allowEditing = columnDef.editable(columnDef, this.props.data); } if (!columnDef.field || !allowEditing) { - const readonlyValue = props.getFieldValue(state.data, columnDef); + const readonlyValue = this.props.getFieldValue( + this.state.data, + columnDef + ); return ( - ); } else { const { editComponent, ...cellProps } = columnDef; - const EditComponent = editComponent || props.components.EditField; + const EditComponent = + editComponent || this.props.components.EditField; let error = { isValid: true, helperText: '' }; if (columnDef.validate) { - const validateResponse = columnDef.validate(state.data); + const validateResponse = columnDef.validate(this.state.data); switch (typeof validateResponse) { case 'object': error = { ...validateResponse }; @@ -122,22 +132,22 @@ export default function MTableEditRow(props) { value={value} error={!error.isValid} helperText={error.helperText} - locale={props.localization.dateTimePickerLocalization} - rowData={state.data} + locale={this.props.localization.dateTimePickerLocalization} + rowData={this.state.data} onChange={(value) => { - const data = { ...state.data }; + const data = { ...this.state.data }; setByString(data, columnDef.field, value); // data[columnDef.field] = value; - setState({ data }, () => { - if (props.onBulkEditRowChanged) { - props.onBulkEditRowChanged(props.data, data); + this.setState({ data }, () => { + if (this.props.onBulkEditRowChanged) { + this.props.onBulkEditRowChanged(this.props.data, data); } }); }} onRowDataChange={(data) => { - setState({ data }, () => { - if (props.onBulkEditRowChanged) { - props.onBulkEditRowChanged(props.data, data); + this.setState({ data }, () => { + if (this.props.onBulkEditRowChanged) { + this.props.onBulkEditRowChanged(this.props.data, data); } }); }} @@ -149,25 +159,29 @@ export default function MTableEditRow(props) { return mapArr; } - const handleSave = () => { - const newData = state.data; + handleSave = () => { + const newData = this.state.data; delete newData.tableData; - props.onEditingApproved(props.mode, state.data, props.data); + this.props.onEditingApproved( + this.props.mode, + this.state.data, + this.props.data + ); }; - function renderActions() { - if (props.mode === 'bulk') { + renderActions() { + if (this.props.mode === 'bulk') { return ; } - const size = CommonValues.elementSize(props); + const size = CommonValues.elementSize(this.props); const localization = { ...MTableEditRow.defaultProps.localization, - ...props.localization, + ...this.props.localization }; - const isValid = props.columns.every((column) => { + const isValid = this.props.columns.every((column) => { if (column.validate) { - const response = column.validate(state.data); + const response = column.validate(this.state.data); switch (typeof response) { case 'object': return response.isValid; @@ -182,18 +196,18 @@ export default function MTableEditRow(props) { }); const actions = [ { - icon: props.icons.Check, + icon: this.props.icons.Check, tooltip: localization.saveTooltip, disabled: !isValid, - onClick: handleSave, + onClick: this.handleSave }, { - icon: props.icons.Clear, + icon: this.props.icons.Clear, tooltip: localization.cancelTooltip, onClick: () => { - props.onEditingCanceled(props.mode, props.data); - }, - }, + this.props.onEditingCanceled(this.props.mode, this.props.data); + } + } ]; return (
-
@@ -218,7 +232,7 @@ export default function MTableEditRow(props) { ); } - function getStyle() { + getStyle() { const style = { // boxShadow: '1px 1px 1px 1px rgba(0,0,0,0.2)', borderBottom: '1px solid red' @@ -227,38 +241,40 @@ export default function MTableEditRow(props) { return style; } - const handleKeyDown = (e) => { + handleKeyDown = (e) => { if (e.keyCode === 13 && e.target.type !== 'textarea') { - handleSave(); + this.handleSave(); } else if (e.keyCode === 13 && e.target.type === 'textarea' && e.shiftKey) { - handleSave(); + this.handleSave(); } else if (e.keyCode === 27) { - props.onEditingCanceled(props.mode, props.data); + this.props.onEditingCanceled(this.props.mode, this.props.data); } }; - function render() { - const size = CommonValues.elementSize(props); + render() { + const size = CommonValues.elementSize(this.props); const localization = { ...MTableEditRow.defaultProps.localization, - ...props.localization, + ...this.props.localization }; let columns; if ( - props.mode === 'add' || - props.mode === 'update' || - props.mode === 'bulk' + this.props.mode === 'add' || + this.props.mode === 'update' || + this.props.mode === 'bulk' ) { - columns = renderColumns(); + columns = this.renderColumns(); } else { - const colSpan = props.columns.filter( + const colSpan = this.props.columns.filter( (columnDef) => !columnDef.hidden && !(columnDef.tableData.groupOrder > -1) ).length; columns = [ @@ -267,14 +283,14 @@ export default function MTableEditRow(props) { ]; } - if (props.options.selection) { + if (this.props.options.selection) { columns.splice( 0, 0, ); } - if (props.isTreeData) { + if (this.props.isTreeData) { columns.splice( 0, 0, @@ -282,29 +298,29 @@ export default function MTableEditRow(props) { ); } - if (props.options.actionsColumnIndex === -1) { - columns.push(renderActions()); - } else if (props.options.actionsColumnIndex >= 0) { + if (this.props.options.actionsColumnIndex === -1) { + columns.push(this.renderActions()); + } else if (this.props.options.actionsColumnIndex >= 0) { let endPos = 0; - if (props.options.selection) { + if (this.props.options.selection) { endPos = 1; } - if (props.isTreeData) { + if (this.props.isTreeData) { endPos = 1; - if (props.options.selection) { + if (this.props.options.selection) { columns.splice(1, 1); } } columns.splice( - props.options.actionsColumnIndex + endPos, + this.props.options.actionsColumnIndex + endPos, 0, - renderActions() + this.renderActions() ); } // Lastly we add detail panel icon - if (props.detailPanel) { - const aligment = props.options.detailPanelColumnAlignment; + if (this.props.detailPanel) { + const aligment = this.props.options.detailPanelColumnAlignment; const index = aligment === 'left' ? 0 : columns.length; columns.splice( index, @@ -313,7 +329,7 @@ export default function MTableEditRow(props) { ); } - props.columns + this.props.columns .filter((columnDef) => columnDef.tableData.groupOrder > -1) .forEach((columnDef) => { columns.splice( @@ -346,18 +362,20 @@ export default function MTableEditRow(props) { onBulkEditRowChanged, scrollWidth, ...rowProps - } = props; + } = this.props; return ( <> - + {columns} ); } - - return render(); } MTableEditRow.defaultProps = { diff --git a/src/utils/data-manager.js b/src/utils/data-manager.js index f271bbda..7da51d8d 100644 --- a/src/utils/data-manager.js +++ b/src/utils/data-manager.js @@ -439,24 +439,15 @@ export default class DataManager { onColumnResized(id, additionalWidth) { const column = this.columns.find((c) => c.tableData.id === id); - if (!column) return; - + if (!column) { + return; + } const nextColumn = this.columns.find((c) => c.tableData.id === id + 1); - if (!nextColumn) return; - - // console.log("S i: " + column.tableData.initialWidth); - // console.log("S a: " + column.tableData.additionalWidth); - // console.log("S w: " + column.tableData.width); - + if (!nextColumn) { + return; + } column.tableData.additionalWidth = additionalWidth; column.tableData.width = `calc(${column.tableData.initialWidth} + ${column.tableData.additionalWidth}px)`; - - // nextColumn.tableData.additionalWidth = -1 * additionalWidth; - // nextColumn.tableData.width = `calc(${nextColumn.tableData.initialWidth} + ${nextColumn.tableData.additionalWidth}px)`; - - // console.log("F i: " + column.tableData.initialWidth); - // console.log("F a: " + column.tableData.additionalWidth); - // console.log("F w: " + column.tableData.width); } expandTreeForNodes = (data) => {