import React, { useEffect, useMemo } from "react"; import Table from "./Table"; import { AddNewRowActions, CompactMode, ReactTableColumnProps, ReactTableFilter, } from "./Constants"; import { Row } from "react-table"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import equal from "fast-deep-equal/es6"; import { ColumnTypes, EditableCell, TableVariant } from "../constants"; import { useCallback } from "react"; export interface ColumnMenuOptionProps { content: string | JSX.Element; closeOnClick?: boolean; isSelected?: boolean; editColumnName?: boolean; columnAccessor?: string; id?: string; category?: boolean; options?: ColumnMenuSubOptionProps[]; onClick?: (columnIndex: number, isSelected: boolean) => void; } export interface ColumnMenuSubOptionProps { content: string | JSX.Element; isSelected?: boolean; closeOnClick?: boolean; onClick?: (columnIndex: number) => void; id?: string; category?: boolean; isHeader?: boolean; } interface ReactTableComponentProps { widgetId: string; widgetName: string; searchKey: string; isDisabled?: boolean; isVisible?: boolean; isLoading: boolean; editMode: boolean; editableCell: EditableCell; width: number; height: number; pageSize: number; totalRecordsCount?: number; tableData: Array>; disableDrag: (disable: boolean) => void; onBulkEditDiscard: () => void; onBulkEditSave: () => void; onRowClick: (rowData: Record, rowIndex: number) => void; selectAllRow: (pageData: Row>[]) => void; unSelectAllRow: (pageData: Row>[]) => void; updatePageNo: (pageNo: number, event?: EventType) => void; sortTableColumn: (column: string, asc: boolean) => void; nextPageClick: () => void; prevPageClick: () => void; pageNo: number; serverSidePaginationEnabled: boolean; selectedRowIndex: number; selectedRowIndices: number[]; multiRowSelection?: boolean; hiddenColumns?: string[]; triggerRowSelection: boolean; columnWidthMap?: { [key: string]: number }; handleResizeColumn: (columnWidthMap: { [key: string]: number }) => void; handleReorderColumn: (columnOrder: string[]) => void; searchTableData: (searchKey: any) => void; filters?: ReactTableFilter[]; applyFilter: (filters: ReactTableFilter[]) => void; columns: ReactTableColumnProps[]; compactMode?: CompactMode; isVisibleSearch?: boolean; isVisibleFilters?: boolean; isVisibleDownload?: boolean; isVisiblePagination?: boolean; delimiter: string; isSortable?: boolean; accentColor: string; borderRadius: string; boxShadow: string; borderColor?: string; borderWidth?: number; variant?: TableVariant; isEditableCellsValid?: Record; primaryColumnId?: string; isAddRowInProgress: boolean; allowAddNewRow: boolean; onAddNewRow: () => void; onAddNewRowAction: ( type: AddNewRowActions, onActionComplete: () => void, ) => void; allowRowSelection: boolean; allowSorting: boolean; disabledAddNewRowSave: boolean; } function ReactTableComponent(props: ReactTableComponentProps) { const { allowAddNewRow, allowRowSelection, allowSorting, applyFilter, borderColor, borderWidth, columns, columnWidthMap, compactMode, delimiter, disabledAddNewRowSave, disableDrag, editableCell, editMode, filters, handleReorderColumn, handleResizeColumn, height, isAddRowInProgress, isLoading, isSortable, isVisibleDownload, isVisibleFilters, isVisiblePagination, isVisibleSearch, multiRowSelection, nextPageClick, onAddNewRow, onAddNewRowAction, onBulkEditDiscard, onBulkEditSave, onRowClick, pageNo, pageSize, prevPageClick, primaryColumnId, searchKey, searchTableData, selectAllRow, selectedRowIndex, selectedRowIndices, serverSidePaginationEnabled, sortTableColumn: _sortTableColumn, tableData, totalRecordsCount, triggerRowSelection, unSelectAllRow, updatePageNo, variant, widgetId, widgetName, width, } = props; const { columnOrder, hiddenColumns } = useMemo(() => { const order: string[] = []; const hidden: string[] = []; columns.forEach((item) => { if (item.isHidden) { hidden.push(item.alias); } else { order.push(item.alias); } }); return { columnOrder: order, hiddenColumns: hidden, }; }, [columns]); useEffect(() => { let dragged = -1; const headers = Array.prototype.slice.call( document.querySelectorAll(`#table${widgetId} .draggable-header`), ); headers.forEach((header, i) => { header.setAttribute("draggable", true); header.ondragstart = (e: React.DragEvent) => { header.style = "background: #efefef; border-radius: 4px; z-index: 100; width: 100%; text-overflow: none; overflow: none;"; e.stopPropagation(); dragged = i; }; header.ondrag = (e: React.DragEvent) => { e.stopPropagation(); }; header.ondragend = (e: React.DragEvent) => { header.style = ""; e.stopPropagation(); setTimeout(() => (dragged = -1), 1000); }; // the dropped header header.ondragover = (e: React.DragEvent) => { if (i !== dragged && dragged !== -1) { if (dragged > i) { header.parentElement.className = "th header-reorder highlight-left"; } else if (dragged < i) { header.parentElement.className = "th header-reorder highlight-right"; } } e.preventDefault(); }; header.ondragenter = (e: React.DragEvent) => { if (i !== dragged && dragged !== -1) { if (dragged > i) { header.parentElement.className = "th header-reorder highlight-left"; } else if (dragged < i) { header.parentElement.className = "th header-reorder highlight-right"; } } e.preventDefault(); }; header.ondragleave = (e: React.DragEvent) => { header.parentElement.className = "th header-reorder"; e.preventDefault(); }; header.ondrop = (e: React.DragEvent) => { header.style = ""; header.parentElement.className = "th header-reorder"; if (i !== dragged && dragged !== -1) { e.preventDefault(); const newColumnOrder = [...columnOrder]; // The dragged column const movedColumnName = newColumnOrder.splice(dragged, 1); // If the dragged column exists if (movedColumnName && movedColumnName.length === 1) { newColumnOrder.splice(i, 0, movedColumnName[0]); } handleReorderColumn([...newColumnOrder, ...hiddenColumns]); } else { dragged = -1; } }; }); }, [props.columns.map((column) => column.alias).toString()]); const sortTableColumn = (columnIndex: number, asc: boolean) => { if (allowSorting) { if (columnIndex === -1) { _sortTableColumn("", asc); } else { const column = columns[columnIndex]; const columnType = column.metaProperties?.type || ColumnTypes.TEXT; if ( columnType !== ColumnTypes.IMAGE && columnType !== ColumnTypes.VIDEO ) { _sortTableColumn(column.alias, asc); } } } }; const selectTableRow = (row: { original: Record; index: number; }) => { if (allowRowSelection) { onRowClick(row.original, row.index); } }; const toggleAllRowSelect = ( isSelect: boolean, pageData: Row>[], ) => { if (allowRowSelection) { if (isSelect) { selectAllRow(pageData); } else { unSelectAllRow(pageData); } } }; const memoziedDisableDrag = useCallback(() => disableDrag(true), [ disableDrag, ]); const memoziedEnableDrag = useCallback(() => disableDrag(false), [ disableDrag, ]); return ( ); } export default React.memo(ReactTableComponent, (prev, next) => { return ( prev.applyFilter === next.applyFilter && prev.compactMode === next.compactMode && prev.delimiter === next.delimiter && prev.disableDrag === next.disableDrag && prev.editMode === next.editMode && prev.isSortable === next.isSortable && prev.filters === next.filters && prev.handleReorderColumn === next.handleReorderColumn && prev.handleResizeColumn === next.handleResizeColumn && prev.height === next.height && prev.isLoading === next.isLoading && prev.isVisibleDownload === next.isVisibleDownload && prev.isVisibleFilters === next.isVisibleFilters && prev.isVisiblePagination === next.isVisiblePagination && prev.isVisibleSearch === next.isVisibleSearch && prev.nextPageClick === next.nextPageClick && prev.onRowClick === next.onRowClick && prev.pageNo === next.pageNo && prev.pageSize === next.pageSize && prev.prevPageClick === next.prevPageClick && prev.searchKey === next.searchKey && prev.searchTableData === next.searchTableData && prev.selectedRowIndex === next.selectedRowIndex && prev.selectedRowIndices === next.selectedRowIndices && prev.serverSidePaginationEnabled === next.serverSidePaginationEnabled && prev.sortTableColumn === next.sortTableColumn && prev.totalRecordsCount === next.totalRecordsCount && prev.triggerRowSelection === next.triggerRowSelection && prev.updatePageNo === next.updatePageNo && prev.widgetId === next.widgetId && prev.widgetName === next.widgetName && prev.width === next.width && prev.borderRadius === next.borderRadius && prev.boxShadow === next.boxShadow && prev.borderWidth === next.borderWidth && prev.borderColor === next.borderColor && prev.accentColor === next.accentColor && equal(prev.columnWidthMap, next.columnWidthMap) && equal(prev.tableData, next.tableData) && // Using JSON stringify becuase isEqual doesnt work with functions, // and we are not changing the columns manually. JSON.stringify(prev.columns) === JSON.stringify(next.columns) && equal(prev.editableCell, next.editableCell) && prev.variant === next.variant && prev.primaryColumnId === next.primaryColumnId && equal(prev.isEditableCellsValid, next.isEditableCellsValid) && prev.isAddRowInProgress === next.isAddRowInProgress && prev.allowAddNewRow === next.allowAddNewRow && prev.allowRowSelection === next.allowRowSelection && prev.allowSorting === next.allowSorting && prev.disabledAddNewRowSave === next.disabledAddNewRowSave ); });