/* * Copyright (c) 2020 - present Cloudogu GmbH * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see https://www.gnu.org/licenses/. */ import React, { FC, ReactElement, useEffect, useState } from "react"; import { Comparator } from "./types"; import SortIcon from "./SortIcon"; import Notification from "../Notification"; import classNames from "classnames"; type Props = { data: any[]; sortable?: boolean; emptyMessage?: string; children: Array; className?: string; }; const Table: FC = ({ data, sortable = true, children, emptyMessage, className }) => { const [tableData, setTableData] = useState(data); useEffect(() => { setTableData(data); }, [data]); const [ascending, setAscending] = useState(false); const [lastSortBy, setlastSortBy] = useState(); const [hoveredColumnIndex, setHoveredColumnIndex] = useState(); const isSortable = (child: ReactElement) => { // @ts-ignore return sortable && child.props.createComparator; }; const sortFunctions: Comparator | undefined[] = []; React.Children.forEach(children, (child, index) => { if (child && isSortable(child as ReactElement)) { // @ts-ignore sortFunctions.push((child as ReactElement).props.createComparator((child as ReactElement).props, index)); } else { sortFunctions.push(undefined); } }); const mapDataToColumns = (row: any, rowIndex: number) => { return ( {React.Children.map(children, (child, columnIndex) => { // @ts-ignore const { className: columnClassName, ...childProperties } = (child as ReactElement).props; return ( {React.cloneElement(child as ReactElement, { ...childProperties, columnIndex, rowIndex, row })} ); })} ); }; const sortDescending = (sortAscending: (a: any, b: any) => number) => { return (a: any, b: any) => { return sortAscending(a, b) * -1; }; }; const tableSort = (index: number) => { const sortFn = sortFunctions[index]; if (!sortFn) { throw new Error(`column with index ${index} is not sortable`); } const sortableData = [...tableData]; let sortOrder = ascending; if (lastSortBy !== index) { setAscending(true); sortOrder = true; } const sortFunction = sortOrder ? sortFn : sortDescending(sortFn); sortableData.sort(sortFunction); setTableData(sortableData); setAscending(!sortOrder); setlastSortBy(index); }; const shouldShowIcon = (index: number) => { return index === lastSortBy || index === hoveredColumnIndex; }; if (!tableData || tableData.length <= 0) { if (emptyMessage) { return {emptyMessage}; } else { return null; } } return ( {React.Children.map(children, (child, index) => ( ))} {tableData.map(mapDataToColumns)}
tableSort(index) : undefined} onMouseEnter={() => setHoveredColumnIndex(index)} onMouseLeave={() => setHoveredColumnIndex(undefined)} key={index} > {/* @ts-ignore */} {(child as ReactElement).props.header} {isSortable(child as ReactElement) && renderSortIcon(child as ReactElement, ascending, shouldShowIcon(index))}
); }; const renderSortIcon = (child: ReactElement, ascending: boolean, showIcon: boolean) => { // @ts-ignore if (child.props.ascendingIcon && child.props.descendingIcon) { // @ts-ignore return ; } else { return ; } }; export default Table;