ToolJet/frontend/src/Editor/Components/Table/Table.jsx

1714 lines
66 KiB
JavaScript

/* eslint-disable no-unused-vars */
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useMemo, useState, useEffect, useCallback, useContext, useReducer, useRef } from 'react';
import {
useTable,
useFilters,
useSortBy,
useGlobalFilter,
useAsyncDebounce,
usePagination,
useBlockLayout,
useResizeColumns,
useRowSelect,
useColumnOrder,
} from 'react-table';
import cx from 'classnames';
import { resolveReferences, validateWidget, determineJustifyContentValue } from '@/_helpers/utils';
import { useExportData } from 'react-table-plugins';
import Papa from 'papaparse';
import { Pagination } from './Pagination';
import { Filter } from './Filter';
import { GlobalFilter } from './GlobalFilter';
var _ = require('lodash');
import loadPropertiesAndStyles from './load-properties-and-styles';
import { reducer, reducerActions, initialState } from './reducer';
import customFilter from './custom-filter';
import generateColumnsData from './columns';
import generateActionsData from './columns/actions';
import autogenerateColumns from './columns/autogenerateColumns';
import IndeterminateCheckbox from './IndeterminateCheckbox';
// eslint-disable-next-line import/no-unresolved
import { useTranslation } from 'react-i18next';
// eslint-disable-next-line import/no-unresolved
import JsPDF from 'jspdf';
// eslint-disable-next-line import/no-unresolved
import 'jspdf-autotable';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
// eslint-disable-next-line import/no-unresolved
import { IconEyeOff } from '@tabler/icons-react';
import * as XLSX from 'xlsx/xlsx.mjs';
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
import Popover from 'react-bootstrap/Popover';
import { useMounted } from '@/_hooks/use-mount';
import GenerateEachCellValue from './GenerateEachCellValue';
// eslint-disable-next-line import/no-unresolved
import { toast } from 'react-hot-toast';
import { Tooltip } from 'react-tooltip';
import { AddNewRowComponent } from './AddNewRowComponent';
import { useAppInfo } from '@/_stores/appDataStore';
import { ButtonSolid } from '@/_ui/AppButton/AppButton';
import SolidIcon from '@/_ui/Icon/SolidIcons';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { OverlayTriggerComponent } from './OverlayTriggerComponent';
// eslint-disable-next-line import/no-unresolved
import { diff } from 'deep-object-diff';
// utilityForNestedNewRow function is used to construct nested object while adding or updating new row when '.' is present in column key for adding new row
const utilityForNestedNewRow = (row) => {
let arr = Object.keys(row);
let obj = {};
arr.forEach((key) => {
let nestedKeys = key.split('.');
let tempObj = obj;
for (let i = 0; i < nestedKeys.length; i++) {
let nestedKey = nestedKeys[i];
if (!tempObj.hasOwnProperty(nestedKey)) {
tempObj[nestedKey] = i === nestedKeys.length - 1 ? '' : {};
}
tempObj = tempObj[nestedKey];
}
});
return obj;
};
export function Table({
id,
width,
height,
component,
onComponentClick,
currentState = { components: {} },
onEvent,
paramUpdated,
changeCanDrag,
onComponentOptionChanged,
onComponentOptionsChanged,
darkMode,
fireEvent,
setExposedVariable,
setExposedVariables,
styles,
properties,
variablesExposedForPreview,
exposeToCodeHinter,
// events,
setProperty,
mode,
exposedVariables,
}) {
const {
color,
serverSidePagination,
serverSideSearch,
serverSideSort,
serverSideFilter,
displaySearchBox,
showDownloadButton,
showFilterButton,
showBulkUpdateActions,
showBulkSelector,
highlightSelectedRow,
loadingState,
columnSizes,
tableType,
cellSize,
borderRadius,
parsedWidgetVisibility,
parsedDisabledState,
actionButtonRadius,
actions,
enableNextButton,
enablePrevButton,
totalRecords,
rowsPerPage,
enabledSort,
hideColumnSelectorButton,
defaultSelectedRow,
showAddNewRowButton,
allowSelection,
enablePagination,
} = loadPropertiesAndStyles(properties, styles, darkMode, component);
const updatedDataReference = useRef([]);
const preSelectRow = useRef(false);
const { events: allAppEvents } = useAppInfo();
const tableEvents = allAppEvents.filter((event) => event.target === 'component' && event.sourceId === id);
const tableColumnEvents = allAppEvents.filter((event) => event.target === 'table_column' && event.sourceId === id);
const tableActionEvents = allAppEvents.filter((event) => event.target === 'table_action' && event.sourceId === id);
const getItemStyle = ({ isDragging, isDropAnimating }, draggableStyle) => ({
...draggableStyle,
userSelect: 'none',
background: isDragging ? 'var(--slate4)' : '',
top: 'auto',
borderRadius: '4px',
...(isDragging && {
// marginLeft: '-280px', // hack changing marginLeft to -280px to bring the draggable header to the correct position at the start of drag
display: 'flex',
alignItems: 'center',
paddingLeft: '10px',
height: '30px',
position: 'absolute',
top: '0',
left: '0',
right: '0',
bottom: '0',
zIndex: '9999',
width: '60px',
}),
...(!isDragging && { transform: 'translate(0,0)', width: '100%' }),
...(isDropAnimating && { transitionDuration: '0.001s' }),
});
const { t } = useTranslation();
const [tableDetails, dispatch] = useReducer(reducer, initialState());
const [hoverAdded, setHoverAdded] = useState(false);
const [generatedColumn, setGeneratedColumn] = useState([]);
const mergeToTableDetails = (payload) => dispatch(reducerActions.mergeToTableDetails(payload));
const mergeToFilterDetails = (payload) => dispatch(reducerActions.mergeToFilterDetails(payload));
const mergeToAddNewRowsDetails = (payload) => dispatch(reducerActions.mergeToAddNewRowsDetails(payload));
const mounted = useMounted();
const [resizingColumnId, setResizingColumnId] = useState(null);
const prevDataFromProps = useRef();
useEffect(() => {
if (mounted) prevDataFromProps.current = properties.data;
}, [JSON.stringify(properties.data)]);
useEffect(() => {
setExposedVariable(
'filters',
tableDetails.filterDetails.filters.map((filter) => filter.value)
);
}, [JSON.stringify(tableDetails?.filterDetails?.filters)]);
useEffect(
() => mergeToTableDetails({ columnProperties: component?.definition?.properties?.columns?.value }),
[component?.definition?.properties]
);
useEffect(() => {
const hoverEvent = tableEvents?.find(({ event }) => {
return event?.eventId == 'onRowHovered';
});
if (hoverEvent?.event?.eventId) {
setHoverAdded(true);
}
}, [JSON.stringify(tableEvents)]);
function showFilters() {
mergeToFilterDetails({ filtersVisible: true });
}
function hideFilters() {
mergeToFilterDetails({ filtersVisible: false });
}
function showAddNewRowPopup() {
mergeToAddNewRowsDetails({ addingNewRows: true });
}
function hideAddNewRowPopup() {
mergeToAddNewRowsDetails({ addingNewRows: false });
}
const defaultColumn = React.useMemo(
() => ({
minWidth: 60,
width: 150,
}),
[]
);
function handleExistingRowCellValueChange(index, key, value, rowData) {
const changeSet = tableDetails.changeSet;
const dataUpdates = tableDetails.dataUpdates || [];
const clonedTableData = _.cloneDeep(tableData);
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 },
};
Object.keys(newChangeset).forEach((key) => {
clonedTableData[key] = {
..._.merge(clonedTableData[key], newChangeset[key]),
};
});
const changesToBeSavedAndExposed = { dataUpdates: newDataUpdates, changeSet: newChangeset };
mergeToTableDetails(changesToBeSavedAndExposed);
fireEvent('onCellValueChanged');
return setExposedVariables({ ...changesToBeSavedAndExposed, updatedData: clonedTableData });
}
const copyOfTableDetails = useRef(tableDetails);
useEffect(() => {
copyOfTableDetails.current = _.cloneDeep(tableDetails);
}, [JSON.stringify(tableDetails)]);
function handleNewRowCellValueChange(index, key, value, rowData) {
const changeSet = copyOfTableDetails.current.addNewRowsDetails.newRowsChangeSet || {};
const dataUpdates = copyOfTableDetails.current.addNewRowsDetails.newRowsDataUpdates || {};
let obj = changeSet ? changeSet[index] || {} : {};
obj = _.set(obj, key, value);
let newChangeset = {
...changeSet,
[index]: {
...obj,
},
};
if (Object.keys(rowData).find((key) => key.includes('.'))) {
rowData = utilityForNestedNewRow(rowData);
}
obj = _.merge({}, rowData, obj);
let newDataUpdates = {
...dataUpdates,
[index]: { ...obj },
};
const changesToBeSaved = { newRowsDataUpdates: newDataUpdates, newRowsChangeSet: newChangeset };
const changesToBeExposed = Object.keys(newDataUpdates).reduce((accumulator, row) => {
accumulator.push({ ...newDataUpdates[row] });
return accumulator;
}, []);
mergeToAddNewRowsDetails(changesToBeSaved);
return setExposedVariables({ newRows: changesToBeExposed });
}
function getExportFileBlob({ columns, fileType, fileName }) {
let headers = columns.map((column) => {
return { exportValue: String(column?.exportValue), key: column.key ? String(column.key) : column?.key };
});
let data = globalFilteredRows.map((row) => {
return headers.reduce((accumulator, header) => {
let value = undefined;
if (header.key && header.key !== header.exportValue) {
value = _.get(row.original, header.key);
} else {
value = _.get(row.original, header.exportValue);
}
accumulator.push(value);
return accumulator;
}, []);
});
headers = headers.map((header) => header.exportValue.toUpperCase());
if (fileType === 'csv') {
const csvString = Papa.unparse({ fields: headers, data });
return new Blob([csvString], { type: 'text/csv' });
} else if (fileType === 'pdf') {
const pdfData = data.map((obj) => Object.values(obj));
const doc = new JsPDF();
doc.autoTable({
head: [headers],
body: pdfData,
styles: {
minCellHeight: 9,
minCellWidth: 20,
fontSize: 11,
color: 'black',
},
theme: 'grid',
});
doc.save(`${fileName}.pdf`);
return;
} else if (fileType === 'xlsx') {
data.unshift(headers); //adding headers array at the beginning of data
let wb = XLSX.utils.book_new();
let ws1 = XLSX.utils.aoa_to_sheet(data);
XLSX.utils.book_append_sheet(wb, ws1, 'React Table Data');
XLSX.writeFile(wb, `${fileName}.xlsx`);
// Returning false as downloading of file is already taken care of
return false;
}
}
function onPageIndexChanged(page) {
onComponentOptionChanged(component, 'pageIndex', page).then(() => {
onEvent('onPageChanged', tableEvents, { component });
});
}
function handleChangesSaved() {
const clonedTableData = _.cloneDeep(tableData);
Object.keys(changeSet).forEach((key) => {
clonedTableData[key] = {
..._.merge(clonedTableData[key], changeSet[key]),
};
});
updatedDataReference.current = _.cloneDeep(clonedTableData);
setExposedVariables({
changeSet: {},
dataUpdates: [],
});
mergeToTableDetails({ dataUpdates: {}, changeSet: {} });
}
function handleChangesDiscarded() {
setExposedVariables({
changeSet: {},
dataUpdates: [],
});
mergeToTableDetails({ dataUpdates: {}, changeSet: {} });
fireEvent('onCancelChanges');
}
const changeSet = tableDetails?.changeSet ?? {};
const computeFontColor = useCallback(() => {
if (color !== undefined) {
return color;
} else {
return darkMode ? '#ffffff' : '#000000';
}
}, [color, darkMode]);
let tableData = [],
dynamicColumn = [];
const useDynamicColumn = resolveReferences(component.definition.properties?.useDynamicColumn?.value, currentState);
if (currentState) {
tableData = resolveReferences(component.definition.properties.data.value, currentState, []);
dynamicColumn = useDynamicColumn
? resolveReferences(component.definition.properties?.columnData?.value, currentState, []) ?? []
: [];
if (!Array.isArray(tableData)) tableData = [];
}
tableData = tableData || [];
const tableRef = useRef();
let columnData = generateColumnsData({
columnProperties: useDynamicColumn ? generatedColumn : component.definition.properties.columns.value,
columnSizes,
currentState,
handleCellValueChange: handleExistingRowCellValueChange,
customFilter,
defaultColumn,
changeSet: tableDetails.changeSet,
tableData,
variablesExposedForPreview,
exposeToCodeHinter,
id,
fireEvent,
tableRef,
t,
darkMode,
tableColumnEvents: tableColumnEvents,
});
columnData = useMemo(
() =>
columnData.filter((column) => {
if (resolveReferences(column?.columnVisibility, currentState)) {
return column;
}
}),
[columnData, currentState]
);
const columnDataForAddNewRows = generateColumnsData({
columnProperties: useDynamicColumn ? generatedColumn : component.definition.properties.columns.value,
columnSizes,
currentState,
handleCellValueChange: handleNewRowCellValueChange,
customFilter,
defaultColumn,
changeSet: tableDetails.addNewRowsDetails.newRowsChangeSet,
tableData,
variablesExposedForPreview,
exposeToCodeHinter,
id,
fireEvent,
tableRef,
t,
darkMode,
});
const [leftActionsCellData, rightActionsCellData] = useMemo(
() =>
generateActionsData({
actions,
columnSizes,
defaultColumn,
fireEvent,
setExposedVariables,
tableActionEvents,
}),
[JSON.stringify(actions), tableActionEvents]
);
const textWrapActions = (id) => {
let wrapOption = tableDetails.columnProperties?.find((item) => {
return item?.id == id;
});
return wrapOption?.textWrap;
};
const optionsData = columnData.map((column) => column?.columnOptions?.selectOptions);
const columns = useMemo(
() => {
return [...leftActionsCellData, ...columnData, ...rightActionsCellData];
},
[
JSON.stringify(columnData),
JSON.stringify(tableData),
JSON.stringify(actions),
leftActionsCellData.length,
rightActionsCellData.length,
tableDetails.changeSet,
JSON.stringify(optionsData),
JSON.stringify(component.definition.properties.columns),
showBulkSelector,
JSON.stringify(variablesExposedForPreview && variablesExposedForPreview[id]),
darkMode,
allowSelection,
highlightSelectedRow,
JSON.stringify(tableActionEvents),
JSON.stringify(tableColumnEvents),
] // Hack: need to fix
);
const columnsForAddNewRow = useMemo(() => {
return [...columnDataForAddNewRows];
}, [JSON.stringify(columnDataForAddNewRows), darkMode, tableDetails.addNewRowsDetails.addingNewRows]);
const data = useMemo(() => {
if (!_.isEqual(properties.data, prevDataFromProps.current)) {
if (!_.isEmpty(updatedDataReference.current)) updatedDataReference.current = [];
if (
!_.isEmpty(exposedVariables.newRows) ||
!_.isEmpty(tableDetails.addNewRowsDetails.newRowsDataUpdates) ||
tableDetails.addNewRowsDetails.addingNewRows
) {
setExposedVariable('newRows', []);
mergeToAddNewRowsDetails({ newRowsDataUpdates: {}, newRowsChangeSet: {}, addingNewRows: false });
}
}
return _.isEmpty(updatedDataReference.current) ? tableData : updatedDataReference.current;
}, [tableData.length, component.definition.properties.data.value, JSON.stringify(properties.data)]);
useEffect(() => {
if (
tableData.length != 0 &&
component.definition.properties.autogenerateColumns?.value &&
(useDynamicColumn || mode === 'edit')
) {
const generatedColumnFromData = autogenerateColumns(
tableData,
component.definition.properties.columns.value,
component.definition.properties?.columnDeletionHistory?.value ?? [],
useDynamicColumn,
dynamicColumn,
setProperty,
component.definition.properties.autogenerateColumns?.generateNestedColumns ?? false
);
useDynamicColumn && setGeneratedColumn(generatedColumnFromData);
}
}, [JSON.stringify(tableData), JSON.stringify(dynamicColumn)]);
const computedStyles = {
// width: `${width}px`,
};
const {
getTableProps,
getTableBodyProps,
headerGroups,
page,
canPreviousPage,
canNextPage,
pageOptions,
gotoPage,
pageCount,
nextPage,
previousPage,
setPageSize,
state,
rows,
prepareRow,
setAllFilters,
preGlobalFilteredRows,
setGlobalFilter,
allColumns,
setColumnOrder,
state: { pageIndex, globalFilter },
exportData,
selectedFlatRows,
globalFilteredRows,
getToggleHideAllColumnsProps,
toggleRowSelected,
toggleAllRowsSelected,
} = useTable(
{
autoResetPage: false,
autoResetGlobalFilter: false,
autoResetHiddenColumns: false,
autoResetFilters: false,
manualGlobalFilter: serverSideSearch,
manualFilters: serverSideFilter,
columns,
data,
defaultColumn,
initialState: { pageIndex: 0, pageSize: -1 },
pageCount: -1,
manualPagination: false,
getExportFileBlob,
disableSortBy: !enabledSort,
manualSortBy: serverSideSort,
stateReducer: (newState, action, prevState) => {
const newStateWithPrevSelectedRows = showBulkSelector
? { ...newState, selectedRowId: { ...prevState.selectedRowIds, ...newState.selectedRowIds } }
: { ...newState.selectedRowId };
if (action.type === 'toggleRowSelected') {
prevState.selectedRowIds[action.id]
? (newState.selectedRowIds = {
...newStateWithPrevSelectedRows.selectedRowIds,
[action.id]: false,
})
: (newState.selectedRowIds = {
...newStateWithPrevSelectedRows.selectedRowIds,
[action.id]: true,
});
}
return newState;
},
},
useColumnOrder,
useFilters,
useGlobalFilter,
useSortBy,
usePagination,
useBlockLayout,
useResizeColumns,
useExportData,
useRowSelect,
(hooks) => {
allowSelection &&
!highlightSelectedRow &&
hooks.visibleColumns.push((columns) => [
{
id: 'selection',
Header: ({ getToggleAllPageRowsSelectedProps }) => {
return (
<div className="d-flex flex-column align-items-center">
{showBulkSelector && <IndeterminateCheckbox {...getToggleAllPageRowsSelectedProps()} />}
</div>
);
},
Cell: ({ row }) => {
return (
<div className="d-flex flex-column align-items-center">
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
);
},
width: 1,
columnType: 'selector',
},
...columns,
]);
}
);
const currentColOrder = React.useRef();
const clientSidePagination = enablePagination && !serverSidePagination;
const sortOptions = useMemo(() => {
if (state?.sortBy?.length === 0) {
return;
}
const columnName = columns.find((column) => column.id === state?.sortBy?.[0]?.id).accessor;
return [
{
column: columnName,
direction: state?.sortBy?.[0]?.desc ? 'desc' : 'asc',
},
];
}, [JSON.stringify(state)]);
const getDetailsOfPreSelectedRow = () => {
const key = Object?.keys(defaultSelectedRow)[0] ?? '';
const value = defaultSelectedRow?.[key] ?? undefined;
const preSelectedRowDetails = rows.find((row) => row?.original?.[key] === value);
return preSelectedRowDetails;
};
useEffect(() => {
if (!sortOptions) {
setExposedVariable('sortApplied', []);
}
if (mounted) {
setExposedVariable('sortApplied', sortOptions);
fireEvent('onSort');
}
}, [JSON.stringify(sortOptions)]);
useEffect(() => {
setExposedVariable('setPage', async function (targetPageIndex) {
setPaginationInternalPageIndex(targetPageIndex);
setExposedVariable('pageIndex', targetPageIndex);
if (!serverSidePagination && clientSidePagination) gotoPage(targetPageIndex - 1);
});
}, [serverSidePagination, clientSidePagination, setPaginationInternalPageIndex]);
useEffect(() => {
setExposedVariable('selectRow', async function (key, value) {
const item = tableData.filter((item) => item[key] == value);
const row = rows.find((item, index) => item.original[key] == value);
if (row != undefined) {
const selectedRowDetails = { selectedRow: item[0], selectedRowId: row.id };
setExposedVariables(selectedRowDetails);
toggleRowSelected(row.id);
mergeToTableDetails(selectedRowDetails);
fireEvent('onRowClicked');
}
});
}, [JSON.stringify(tableData), JSON.stringify(tableDetails.selectedRow)]);
useEffect(() => {
setExposedVariable('deselectRow', async function () {
if (!_.isEmpty(tableDetails.selectedRow)) {
const selectedRowDetails = { selectedRow: {}, selectedRowId: {} };
setExposedVariables(selectedRowDetails);
if (allowSelection && !showBulkSelector) toggleRowSelected(tableDetails.selectedRowId, false);
mergeToTableDetails(selectedRowDetails);
}
return;
});
}, [JSON.stringify(tableData), JSON.stringify(tableDetails.selectedRow)]);
useEffect(() => {
setExposedVariable('discardChanges', async function () {
if (Object.keys(tableDetails.changeSet || {}).length > 0) {
setExposedVariables({
changeSet: {},
dataUpdates: [],
});
mergeToTableDetails({ dataUpdates: {}, changeSet: {} });
}
});
}, [JSON.stringify(tableData), JSON.stringify(tableDetails.changeSet)]);
useEffect(() => {
setExposedVariable('discardNewlyAddedRows', async function () {
if (
!_.isEmpty(exposedVariables.newRows) ||
!_.isEmpty(tableDetails.addNewRowsDetails.newRowsChangeSet) ||
!_.isEmpty(tableDetails.addNewRowsDetails.newRowsChangeSet)
) {
setExposedVariables({
newRows: [],
});
mergeToAddNewRowsDetails({ newRowsChangeSet: {}, newRowsDataUpdates: {}, addingNewRows: false });
}
});
}, [
JSON.stringify(tableDetails.addNewRowsDetails.newRowsChangeSet),
tableDetails.addNewRowsDetails.addingNewRows,
JSON.stringify(tableDetails.addNewRowsDetails.newRowsDataUpdates),
]);
useEffect(() => {
if (showBulkSelector) {
const selectedRowsOriginalData = selectedFlatRows.map((row) => row.original);
const selectedRowsId = selectedFlatRows.map((row) => row.id);
setExposedVariables({ selectedRows: selectedRowsOriginalData, selectedRowsId: selectedRowsId });
const selectedRowsDetails = selectedFlatRows.reduce((accumulator, row) => {
accumulator.push({ selectedRowId: row.id, selectedRow: row.original });
return accumulator;
}, []);
mergeToTableDetails({ selectedRowsDetails });
}
if (
(!showBulkSelector && !highlightSelectedRow) ||
(showBulkSelector && !highlightSelectedRow && preSelectRow.current)
) {
const selectedRow = selectedFlatRows?.[0]?.original ?? {};
const selectedRowId = selectedFlatRows?.[0]?.id ?? null;
setExposedVariables({ selectedRow, selectedRowId });
mergeToTableDetails({ selectedRow, selectedRowId });
}
}, [selectedFlatRows.length, selectedFlatRows]);
useEffect(() => {
setExposedVariable('downloadTableData', async function (format) {
exportData(format, true);
});
}, [_.toString(globalFilteredRows), columns]);
useEffect(() => {
if (mounted) {
setExposedVariables({ selectedRows: [], selectedRowsId: [], selectedRow: {}, selectedRowId: null });
mergeToTableDetails({ selectedRowsDetails: [], selectedRow: {}, selectedRowId: null });
toggleAllRowsSelected(false);
}
}, [showBulkSelector, highlightSelectedRow, allowSelection]);
React.useEffect(() => {
if (enablePagination) {
if (serverSidePagination || !clientSidePagination) {
setPageSize(rows?.length || 10);
}
if (!serverSidePagination && clientSidePagination) {
setPageSize(rowsPerPage || 10);
}
} else {
setPageSize(rows?.length || 10);
}
}, [clientSidePagination, serverSidePagination, rows, rowsPerPage]);
useEffect(() => {
const pageData = page.map((row) => row.original);
if (preSelectRow.current) {
preSelectRow.current = false;
} else {
onComponentOptionsChanged(component, [
['currentPageData', pageData],
['currentData', data],
['selectedRow', []],
['selectedRowId', null],
]).then(() => {
if (tableDetails.selectedRowId || !_.isEmpty(tableDetails.selectedRowDetails)) {
toggleAllRowsSelected(false);
mergeToTableDetails({ selectedRow: {}, selectedRowId: null, selectedRowDetails: [] });
}
});
}
}, [tableData.length, _.toString(page), pageIndex, _.toString(data)]);
useEffect(() => {
const newColumnSizes = { ...columnSizes, ...state.columnResizing.columnWidths };
const isColumnSizeChanged = !_.isEmpty(diff(columnSizes, newColumnSizes));
if (isColumnSizeChanged && !state.columnResizing.isResizingColumn && !_.isEmpty(newColumnSizes)) {
changeCanDrag(true);
paramUpdated(
id,
'columnSizes',
{
value: newColumnSizes,
},
{ componentDefinitionChanged: true }
);
} else {
changeCanDrag(false);
}
}, [state.columnResizing.isResizingColumn]);
const [paginationInternalPageIndex, setPaginationInternalPageIndex] = useState(pageIndex ?? 1);
const [rowDetails, setRowDetails] = useState();
useEffect(() => {
if (pageCount <= pageIndex) gotoPage(pageCount - 1);
}, [pageCount]);
const hoverRef = useRef();
useEffect(() => {
if (rowDetails?.hoveredRowId !== '' && hoverRef.current !== rowDetails?.hoveredRowId) rowHover();
}, [rowDetails]);
useEffect(() => {
setExposedVariable(
'filteredData',
globalFilteredRows.map((row) => row.original)
);
}, [JSON.stringify(globalFilteredRows.map((row) => row.original))]);
const rowHover = () => {
mergeToTableDetails(rowDetails);
setExposedVariables(rowDetails);
fireEvent('onRowHovered');
};
useEffect(() => {
if (_.isEmpty(changeSet)) {
setExposedVariable(
'updatedData',
_.isEmpty(updatedDataReference.current) ? tableData : updatedDataReference.current
);
}
}, [JSON.stringify(changeSet)]);
useEffect(() => {
if (
allowSelection &&
typeof defaultSelectedRow === 'object' &&
!_.isEmpty(defaultSelectedRow) &&
!_.isEmpty(data)
) {
const preSelectedRowDetails = getDetailsOfPreSelectedRow();
if (_.isEmpty(preSelectedRowDetails)) return;
const selectedRow = preSelectedRowDetails?.original ?? {};
const selectedRowId = preSelectedRowDetails?.id ?? null;
const pageNumber = Math.floor(selectedRowId / rowsPerPage) + 1;
preSelectRow.current = true;
if (highlightSelectedRow) {
setExposedVariables({ selectedRow: selectedRow, selectedRowId: selectedRowId });
toggleRowSelected(selectedRowId, true);
mergeToTableDetails({ selectedRow: selectedRow, selectedRowId: selectedRowId });
} else {
toggleRowSelected(selectedRowId, true);
}
if (pageIndex >= 0 && pageNumber !== pageIndex + 1) {
gotoPage(pageNumber - 1);
}
}
//hack : in the initial render, data is undefined since, upon feeding data to the table from some query, query inside current state is {}. Hence we added data in the dependency array, now question is should we add data or rows?
}, [JSON.stringify(defaultSelectedRow), JSON.stringify(data)]);
function downlaodPopover() {
const options = [
{ dataCy: 'option-download-CSV', text: 'Download as CSV', value: 'csv' },
{ dataCy: 'option-download-execel', text: 'Download as Excel', value: 'xlsx' },
{ dataCy: 'option-download-pdf', text: 'Download as PDF', value: 'pdf' },
];
return (
<Popover
id="popover-basic"
data-cy="popover-card"
className={`${darkMode && 'dark-theme'} shadow table-widget-download-popup`}
placement="top-end"
>
<Popover.Body className="p-0">
<div className="table-download-option cursor-pointer">
<span data-cy={`option-download-CSV`} className="cursor-pointer" onClick={() => exportData('csv', true)}>
Download as CSV
</span>
<span
data-cy={`option-download-execel`}
className="pt-2 cursor-pointer"
onClick={() => exportData('xlsx', true)}
>
Download as Excel
</span>
<span
data-cy={`option-download-pdf`}
className="pt-2 cursor-pointer"
onClick={() => exportData('pdf', true)}
>
Download as PDF
</span>
</div>
</Popover.Body>
</Popover>
);
}
function hideColumnsPopover() {
const heightOfTableComponent = document.querySelector('.card.jet-table.table-component')?.offsetHeight;
return (
<Popover
className={`${darkMode && 'dark-theme'}`}
style={{ maxHeight: `${heightOfTableComponent - 79}px`, overflowY: 'auto' }}
>
<div
data-cy={`dropdown-hide-column`}
className={`dropdown-table-column-hide-common ${
darkMode ? 'dropdown-table-column-hide-dark-themed dark-theme' : 'dropdown-table-column-hide'
} `}
placement="top-end"
>
<div className="dropdown-item cursor-pointer">
<IndeterminateCheckbox {...getToggleHideAllColumnsProps()} />
<span className="hide-column-name tj-text-xsm" data-cy={`options-select-all-coloumn`}>
Select All
</span>
</div>
{allColumns.map(
(column) =>
typeof column?.Header === 'string' && (
<div key={column.id}>
<div>
<label className="dropdown-item d-flex cursor-pointer">
<input
type="checkbox"
data-cy={`checkbox-coloumn-${String(column.Header).toLowerCase().replace(/\s+/g, '-')}`}
{...column.getToggleHiddenProps()}
/>
<span
className="hide-column-name tj-text-xsm"
data-cy={`options-coloumn-${String(column.Header).toLowerCase().replace(/\s+/g, '-')}`}
>
{` ${column.Header}`}
</span>
</label>
</div>
</div>
)
)}
</div>
</Popover>
);
}
const calculateWidthOfActionColumnHeader = (position) => {
let totalWidth = null;
if (position === 'rightActions') {
const rightActionBtn = document.querySelector('.has-right-actions');
totalWidth = rightActionBtn?.offsetWidth;
}
if (position === 'leftActions') {
const leftActionBtn = document.querySelector('.has-left-actions');
totalWidth = leftActionBtn?.offsetWidth;
}
return totalWidth;
};
return (
<div
data-cy={`draggable-widget-${String(component.name).toLowerCase()}`}
data-disabled={parsedDisabledState}
className={`card jet-table table-component ${darkMode && 'dark-theme'}`}
style={{
width: `100%`,
height: `${height}px`,
display: parsedWidgetVisibility ? '' : 'none',
overflow: 'hidden',
borderRadius: Number.parseFloat(borderRadius),
boxShadow: styles.boxShadow,
padding: '8px',
}}
onClick={(event) => {
onComponentClick(id, component, event);
}}
ref={tableRef}
>
{(displaySearchBox || showFilterButton) && (
<div
className={`table-card-header d-flex justify-content-between align-items-center ${
(tableDetails.addNewRowsDetails.addingNewRows || tableDetails.filterDetails.filtersVisible) && 'disabled'
}`}
style={{ padding: '12px', height: 56 }}
>
<div>
{loadingState && (
<SkeletonTheme baseColor="var(--slate3)">
<Skeleton count={1} width={83} height={28} className="mb-1" />
</SkeletonTheme>
)}
{showFilterButton && !loadingState && (
<div className="position-relative">
{''}
<Tooltip id="tooltip-for-filter-data" className="tooltip" />
<ButtonSolid
variant="tertiary"
className={`tj-text-xsm ${tableDetails.filterDetails.filtersVisible && 'always-active-btn'}`}
customStyles={{ minWidth: '32px' }}
leftIcon="filter"
fill={`var(--slate11)`}
iconWidth="16"
onClick={(e) => {
if (tableDetails?.filterDetails?.filtersVisible) {
hideFilters();
if (document.activeElement === e.currentTarget) {
e.currentTarget.blur();
}
} else {
showFilters();
}
}}
size="md"
data-tooltip-id="tooltip-for-filter-data"
data-tooltip-content="Filter data"
></ButtonSolid>
{(tableDetails?.filterDetails?.filtersVisible || !_.isEmpty(tableDetails.filterDetails.filters)) && (
<div className="filter-applied-state position-absolute">
<svg
className="filter-applied-svg"
xmlns="http://www.w3.org/2000/svg"
width="17"
height="17"
viewBox="0 0 17 17"
fill="none"
>
<circle
cx="8.3606"
cy="8.08325"
r="6.08325"
stroke="var(--slate1)"
fill="var(--indigo9)"
stroke-width="4"
/>
</svg>
</div>
)}
</div>
)}
</div>
<div className="d-flex custom-gap-8" style={{ maxHeight: 32 }}>
{loadingState && (
<SkeletonTheme baseColor="var(--slate3)">
<Skeleton count={1} width={100} height={28} className="mb-1" />
</SkeletonTheme>
)}
{displaySearchBox && !loadingState && (
<GlobalFilter
globalFilter={state.globalFilter}
setGlobalFilter={setGlobalFilter}
onComponentOptionChanged={onComponentOptionChanged}
component={component}
onEvent={onEvent}
darkMode={darkMode}
tableEvents={tableEvents}
/>
)}
</div>
</div>
)}
<div
className={`table-responsive jet-data-table ${(loadingState || page.length === 0) && 'overflow-hidden'} ${
page.length === 0 && 'position-relative'
}`}
>
<table
{...getTableProps()}
className={`table table-vcenter table-nowrap ${tableType} ${darkMode && 'table-dark'} ${
tableDetails.addNewRowsDetails.addingNewRows && 'disabled'
} ${!loadingState && page.length !== 0 && 'h-100'}`}
style={computedStyles}
>
<thead>
{headerGroups.map((headerGroup, index) => (
<DragDropContext
key={index}
onDragStart={() => {
currentColOrder.current = allColumns?.map((o) => o.id);
}}
onDragEnd={(dragUpdateObj) => {
const colOrder = [...currentColOrder.current];
const sIndex = dragUpdateObj.source.index;
const dIndex = dragUpdateObj.destination && dragUpdateObj.destination.index;
if (typeof sIndex === 'number' && typeof dIndex === 'number') {
colOrder.splice(sIndex, 1);
colOrder.splice(dIndex, 0, dragUpdateObj.draggableId);
setColumnOrder(colOrder);
}
}}
>
<Droppable droppableId="droppable" direction="horizontal">
{(droppableProvided, snapshot) => (
<tr
ref={droppableProvided.innerRef}
key={index}
{...headerGroup.getHeaderGroupProps()}
className="tr"
>
{loadingState && (
<div className="w-100">
<SkeletonTheme baseColor="var(--slate3)" width="100%">
<Skeleton count={1} width={'100%'} height={28} className="mb-1" />
</SkeletonTheme>
</div>
)}
{!loadingState &&
headerGroup.headers.map((column, index) => {
return (
<Draggable
key={column.id}
draggableId={column.id}
index={index}
isDragDisabled={!column.accessor}
>
{(provided, snapshot) => {
let headerProps = { ...column.getHeaderProps() };
if (column.columnType === 'selector') {
headerProps = {
...headerProps,
style: {
...headerProps.style,
width: 40,
padding: 0,
display: 'flex',
'align-items': 'center',
'justify-content': 'center',
},
};
}
if (column.Header === 'Actions') {
headerProps = {
...headerProps,
style: {
...headerProps.style,
width: calculateWidthOfActionColumnHeader(column.id),
maxWidth: calculateWidthOfActionColumnHeader(column.id),
padding: 0,
display: 'flex',
'align-items': 'center',
'justify-content': 'center',
},
};
}
if (
headerGroup?.headers?.[headerGroup?.headers?.length - 1]?.Header === 'Actions' &&
index === headerGroup?.headers?.length - 2
) {
headerProps = {
...headerProps,
style: {
...headerProps.style,
flex: '1 1 auto',
},
};
}
const isEditable = resolveReferences(column?.isEditable ?? false, currentState);
return (
<th
key={index}
{...headerProps}
className={`th tj-text-xsm font-weight-400 ${
column.isSorted && (column.isSortedDesc ? '' : '')
} ${column.isResizing && 'resizing-column'} ${
column.Header === 'Actions' && 'has-actions'
} position-relative ${column.columnType === 'selector' && 'selector-header'}`}
>
<div
className={`${
column.columnType !== 'selector' &&
'd-flex justify-content-between custom-gap-12'
} ${column.columnType === 'selector' && 'd-flex justify-content-center w-100'}`}
{...column.getSortByToggleProps()}
{...provided.draggableProps}
{...provided.dragHandleProps}
// {...extraProps}
ref={provided.innerRef}
style={{
...getItemStyle(snapshot, provided.draggableProps.style),
}}
>
<div
className={`d-flex thead-editable-icon-header-text-wrapper
${
column.columnType === 'selector'
? 'justify-content-center'
: `justify-content-${determineJustifyContentValue(
column?.horizontalAlignment ?? ''
)}`
}
${column.columnType !== 'selector' && isEditable && 'custom-gap-4'}
`}
>
<div>
{column.columnType !== 'selector' && isEditable && (
<SolidIcon
name="editable"
width="16px"
height="16px"
fill={darkMode ? '#4C5155' : '#C1C8CD'}
vievBox="0 0 16 16"
/>
)}
</div>
<div
data-cy={`column-header-${String(column.exportValue)
.toLowerCase()
.replace(/\s+/g, '-')}`}
className={`header-text ${
column.id === 'selection' &&
column.columnType === 'selector' &&
'selector-column'
}`}
>
{column.render('Header')}
</div>
</div>
<div
style={{
display:
column?.columnType !== 'selector' && column?.isSorted ? 'block' : 'none',
}}
>
{column?.isSortedDesc ? (
<SolidIcon
name="arrowdown"
width="16"
height="16"
fill={darkMode ? '#ECEDEE' : '#11181C'}
/>
) : (
<SolidIcon
name="arrowup"
width="16"
height="16"
fill={darkMode ? '#ECEDEE' : '#11181C'}
/>
)}
</div>
</div>
<div
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
onMouseMove={(e) => {
if (column.id !== resizingColumnId) {
setResizingColumnId(column.id);
}
}}
onMouseLeave={(e) => {
if (resizingColumnId) {
setResizingColumnId(null);
}
}}
draggable="true"
{...column.getResizerProps()}
className={`${
(column.id === 'selection' && column.columnType === 'selector') ||
column.Header === 'Actions'
? ''
: 'resizer'
} ${column.isResizing ? 'isResizing' : ''}`}
></div>
</th>
);
}}
</Draggable>
);
})}
</tr>
)}
</Droppable>
</DragDropContext>
))}
</thead>
{!loadingState && (
<tbody {...getTableBodyProps()} style={{ color: computeFontColor() }}>
{page.map((row, index) => {
prepareRow(row);
return (
<tr
key={index}
className={`table-row table-editor-component-row ${
allowSelection &&
highlightSelectedRow &&
((row.isSelected && row.id === tableDetails.selectedRowId) ||
(showBulkSelector &&
row.isSelected &&
tableDetails?.selectedRowsDetails?.some((singleRow) => singleRow.selectedRowId === row.id)))
? 'selected'
: ''
}`}
{...row.getRowProps()}
onClick={async (e) => {
e.stopPropagation();
// toggleRowSelected will triggered useRededcuer function in useTable and in result will get the selectedFlatRows consisting row which are selected
if (allowSelection) {
await toggleRowSelected(row.id);
}
const selectedRow = row.original;
const selectedRowId = row.id;
setExposedVariables({ selectedRow, selectedRowId });
mergeToTableDetails({ selectedRow, selectedRowId });
fireEvent('onRowClicked');
}}
onMouseOver={(e) => {
if (hoverAdded) {
const hoveredRowDetails = { hoveredRowId: row.id, hoveredRow: row.original };
setRowDetails(hoveredRowDetails);
hoverRef.current = rowDetails?.hoveredRowId;
}
}}
onMouseLeave={(e) => {
hoverAdded && setRowDetails({ hoveredRowId: '', hoveredRow: '' });
}}
>
{row.cells.map((cell, index) => {
let cellProps = cell.getCellProps();
cellProps.style.textAlign = cell.column?.horizontalAlignment;
if (tableDetails.changeSet) {
if (tableDetails.changeSet[cell.row.index]) {
const currentColumn = columnData.find((column) => column.id === cell.column.id);
if (
_.get(tableDetails.changeSet[cell.row.index], currentColumn?.accessor, undefined) !==
undefined
) {
cellProps.style.backgroundColor = 'var(--orange3)';
cellProps.style['--tblr-table-accent-bg'] = 'var(--orange3)';
}
}
}
if (cell.column.columnType === 'selector') {
cellProps.style.width = 40;
cellProps.style.padding = 0;
}
if (cell.column.Header === 'Actions') {
cellProps.style.width = 'fit-content';
cellProps.style.maxWidth = 'fit-content';
}
if (
row.cells?.[row.cells?.length - 1]?.column.Header === 'Actions' &&
index === row?.cells?.length - 2
) {
cellProps.style.flex = '1 1 auto';
}
const wrapAction = textWrapActions(cell.column.id);
const rowChangeSet = changeSet ? changeSet[cell.row.index] : null;
const cellValue = rowChangeSet ? rowChangeSet[cell.column.name] || cell.value : cell.value;
const rowData = tableData[cell.row.index];
const cellBackgroundColor = resolveReferences(
cell.column?.cellBackgroundColor,
currentState,
'',
{
cellValue,
rowData,
}
);
const cellTextColor = resolveReferences(cell.column?.textColor, currentState, '', {
cellValue,
rowData,
});
const actionButtonsArray = actions.map((action) => {
return {
...action,
isDisabled: resolveReferences(action?.disableActionButton ?? false, currentState, '', {
cellValue,
rowData,
}),
};
});
const isEditable = resolveReferences(cell.column?.isEditable ?? false, currentState, '', {
cellValue,
rowData,
});
const horizontalAlignment = cell.column?.horizontalAlignment;
return (
// Does not require key as its already being passed by react-table via cellProps
// eslint-disable-next-line react/jsx-key
<td
data-cy={`${cell.column.columnType ?? ''}${String(
cell.column.id === 'rightActions' || cell.column.id === 'leftActions' ? cell.column.id : ''
)}${String(cellValue ?? '').toLocaleLowerCase()}-cell-${index}`}
className={cx(
`table-text-align-${cell.column.horizontalAlignment} ${
wrapAction ? wrapAction : cell?.column?.Header === 'Actions' ? '' : 'wrap'
}-wrapper td`,
{
'has-actions': cell.column.id === 'rightActions' || cell.column.id === 'leftActions',
'has-left-actions': cell.column.id === 'leftActions',
'has-right-actions': cell.column.id === 'rightActions',
'has-text': cell.column.columnType === 'text' || isEditable,
'has-dropdown': cell.column.columnType === 'dropdown',
'has-multiselect': cell.column.columnType === 'multiselect',
'has-datepicker': cell.column.columnType === 'datepicker',
'align-items-center flex-column': cell.column.columnType === 'selector',
[cellSize]: true,
'selector-column':
cell.column.columnType === 'selector' && cell.column.id === 'selection',
'resizing-column': cell.column.isResizing || cell.column.id === resizingColumnId,
}
)}
{...cellProps}
style={{ ...cellProps.style, backgroundColor: cellBackgroundColor ?? 'inherit' }}
onClick={(e) => {
setExposedVariable('selectedCell', {
columnName: cell.column.exportValue,
columnKey: cell.column.key,
value: cellValue,
});
}}
>
<div
className={`td-container ${
cell.column.columnType === 'image' && 'jet-table-image-column'
} ${cell.column.columnType !== 'image' && `w-100 h-100`}`}
>
<GenerateEachCellValue
cellValue={cellValue}
globalFilter={state.globalFilter}
cellRender={cell.render('Cell', {
cell,
actionButtonsArray,
isEditable,
horizontalAlignment,
})}
rowChangeSet={rowChangeSet}
isEditable={isEditable}
columnType={cell.column.columnType}
isColumnTypeAction={['rightActions', 'leftActions'].includes(cell.column.id)}
cellTextColor={cellTextColor}
cell={cell}
currentState={currentState}
/>
</div>
</td>
);
})}
</tr>
);
})}
</tbody>
)}
</table>
{!loadingState && page.length === 0 && (
<div
className="d-flex flex-column align-items-center custom-gap-8 justify-content-center h-100"
style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translateY(-50%) translateX(-50%)',
}}
>
<div className="warning-no-data">
<div className="warning-svg-wrapper">
<SolidIcon name="warning" width="16" />
</div>
</div>
<div className="warning-no-data-text">No data</div>
</div>
)}
{loadingState === true && (
<div style={{ width: '100%' }} className="p-2 h-100 ">
<div className="d-flex align-items-center justify-content-center h-100">
<svg
className="loading-spinner-table-component"
width="48"
height="48"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="var(--indigo6)"
>
<style>.spinner_ajPY{}</style>
<path d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25" />
<path
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
class="spinner_ajPY"
fill="var(--indigo9)"
/>
</svg>
</div>
</div>
)}
</div>
{(enablePagination ||
Object.keys(tableDetails.changeSet || {}).length > 0 ||
showAddNewRowButton ||
showDownloadButton) && (
<div
className={`card-footer d-flex align-items-center jet-table-footer justify-content-center ${
darkMode && 'dark-theme'
} ${
(tableDetails.addNewRowsDetails.addingNewRows || tableDetails.filterDetails.filtersVisible) && 'disabled'
}`}
>
<div className={`table-footer row gx-0 d-flex align-items-center h-100`}>
<div className="col d-flex justify-content-start custom-gap-4">
{loadingState && (
<SkeletonTheme baseColor="var(--slate3)" width="100%">
<Skeleton count={1} width={83} height={28} className="mb-1" />
</SkeletonTheme>
)}
{!loadingState &&
(showBulkUpdateActions && Object.keys(tableDetails.changeSet || {}).length > 0 ? (
<>
<ButtonSolid
variant="primary"
className={`tj-text-xsm`}
onClick={() => {
onEvent('onBulkUpdate', tableEvents, { component }).then(() => {
handleChangesSaved();
});
}}
data-cy={`table-button-save-changes`}
size="md"
isLoading={tableDetails.isSavingChanges ? true : false}
customStyles={{ minWidth: '32px', padding: width > 650 ? '6px 16px' : 0 }}
leftIcon={width > 650 ? '' : 'save'}
fill="#FDFDFE"
iconWidth="16"
>
{width > 650 ? <span>Save changes</span> : ''}
</ButtonSolid>
<ButtonSolid
variant="tertiary"
className={`tj-text-xsm`}
onClick={() => {
handleChangesDiscarded();
}}
data-cy={`table-button-discard-changes`}
size="md"
customStyles={{ minWidth: '32px', padding: width > 650 ? '6px 16px' : 0 }}
leftIcon={width > 650 ? '' : 'cross'}
fill={'var(--slate11)'}
iconWidth="16"
>
{width > 650 ? <span>Discard</span> : ''}
</ButtonSolid>
</>
) : (
!loadingState && (
<span data-cy={`footer-number-of-records`} className="font-weight-500 color-slate11">
{clientSidePagination && !serverSidePagination && `${globalFilteredRows.length} Records`}
{serverSidePagination && totalRecords ? `${totalRecords} Records` : ''}
</span>
)
))}
</div>
<div className={`col d-flex justify-content-center h-100 ${loadingState && 'w-100'}`}>
{enablePagination && (
<Pagination
lastActivePageIndex={pageIndex}
serverSide={serverSidePagination}
autoGotoPage={gotoPage}
autoCanNextPage={canNextPage}
autoPageCount={pageCount}
autoPageOptions={pageOptions}
onPageIndexChanged={onPageIndexChanged}
pageIndex={paginationInternalPageIndex}
setPageIndex={setPaginationInternalPageIndex}
enableNextButton={enableNextButton}
enablePrevButton={enablePrevButton}
darkMode={darkMode}
tableWidth={width}
loadingState={loadingState}
/>
)}
</div>
<div className="col d-flex justify-content-end ">
{loadingState && (
<SkeletonTheme baseColor="var(--slate3)" width="100%">
<Skeleton count={1} width={83} height={28} className="mb-1" />
</SkeletonTheme>
)}
{!loadingState && showAddNewRowButton && (
<>
<Tooltip id="tooltip-for-add-new-row" className="tooltip" />
<ButtonSolid
variant="ghostBlack"
fill={`var(--slate11)`}
className={`tj-text-xsm ${
tableDetails.addNewRowsDetails.addingNewRows && 'cursor-not-allowed always-active-btn'
}`}
customStyles={{ minWidth: '32px' }}
leftIcon="plus"
iconWidth="16"
onClick={() => {
if (!tableDetails.addNewRowsDetails.addingNewRows) {
showAddNewRowPopup();
}
}}
size="md"
data-tooltip-id="tooltip-for-add-new-row"
data-tooltip-content="Add new row"
></ButtonSolid>
</>
)}
{!loadingState && showDownloadButton && (
<div>
<Tooltip id="tooltip-for-download" className="tooltip" />
<OverlayTriggerComponent
trigger="click"
overlay={downlaodPopover()}
rootClose={true}
placement={'top-end'}
>
<ButtonSolid
variant="ghostBlack"
className={`tj-text-xsm `}
customStyles={{
minWidth: '32px',
}}
leftIcon="filedownload"
fill={`var(--slate11)`}
iconWidth="16"
size="md"
data-tooltip-id="tooltip-for-download"
data-tooltip-content="Download"
onClick={(e) => {
if (document.activeElement === e.currentTarget) {
e.currentTarget.blur();
}
}}
></ButtonSolid>
</OverlayTriggerComponent>
</div>
)}
{!loadingState && !hideColumnSelectorButton && (
<>
<Tooltip id="tooltip-for-manage-columns" className="tooltip" />
<OverlayTriggerComponent
trigger="click"
rootClose={true}
overlay={hideColumnsPopover()}
placement={'top-end'}
>
<ButtonSolid
variant="ghostBlack"
className={`tj-text-xsm `}
customStyles={{ minWidth: '32px' }}
leftIcon="eye1"
fill={`var(--slate11)`}
iconWidth="16"
size="md"
data-cy={`select-column-icon`}
onClick={(e) => {
if (document.activeElement === e.currentTarget) {
e.currentTarget.blur();
}
}}
data-tooltip-id="tooltip-for-manage-columns"
data-tooltip-content="Manage columns"
></ButtonSolid>
</OverlayTriggerComponent>
</>
)}
</div>
</div>
</div>
)}
{tableDetails.filterDetails.filtersVisible && (
<Filter
hideFilters={hideFilters}
filters={tableDetails.filterDetails.filters}
columns={columnData.map((column) => {
return { name: column.Header, value: column.id };
})}
mergeToFilterDetails={mergeToFilterDetails}
filterDetails={tableDetails.filterDetails}
darkMode={darkMode}
setAllFilters={setAllFilters}
fireEvent={fireEvent}
/>
)}
{tableDetails.addNewRowsDetails.addingNewRows && (
<AddNewRowComponent
hideAddNewRowPopup={hideAddNewRowPopup}
tableType={tableType}
darkMode={darkMode}
mergeToAddNewRowsDetails={mergeToAddNewRowsDetails}
onEvent={onEvent}
component={component}
setExposedVariable={setExposedVariable}
allColumns={allColumns}
defaultColumn={defaultColumn}
columns={columnsForAddNewRow}
addNewRowsDetails={tableDetails.addNewRowsDetails}
utilityForNestedNewRow={utilityForNestedNewRow}
tableEvents={tableEvents}
/>
)}
</div>
);
}