Refactor ObjectChildTable component to enhance editing functionality

- Removed unused formListName prop and related logic for cleaner code.
- Introduced resolveChangeValue function to streamline value handling during edits.
- Updated cell rendering to include a new handleCellChange function for better state management.
- Simplified the component structure by eliminating unnecessary Form.List integration.
This commit is contained in:
Tom Butcher 2025-12-28 01:08:46 +00:00
parent b38af41929
commit 4bd9acdc11

View File

@ -1,6 +1,6 @@
import { useMemo, useEffect, useRef } from 'react' import { useMemo, useEffect, useRef } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Table, Skeleton, Card, Button, Flex, Form, Typography } from 'antd' import { Table, Skeleton, Card, Button, Flex, Typography } from 'antd'
import PlusIcon from '../../Icons/PlusIcon' import PlusIcon from '../../Icons/PlusIcon'
import ObjectProperty from './ObjectProperty' import ObjectProperty from './ObjectProperty'
import { LoadingOutlined } from '@ant-design/icons' import { LoadingOutlined } from '@ant-design/icons'
@ -21,6 +21,14 @@ const getDefaultWidth = (type) => {
return DEFAULT_COLUMN_WIDTHS[type] || 200 return DEFAULT_COLUMN_WIDTHS[type] || 200
} }
const resolveChangeValue = (val, type) => {
if (type === 'bool') return val
if (val?.target && typeof val.target === 'object') {
return val.target.value
}
return val
}
const createSkeletonRows = (rowCount, keyPrefix, keyName) => { const createSkeletonRows = (rowCount, keyPrefix, keyName) => {
return Array.from({ length: rowCount }).map((_, index) => { return Array.from({ length: rowCount }).map((_, index) => {
const skeletonKey = `${keyPrefix}-${index}` const skeletonKey = `${keyPrefix}-${index}`
@ -51,7 +59,6 @@ const ObjectChildTable = ({
additionalColumns = [], additionalColumns = [],
emptyText = 'No items', emptyText = 'No items',
isEditing = false, isEditing = false,
formListName,
value = [], value = [],
rollups = [], rollups = [],
onChange, onChange,
@ -103,34 +110,44 @@ const ObjectChildTable = ({
return value ?? [] return value ?? []
}, [value]) }, [value])
// When used with antd Form.List, grab the form instance so we can read
// the latest row values and pass them into ObjectProperty as objectData.
// Assumes this component is rendered within a Form context when editing.
const formInstance = Form.useFormInstance()
const listNamePath = useMemo(() => {
if (!formListName) return null
return Array.isArray(formListName) ? formListName : [formListName]
}, [formListName])
const tableColumns = useMemo(() => { const tableColumns = useMemo(() => {
const propertyColumns = resolvedProperties.map((property) => ({ const propertyColumns = resolvedProperties.map((property) => ({
title: property.label || property.name, title: property.label || property.name,
dataIndex: property.name, dataIndex: property.name,
key: property.name, key: property.name,
width: property.columnWidth || getDefaultWidth(property.type), width: property.columnWidth || getDefaultWidth(property.type),
render: (_text, record) => { render: (_text, record, index) => {
if (record?.isSkeleton) { if (record?.isSkeleton) {
return ( return (
<Skeleton.Input active size='small' style={{ width: '100%' }} /> <Skeleton.Input active size='small' style={{ width: '100%' }} />
) )
} }
const handleCellChange = (newVal) => {
const resolved = resolveChangeValue(newVal, property.type)
const currentItems = Array.isArray(itemsSource)
? [...itemsSource]
: []
const updatedItem = {
...currentItems[index],
[property.name]: resolved
}
currentItems[index] = updatedItem
if (typeof onChange === 'function') {
onChange(currentItems)
}
}
return ( return (
<ObjectProperty <ObjectProperty
{...property} {...property}
longId={false} longId={false}
objectData={record} objectData={record}
isEditing={isEditing} isEditing={isEditing}
useFormItem={false}
name={undefined}
value={record[property.name]}
onChange={handleCellChange}
/> />
) )
} }
@ -416,122 +433,6 @@ const ObjectChildTable = ({
</Flex> </Flex>
) )
// When editing and a Form.List name is provided, bind rows via Form.List
// instead of the manual value/onChange mechanism.
if (isEditing === true && formListName) {
return (
<Form.List name={formListName}>
{(fields, { add, remove }) => {
const listDataSource = fields.map((field, index) => ({
_field: field,
_index: index,
key: field.key
}))
const listColumns = resolvedProperties.map((property) => ({
title: property.label || property.name,
dataIndex: property.name,
key: property.name,
width: property.columnWidth || getDefaultWidth(property.type),
render: (_text, record) => {
const field = record?._field
if (!field) return null
// Resolve the most up-to-date row data for this index from the form
let rowObjectData = undefined
if (formInstance && listNamePath) {
const namePath = [...listNamePath, field.name]
rowObjectData = formInstance.getFieldValue(namePath)
}
return (
<ObjectProperty
{...property}
// Bind directly to this list item + property via NamePath
name={[field.name, property.name]}
longId={false}
isEditing={true}
objectData={rowObjectData}
/>
)
}
}))
const deleteColumn = {
title: '',
key: 'delete',
width: 10,
fixed: 'right',
render: (_text, record) => {
const field = record?._field
const index = record?._index
if (!field || index == null) return null
return (
<Button
type='text'
danger
size='small'
icon={<BinIcon />}
onClick={(e) => {
e.stopPropagation()
if (typeof remove === 'function') {
remove(index)
}
}}
/>
)
}
}
const listTable = (
<Flex vertical>
<div ref={mainTableWrapperRef}>
<Table
dataSource={listDataSource}
columns={[...listColumns, ...additionalColumns, deleteColumn]}
pagination={false}
size={size}
loading={loading}
rowKey={(record) => record.key ?? record._index}
scroll={scrollConfig}
locale={{ emptyText }}
className={hasRollups ? 'child-table-rollups' : 'child-table'}
style={{ maxWidth, minWidth: 0 }}
{...tableProps}
/>
</div>
{rollupTable}
</Flex>
)
const handleAddListItem = () => {
const newItem = {}
resolvedProperties.forEach((property) => {
if (property?.name) {
newItem[property.name] = null
}
})
add(newItem)
}
return (
<Card style={{ minWidth: 0 }}>
<Flex vertical gap={'middle'}>
<Flex justify={'space-between'}>
<Button>Actions</Button>
<Button
type='primary'
icon={<PlusIcon />}
onClick={handleAddListItem}
/>
</Flex>
{listTable}
</Flex>
</Card>
)
}}
</Form.List>
)
}
if (isEditing === true) { if (isEditing === true) {
return ( return (
<Card> <Card>
@ -571,7 +472,6 @@ ObjectChildTable.propTypes = {
additionalColumns: PropTypes.arrayOf(PropTypes.object), additionalColumns: PropTypes.arrayOf(PropTypes.object),
emptyText: PropTypes.node, emptyText: PropTypes.node,
isEditing: PropTypes.bool, isEditing: PropTypes.bool,
formListName: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
value: PropTypes.arrayOf(PropTypes.object), value: PropTypes.arrayOf(PropTypes.object),
onChange: PropTypes.func, onChange: PropTypes.func,
maxWidth: PropTypes.string, maxWidth: PropTypes.string,