@adaptabletools/adaptable
Version:
Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements
352 lines (351 loc) • 15.9 kB
JavaScript
import * as React from 'react';
import useSelection from './useSelection';
import { CheckBox } from '../../../components/CheckBox';
import Radio from '../../../components/Radio';
import Dropdown from '../../../components/Dropdown';
import Input from '../../../components/Input';
import { useRef, useState } from 'react';
import StringExtensions from '../../../Utilities/Extensions/StringExtensions';
import { DataSource, InfiniteTable } from '../../../components/InfiniteTable';
import { Box, Flex } from 'rebass';
import throttle from 'lodash/throttle';
const dataTypes = [
{
value: 'text',
label: 'Text (string)',
},
{ value: 'number', label: 'Number' },
{
value: 'date',
label: 'Date',
},
{
value: 'boolean',
label: 'Boolean',
},
];
const inputStyle = {
width: '100%',
minWidth: 80,
textAlign: 'start',
};
const useForceRender = () => {
const [, setNow] = useState(Date.now());
return () => {
setNow(Date.now());
};
};
const ThrottledInput = React.memo((props) => {
const [stateValue, setStateValue] = useState(props.value);
const throttleOnChange = React.useRef(throttle(props.onChange, 600, { leading: false }));
const handleChange = React.useCallback((event) => {
const newValue = event.target.value;
setStateValue(newValue);
throttleOnChange.current(newValue);
}, []);
React.useEffect(() => {
setStateValue(props.value);
}, [props.value]);
return React.createElement(Input, { style: props.style, value: stateValue, onChange: handleChange });
});
const tableDOMProps = {
style: {
height: '100%',
minWidth: '10rem',
minHeight: 600,
},
};
const ColumnsList = ({ columns: cols, handle, onValidityChange, onChange, onSelectionChange, selectedColumns, theme, }) => {
const rerender = useForceRender();
const columnsRef = useRef(cols);
const silentSetColumns = (columns) => {
columnsRef.current = columns;
onChange?.(columns);
};
const setColumns = (columns) => {
silentSetColumns(columns);
rerender();
};
const getColumns = () => columnsRef.current;
const columns = columnsRef.current;
const onColumnChange = (col, value, property) => {
col = { ...col, [property]: value };
const cols = getColumns().map((c) => {
if (c.field === col.field) {
return col;
}
return c;
});
setColumns(cols);
};
const onColumnBatchChange = (value, property) => {
const cols = getColumns().map((c) => ({ ...c, [property]: value }));
setColumns(cols);
};
const setColumnCaption = (caption, field) => {
const cols = getColumns().map((c) => {
if (c.field === field) {
const newCol = { ...c, caption };
return newCol;
}
return c;
});
setColumns(cols);
};
const onColumnTypeChange = (col, type) => {
const cols = getColumns().map((c) => {
if (c.field === col.field) {
return { ...c, type };
}
return c;
});
setColumns(cols);
};
const [primaryKeyField, setPrimaryKeyField] = React.useState(columns[0].field);
const { selected: includedColumnsMap, isSelected: isIncludedColumn, isAllSelected: isAllIncludedColumns, isNoneSelected: isNoneIncludedColumns, selectColumn: includeColumn, deselectColumn: excludeColumn, selectAll: includeAllColumns, deselectAll: excludeAllColumns, } = useSelection(columns, selectedColumns ?? true, null, { onChange: onSelectionChange });
handle.current = {
getColumns: () => getColumns().filter((col) => isIncludedColumn(col.field)),
getPrimaryKey: () => primaryKeyField,
};
const { isSelected: isSortableColumn, isAllSelected: isAllSortableColumns, isNoneSelected: isNoneSortableColumns, selectColumn: setSortableColumn, deselectColumn: setUnsortableColumn, selectAll: setAllSortable, deselectAll: setAllUnsortable, } = useSelection(columns, true, 'sortable', {
onBatchChange: (flag) => {
onColumnBatchChange(flag, 'sortable');
},
onChange: (col, flag) => {
onColumnChange(col, flag, 'sortable');
},
});
const { isSelected: isGroupableColumn, isAllSelected: isAllGroupableColumns, isNoneSelected: isNoneGroupableColumns, selectColumn: setGroupableColumn, deselectColumn: setUngroupableColumn, selectAll: setAllGroupable, deselectAll: setAllUngroupable, } = useSelection(columns, true, 'enableRowGroup', {
onBatchChange: (flag) => {
onColumnBatchChange(flag, 'enableRowGroup');
},
onChange: (col, flag) => {
onColumnChange(col, flag, 'enableRowGroup');
},
});
const { isSelected: isResizableColumn, isAllSelected: isAllResizableColumns, isNoneSelected: isNoneResizableColumns, selectColumn: setResizableColumn, deselectColumn: setUnresizableColumn, selectAll: setAllResizable, deselectAll: setAllUnresizable, } = useSelection(columns, true, 'resizable', {
onBatchChange: (flag) => {
onColumnBatchChange(flag, 'resizable');
},
onChange: (col, flag) => {
onColumnChange(col, flag, 'resizable');
},
});
const { isSelected: isEditableColumn, isAllSelected: isAllEditableColumns, isNoneSelected: isNoneEditableColumns, selectColumn: setEditableColumn, deselectColumn: setUneditableColumn, selectAll: setAllEditable, deselectAll: setAllUneditable, } = useSelection(columns, true, 'editable', {
onBatchChange: (flag) => {
onColumnBatchChange(flag, 'editable');
},
onChange: (col, flag) => {
onColumnChange(col, flag, 'editable');
},
});
const { isSelected: isFilterableColumn, isAllSelected: isAllFilterableColumns, isNoneSelected: isNoneFilterableColumns, selectColumn: setFilterableColumn, deselectColumn: setUnFilterableColumn, selectAll: setAllFilterable, deselectAll: setAllUnfilterable, } = useSelection(columns, true, 'filter', {
onBatchChange: (flag) => {
onColumnBatchChange(flag, 'filter');
},
onChange: (col, flag) => {
onColumnChange(col, flag, 'filter');
},
});
const allIncluded = isAllIncludedColumns();
const allExcluded = isNoneIncludedColumns();
React.useEffect(() => {
onValidityChange(isIncludedColumn(primaryKeyField));
}, [includedColumnsMap, primaryKeyField]);
const data = columns;
const CellWrapper = (props) => {
return (React.createElement(Flex, { ...props, style: {
...props.style,
position: 'absolute',
textAlign: 'center',
top: 0,
left: 0,
height: '100%',
width: '100%',
whiteSpace: 'normal',
}, justifyContent: "center", alignItems: "center" }, props.children));
};
const HeaderCellWrapper = (props) => (React.createElement(CellWrapper, { ...props, fontSize: 3, style: { fontWeight: 600 } }));
const HeaderWithCheckbox = (props) => {
const { label, onChange, checked, ...flexProps } = props;
return (React.createElement(HeaderCellWrapper, { ...flexProps, flexDirection: "column" },
React.createElement(Box, null, label),
React.createElement(Box, { style: { textAlign: 'center' } },
React.createElement(CheckBox, { checked: checked, onChange: onChange }))));
};
const columnsMap = {
pk: {
header: () => React.createElement(HeaderCellWrapper, null, "Primary Key"),
maxWidth: 80,
defaultSortable: false,
renderMenuIcon: false,
render: (params) => {
const { data } = params;
const column = data;
const isPrimaryKey = column?.field === primaryKeyField;
return (React.createElement(Radio, { checked: isPrimaryKey, onChange: (checked) => {
if (checked && isIncludedColumn(column.field)) {
setPrimaryKeyField(column.field);
}
} }));
},
},
included: {
maxWidth: 80,
resizable: true,
renderMenuIcon: false,
header: () => (React.createElement(HeaderWithCheckbox, { label: "Included", checked: allIncluded ? true : allExcluded ? false : null, onChange: (allIncluded) => {
if (allIncluded) {
includeAllColumns();
}
else {
excludeAllColumns();
}
} })),
render: (params) => {
const column = params.data;
return (React.createElement(CellWrapper, null,
React.createElement(CheckBox, { checked: isIncludedColumn(column.field), onChange: (included) => {
if (included) {
includeColumn(column.field);
}
else {
excludeColumn(column.field);
}
} })));
},
},
field: {
header: React.createElement(HeaderCellWrapper, null, "Field"),
field: 'field',
minWidth: 150,
renderMenuIcon: false,
render: (params) => {
const column = params.data;
const humanized = StringExtensions.Humanize(column.field);
return (React.createElement(Box, { p: 2 },
React.createElement(ThrottledInput, { key: column.field, style: inputStyle, value: column.caption != undefined ? column.caption : humanized, placeholder: humanized, onChange: (value) => {
setColumnCaption(value, column.field);
} })));
},
},
type: {
header: React.createElement(HeaderCellWrapper, null, "Type"),
maxWidth: 140,
renderMenuIcon: false,
render: (params) => {
const column = params.data;
return (React.createElement(CellWrapper, { paddingLeft: 1, paddingRight: 1 },
React.createElement(Dropdown, { style: inputStyle, showClearButton: false, options: dataTypes, value: column?.type ?? '', onChange: onColumnTypeChange.bind(null, column) })));
},
},
sortable: {
renderMenuIcon: false,
header: (React.createElement(HeaderWithCheckbox, { label: "Sortable", checked: isAllSortableColumns() ? true : isNoneSortableColumns() ? false : null, onChange: (allSortable) => {
if (allSortable) {
setAllSortable();
}
else {
setAllUnsortable();
}
} })),
render: (params) => {
const column = params.data;
return (React.createElement(CheckBox, { checked: isSortableColumn(column.field), onChange: (sortable) => {
sortable ? setSortableColumn(column.field) : setUnsortableColumn(column.field);
} }));
},
},
editable: {
renderMenuIcon: false,
header: (React.createElement(HeaderWithCheckbox, { label: "Editable", checked: isAllEditableColumns() ? true : isNoneEditableColumns() ? false : null, onChange: (allEditable) => {
if (allEditable) {
setAllEditable();
}
else {
setAllUneditable();
}
} })),
render: (params) => {
const col = params.data;
return (React.createElement(CellWrapper, null,
React.createElement(CheckBox, { checked: isEditableColumn(col.field), onChange: (editable) => {
editable ? setEditableColumn(col.field) : setUneditableColumn(col.field);
} })));
},
},
resizable: {
renderMenuIcon: false,
header: (React.createElement(HeaderWithCheckbox, { label: "Resizable", checked: isAllResizableColumns() ? true : isNoneResizableColumns() ? false : null, onChange: (allResizable) => {
if (allResizable) {
setAllResizable();
}
else {
setAllUnresizable();
}
} })),
render: (params) => {
const col = params.data;
return (React.createElement(CellWrapper, null,
React.createElement(CheckBox, { checked: isResizableColumn(col.field), onChange: (resizable) => {
resizable ? setResizableColumn(col.field) : setUnresizableColumn(col.field);
} })));
},
},
groupable: {
renderMenuIcon: false,
header: (React.createElement(HeaderWithCheckbox, { label: "Groupable", checked: isAllGroupableColumns() ? true : isNoneGroupableColumns() ? false : null, onChange: (allGroupable) => {
if (allGroupable) {
setAllGroupable();
}
else {
setAllUngroupable();
}
} })),
render: (params) => {
const col = params.data;
return (React.createElement(CellWrapper, null,
React.createElement(CheckBox, { checked: isGroupableColumn(col.field), onChange: (groupable) => {
groupable ? setGroupableColumn(col.field) : setUngroupableColumn(col.field);
} })));
},
},
filterable: {
renderMenuIcon: false,
header: (React.createElement(HeaderWithCheckbox, { label: "Filterable", checked: isAllFilterableColumns() ? true : isNoneFilterableColumns() ? false : null, onChange: (allFilterable) => {
if (allFilterable) {
setAllFilterable();
}
else {
setAllUnfilterable();
}
} })),
render: (params) => {
const col = params.data;
return (React.createElement(CellWrapper, null,
React.createElement(CheckBox, { checked: isFilterableColumn(col.field), onChange: (filterable) => {
filterable ? setFilterableColumn(col.field) : setUnFilterableColumn(col.field);
} })));
},
},
};
return (React.createElement(Box, { p: 2, style: { height: '100%' }, className: theme },
React.createElement(DataSource, { data: data, primaryKey: "colId" },
React.createElement(InfiniteTable, { columnTypes: {
default: {
defaultFlex: 1,
align: 'center',
defaultSortable: false,
},
}, headerOptions: {
alwaysReserveSpaceForSortIcon: false,
}, domProps: tableDOMProps, columns: columnsMap }))));
};
function areEqual() {
/**
* Make the ColumnList not render on subsequent prop changes
* in order to boost performance
*/
return true;
}
export default React.memo(ColumnsList, areEqual);