import React, { useMemo, useState, useEffect } from 'react'; import { useTable, useFilters, useSortBy, useGlobalFilter, useAsyncDebounce, usePagination, useBlockLayout, useResizeColumns } from 'react-table'; import { resolveReferences } from '@/_helpers/utils'; import Skeleton from 'react-loading-skeleton'; import SelectSearch, { fuzzySearch } from 'react-select-search'; import { useExportData } from 'react-table-plugins'; import Papa from 'papaparse'; import { Pagination } from './Pagination'; import { CustomSelect } from './CustomSelect'; import { Tags } from './Tags'; import { Radio } from './Radio'; import { Toggle } from './Toggle' var _ = require('lodash'); export function Table({ id, width, height, component, onComponentClick, currentState = { components: {} }, onEvent, paramUpdated, changeCanDrag, onComponentOptionChanged, onComponentOptionsChanged, darkMode }) { const color = component.definition.styles.textColor.value; const actions = component.definition.properties.actions || { value: [] }; const serverSidePaginationProperty = component.definition.properties.serverSidePagination; const serverSidePagination = serverSidePaginationProperty ? serverSidePaginationProperty.value : false; const serverSideSearchProperty = component.definition.properties.serverSideSearch; const serverSideSearch = serverSideSearchProperty ? serverSideSearchProperty.value : false; const displaySearchBoxProperty = component.definition.properties.displaySearchBox; const displaySearchBox = displaySearchBoxProperty ? displaySearchBoxProperty.value : true; const [loadingState, setLoadingState] = useState(false); useEffect(() => { const loadingStateProperty = component.definition.properties.loadingState; if (loadingStateProperty && currentState) { const newState = resolveReferences(loadingStateProperty.value, currentState, false); setLoadingState(newState); } }, [currentState]); const [componentState, setcomponentState] = useState(currentState.components[component.component] || {}); useEffect(() => { setcomponentState(currentState.components[component.name] || {}); }, [currentState.components[component.name]]); const [isFiltersVisible, setFiltersVisibility] = useState(false); const [filters, setFilters] = useState([]); function showFilters() { setFiltersVisibility(true); } function hideFilters() { setFiltersVisibility(false); } function filterColumnChanged(index, value) { const newFilters = filters; newFilters[index].id = value; setFilters(newFilters); setAllFilters(newFilters.filter((filter) => filter.id !== '')); } function filterOperationChanged(index, value) { const newFilters = filters; newFilters[index].value = { ...newFilters[index].value, operation: value }; setFilters(newFilters); setAllFilters(newFilters.filter((filter) => filter.id !== '')); } function filterValueChanged(index, value) { const newFilters = filters; newFilters[index].value = { ...newFilters[index].value, value: value }; setFilters(newFilters); setAllFilters(newFilters.filter((filter) => filter.id !== '')); } function addFilter() { setFilters([...filters, { id: '', value: { operation: 'contains', value: '' } }]); } function removeFilter(index) { let newFilters = filters; newFilters.splice(index, 1); setFilters(newFilters); setAllFilters(newFilters); } function clearFilters() { setFilters([]); setAllFilters([]); } const defaultColumn = React.useMemo( () => ({ minWidth: 60, width: 268 }), [] ); const columnSizes = component.definition.properties.columnSizes || {}; function handleCellValueChange(index, key, value, rowData) { const changeSet = componentState.changeSet; const dataUpdates = componentState.dataUpdates || []; let obj = changeSet ? changeSet[index] || {} : {}; obj = _.set(obj, key, value); let newChangeset = { ...changeSet, [index]: { ...obj } }; obj = _.set(rowData, key, value); let newDataUpdates = { ...dataUpdates, [index]: { ...obj } }; onComponentOptionsChanged(component, [ ['dataUpdates', newDataUpdates], ['changeSet', newChangeset] ]); } function getExportFileBlob({ columns, data }) { const headerNames = columns.map((col) => col.exportValue); const csvString = Papa.unparse({ fields: headerNames, data }); return new Blob([csvString], { type: 'text/csv' }); } function onPageIndexChanged(page) { onComponentOptionChanged(component, 'pageIndex', page).then(() => { onEvent('onPageChanged', { component, data: {} }); }); } function handleChangesSaved() { Object.keys(changeSet).forEach((key) => { tableData[key] = { ..._.merge(tableData[key], changeSet[key]) }; }); onComponentOptionChanged(component, 'changeSet', {}); onComponentOptionChanged(component, 'dataUpdates', []); } function handleChangesDiscarded() { onComponentOptionChanged(component, 'changeSet', {}); onComponentOptionChanged(component, 'dataUpdates', []); } function customFilter(rows, columnIds, filterValue) { try { if (filterValue.operation === 'equals') { return rows.filter((row) => row.values[columnIds[0]] === filterValue.value); } if (filterValue.operation === 'matches') { return rows.filter((row) => row.values[columnIds[0]].toString().toLowerCase().includes(filterValue.value.toLowerCase())); } if (filterValue.operation === 'gt') { return rows.filter((row) => row.values[columnIds[0]] > filterValue.value); } if (filterValue.operation === 'lt') { return rows.filter((row) => row.values[columnIds[0]] < filterValue.value); } if (filterValue.operation === 'gte') { return rows.filter((row) => row.values[columnIds[0]] >= filterValue.value); } if (filterValue.operation === 'lte') { return rows.filter((row) => row.values[columnIds[0]] <= filterValue.value); } let value = filterValue.value; if (typeof value === 'string') { value = value.toLowerCase(); } return rows.filter((row) => { let rowValue = row.values[columnIds[0]]; if (typeof rowValue === 'string') { rowValue = rowValue.toLowerCase(); } return rowValue.includes(value); }); } catch { return rows; } } const changeSet = componentState ? componentState.changeSet : {}; const columnData = component.definition.properties.columns.value.map((column) => { const columnSize = columnSizes[column.id] || columnSizes[column.name]; const columnType = column.columnType; const columnOptions = {}; if (columnType === 'dropdown' || columnType === 'multiselect' || columnType === 'badge' || columnType === 'badges' || columnType === 'radio') { const values = resolveReferences(column.values, currentState) || []; const labels = resolveReferences(column.labels, currentState, []) || []; if (typeof labels === 'object') { columnOptions.selectOptions = labels.map((label, index) => { return { name: label, value: values[index] }; }); } } const width = columnSize || defaultColumn.width; return { id: column.id, Header: column.name, accessor: column.key || column.name, filter: customFilter, width: width, Cell: function (cell) { const rowChangeSet = changeSet ? changeSet[cell.row.index] : null; const cellValue = rowChangeSet ? rowChangeSet[column.name] || cell.value : cell.value; if (columnType === undefined || columnType === 'default') { return {cellValue}; } if (columnType === 'string') { if (column.isEditable) { return ( { if (e.key === 'Enter') { handleCellValueChange(cell.row.index, column.key || column.name, e.target.value, cell.row.original); } }} onBlur={(e) => { handleCellValueChange(cell.row.index, column.key || column.name, e.target.value, cell.row.original); }} className="form-control-plaintext form-control-plaintext-sm" defaultValue={cellValue} /> ); } return {cellValue}; } if (columnType === 'text') { return ; } if (columnType === 'dropdown') { return (
{ handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original); }} filterOptions={fuzzySearch} placeholder="Select.." />
); } if (columnType === 'multiselect') { return (
{ handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original); }} />
); } if (columnType === 'badge') { return (
{ handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original); }} />
); } if (columnType === 'badges') { return (
{ handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original); }} />
); } if (columnType === 'tags') { return (
{ handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original); }} />
); } if (columnType === 'radio') { return (
{ handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original); }} />
); } if (columnType === 'toggle') { return (
{ handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original); }} />
); } return cellValue || ''; } }; }); let tableData = []; if (currentState) { tableData = resolveReferences(component.definition.properties.data.value, currentState, []); if (!Array.isArray(tableData)) tableData = []; console.log('resolved param', tableData); } tableData = tableData || []; const actionsCellData = actions.value.length > 0 ? [ { id: 'actions', Header: 'Actions', accessor: 'edit', width: columnSizes.actions || defaultColumn.width, Cell: (cell) => { return actions.value.map((action) => ( )); } } ] : []; const columns = useMemo( () => [...columnData, ...actionsCellData], [JSON.stringify(columnData), actionsCellData.length, componentState.changeSet] // Hack: need to fix ); const data = useMemo(() => tableData, [tableData.length]); const computedStyles = { color, width: `${width}px` }; const { getTableProps, getTableBodyProps, headerGroups, page, canPreviousPage, canNextPage, pageOptions, gotoPage, pageCount, nextPage, previousPage, setPageSize, state, prepareRow, setAllFilters, preGlobalFilteredRows, setGlobalFilter, state: { pageIndex, pageSize }, exportData } = useTable( { columns, data, defaultColumn, initialState: { pageIndex: 0, pageSize: serverSidePagination ? -1 : 10}, // pageSize should be unset if server-side pagination is enabled pageCount: -1, manualPagination: false, getExportFileBlob }, useFilters, useGlobalFilter, useSortBy, usePagination, useBlockLayout, useResizeColumns, useExportData ); useEffect(() => { if (!state.columnResizing.isResizingColumn) { changeCanDrag(true); paramUpdated(id, 'columnSizes', { ...columnSizes, ...state.columnResizing.columnWidths}); } else { changeCanDrag(false); } }, [state.columnResizing.isResizingColumn]); function GlobalFilter() { const count = preGlobalFilteredRows.length; const [value, setValue] = React.useState(state.globalFilter); const onChange = useAsyncDebounce((filterValue) => { setGlobalFilter(filterValue || undefined); }, 200); const handleSearchTextChange = (text) => { setValue(text); onChange(text); onComponentOptionChanged(component, 'searchText', text).then(() => { if(serverSideSearch === true ) { onEvent('onSearch', { component, data: {} }); } }); } return (
Search:{' '} { handleSearchTextChange(e.target.value) }} onKeyDown={(e) => { if(e.key === 'Enter') { handleSearchTextChange(e.target.value) } } } placeholder={`${count} records`} style={{ border: '0' }} />
); } return (
onComponentClick(id, component)} > {/* Show top bar unless search box is disabled and server pagination is enabled */} {(!(!displaySearchBox && serverSidePagination) &&
{!serverSidePagination &&
Show
entries
} {displaySearchBox &&
}
)}
{headerGroups.map((headerGroup) => ( {headerGroup.headers.map((column) => ( ))} ))} {!loadingState && page.length === 0 &&
no data
} {!loadingState && ( {console.log('page', page)} {page.map((row) => { prepareRow(row); return ( { e.stopPropagation(); onEvent('onRowClicked', { component, data: row.original }); }} > {row.cells.map((cell) => { let cellProps = cell.getCellProps(); if (componentState.changeSet) { if (componentState.changeSet[cell.row.index]) { if (_.get(componentState.changeSet[cell.row.index], cell.column.Header, undefined) !== undefined) { console.log('componentState.changeSet', componentState.changeSet); cellProps.style.backgroundColor = '#ffffde'; } } } return ; })} ); })} )}
{column.render('Header')}
{cell.render('Cell')}
{loadingState === true && (
)}
{Object.keys(componentState.changeSet || {}).length > 0 && (
)}
showFilters()}> {filters.length > 0 && } exportData('csv', true)} >
{isFiltersVisible && (

Filters

{filters.map((filter, index) => (
{index > 0 ? 'and' : 'where'}
{ return { name: column.Header, value: column.accessor }; })} value={filter.id} search={true} onChange={(value) => { filterColumnChanged(index, value); }} filterOptions={fuzzySearch} placeholder="Select.." />
{ filterOperationChanged(index, value); }} filterOptions={fuzzySearch} placeholder="Select.." />
filterValueChanged(index, e.target.value)} />
))} {filters.length === 0 && (
no filters yet.
)}
)}
); }