1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-04-22 22:16:13 -04:00

Upgrade react-dnd and DnD Components to TypeScript

This commit is contained in:
Mark McDowall
2024-12-29 18:21:01 -08:00
parent 572bdc979c
commit 1bc1b080d1
85 changed files with 3525 additions and 4767 deletions
@@ -1,3 +1,7 @@
.columnContainer {
margin: 4px 0;
}
.column {
display: flex;
align-items: stretch;
@@ -43,6 +47,17 @@
opacity: 0.25;
}
.notDragable {
padding: 4px 0;
.placeholder {
width: 100%;
height: 36px;
border: 1px dotted #aaa;
border-radius: 4px;
}
.placeholderBefore {
margin-bottom: 8px;
}
.placeholderAfter {
margin-top: 8px;
}
@@ -3,11 +3,14 @@
interface CssExports {
'checkContainer': string;
'column': string;
'columnContainer': string;
'dragHandle': string;
'dragIcon': string;
'isDragging': string;
'label': string;
'notDragable': string;
'placeholder': string;
'placeholderAfter': string;
'placeholderBefore': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -1,68 +0,0 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import CheckInput from 'Components/Form/CheckInput';
import Icon from 'Components/Icon';
import { icons } from 'Helpers/Props';
import styles from './TableOptionsColumn.css';
function TableOptionsColumn(props) {
const {
name,
label,
isVisible,
isModifiable,
isDragging,
connectDragSource,
onVisibleChange
} = props;
return (
<div className={isModifiable ? undefined : styles.notDragable}>
<div
className={classNames(
styles.column,
isDragging && styles.isDragging
)}
>
<label
className={styles.label}
>
<CheckInput
containerClassName={styles.checkContainer}
name={name}
value={isVisible}
isDisabled={isModifiable === false}
onChange={onVisibleChange}
/>
{typeof label === 'function' ? label() : label}
</label>
{
!!connectDragSource &&
connectDragSource(
<div className={styles.dragHandle}>
<Icon
className={styles.dragIcon}
name={icons.REORDER}
/>
</div>
)
}
</div>
</div>
);
}
TableOptionsColumn.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
isVisible: PropTypes.bool.isRequired,
isModifiable: PropTypes.bool.isRequired,
index: PropTypes.number.isRequired,
isDragging: PropTypes.bool,
connectDragSource: PropTypes.func,
onVisibleChange: PropTypes.func.isRequired
};
export default TableOptionsColumn;
@@ -0,0 +1,163 @@
import classNames from 'classnames';
import React, { useRef } from 'react';
import { DragSourceMonitor, useDrag, useDrop, XYCoord } from 'react-dnd';
import CheckInput from 'Components/Form/CheckInput';
import Icon from 'Components/Icon';
import DragType from 'Helpers/DragType';
import { icons } from 'Helpers/Props';
import { CheckInputChanged } from 'typings/inputs';
import Column from '../Column';
import styles from './TableOptionsColumn.css';
interface DragItem {
name: string;
index: number;
}
interface TableOptionsColumnProps {
name: string;
label: Column['label'];
isDraggingDown: boolean;
isDraggingUp: boolean;
isVisible: boolean;
isModifiable: boolean;
index: number;
onVisibleChange: (change: CheckInputChanged) => void;
onColumnDragEnd: (didDrop: boolean) => void;
onColumnDragMove: (dragIndex: number, hoverIndex: number) => void;
}
function TableOptionsColumn({
name,
label,
index,
isDraggingDown,
isDraggingUp,
isVisible,
isModifiable,
onVisibleChange,
onColumnDragEnd,
onColumnDragMove,
}: TableOptionsColumnProps) {
const ref = useRef<HTMLDivElement>(null);
const [{ isOver }, dropRef] = useDrop<DragItem, void, { isOver: boolean }>({
accept: DragType.TableColumn,
collect(monitor) {
return {
isOver: monitor.isOver(),
};
},
hover(item: DragItem, monitor) {
if (!ref.current) {
return;
}
if (!isModifiable) {
return;
}
const dragIndex = item.index;
const hoverIndex = index;
// Don't replace items with themselves
if (dragIndex === hoverIndex) {
return;
}
// Determine rectangle on screen
const hoverBoundingRect = ref.current?.getBoundingClientRect();
// Get vertical middle
const hoverMiddleY =
(hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
// Determine mouse position
const clientOffset = monitor.getClientOffset();
// Get pixels to the top
const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top;
// When moving up, only trigger if drag position is above 50% and
// when moving down, only trigger if drag position is below 50%.
// If we're moving down the hoverIndex needs to be increased
// by one so it's ordered properly. Otherwise the hoverIndex will work.
// Dragging downwards
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
return;
}
// Dragging upwards
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
return;
}
onColumnDragMove(dragIndex, hoverIndex);
},
});
const [{ isDragging }, dragRef, previewRef] = useDrag<
DragItem,
unknown,
{ isDragging: boolean }
>({
type: DragType.TableColumn,
item: () => {
return {
name,
index,
};
},
collect: (monitor: DragSourceMonitor<unknown, unknown>) => ({
isDragging: monitor.isDragging(),
}),
end: (_item: DragItem, monitor) => {
onColumnDragEnd(monitor.didDrop());
},
});
dropRef(previewRef(ref));
const isBefore = !isDragging && isDraggingUp && isOver;
const isAfter = !isDragging && isDraggingDown && isOver;
return (
<div ref={ref} className={styles.columnContainer}>
{isBefore ? (
<div
className={classNames(styles.placeholder, styles.placeholderBefore)}
/>
) : null}
<div
className={classNames(styles.column, isDragging && styles.isDragging)}
>
<label className={styles.label}>
<CheckInput
containerClassName={styles.checkContainer}
name={name}
value={isVisible}
isDisabled={isModifiable === false}
onChange={onVisibleChange}
/>
{typeof label === 'function' ? label() : label}
</label>
{isModifiable ? (
<div ref={dragRef} className={styles.dragHandle}>
<Icon className={styles.dragIcon} name={icons.REORDER} />
</div>
) : null}
</div>
{isAfter ? (
<div
className={classNames(styles.placeholder, styles.placeholderAfter)}
/>
) : null}
</div>
);
}
export default TableOptionsColumn;
@@ -1,4 +0,0 @@
.dragPreview {
width: 380px;
opacity: 0.75;
}
@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'dragPreview': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -1,78 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { DragLayer } from 'react-dnd';
import DragPreviewLayer from 'Components/DragPreviewLayer';
import { TABLE_COLUMN } from 'Helpers/dragTypes';
import dimensions from 'Styles/Variables/dimensions.js';
import TableOptionsColumn from './TableOptionsColumn';
import styles from './TableOptionsColumnDragPreview.css';
const formGroupSmallWidth = parseInt(dimensions.formGroupSmallWidth);
const formLabelLargeWidth = parseInt(dimensions.formLabelLargeWidth);
const formLabelRightMarginWidth = parseInt(dimensions.formLabelRightMarginWidth);
const dragHandleWidth = parseInt(dimensions.dragHandleWidth);
function collectDragLayer(monitor) {
return {
item: monitor.getItem(),
itemType: monitor.getItemType(),
currentOffset: monitor.getSourceClientOffset()
};
}
class TableOptionsColumnDragPreview extends Component {
//
// Render
render() {
const {
item,
itemType,
currentOffset
} = this.props;
if (!currentOffset || itemType !== TABLE_COLUMN) {
return null;
}
// The offset is shifted because the drag handle is on the right edge of the
// list item and the preview is wider than the drag handle.
const { x, y } = currentOffset;
const handleOffset = formGroupSmallWidth - formLabelLargeWidth - formLabelRightMarginWidth - dragHandleWidth;
const transform = `translate3d(${x - handleOffset}px, ${y}px, 0)`;
const style = {
position: 'absolute',
WebkitTransform: transform,
msTransform: transform,
transform
};
return (
<DragPreviewLayer>
<div
className={styles.dragPreview}
style={style}
>
<TableOptionsColumn
isDragging={false}
{...item}
/>
</div>
</DragPreviewLayer>
);
}
}
TableOptionsColumnDragPreview.propTypes = {
item: PropTypes.object,
itemType: PropTypes.string,
currentOffset: PropTypes.shape({
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired
})
};
export default DragLayer(collectDragLayer)(TableOptionsColumnDragPreview);
@@ -1,18 +0,0 @@
.columnDragSource {
padding: 4px 0;
}
.columnPlaceholder {
width: 100%;
height: 36px;
border: 1px dotted #aaa;
border-radius: 4px;
}
.columnPlaceholderBefore {
margin-bottom: 8px;
}
.columnPlaceholderAfter {
margin-top: 8px;
}
@@ -1,10 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'columnDragSource': string;
'columnPlaceholder': string;
'columnPlaceholderAfter': string;
'columnPlaceholderBefore': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -1,164 +0,0 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { DragSource, DropTarget } from 'react-dnd';
import { findDOMNode } from 'react-dom';
import { TABLE_COLUMN } from 'Helpers/dragTypes';
import TableOptionsColumn from './TableOptionsColumn';
import styles from './TableOptionsColumnDragSource.css';
const columnDragSource = {
beginDrag(column) {
return column;
},
endDrag(props, monitor, component) {
props.onColumnDragEnd(monitor.getItem(), monitor.didDrop());
}
};
const columnDropTarget = {
hover(props, monitor, component) {
const dragIndex = monitor.getItem().index;
const hoverIndex = props.index;
const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
const clientOffset = monitor.getClientOffset();
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
if (dragIndex === hoverIndex) {
return;
}
// When moving up, only trigger if drag position is above 50% and
// when moving down, only trigger if drag position is below 50%.
// If we're moving down the hoverIndex needs to be increased
// by one so it's ordered properly. Otherwise the hoverIndex will work.
// Dragging downwards
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
return;
}
// Dragging upwards
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
return;
}
props.onColumnDragMove(dragIndex, hoverIndex);
}
};
function collectDragSource(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging()
};
}
function collectDropTarget(connect, monitor) {
return {
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver()
};
}
class TableOptionsColumnDragSource extends Component {
//
// Render
render() {
const {
name,
label,
isVisible,
isModifiable,
index,
isDragging,
isDraggingUp,
isDraggingDown,
isOver,
connectDragSource,
connectDropTarget,
onVisibleChange
} = this.props;
const isBefore = !isDragging && isDraggingUp && isOver;
const isAfter = !isDragging && isDraggingDown && isOver;
// if (isDragging && !isOver) {
// return null;
// }
return connectDropTarget(
<div
className={classNames(
styles.columnDragSource,
isBefore && styles.isDraggingUp,
isAfter && styles.isDraggingDown
)}
>
{
isBefore &&
<div
className={classNames(
styles.columnPlaceholder,
styles.columnPlaceholderBefore
)}
/>
}
<TableOptionsColumn
name={name}
label={typeof label === 'function' ? label() : label}
isVisible={isVisible}
isModifiable={isModifiable}
index={index}
isDragging={isDragging}
isOver={isOver}
connectDragSource={connectDragSource}
onVisibleChange={onVisibleChange}
/>
{
isAfter &&
<div
className={classNames(
styles.columnPlaceholder,
styles.columnPlaceholderAfter
)}
/>
}
</div>
);
}
}
TableOptionsColumnDragSource.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
isVisible: PropTypes.bool.isRequired,
isModifiable: PropTypes.bool.isRequired,
index: PropTypes.number.isRequired,
isDragging: PropTypes.bool,
isDraggingUp: PropTypes.bool,
isDraggingDown: PropTypes.bool,
isOver: PropTypes.bool,
connectDragSource: PropTypes.func,
connectDropTarget: PropTypes.func,
onVisibleChange: PropTypes.func.isRequired,
onColumnDragMove: PropTypes.func.isRequired,
onColumnDragEnd: PropTypes.func.isRequired
};
export default DropTarget(
TABLE_COLUMN,
columnDropTarget,
collectDropTarget
)(DragSource(
TABLE_COLUMN,
columnDragSource,
collectDragSource
)(TableOptionsColumnDragSource));
@@ -1,263 +0,0 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { DndProvider } from 'react-dnd-multi-backend';
import HTML5toTouch from 'react-dnd-multi-backend/dist/esm/HTML5toTouch';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormInputHelpText from 'Components/Form/FormInputHelpText';
import FormLabel from 'Components/Form/FormLabel';
import Button from 'Components/Link/Button';
import Modal from 'Components/Modal/Modal';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import TableOptionsColumn from './TableOptionsColumn';
import TableOptionsColumnDragPreview from './TableOptionsColumnDragPreview';
import TableOptionsColumnDragSource from './TableOptionsColumnDragSource';
import styles from './TableOptionsModal.css';
class TableOptionsModal extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
hasPageSize: !!props.pageSize,
pageSize: props.pageSize,
pageSizeError: null,
dragIndex: null,
dropIndex: null
};
}
componentDidUpdate(prevProps) {
if (prevProps.pageSize !== this.state.pageSize) {
this.setState({ pageSize: this.props.pageSize });
}
}
//
// Listeners
onPageSizeChange = ({ value }) => {
let pageSizeError = null;
const maxPageSize = this.props.maxPageSize ?? 250;
if (value < 5) {
pageSizeError = translate('TablePageSizeMinimum', { minimumValue: '5' });
} else if (value > maxPageSize) {
pageSizeError = translate('TablePageSizeMaximum', { maximumValue: `${maxPageSize}` });
} else {
this.props.onTableOptionChange({ pageSize: value });
}
this.setState({
pageSize: value,
pageSizeError
});
};
onVisibleChange = ({ name, value }) => {
const columns = _.cloneDeep(this.props.columns);
const column = _.find(columns, { name });
column.isVisible = value;
this.props.onTableOptionChange({ columns });
};
onColumnDragMove = (dragIndex, dropIndex) => {
if (this.state.dragIndex !== dragIndex || this.state.dropIndex !== dropIndex) {
this.setState({
dragIndex,
dropIndex
});
}
};
onColumnDragEnd = ({ id }, didDrop) => {
const {
dragIndex,
dropIndex
} = this.state;
if (didDrop && dropIndex !== null) {
const columns = _.cloneDeep(this.props.columns);
const items = columns.splice(dragIndex, 1);
columns.splice(dropIndex, 0, items[0]);
this.props.onTableOptionChange({ columns });
}
this.setState({
dragIndex: null,
dropIndex: null
});
};
//
// Render
render() {
const {
isOpen,
columns,
canModifyColumns,
optionsComponent: OptionsComponent,
onTableOptionChange,
onModalClose
} = this.props;
const {
hasPageSize,
pageSize,
pageSizeError,
dragIndex,
dropIndex
} = this.state;
const isDragging = dropIndex !== null;
const isDraggingUp = isDragging && dropIndex < dragIndex;
const isDraggingDown = isDragging && dropIndex > dragIndex;
return (
<DndProvider options={HTML5toTouch}>
<Modal
isOpen={isOpen}
onModalClose={onModalClose}
>
{
isOpen ?
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{translate('TableOptions')}
</ModalHeader>
<ModalBody>
<Form>
{
hasPageSize ?
<FormGroup>
<FormLabel>{translate('TablePageSize')}</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="pageSize"
value={pageSize || 0}
helpText={translate('TablePageSizeHelpText')}
errors={pageSizeError ? [{ message: pageSizeError }] : undefined}
onChange={this.onPageSizeChange}
/>
</FormGroup> :
null
}
{
OptionsComponent ?
<OptionsComponent
onTableOptionChange={onTableOptionChange}
/> : null
}
{
canModifyColumns ?
<FormGroup>
<FormLabel>{translate('TableColumns')}</FormLabel>
<div>
<FormInputHelpText
text={translate('TableColumnsHelpText')}
/>
<div className={styles.columns}>
{
columns.map((column, index) => {
const {
name,
label,
columnLabel,
isVisible,
isModifiable
} = column;
if (isModifiable !== false) {
return (
<TableOptionsColumnDragSource
key={name}
name={name}
label={columnLabel || label}
isVisible={isVisible}
isModifiable={true}
index={index}
isDragging={isDragging}
isDraggingUp={isDraggingUp}
isDraggingDown={isDraggingDown}
onVisibleChange={this.onVisibleChange}
onColumnDragMove={this.onColumnDragMove}
onColumnDragEnd={this.onColumnDragEnd}
/>
);
}
return (
<TableOptionsColumn
key={name}
name={name}
label={columnLabel || label}
isVisible={isVisible}
index={index}
isModifiable={false}
onVisibleChange={this.onVisibleChange}
/>
);
})
}
<TableOptionsColumnDragPreview />
</div>
</div>
</FormGroup> :
null
}
</Form>
</ModalBody>
<ModalFooter>
<Button
onPress={onModalClose}
>
{translate('Close')}
</Button>
</ModalFooter>
</ModalContent> :
null
}
</Modal>
</DndProvider>
);
}
}
TableOptionsModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
pageSize: PropTypes.number,
maxPageSize: PropTypes.number,
canModifyColumns: PropTypes.bool.isRequired,
optionsComponent: PropTypes.elementType,
onTableOptionChange: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
TableOptionsModal.defaultProps = {
canModifyColumns: true
};
export default TableOptionsModal;
@@ -0,0 +1,216 @@
import _ from 'lodash';
import { HTML5toTouch } from 'rdndmb-html5-to-touch';
import React, { useCallback, useEffect, useState } from 'react';
import { DndProvider } from 'react-dnd-multi-backend';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormInputHelpText from 'Components/Form/FormInputHelpText';
import FormLabel from 'Components/Form/FormLabel';
import Button from 'Components/Link/Button';
import Modal from 'Components/Modal/Modal';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes } from 'Helpers/Props';
import { CheckInputChanged, InputChanged } from 'typings/inputs';
import { TableOptionsChangePayload } from 'typings/Table';
import translate from 'Utilities/String/translate';
import Column from '../Column';
import TableOptionsColumn from './TableOptionsColumn';
import styles from './TableOptionsModal.css';
interface TableOptionsModalProps {
isOpen: boolean;
columns: Column[];
pageSize?: number;
maxPageSize?: number;
canModifyColumns: boolean;
optionsComponent?: React.ElementType;
onTableOptionChange: (payload: TableOptionsChangePayload) => void;
onModalClose: () => void;
}
function TableOptionsModal({
isOpen,
columns,
canModifyColumns = true,
optionsComponent: OptionsComponent,
pageSize: propsPageSize,
maxPageSize = 250,
onTableOptionChange,
onModalClose,
}: TableOptionsModalProps) {
const [pageSize, setPageSize] = useState(propsPageSize);
const [pageSizeError, setPageSizeError] = useState<string | null>(null);
const [dragIndex, setDragIndex] = useState<number | null>(null);
const [dropIndex, setDropIndex] = useState<number | null>(null);
const hasPageSize = !!propsPageSize;
const isDragging = dropIndex !== null;
const isDraggingUp =
isDragging &&
dropIndex != null &&
dragIndex != null &&
dropIndex < dragIndex;
const isDraggingDown =
isDragging &&
dropIndex != null &&
dragIndex != null &&
dropIndex > dragIndex;
const handlePageSizeChange = useCallback(
({ value }: InputChanged<number>) => {
let error: string | null = null;
if (value < 5) {
error = translate('TablePageSizeMinimum', {
minimumValue: '5',
});
} else if (value > maxPageSize) {
error = translate('TablePageSizeMaximum', {
maximumValue: `${maxPageSize}`,
});
} else {
onTableOptionChange({ pageSize: value });
}
setPageSize(value);
setPageSizeError(error);
},
[maxPageSize, onTableOptionChange]
);
const handleVisibleChange = useCallback(
({ name, value }: CheckInputChanged) => {
const newColumns = columns.map((column) => {
if (column.name === name) {
return {
...column,
isVisible: value,
};
}
return column;
});
onTableOptionChange({ columns: newColumns });
},
[columns, onTableOptionChange]
);
const handleColumnDragMove = useCallback(
(newDragIndex: number, newDropIndex: number) => {
setDropIndex(newDropIndex);
setDragIndex(newDragIndex);
},
[]
);
const handleColumnDragEnd = useCallback(
(didDrop: boolean) => {
if (didDrop && dragIndex && dropIndex !== null) {
const newColumns = [...columns];
const items = newColumns.splice(dragIndex, 1);
newColumns.splice(dropIndex, 0, items[0]);
onTableOptionChange({ columns: newColumns });
}
setDragIndex(null);
setDropIndex(null);
},
[dragIndex, dropIndex, columns, onTableOptionChange]
);
useEffect(() => {
setPageSize(propsPageSize);
}, [propsPageSize]);
return (
<DndProvider options={HTML5toTouch}>
<Modal isOpen={isOpen} onModalClose={onModalClose}>
{isOpen ? (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>{translate('TableOptions')}</ModalHeader>
<ModalBody>
<Form>
{hasPageSize ? (
<FormGroup>
<FormLabel>{translate('TablePageSize')}</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="pageSize"
value={pageSize || 0}
helpText={translate('TablePageSizeHelpText')}
errors={
pageSizeError ? [{ message: pageSizeError }] : undefined
}
onChange={handlePageSizeChange}
/>
</FormGroup>
) : null}
{OptionsComponent ? (
<OptionsComponent onTableOptionChange={onTableOptionChange} />
) : null}
{canModifyColumns ? (
<FormGroup>
<FormLabel>{translate('TableColumns')}</FormLabel>
<div>
<FormInputHelpText
text={translate('TableColumnsHelpText')}
/>
<div className={styles.columns}>
{columns.map((column, index) => {
const {
name,
label,
columnLabel,
isVisible,
isModifiable = true,
} = column;
return (
<TableOptionsColumn
key={name}
name={name}
label={columnLabel ?? label}
isVisible={isVisible}
isModifiable={isModifiable}
index={index}
isDraggingUp={isDraggingUp}
isDraggingDown={isDraggingDown}
onVisibleChange={handleVisibleChange}
onColumnDragMove={handleColumnDragMove}
onColumnDragEnd={handleColumnDragEnd}
/>
);
})}
</div>
</div>
</FormGroup>
) : null}
</Form>
</ModalBody>
<ModalFooter>
<Button onPress={onModalClose}>{translate('Close')}</Button>
</ModalFooter>
</ModalContent>
) : null}
</Modal>
</DndProvider>
);
}
TableOptionsModal.defaultProps = {
canModifyColumns: true,
};
export default TableOptionsModal;