UNPKG

@vaadin/hilla-react-crud

Version:

Hilla CRUD utils for React

187 lines 8.64 kB
import { jsx as _jsx } from "react/jsx-runtime"; import { Grid } from '@vaadin/react-components/Grid.js'; import { GridColumn } from '@vaadin/react-components/GridColumn.js'; import { GridColumnGroup } from '@vaadin/react-components/GridColumnGroup.js'; import { cloneElement, forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react'; import { ColumnContext, CustomColumnContext } from './autogrid-column-context.js'; import { getColumnOptions } from './autogrid-columns.js'; import { AutoGridFooterItemCountRenderer, AutoGridRowNumberRenderer, FooterContext } from './autogrid-renderers.js'; import css from './autogrid.obj.js'; import { createDataProvider, isCountService } from './data-provider.js'; import { NoHeaderFilter, HeaderFilterWrapper } from './header-filter'; import { HeaderSorter } from './header-sorter'; import { getDefaultProperties, ModelInfo } from './model-info.js'; import { isFilterEmpty, registerStylesheet } from './util'; registerStylesheet(css); function wrapCustomColumn(column, setColumnFilter, options) { const key = column.key ?? 'no-key'; const { header, headerRenderer } = column.props; const customOptions = options.columnOptions?.[key]; const { header: customHeader, headerRenderer: customHeaderRenderer, headerFilterRenderer } = customOptions ?? {}; const columnWithoutHeader = cloneElement(column, { header: null, headerRenderer: HeaderFilterWrapper, }); return (_jsx(CustomColumnContext.Provider, { value: { setColumnFilter, headerFilterRenderer: headerFilterRenderer ?? NoHeaderFilter, filterKey: key, }, children: _jsx(GridColumnGroup, { header: customHeader ?? header, headerRenderer: customHeaderRenderer ?? headerRenderer, children: columnWithoutHeader }, key) }, key)); } function addCustomColumns(columns, options, setColumnFilter) { if (!options.customColumns) { return columns; } const customColumns = options.noHeaderFilters ? options.customColumns : options.customColumns.map((column) => wrapCustomColumn(column, setColumnFilter, options)); if (options.visibleColumns) { const columnMap = [...columns, ...customColumns].reduce((map, column) => { const { key } = column; if (key) { map.set(key, column); } return map; }, new Map()); return options.visibleColumns.map((path) => columnMap.get(path)).filter(Boolean); } return [...columns, ...customColumns]; } function useColumns(properties, setColumnFilter, options) { const sortableProperties = properties.filter((propertyInfo) => options.columnOptions?.[propertyInfo.name]?.sortable !== false); const [sortState, setSortState] = useState(sortableProperties.length > 0 ? { [sortableProperties[0].name]: { direction: 'asc' } } : {}); let columns = properties.map((propertyInfo) => { let column; const customColumnOptions = options.columnOptions?.[propertyInfo.name]; const { headerFilterRenderer, ...columnProps } = getColumnOptions(propertyInfo, customColumnOptions); if (!options.noHeaderFilters) { column = (_jsx(GridColumnGroup, { headerRenderer: HeaderSorter, children: _jsx(GridColumn, { path: propertyInfo.name, headerRenderer: HeaderFilterWrapper, ...columnProps }) })); } else { column = _jsx(GridColumn, { path: propertyInfo.name, headerRenderer: HeaderSorter, ...columnProps }); } return (_jsx(ColumnContext.Provider, { value: { propertyInfo, setColumnFilter, sortState, setSortState, customColumnOptions, headerFilterRenderer: headerFilterRenderer ?? NoHeaderFilter, filterKey: propertyInfo.name, }, children: column }, propertyInfo.name)); }); columns = addCustomColumns(columns, options, setColumnFilter); if (options.hiddenColumns) { columns = columns.filter(({ key }) => !(key && options.hiddenColumns?.includes(key))); } if (options.rowNumbers) { columns = [ _jsx(GridColumn, { width: "4em", flexGrow: 0, renderer: AutoGridRowNumberRenderer }, "rownumbers"), ...columns, ]; } const { totalCount, filteredCount, itemCounts, footerCountRenderer } = options; if (totalCount ?? filteredCount) { const col = (_jsx(FooterContext.Provider, { value: { totalCount, filteredCount, footerCountRenderer, itemCounts, }, children: _jsx(GridColumnGroup, { footerRenderer: AutoGridFooterItemCountRenderer, children: columns }) }, "grid-footer")); columns = [col]; } return columns; } function AutoGridInner({ service, model, itemIdProperty, experimentalFilter, visibleColumns, hiddenColumns, noHeaderFilters, customColumns, columnOptions, rowNumbers, totalCount, filteredCount, footerCountRenderer, ...gridProps }, ref) { const [internalFilter, setInternalFilter] = useState({ '@type': 'and', children: [] }); const [itemCounts, setItemCounts] = useState(); const gridRef = useRef(null); const dataProviderRef = useRef(undefined); useImperativeHandle(ref, () => ({ get grid() { return gridRef.current; }, refresh() { dataProviderRef.current?.reset(); gridRef.current?.clearCache(); }, }), []); const setHeaderFilter = (filter, filterKey) => { let changed = false; filter.key = filterKey; const indexOfFilter = filterKey ? internalFilter.children.findIndex((f) => f.key === filterKey) : -1; const isEmptyFilter = isFilterEmpty(filter); if (indexOfFilter >= 0 && isEmptyFilter) { internalFilter.children.splice(indexOfFilter, 1); changed = true; } else if (!isEmptyFilter) { if (indexOfFilter >= 0) { internalFilter.children[indexOfFilter] = filter; changed = true; } else { internalFilter.children.push(filter); changed = true; } } if (changed) { setInternalFilter({ ...internalFilter }); } }; const modelInfo = useMemo(() => new ModelInfo(model, itemIdProperty), [model]); const properties = visibleColumns ? modelInfo.getProperties(visibleColumns) : getDefaultProperties(modelInfo); const children = useColumns(properties, setHeaderFilter, { visibleColumns, hiddenColumns, noHeaderFilters, customColumns, columnOptions, rowNumbers, totalCount, filteredCount, footerCountRenderer, itemCounts, }); useEffect(() => { if (noHeaderFilters) { setInternalFilter({ '@type': 'and', children: [] }); } }, [noHeaderFilters]); useEffect(() => { if ((!isCountService(service) && totalCount) ?? filteredCount) { console.error('"totalCount" and "filteredCount" props require the provided service to implement the CountService interface.'); } const grid = gridRef.current; const timeoutId = setTimeout(() => { let firstUpdate = true; const dataProvider = createDataProvider(service, { initialFilter: experimentalFilter ?? internalFilter, loadTotalCount: totalCount, afterLoad(newItemCounts) { setItemCounts(newItemCounts); if (firstUpdate) { firstUpdate = false; setTimeout(() => grid.recalculateColumnWidths(), 0); } }, }); dataProviderRef.current = dataProvider; gridRef.current.dataProvider = dataProvider.load.bind(dataProvider); }, 1); return () => clearTimeout(timeoutId); }, [model, service]); useEffect(() => { const dataProvider = dataProviderRef.current; const grid = gridRef.current; if (grid && dataProvider) { dataProvider.setFilter(experimentalFilter ?? internalFilter); grid.clearCache(); } }, [experimentalFilter, internalFilter]); return (_jsx(Grid, { itemIdPath: modelInfo.idProperty?.name, ...gridProps, ref: gridRef, children: children })); } export const AutoGrid = forwardRef(AutoGridInner); //# sourceMappingURL=autogrid.js.map