asb_cloud_front/src/components/Table/EditableTable.jsx

224 lines
6.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { memo, useCallback, useState, useEffect } from 'react'
import { Form, Button, Popconfirm } from 'antd'
import { EditOutlined, SaveOutlined, PlusOutlined, CloseCircleOutlined, DeleteOutlined } from '@ant-design/icons'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { Table } from '.'
import { EditableCell } from './EditableCell'
const newRowKeyValue = 'newRow'
const actions = [
[['insert'], (data) => [data]],
[['insertRange'], (data) => [[data].flat(1)]],
[['edit', 'update', 'put'], (data) => data.id && [data.id, data]],
[['delete'], (data) => data.id && [data.id]],
]
export const makeActionHandler = (action, { idWell, service, setLoader, errorMsg, onComplete }, recordParser, actionName) => service && action && (
(record) => invokeWebApiWrapperAsync(
async () => {
const addIdWell = (...params) => idWell ? [idWell, ...params] : params
if (typeof recordParser === 'function')
record = recordParser(record)
const actionId = actions.findIndex((elm) => elm[0].includes(action))
const params = actions[actionId]?.[1](record)
if (params) await service[action](...addIdWell(...params))
await onComplete?.()
},
setLoader,
errorMsg,
actionName
)
)
export const tryAddKeys = (items) => {
if (!items?.length || !items[0])
return []
if (items[0].key)
return items
return items.map((item, index) => ({...item, key: item.key ?? item.id ?? index }))
}
export const EditableTable = memo(({
columns,
dataSource,
onChange, // Метод вызывается со всем dataSource с измененными элементами после любого действия
onRowAdd, // Метод вызывается с новой добавленной записью. Если метод не определен, то кнопка добавления строки не показывается
onRowEdit, // Метод вызывается с новой отредактированной записью. Если метод не поределен, то кнопка редактирования строки не показывается
onRowDelete, // Метод вызывается с удаленной записью. Если метод не поределен, то кнопка удаления строки не показывается
additionalButtons,
buttonsWidth,
...otherTableProps
}) => {
const [form] = Form.useForm()
const [data, setData] = useState(tryAddKeys(dataSource))
const [editingKey, setEditingKey] = useState('')
useEffect(() => {
setData(tryAddKeys(dataSource))
}, [dataSource])
const isEditing = useCallback((record) => record?.key === editingKey, [editingKey])
const edit = useCallback((record) => {
form.setFieldsValue({...record})
setEditingKey(record.key)
}, [form])
const cancel = useCallback(() => {
if (editingKey === newRowKeyValue) {
const newData = [...data]
const index = newData.findIndex((item) => newRowKeyValue === item.key)
newData.splice(index, 1)
setData(newData)
}
setEditingKey('')
}, [data, editingKey])
const addNewRow = useCallback(async () => {
let newRow = {
...form.initialValues,
key:newRowKeyValue
}
const newData = [newRow, ...data]
setData(newData)
edit(newRow)
}, [data, edit, form.initialValues])
const save = useCallback(async (record) => {
try {
const row = await form.validateFields()
const newData = [...data]
const index = newData.findIndex((item) => record.key === item.key)
const item = newData[index]
const newItem = { ...item, ...row }
newData.splice(index, 1, newItem)
if (item.key === newRowKeyValue)
item.key = newRowKeyValue + newData.length
const isAdding = editingKey === newRowKeyValue
setEditingKey('')
setData(newData)
if (isAdding)
try {
onRowAdd(newItem)
} catch (err) {
console.log('callback onRowAdd fault:', err)
}
else
try {
onRowEdit(newItem)
} catch (err) {
console.log('callback onRowEdit fault:', err)
}
try {
onChange?.(newData)
} catch (err) {
console.log('callback onChange fault:', err)
}
} catch (errInfo) {
console.log('Validate Failed:', errInfo)
}
}, [data, editingKey, form, onChange, onRowAdd, onRowEdit])
const deleteRow = useCallback((record) => {
const newData = [...data]
const index = newData.findIndex((item) => record.key === item.key)
newData.splice(index, 1)
setData(newData)
onRowDelete(record)
onChange?.(newData)
}, [data, onChange, onRowDelete])
const operationColumn = {
width: buttonsWidth ?? 82,
title: !!onRowAdd && (
<Button
onClick={addNewRow}
disabled={editingKey !== ''}
icon={<PlusOutlined/>}
/>
),
dataIndex: 'operation',
render: (_, record) => isEditing(record) ? (
<span>
<Button onClick={() => save(record)} icon={<SaveOutlined/>}/>
<Button onClick={cancel} icon={<CloseCircleOutlined/>}/>
{additionalButtons?.(record, editingKey)}
</span>
) : (
<span>
{onRowEdit && (
<Button
disabled={editingKey !== ''}
onClick={() => edit(record)}
icon={<EditOutlined/>}
/>
)}
{onRowDelete && (
<Popconfirm title={'Удалить?'} onConfirm={() => deleteRow(record)}>
<Button icon={<DeleteOutlined/>}/>
</Popconfirm>
)}
{additionalButtons?.(record, editingKey)}
</span>
),
}
const handleColumn = useCallback((col) => {
if (col.children)
col.children = col.children.map(handleColumn)
if (!col.editable)
return col
return {
...col,
onCell: (record) => ({
...col.onCell?.(record),
editing: isEditing(record),
record,
dataIndex: col.dataIndex ?? col.key,
key: col.key ?? col.dataIndex,
input: col.input,
isRequired: col.isRequired,
title: col.title,
datatype: col.datatype,
formItemClass: col.formItemClass,
formItemRules: col.formItemRules,
initialValue: col.initialValue,
}),
}
}, [isEditing])
const mergedColumns = [...columns.map(handleColumn), operationColumn]
return (
<Form form={form}>
<Table
components={{
body: {
cell: EditableCell,
},
}}
columns={mergedColumns}
dataSource={data}
{...otherTableProps}
/>
</Form>
)
})