UNPKG

@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
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);