import { useMemo, useEffect, useRef } from 'react' import PropTypes from 'prop-types' import { Table, Skeleton, Card, Button, Flex, Form, Typography } from 'antd' import PlusIcon from '../../Icons/PlusIcon' import ObjectProperty from './ObjectProperty' import { LoadingOutlined } from '@ant-design/icons' import BinIcon from '../../Icons/BinIcon' const { Text } = Typography const DEFAULT_COLUMN_WIDTHS = { text: 200, number: 120, dateTime: 200, state: 200, id: 180, bool: 120, tags: 200 } const getDefaultWidth = (type) => { return DEFAULT_COLUMN_WIDTHS[type] || 200 } const createSkeletonRows = (rowCount, keyPrefix, keyName) => { return Array.from({ length: rowCount }).map((_, index) => { const skeletonKey = `${keyPrefix}-${index}` const row = { isSkeleton: true, _objectChildTableKey: skeletonKey } if (typeof keyName === 'string') { row[keyName] = skeletonKey } return row }) } const ObjectChildTable = ({ maxWidth = '100%', properties = [], columns = [], visibleColumns = {}, objectData = null, scrollHeight = 240, size = 'small', loading = false, rowKey = '_id', skeletonRows = 5, additionalColumns = [], emptyText = 'No items', isEditing = false, formListName, value = [], rollups = [], onChange, ...tableProps }) => { const mainTableWrapperRef = useRef(null) const rollupTableWrapperRef = useRef(null) const propertyMap = useMemo(() => { const map = new Map() properties.forEach((property) => { if (property?.name) { map.set(property.name, property) } }) return map }, [properties]) const orderedPropertyNames = useMemo(() => { if (columns && columns.length > 0) { return columns } return properties.map((property) => property.name).filter(Boolean) }, [columns, properties]) const resolvedProperties = useMemo(() => { const explicit = orderedPropertyNames .map((name) => propertyMap.get(name)) .filter(Boolean) const remaining = properties.filter( (property) => !orderedPropertyNames.includes(property.name) ) return [...explicit, ...remaining].filter((property) => { if (!property?.name) return false if ( visibleColumns && Object.prototype.hasOwnProperty.call(visibleColumns, property.name) ) { return visibleColumns[property.name] !== false } return true }) }, [orderedPropertyNames, propertyMap, properties, visibleColumns]) // When used inside antd Form.Item without Form.List, `value` will be the controlled array. const itemsSource = useMemo(() => { return 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 propertyColumns = resolvedProperties.map((property) => ({ title: property.label || property.name, dataIndex: property.name, key: property.name, width: property.columnWidth || getDefaultWidth(property.type), render: (_text, record) => { if (record?.isSkeleton) { return ( ) } return ( ) } })) const deleteColumn = isEditing ? { title: '', key: 'delete', width: 10, fixed: 'right', render: (_text, record, index) => { if (record?.isSkeleton) { return null } return (