/* Парус 8 - Панели мониторинга Компонент: Таблица */ //--------------------- //Подключение библиотек //--------------------- import React, { useEffect, useState, useReducer } from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Pagination, Paper, IconButton, Icon, Stack, Button, Container, Link } from "@mui/material"; //Интерфейсные компоненты import { P8PAppInlineError, P8PHintDialog } from "../p8p_app_message"; //Встраиваемое сообщение об ошибке import { P8P_TABLE_AT, HEADER_INITIAL_STATE, p8pTableReducer } from "./p8p_table_reducer"; //Редьюсер состояния import { P8PTableColumnToolBarLeft } from "./p8p_table_column_toolbar_left"; //Таблица - Панель инструментов столбца (левая) import { P8PTableColumnToolBarRight } from "./p8p_table_column_toolbar_right"; //Таблица - Панель инструментов столбца (правая) import { P8PTableColumnMenu } from "./p8p_table_column_menu"; //Таблица - Меню столбца import { P8PTableColumnFilterDialog } from "./p8p_table_column_filter_dialog"; //Таблица - Диалог фильтра import { P8PTableFiltersChips } from "./p8p_table_filters_chips"; //Таблица - Сводный фильтр import { P8P_TABLE_SIZE, P8P_TABLE_DATA_TYPE, P8P_TABLE_COLUMN_ORDER_DIRECTIONS, P8P_TABLE_COLUMN_TOOL_BAR_ACTIONS, P8P_TABLE_COLUMN_MENU_ACTIONS, P8P_TABLE_FILTER_SHAPE, P8P_TABLE_ORDER_SHAPE, P8P_TABLE_PAGINATOR_ALIGN, P8P_TABLE_PAGINATOR_POSITION, P8P_TABLE_MORE_HEIGHT, P8P_TABLE_FILTERS_HEIGHT } from "./p8p_table_constants"; //Константы таблицы import { P8P_TABLE_VARIANT } from "../../theme/variants/p8p_table_variants"; //Варианты таблиц import { P8P_TABLE_HEAD_VARIANT } from "../../theme/variants/p8p_table_head_variants"; //Варианты заголовков таблиц import { P8P_TABLE_CELL_VARIANT } from "../../theme/variants/p8p_table_cell_variants"; //Варианты ячеек таблиц import { P8P_TABLE_ROW_VARIANT } from "../../theme/variants/p8p_table_row_variants"; //Варианты строк таблиц import { P8P_CONTAINER_VARIANT } from "../../theme/variants/p8p_container_variants"; //Варианты контейнеров import { P8P_PAGINATION_VARIANT } from "../../theme/variants/p8p_pagination_variants"; //Варианты пагинаторов import { P8P_TYPOGRAPHY_VARIANT } from "../../theme/p8p_typography"; //Варианты шрифтов //----------- //Тело модуля //----------- //Таблица const P8PTable = ({ style = {}, tableStyle = {}, columnsDef = [], groups = [], rows = [], orders, filters, size, pageNumber = 1, pagesCount = 0, pagesAlign = P8P_TABLE_PAGINATOR_ALIGN.RIGHT, pagesPosition = P8P_TABLE_PAGINATOR_POSITION.BOTTOM, fixedHeader = false, fixedColumns = 0, morePages = false, reloading = false, expandable, orderAscMenuItemCaption, orderDescMenuItemCaption, filterMenuItemCaption, valueFilterCaption, valueFromFilterCaption, valueToFilterCaption, okFilterBtnCaption, clearFilterBtnCaption, cancelFilterBtnCaption, morePagesBtnCaption, morePagesBtnProps, noDataFoundText, headCellRender, dataCellRender, groupCellRender, rowExpandRender, valueFormatter, headExpandCellStyle, onOrderChanged, onFilterChanged, onPagesCountChanged, onPageChanged, objectsCopier, containerComponent, containerComponentProps }) => { //Собственное состояние - описание заголовка const [header, dispatchHeaderAction] = useReducer(p8pTableReducer, HEADER_INITIAL_STATE()); //Собственное состояние - фильтруемая колонка const [filterColumn, setFilterColumn] = useState(null); //Собственное состояние - развёрнутые строки const [expanded, setExpanded] = useState({}); //Собственное состояния - развёрнутые группы const [expandedGroups, setExpandedGroups] = useState( Array.isArray(groups) && groups.length > 0 ? Object.assign({}, ...groups.map(g => ({ [g.name]: g.expanded }))) : {} ); //Собственное состояние - колонка с отображаемой подсказкой const [displayHintColumn, setDisplayHintColumn] = useState(null); //Описание фильтруемой колонки const filterColumnDef = filterColumn ? columnsDef.find(columnDef => columnDef.name == filterColumn) || null : null; //Описание колонки с отображаемой подсказкой const displayHintColumnDef = displayHintColumn ? columnsDef.find(columnDef => columnDef.name == displayHintColumn) || null : null; //Значения фильтра фильтруемой колонки const [filterColumnFrom, filterColumnTo] = filterColumn ? (() => { const filter = filters.find(filter => filter.name == filterColumn); return filter ? [filter.from == null ? "" : filter.from, filter.to == null ? "" : filter.to] : ["", ""]; })() : ["", ""]; //Формирование заголовка таблицы const setHeader = ({ columnsDef, expandable, fixedColumns, objectsCopier }) => dispatchHeaderAction({ type: P8P_TABLE_AT.SET_HEADER, payload: { columnsDef, expandable, fixedColumns, objectsCopier } }); //Сворачивание/разворачивание уровня заголовка таблицы const toggleHeaderExpand = ({ columnName, objectsCopier }) => dispatchHeaderAction({ type: P8P_TABLE_AT.TOGGLE_HEADER_EXPAND, payload: { columnName, expandable, fixedColumns, objectsCopier } }); //Выравнивание в зависимости от типа данных const getAlignByDataType = ({ dataType, hasChild }) => dataType === P8P_TABLE_DATA_TYPE.DATE || hasChild ? "center" : dataType === P8P_TABLE_DATA_TYPE.NUMB ? "right" : "left"; //Упорядочение содержимого в зависимости от типа данных const getJustifyContentByDataType = ({ dataType, hasChild }) => dataType === P8P_TABLE_DATA_TYPE.DATE || hasChild ? "center" : dataType === P8P_TABLE_DATA_TYPE.NUMB ? "flex-end" : "flex-start"; //Отработка нажатия на элемент пункта меню const handleToolBarItemClick = (action, columnName) => { switch (action) { case P8P_TABLE_COLUMN_TOOL_BAR_ACTIONS.ORDER_TOGGLE: { const colOrder = orders.find(o => o.name == columnName); const newDirection = colOrder?.direction == P8P_TABLE_COLUMN_ORDER_DIRECTIONS.ASC ? P8P_TABLE_COLUMN_ORDER_DIRECTIONS.DESC : colOrder?.direction == P8P_TABLE_COLUMN_ORDER_DIRECTIONS.DESC ? null : P8P_TABLE_COLUMN_ORDER_DIRECTIONS.ASC; if (onOrderChanged) onOrderChanged({ columnName, direction: newDirection }); break; } case P8P_TABLE_COLUMN_TOOL_BAR_ACTIONS.FILTER_TOGGLE: setFilterColumn(columnName); break; case P8P_TABLE_COLUMN_TOOL_BAR_ACTIONS.EXPAND_TOGGLE: toggleHeaderExpand({ columnName, objectsCopier }); break; } }; //Отработка нажатия на пункты меню const handleMenuItemClick = (action, columnName) => { switch (action) { case P8P_TABLE_COLUMN_MENU_ACTIONS.ORDER_ASC: onOrderChanged({ columnName, direction: P8P_TABLE_COLUMN_ORDER_DIRECTIONS.ASC }); break; case P8P_TABLE_COLUMN_MENU_ACTIONS.ORDER_DESC: onOrderChanged({ columnName, direction: P8P_TABLE_COLUMN_ORDER_DIRECTIONS.DESC }); break; case P8P_TABLE_COLUMN_MENU_ACTIONS.FILTER: setFilterColumn(columnName); break; } }; //Отработка ввода значения фильтра колонки const handleFilterOk = (columnName, from, to) => { if (onFilterChanged) onFilterChanged({ columnName, from: from === "" ? null : from, to: to === "" ? null : to }); setFilterColumn(null); }; //Отработка очистки значения фильтра колонки const handleFilterClear = columnName => { if (onFilterChanged) onFilterChanged({ columnName, from: null, to: null }); setFilterColumn(null); }; //Отработка отмены ввода значения фильтра колонки const handleFilterCancel = () => { setFilterColumn(null); }; //Отработка нажатия на элемент сводного фильтра const handleFilterChipClick = columnName => setFilterColumn(columnName); //Отработка удаления элемента сводного фильтра const handleFilterChipDelete = columnName => (onFilterChanged ? onFilterChanged({ columnName, from: null, to: null }) : null); //Отработка нажатия на кнопку догрузки страницы const handleMorePagesBtnClick = () => { if (onPagesCountChanged) onPagesCountChanged(); }; //Отработка нажатия на элемент отображения подсказки по колонке const handleColumnShowHintClick = columnName => setDisplayHintColumn(columnName); //Отработка сокрытия подсказки по колонке const handleHintOk = () => setDisplayHintColumn(null); //Отработка нажатия на кнопку раскрытия элемента const handleExpandClick = rowIndex => { if (expanded[rowIndex] === true) setExpanded(pv => { let res = { ...pv }; delete res[rowIndex]; return res; }); else setExpanded(pv => ({ ...pv, [rowIndex]: true })); }; //Отработка изменения страницы const handlePageChange = (e, page) => onPageChanged && onPageChanged({ page }); //При перезагрузке данных useEffect(() => { if (reloading) setExpanded({}); }, [reloading]); //При изменении описания колонок useEffect(() => { setHeader({ columnsDef, expandable, fixedColumns, objectsCopier }); }, [columnsDef, expandable, fixedColumns, objectsCopier]); //Генерация заголовка группы const renderGroupCell = group => { let customRender = {}; if (groupCellRender) customRender = groupCellRender({ columnsDef: header.columnsDef, group }) || {}; return header.displayDataColumns.map((columnDef, i) => { return ( {i == 0 ? ( {group.expandable ? ( { setExpandedGroups(pv => ({ ...pv, ...{ [group.name]: !pv[group.name] } })); }} > {expandedGroups[group.name] ? "indeterminate_check_box" : "add_box"} ) : null} {customRender.data ? customRender.data : group.caption} ) : null} ); }); }; //Генерация области страниц const renderPagination = position => { //Признак отображения в конкретной области const isVisible = [ position === P8P_TABLE_PAGINATOR_POSITION.TOP ? P8P_TABLE_PAGINATOR_POSITION.TOP : P8P_TABLE_PAGINATOR_POSITION.BOTTOM, P8P_TABLE_PAGINATOR_POSITION.BOTH ].includes(pagesPosition); //Генерация содержимого return ( <> {pagesCount && pagesCount > 0 && isVisible ? ( ) : null} ); }; //Генерация содержимого return (
{displayHintColumn ? : null} {filterColumn ? ( ) : null} {Array.isArray(filters) && filters.length > 0 ? ( ) : null} {renderPagination(P8P_TABLE_PAGINATOR_POSITION.TOP)} {header.displayLevels.map((level, i) => ( {expandable && rowExpandRender && i == 0 ? ( ) : null} {header.displayLevelsColumns[level].map((columnDef, j) => { let customRender = {}; if (headCellRender) customRender = headCellRender({ columnDef }) || {}; return ( {customRender.data ? ( customRender.data ) : columnDef.hint ? ( handleColumnShowHintClick(columnDef.name)} > {columnDef.caption} ) : ( columnDef.caption )} ); })} ))} {rows.length > 0 ? ( (Array.isArray(groups) && groups.length > 0 ? groups : [{}]).map((group, g) => { const rowsView = rows.map((row, i) => !group?.name || group?.name == row.groupName ? ( {expandable && rowExpandRender ? ( handleExpandClick(i)}> {expanded[i] === true ? "keyboard_arrow_down" : "keyboard_arrow_right"} ) : null} {header.displayDataColumns.map((columnDef, j) => { let customRender = {}; if (dataCellRender) customRender = dataCellRender({ row, columnDef }) || {}; return ( {customRender.data ? customRender.data : valueFormatter ? valueFormatter({ value: row[columnDef.name], columnDef }) : row[columnDef.name]} ); })} {expandable && rowExpandRender && expanded[i] === true ? ( {rowExpandRender({ columnsDef, row })} ) : null} ) : null ); return !group?.name ? ( rowsView ) : ( {renderGroupCell(group)} {!group.expandable || expandedGroups[group.name] === true ? rowsView : null} ); }) ) : noDataFoundText && !reloading ? ( ) : null}
{renderPagination(P8P_TABLE_PAGINATOR_POSITION.BOTTOM)} {morePages && (!pagesCount || pagesCount <= 0) ? ( ) : null}
); }; //Контроль свойств - Таблица P8PTable.propTypes = { style: PropTypes.object, tableStyle: PropTypes.object, columnsDef: PropTypes.arrayOf( PropTypes.shape({ name: PropTypes.string.isRequired, caption: PropTypes.string.isRequired, order: PropTypes.bool.isRequired, filter: PropTypes.bool.isRequired, dataType: PropTypes.string.isRequired, visible: PropTypes.bool.isRequired, values: PropTypes.array, parent: PropTypes.string, expandable: PropTypes.bool.isRequired, expanded: PropTypes.bool.isRequired, width: PropTypes.number }) ).isRequired, groups: PropTypes.arrayOf( PropTypes.shape({ name: PropTypes.string.isRequired, caption: PropTypes.string.isRequired, expandable: PropTypes.bool.isRequired, expanded: PropTypes.bool.isRequired }) ), rows: PropTypes.array.isRequired, orders: PropTypes.arrayOf(P8P_TABLE_ORDER_SHAPE).isRequired, filters: PropTypes.arrayOf(P8P_TABLE_FILTER_SHAPE).isRequired, size: PropTypes.string, pageNumber: PropTypes.number, pagesCount: PropTypes.number, pagesAlign: PropTypes.string, pagesPosition: PropTypes.string, fixedHeader: PropTypes.bool, fixedColumns: PropTypes.number, morePages: PropTypes.bool, reloading: PropTypes.bool, expandable: PropTypes.bool, orderAscMenuItemCaption: PropTypes.string.isRequired, orderDescMenuItemCaption: PropTypes.string.isRequired, filterMenuItemCaption: PropTypes.string.isRequired, valueFilterCaption: PropTypes.string.isRequired, valueFromFilterCaption: PropTypes.string.isRequired, valueToFilterCaption: PropTypes.string.isRequired, okFilterBtnCaption: PropTypes.string.isRequired, clearFilterBtnCaption: PropTypes.string.isRequired, cancelFilterBtnCaption: PropTypes.string.isRequired, morePagesBtnCaption: PropTypes.string.isRequired, morePagesBtnProps: PropTypes.object, noDataFoundText: PropTypes.string, headCellRender: PropTypes.func, dataCellRender: PropTypes.func, groupCellRender: PropTypes.func, rowExpandRender: PropTypes.func, valueFormatter: PropTypes.func, headExpandCellStyle: PropTypes.object, onOrderChanged: PropTypes.func, onFilterChanged: PropTypes.func, onPagesCountChanged: PropTypes.func, onPageChanged: PropTypes.func, objectsCopier: PropTypes.func.isRequired, containerComponent: PropTypes.oneOfType([PropTypes.elementType, PropTypes.string]), containerComponentProps: PropTypes.object }; //---------------- //Интерфейс модуля //---------------- export { P8P_TABLE_DATA_TYPE, P8P_TABLE_SIZE, P8P_TABLE_FILTER_SHAPE, P8P_TABLE_ORDER_SHAPE, P8P_TABLE_MORE_HEIGHT, P8P_TABLE_FILTERS_HEIGHT, P8P_TABLE_PAGINATOR_ALIGN, P8P_TABLE_PAGINATOR_POSITION, P8PTable };