Skip to content

Instantly share code, notes, and snippets.

@calebklc
Last active February 24, 2020 16:27
Show Gist options
  • Save calebklc/34f8ecf60488b3670fc5a35f4015f2a1 to your computer and use it in GitHub Desktop.
Save calebklc/34f8ecf60488b3670fc5a35f4015f2a1 to your computer and use it in GitHub Desktop.
Question about dynamic fields in ArrayField
import React from 'react';
import { asField } from 'informed';
import { Form, Input } from 'antd';
const AntdInput = asField(({ fieldState, fieldApi, ...props }) => {
const { value } = fieldState;
const { setValue, setTouched } = fieldApi;
const {
onChange,
onBlur,
initialValue,
forwardedRef,
formItemProps,
...rest
} = props;
const validateStatus = fieldState.error ? 'error' : '';
const help = fieldState.error || null;
return (
<Form.Item validateStatus={validateStatus} help={help} {...formItemProps}>
<Input
{...rest}
ref={forwardedRef}
value={!value && value !== 0 ? '' : value}
onChange={e => {
setValue(e.target.value);
if (onChange) {
onChange(e);
}
}}
onBlur={e => {
setTouched(true);
if (onBlur) {
onBlur(e);
}
}}
/>
</Form.Item>
);
});
export default AntdInput;
import React, { Fragment, cloneElement, PureComponent } from 'react';
import { ArrayField } from 'informed';
import PropTypes from 'prop-types';
import { Icon, Button, Row, Col } from 'antd';
import {
MatrixDynamicTable,
MatrixDynamicRowItem,
MatrixDynamicAddRowBtn,
} from './matrix-dynamic.less';
import { AntdInput } from '.';
class AntdMatrixDynamic extends React.Component {
static propTypes = {
columnHeaders: PropTypes.array.isRequired,
columns: PropTypes.array.isRequired,
fields: PropTypes.array.isRequired,
add: PropTypes.func.isRequired,
columnWidths: PropTypes.array,
indexHeader: PropTypes.element,
indexColumn: PropTypes.oneOf([PropTypes.func, PropTypes.element]),
minRowCount: PropTypes.number,
maxRowCount: PropTypes.number
};
static defaultProps = {
columnWidths: [],
indexHeader: null,
indexColumn: null,
minRowCount: 1,
maxRowCount: 0
};
labelTdWidth = '2%';
deleteBtnTdWidth = '2%';
fieldTdWidths = [];
constructor(props) {
super(props);
this.addMinRows();
this.calculateTdWidths();
}
addMinRows = () => {
const { minRowCount, add } = this.props;
for (let i = 0; i < minRowCount; i++) {
add();
}
};
calculateTdWidths = () => {
const { columnWidths, columns, indexColumn, indexHeader } = this.props;
if (columnWidths.length === 0) {
let tdWidths = [];
const tdWidth = `${Math.round(100 / columns.length)}%`;
if (indexColumn || indexHeader) {
tdWidths.push(this.labelTdWidth);
}
tdWidths = tdWidths.concat([
...columns.map(c => tdWidth),
this.deleteBtnTdWidth
]);
return tdWidths;
}
const newLabelTdWidth = columnWidths.shift();
const newDeleteBtnTdWidth = columnWidths.pop();
if (newLabelTdWidth != null) {
this.labelTdWidth = newLabelTdWidth;
}
if (newDeleteBtnTdWidth != null) {
this.deleteBtnTdWidth = newDeleteBtnTdWidth;
}
const remainedWidth = columnWidths
.filter(columnWidth => columnWidth != null)
.reduce((total, columnWidth) => total - columnWidth, 100);
const useDefaultWidthCount = columnWidths.filter(
columnWidth => columnWidth == null
).length;
const defaultTdWidth = `${Math.round(
remainedWidth / useDefaultWidthCount
)}%`;
return columnWidths.map(
columnWidth => (columnWidth == null ? defaultTdWidth : `${columnWidth}%`)
);
};
renderAddButton = ({ colspan }) => {
const { add } = this.props;
return (
<td colSpan={colspan}>
<Button
type="dashed"
onClick={() => {
add();
}}
className={MatrixDynamicAddRowBtn}
>
<Icon type="plus" /> Add
</Button>
</td>
);
};
renderFields = fields => {
const { maxRowCount, minRowCount, indexColumn, columns } = this.props;
const displayRemoveBtn = fields.length > minRowCount;
const displayAddBtn = fields.length !== maxRowCount;
return (
<Fragment>
{fields.map(({ field, key, remove, initialValue }, index) => {
const newColumns = columns.map(({ name, component }) => {
const defaultValue = initialValue && initialValue[name];
return (
<td key={`${key}.${field}.${name}.td`}>
{component({
field: `${field}.${name}`,
className: MatrixDynamicRowItem,
initialValue: defaultValue
})}
</td>
);
});
return (
<tr key={key}>
{indexColumn && <td>{indexColumn(index + 1)}</td>}
{newColumns}
<td>
{displayRemoveBtn && (
<Button
type="danger"
icon="delete"
htmlType="button"
onClick={() => {
remove();
}}
/>
)}
</td>
</tr>
);
})}
<tr>
{displayAddBtn && this.renderAddButton({ colspan: columns.length })}
<td />
</tr>
</Fragment>
);
};
render() {
const { columnHeaders, indexHeader = null, fields } = this.props;
return (
<Fragment>
<Row>
<table className={MatrixDynamicTable}>
<tr>
{indexHeader && <th width={this.labelTdWidth}>{indexHeader}</th>}
{columnHeaders.map((header, index) => (
<th
key={`${header}.th${index}`}
width={this.fieldTdWidths[index]}
>
{header}
</th>
))}
<th width={this.deleteBtnTdWidth}>&nbsp;</th>
</tr>
{this.renderFields(fields)}
</table>
</Row>
</Fragment>
);
}
}
export default AntdMatrixDynamic;
import { Button, Checkbox, Col, Row } from "antd";
import { LicFormPanel, intl } from "@my/common-modules";
import { ArrayField } from "informed";
import React from "react";
import {
AntdCheckboxGroup,
AntdDatepicker,
AntdInput,
AntdMatrixDynamic
} from "../../../../../components/informed";
const MainPage = () => {
return (
<LicFormPanel title={intl.get("formsPart0")}>
<ArrayField field="matrixdynamic">
{props => (
<AntdMatrixDynamic
columnHeaders={[
"First Name",
<span style={{ color: "red" }}>Last Name</span>,
"Date"
]}
columns={[
{
name: "firstName",
component: columnProps => <AntdInput {...columnProps} />
},
{
name: "lastName",
component: columnProps => <AntdInput {...columnProps} />
},
{
name: "date",
component: columnProps => (
<AntdDatepicker {...columnProps} style={{ width: "100%" }} />
)
}
]}
{...props}
/>
)}
</ArrayField>
</LicFormPanel>
);
};
MainPage.displayName = "MainPage";
export default MainPage;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment