From e243a85a4a93051ef2d4ca63ceefec67b587c046 Mon Sep 17 00:00:00 2001 From: Dollerino Date: Thu, 30 Jul 2026 19:54:37 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A6=D0=98=D0=A2=D0=9A-1076=20-=20=D0=94?= =?UTF-8?q?=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=B5=D0=B4=D0=B8?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20=D1=81=D1=82=D0=B8=D0=BB=D1=8C=20P8P*=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=D0=BE?= =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/p8p_app_message.js | 101 ++- app/components/p8p_app_progress.js | 12 +- app/components/p8p_app_workspace.js | 46 +- app/components/p8p_cyclogram.js | 640 ++---------------- .../p8p_cyclogram/p8p_cyclogram_column.js | 56 ++ .../p8p_cyclogram_column_lines.js | 51 ++ .../p8p_cyclogram/p8p_cyclogram_constants.js | 80 +++ .../p8p_cyclogram/p8p_cyclogram_grid.js | 49 ++ .../p8p_cyclogram/p8p_cyclogram_header.js | 63 ++ .../p8p_cyclogram/p8p_cyclogram_row_lines.js | 42 ++ .../p8p_cyclogram/p8p_cyclogram_rows_grid.js | 44 ++ .../p8p_cyclogram/p8p_cyclogram_task.js | 77 +++ .../p8p_cyclogram_task_editor.js | 117 ++++ .../p8p_cyclogram/p8p_cyclogram_utils.js | 34 + .../p8p_cyclogram/p8p_cyclogram_view.js | 171 +++++ app/components/p8p_data_grid.js | 5 +- app/components/p8p_dialog.js | 29 +- app/components/p8p_fullscreen_dialog.js | 23 +- app/components/p8p_gantt.js | 359 +--------- .../p8p_gantt/p8p_gantt_constants.js | 81 +++ .../{ => p8p_gantt}/p8p_gantt_hooks.js | 4 +- .../p8p_gantt/p8p_gantt_task_editor.js | 267 ++++++++ app/components/p8p_indicator.js | 87 +-- app/components/p8p_input.js | 105 +-- app/components/p8p_panels_menu.js | 91 ++- app/components/p8p_settings_dialog.js | 36 +- app/components/p8p_settings_list.js | 24 +- app/components/p8p_svg.js | 16 +- app/components/{ => p8p_table}/p8p_table.js | 613 +++-------------- .../p8p_table_column_filter_dialog.js | 164 +++++ .../p8p_table/p8p_table_column_menu.js | 106 +++ .../p8p_table_column_toolbar_left.js | 44 ++ .../p8p_table_column_toolbar_right.js | 63 ++ .../p8p_table/p8p_table_constants.js | 99 +++ .../p8p_table/p8p_table_filters_chips.js | 74 ++ .../{ => p8p_table}/p8p_table_reducer.js | 0 app/config_wrapper.js | 2 +- app/root.js | 22 +- app/theme/colors/common.js | 42 ++ app/theme/colors/green.js | 21 + app/theme/colors/grey.js | 21 + app/theme/colors/orange.js | 21 + app/theme/colors/red.js | 21 + app/theme/p8p_components.js | 67 ++ app/theme/p8p_palette.js | 72 ++ app/theme/p8p_typography.js | 217 ++++++ app/theme/styles/box.js | 152 +++++ app/theme/styles/common.js | 58 ++ app/theme/styles/icon.js | 32 + app/theme/styles/main.js | 13 + app/theme/styles/paper.js | 66 ++ app/theme/styles/stack.js | 22 + app/theme/styles/typography.js | 86 +++ app/theme/theme.js | 24 + app/theme/utils.js | 43 ++ app/theme/variants/p8p_app_bar_variants.js | 33 + .../variants/p8p_autocomplete_variants.js | 31 + app/theme/variants/p8p_button_variants.js | 106 +++ .../variants/p8p_card_actions_variants.js | 31 + app/theme/variants/p8p_card_variants.js | 44 ++ app/theme/variants/p8p_container_variants.js | 48 ++ .../p8p_dialog_content_text_variants.js | 37 + .../variants/p8p_dialog_content_variants.js | 48 ++ .../variants/p8p_dialog_title_variants.js | 37 + app/theme/variants/p8p_drawer_variants.js | 37 + app/theme/variants/p8p_grid_variants.js | 52 ++ .../variants/p8p_icon_button_variants.js | 33 + app/theme/variants/p8p_icon_variants.js | 51 ++ .../variants/p8p_input_label_variants.js | 31 + app/theme/variants/p8p_input_variants.js | 40 ++ .../variants/p8p_list_item_text_variants.js | 38 ++ app/theme/variants/p8p_list_variants.js | 46 ++ app/theme/variants/p8p_menu_item_variants.js | 31 + app/theme/variants/p8p_pagination_variants.js | 51 ++ app/theme/variants/p8p_select_variants.js | 38 ++ app/theme/variants/p8p_table_cell_variants.js | 93 +++ app/theme/variants/p8p_table_head_variants.js | 35 + app/theme/variants/p8p_table_row_variants.js | 33 + app/theme/variants/p8p_table_variants.js | 38 ++ app/theme/variants/p8p_text_field_variants.js | 56 ++ 80 files changed, 4210 insertions(+), 1783 deletions(-) create mode 100644 app/components/p8p_cyclogram/p8p_cyclogram_column.js create mode 100644 app/components/p8p_cyclogram/p8p_cyclogram_column_lines.js create mode 100644 app/components/p8p_cyclogram/p8p_cyclogram_constants.js create mode 100644 app/components/p8p_cyclogram/p8p_cyclogram_grid.js create mode 100644 app/components/p8p_cyclogram/p8p_cyclogram_header.js create mode 100644 app/components/p8p_cyclogram/p8p_cyclogram_row_lines.js create mode 100644 app/components/p8p_cyclogram/p8p_cyclogram_rows_grid.js create mode 100644 app/components/p8p_cyclogram/p8p_cyclogram_task.js create mode 100644 app/components/p8p_cyclogram/p8p_cyclogram_task_editor.js create mode 100644 app/components/p8p_cyclogram/p8p_cyclogram_utils.js create mode 100644 app/components/p8p_cyclogram/p8p_cyclogram_view.js create mode 100644 app/components/p8p_gantt/p8p_gantt_constants.js rename app/components/{ => p8p_gantt}/p8p_gantt_hooks.js (96%) create mode 100644 app/components/p8p_gantt/p8p_gantt_task_editor.js rename app/components/{ => p8p_table}/p8p_table.js (54%) create mode 100644 app/components/p8p_table/p8p_table_column_filter_dialog.js create mode 100644 app/components/p8p_table/p8p_table_column_menu.js create mode 100644 app/components/p8p_table/p8p_table_column_toolbar_left.js create mode 100644 app/components/p8p_table/p8p_table_column_toolbar_right.js create mode 100644 app/components/p8p_table/p8p_table_constants.js create mode 100644 app/components/p8p_table/p8p_table_filters_chips.js rename app/components/{ => p8p_table}/p8p_table_reducer.js (100%) create mode 100644 app/theme/colors/common.js create mode 100644 app/theme/colors/green.js create mode 100644 app/theme/colors/grey.js create mode 100644 app/theme/colors/orange.js create mode 100644 app/theme/colors/red.js create mode 100644 app/theme/p8p_components.js create mode 100644 app/theme/p8p_palette.js create mode 100644 app/theme/p8p_typography.js create mode 100644 app/theme/styles/box.js create mode 100644 app/theme/styles/common.js create mode 100644 app/theme/styles/icon.js create mode 100644 app/theme/styles/main.js create mode 100644 app/theme/styles/paper.js create mode 100644 app/theme/styles/stack.js create mode 100644 app/theme/styles/typography.js create mode 100644 app/theme/theme.js create mode 100644 app/theme/utils.js create mode 100644 app/theme/variants/p8p_app_bar_variants.js create mode 100644 app/theme/variants/p8p_autocomplete_variants.js create mode 100644 app/theme/variants/p8p_button_variants.js create mode 100644 app/theme/variants/p8p_card_actions_variants.js create mode 100644 app/theme/variants/p8p_card_variants.js create mode 100644 app/theme/variants/p8p_container_variants.js create mode 100644 app/theme/variants/p8p_dialog_content_text_variants.js create mode 100644 app/theme/variants/p8p_dialog_content_variants.js create mode 100644 app/theme/variants/p8p_dialog_title_variants.js create mode 100644 app/theme/variants/p8p_drawer_variants.js create mode 100644 app/theme/variants/p8p_grid_variants.js create mode 100644 app/theme/variants/p8p_icon_button_variants.js create mode 100644 app/theme/variants/p8p_icon_variants.js create mode 100644 app/theme/variants/p8p_input_label_variants.js create mode 100644 app/theme/variants/p8p_input_variants.js create mode 100644 app/theme/variants/p8p_list_item_text_variants.js create mode 100644 app/theme/variants/p8p_list_variants.js create mode 100644 app/theme/variants/p8p_menu_item_variants.js create mode 100644 app/theme/variants/p8p_pagination_variants.js create mode 100644 app/theme/variants/p8p_select_variants.js create mode 100644 app/theme/variants/p8p_table_cell_variants.js create mode 100644 app/theme/variants/p8p_table_head_variants.js create mode 100644 app/theme/variants/p8p_table_row_variants.js create mode 100644 app/theme/variants/p8p_table_variants.js create mode 100644 app/theme/variants/p8p_text_field_variants.js diff --git a/app/components/p8p_app_message.js b/app/components/p8p_app_message.js index c00fdb5..556714a 100644 --- a/app/components/p8p_app_message.js +++ b/app/components/p8p_app_message.js @@ -19,7 +19,13 @@ import Button from "@mui/material/Button"; //Кнопки import Container from "@mui/material/Container"; //Контейнер import Box from "@mui/material/Box"; //Обёртка import { BUTTONS, STATE } from "../../app.text"; //Типовые текстовые ресурсы и константы -import { APP_COLORS } from "../../app.styles"; //Типовые стили +import { useTheme } from "@mui/material"; //Тема +import { P8P_TYPOGRAPHY_VARIANT } from "../theme/p8p_typography"; //Варианты шрифтов +import { P8P_DIALOG_TITLE_VARIANT } from "../theme/variants/p8p_dialog_title_variants"; //Варианты заголовков диалога +import { P8P_BUTTON_VARIANT } from "../theme/variants/p8p_button_variants"; //Варианты кнопок +import { P8P_CONTAINER_VARIANT } from "../theme/variants/p8p_container_variants"; //Варианты контейнеров +import { P8P_DIALOG_CONTENT_VARIANT } from "../theme/variants/p8p_dialog_content_variants"; //Варианты содержимого диалогов +import { P8P_DIALOG_CONTENT_TEXT_VARIANT } from "../theme/variants/p8p_dialog_content_text_variants"; //Варианты текста содержимого диалога //--------- //Константы @@ -32,44 +38,6 @@ const P8P_APP_MESSAGE_VARIANT = { ERR: STATE.ERR }; -//Стили -const STYLES = { - DEFAULT: { - wordBreak: "break-word" - }, - INFO: { - titleText: { - color: APP_COLORS[STATE.INFO].contrColor - }, - bodyText: { - color: APP_COLORS[STATE.INFO].contrColor - } - }, - WARN: { - titleText: { - color: APP_COLORS[STATE.WARN].contrColor - }, - bodyText: { - color: APP_COLORS[STATE.WARN].contrColor - } - }, - ERR: { - titleText: { - color: APP_COLORS[STATE.ERR].contrColor - }, - bodyText: { - color: APP_COLORS[STATE.ERR].contrColor - } - }, - INLINE_MESSAGE: { - with: "100%", - textAlign: "center" - }, - FULL_ERROR_TEXT_BUTTON: { - color: APP_COLORS[STATE.WARN].contrColor - } -}; - //----------- //Тело модуля //----------- @@ -94,37 +62,50 @@ const P8PAppMessage = ({ //Состояние подробной информации об ошибке const [showFullErrorText, setShowFullErrorText] = useState(false); - //Подбор стиля и ресурсов - let style = STYLES.INFO; + //Определяем вариант стилизации + let titleVariant = P8P_DIALOG_TITLE_VARIANT.INFO; + let contentVariant = P8P_DIALOG_CONTENT_TEXT_VARIANT.INFO; switch (variant) { case P8P_APP_MESSAGE_VARIANT.INFO: { - style = STYLES.INFO; + titleVariant = P8P_DIALOG_TITLE_VARIANT.INFO; + contentVariant = P8P_DIALOG_CONTENT_TEXT_VARIANT.INFO; break; } case P8P_APP_MESSAGE_VARIANT.WARN: { - style = STYLES.WARN; + titleVariant = P8P_DIALOG_TITLE_VARIANT.WARN; + contentVariant = P8P_DIALOG_CONTENT_TEXT_VARIANT.WARN; break; } case P8P_APP_MESSAGE_VARIANT.ERR: { - style = STYLES.ERR; + titleVariant = P8P_DIALOG_TITLE_VARIANT.ERROR; + contentVariant = P8P_DIALOG_CONTENT_TEXT_VARIANT.ERROR; break; } } //Заголовок let titlePart; - if (title && titleText) titlePart = {titleText}; + if (title && titleText) + titlePart = ( + + {titleText} + + ); //Кнопка Отмена let cancelBtnPart; if (cancelBtn && cancelBtnCaption && variant === P8P_APP_MESSAGE_VARIANT.WARN) - cancelBtnPart = ; + cancelBtnPart = ( + + ); //Кнопка OK let okBtnPart; if (okBtn && okBtnCaption) okBtnPart = ( - ); @@ -133,7 +114,7 @@ const P8PAppMessage = ({ let fullErrorTextBtn; if (fullErrorText && showErrMoreCaption && hideErrMoreCaption && variant === P8P_APP_MESSAGE_VARIANT.ERR) fullErrorTextBtn = ( - ); @@ -154,7 +135,7 @@ const P8PAppMessage = ({ (onCancel ? onCancel() : null)}> {titlePart} - {!showFullErrorText ? text : fullErrorText} + {!showFullErrorText ? text : fullErrorText} {actionsPart} @@ -181,24 +162,28 @@ P8PAppMessage.propTypes = { //Встроенное сообщение const P8PAppInlineMessage = ({ variant, text, okBtn, onOk, okBtnCaption }) => { + //Определяем тему + const theme = useTheme(); + //Генерация содержимого return ( - + {text} {okBtn && okBtnCaption ? ( - @@ -254,12 +239,14 @@ const P8PAppInlineInfo = props => buildVariantInlineMessage(props, P8P_APP_MESSA const P8PHintDialog = ({ title, hint, onOk }) => { return ( (onOk ? onOk(e) : null)}> - {title} - + {title} +
- +
); diff --git a/app/components/p8p_app_progress.js b/app/components/p8p_app_progress.js index fb208ce..530e0b6 100644 --- a/app/components/p8p_app_progress.js +++ b/app/components/p8p_app_progress.js @@ -14,6 +14,8 @@ import DialogTitle from "@mui/material/DialogTitle"; //Заголовок диа import DialogContent from "@mui/material/DialogContent"; //Содержимое диалога import DialogContentText from "@mui/material/DialogContentText"; //Текст содержимого диалога import LinearProgress from "@mui/material/LinearProgress"; //Индикатор +import { P8P_DIALOG_TITLE_VARIANT } from "../theme/variants/p8p_dialog_title_variants"; //Варианты заголовков диалога +import { P8P_DIALOG_CONTENT_TEXT_VARIANT } from "../theme/variants/p8p_dialog_content_text_variants"; //Варианты текста содержимого диалога //----------- //Тело модуля @@ -28,9 +30,15 @@ const P8PAppProgress = props => { return (
- {title ? {title} : null} + {title ? ( + + {title} + + ) : null} - {text} + + {text} + diff --git a/app/components/p8p_app_workspace.js b/app/components/p8p_app_workspace.js index 082e9ae..3a77fa4 100644 --- a/app/components/p8p_app_workspace.js +++ b/app/components/p8p_app_workspace.js @@ -25,7 +25,13 @@ import { Divider } from "@mui/material"; //Интерфейсные компоненты import { P8PPanelsMenuDrawer, P8P_PANELS_MENU_PANEL_SHAPE } from "./p8p_panels_menu"; //Меню -import { APP_STYLES } from "../../app.styles"; //Типовые стили +import { P8P_TYPOGRAPHY_VARIANT } from "../theme/p8p_typography"; //Варианты шрифтов +import { P8P_ICON_BUTTON_VARIANT } from "../theme/variants/p8p_icon_button_variants"; //Варианты кнопок-иконок +import { P8P_APP_BAR_VARIANT } from "../theme/variants/p8p_app_bar_variants"; //Варианты областей заголовка +import { P8P_DRAWER_VARIANT } from "../theme/variants/p8p_drawer_variants"; //Варианты выезжающих областей +import { P8P_BOX_CENTER_START, P8P_BOX_CENTER_END, P8P_BOX_FLEX, P8P_BOX_APP_WORKSPACE } from "../theme/styles/box"; //Стили контейнеров +import { P8P_MAIN_APP_WORKSPACE } from "../theme/styles/main"; //Стили содержимого страницы +import { P8P_LIST_ITEM_TEXT_VARIANT } from "../theme/variants/p8p_list_item_text_variants"; //Варианты значений списков //--------- //Константы @@ -34,18 +40,6 @@ import { APP_STYLES } from "../../app.styles"; //Типовые стили //Высота главного меню const APP_BAR_HEIGHT = "64px"; -//Стили -const STYLES = { - DRAWER: { [`& .MuiDrawer-paper`]: { ...APP_STYLES.SCROLL } }, - ROOT_BOX: { display: "flex" }, - APP_BAR: { position: "fixed" }, - APP_BAR_MAIN_BOX: { display: "flex", width: "100vw", alignItems: "center", justifyContent: "space-between" }, - APP_BAR_LEFT_SIDE: { display: "flex", alignItems: "center", justifyContent: "flex-start" }, - APP_BAR_RIGHT_SIDE: { display: "flex", alignItems: "center", justifyContent: "flex-end" }, - APP_BAR_BUTTON: { mr: 2 }, - MAIN: { flexGrow: 1 } -}; - //----------- //Тело модуля //----------- @@ -92,35 +86,35 @@ const P8PAppWorkspace = ({ //Генерация содержимого return ( - + {showAppBar && ( <> - + - - + + {open ? "chevron_left" : "menu"} - + {caption || selectedPanel?.caption} - + {showAppBarSettings && selectedPanel.showUserSettings ? ( handleSettingsDialog(selectedPanel.name)} edge="end" - sx={STYLES.APP_BAR_BUTTON} + variant={P8P_ICON_BUTTON_VARIANT.APP_BAR_BUTTON} > settings @@ -129,33 +123,33 @@ const P8PAppWorkspace = ({ - + close - + home - + handleSettingsDialog()}> settings - + )} -
+
{showAppBar && } {children}
diff --git a/app/components/p8p_cyclogram.js b/app/components/p8p_cyclogram.js index bda3ca2..46f8b8f 100644 --- a/app/components/p8p_cyclogram.js +++ b/app/components/p8p_cyclogram.js @@ -9,604 +9,32 @@ import React, { useEffect, useState, useRef } from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента -import { - Box, - Typography, - Dialog, - DialogActions, - DialogContent, - Button, - List, - ListItem, - ListItemText, - Link, - Divider, - IconButton, - Icon -} from "@mui/material"; //Интерфейсные компоненты +import { Box, Typography, Link, IconButton, Icon } from "@mui/material"; //Интерфейсные компоненты import { P8PAppInlineError } from "./p8p_app_message"; //Встраиваемое сообщение об ошибке -import { hasValue } from "../core/utils"; //Вспомогательный функции import { useP8PCyclogram } from "./p8p_cyclogram_hooks"; //Хук для циклограммы +import { P8PCyclogramTaskEditor } from "./p8p_cyclogram/p8p_cyclogram_task_editor"; //Редактор задачи +import { P8P_TYPOGRAPHY_VARIANT } from "../theme/p8p_typography"; //Варианты шрифтов +import { P8P_COMPONENT_HEIGHT } from "../theme/styles/common"; //Стили - общие +import { P8P_BOX_CYCLOGRAM } from "../theme/styles/box"; //Стили контейнеров +import { getShift } from "./p8p_cyclogram/p8p_cyclogram_utils"; //Вспомогательные функции циклограммы +import { + P8P_CYCLOGRAM_ZOOM, + NDEFAULT_LINE_HEIGHT, + NDEFAULT_HEADER_HEIGHT, + TITLE_HEIGHT, + ZOOM_HEIGHT, + P8P_CYCLOGRAM_COLUMN_SHAPE, + P8P_CYCLOGRAM_GROUP_SHAPE, + P8P_CYCLOGRAM_TASK_SHAPE, + P8P_CYCLOGRAM_TASK_ATTRIBUTE_SHAPE +} from "./p8p_cyclogram/p8p_cyclogram_constants"; //Константы циклограммы +import { P8PCyclogramGrid } from "./p8p_cyclogram/p8p_cyclogram_grid"; //Фон таблицы циклограммы +import { P8PCyclogramView } from "./p8p_cyclogram/p8p_cyclogram_view"; //Представление циклограммы +import { P8P_TYPOGRAPHY_TITLE } from "../theme/styles/typography"; //Стили текста -//--------- -//Константы -//--------- - -//Уровни масштаба -const P8P_CYCLOGRAM_ZOOM = [0.2, 0.4, 0.7, 1, 1.5, 2, 2.5]; - -//Параметры элементов циклограммы -const NDEFAULT_LINE_HEIGHT = 20; -const NDEFAULT_HEADER_HEIGHT = 35; - -//Высота заголовка -const TITLE_HEIGHT = "44px"; - -//Высота панели масштабирования -const ZOOM_HEIGHT = "56px"; - -//Стили -const STYLES = { - CYCLOGRAM_TITLE: { height: TITLE_HEIGHT }, - CYCLOGRAM_ZOOM: { height: ZOOM_HEIGHT }, - HEADER_COLUMN: { - fontSize: "12px", - textOverflow: "ellipsis", - overflow: "hidden", - whiteSpace: "pre", - textAlign: "center", - lineHeight: "3", - padding: "0px 5px" - }, - CYCLOGRAM_BOX: (noData, title, zoomBar) => ({ - position: "relative", - overflow: "auto", - padding: "0px 8px", - height: `calc(100% - ${zoomBar ? ZOOM_HEIGHT : "0px"} - ${title ? TITLE_HEIGHT : "0px"})`, - display: noData ? "none" : "" - }), - GRID_ROW: index => (index % 2 === 0 ? { backgroundColor: "#ffffff" } : { backgroundColor: "#f5f5f5" }), - GROUP_HEADER_BOX: { - border: "1px solid", - backgroundColor: "#ebebeb", - display: "flex", - alignItems: "center", - justifyContent: "center" - }, - GROUP_HEADER: { - fontSize: "14px", - textAlign: "center", - wordWrap: "break-word" - }, - TASK_EDITOR_CONTENT: { minWidth: 400, overflowX: "auto" }, - TASK_EDITOR_LIST: { width: "100%", minWidth: 300, maxWidth: 700, bgcolor: "background.paper" }, - TASK_BOX: (lineHeight, bgColor, textColor, highlightColor) => ({ - display: "flex", - alignItems: "center", - backgroundColor: bgColor ? bgColor : "#b4b9bf", - ...(textColor ? { color: textColor } : {}), - height: lineHeight, - "&:hover": { - ...(highlightColor - ? { backgroundColor: `${highlightColor} !important`, filter: "brightness(1) !important" } - : { filter: "brightness(1.25) !important" }), - cursor: "pointer !important" - } - }), - TASK: lineHeight => { - const availableLines = Math.floor(lineHeight / 18); - return { - width: "100%", - fontSize: "12px", - overflowWrap: "break-word", - wordBreak: "break-all", - overflow: "hidden", - textOverflow: "ellipsis", - display: "-webkit-box", - lineHeight: "18px", - maxHeight: lineHeight, - WebkitLineClamp: availableLines < 1 ? 1 : availableLines, - WebkitBoxOrient: "vertical" - }; - } -}; - -//Структура колонки -const P8P_CYCLOGRAM_COLUMN_SHAPE = PropTypes.shape({ - name: PropTypes.string.isRequired, - start: PropTypes.number.isRequired, - end: PropTypes.number.isRequired -}); - -//Структура группы -const P8P_CYCLOGRAM_GROUP_SHAPE = PropTypes.shape({ - name: PropTypes.string.isRequired, - height: PropTypes.number.isRequired, - width: PropTypes.number.isRequired, - visible: PropTypes.bool.isRequired -}); - -//Структура задачи -const P8P_CYCLOGRAM_TASK_SHAPE = PropTypes.shape({ - id: PropTypes.string.isRequired, - rn: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - fullName: PropTypes.string.isRequired, - lineNumb: PropTypes.number.isRequired, - start: PropTypes.number.isRequired, - end: PropTypes.number.isRequired, - group: PropTypes.string, - bgColor: PropTypes.string, - textColor: PropTypes.string, - highlightColor: PropTypes.string -}); - -//Структура динамического атрибута задачи -const P8P_CYCLOGRAM_TASK_ATTRIBUTE_SHAPE = PropTypes.shape({ - name: PropTypes.string.isRequired, - caption: PropTypes.string.isRequired, - visible: PropTypes.bool.isRequired -}); - -//-------------------------------- -//Вспомогательные классы и функции -//-------------------------------- - -//Определение сдвига для максимальной ширины колонок -const getShift = (columns, currentColumnsMaxWidth, maxCyclogramWidth) => { - //Определяем доступное пространство для расширения - let maxWidthDiff = maxCyclogramWidth - currentColumnsMaxWidth; - //Инициализируем значение сдвига - let shift = 1; - //Если доступно больше ширины и есть пространство для расширения - if (maxCyclogramWidth > currentColumnsMaxWidth && maxCyclogramWidth - maxWidthDiff > columns.length) { - //Определяем доступный сдвиг колонок - shift = maxCyclogramWidth / currentColumnsMaxWidth; - } - //Возвращаем сдвиг - return shift; -}; - -//Формирование стилей для группы -const getGroupStyles = (indexGrp, highlightColor) => { - return `.main .TaskGrp${indexGrp}:hover .TaskGrp${indexGrp} { - ${highlightColor ? `background: ${highlightColor};` : `filter: brightness(1.15);`} - } - .main:has(.TaskGrp${indexGrp}:hover) .TaskGrpHeader${indexGrp} { - display: block; - } - `; - //cursor: pointer; -}; - -//Фон строк таблицы -const P8PCyclogramRowsGrid = ({ rows, maxWidth, lineHeight }) => { - return ( - - {rows.map((el, index) => ( - - - - ))} - - ); -}; - -//Контроль свойств - Фон строк таблицы -P8PCyclogramRowsGrid.propTypes = { - rows: PropTypes.array.isRequired, - maxWidth: PropTypes.number.isRequired, - lineHeight: PropTypes.number.isRequired -}; - -//Линии строк таблицы -const P8PCyclogramRowsLines = ({ rows, maxWidth, lineHeight }) => { - return ( - - {rows.map((el, index) => ( - - ))} - - ); -}; - -//Контроль свойств - Линии строк таблицы -P8PCyclogramRowsLines.propTypes = { - rows: PropTypes.array.isRequired, - maxWidth: PropTypes.number.isRequired, - lineHeight: PropTypes.number.isRequired -}; - -//Линии колонок таблицы -const P8PCyclogramColumnsLines = ({ columns, shift, y1, y2 }) => { - //Инициализируем старт текущей колонки - let prevColumnEnd = 0; - return ( - - {columns.map((column, index) => { - //Аккумулируем окончание последней колонки с учетом сдвига - prevColumnEnd = index !== 0 ? prevColumnEnd + (columns[index - 1].end - columns[index - 1].start) * shift : 0; - return ; - })} - - - ); -}; - -//Контроль свойств - Линии колонок таблицы -P8PCyclogramColumnsLines.propTypes = { - columns: PropTypes.array.isRequired, - shift: PropTypes.number.isRequired, - y1: PropTypes.number.isRequired, - y2: PropTypes.number.isRequired -}; - -//Фон таблицы циклограммы -const P8PCyclogramGrid = ({ tasks, columns, shift, maxWidth, maxHeight, lineHeight }) => { - //Формируем массив строк исходя из максимального значения строки задачи - const rows = Array.from(Array(Math.max(...tasks.map(o => o.lineNumb)) + 1).keys()); - return ( - - - - - - - ); -}; - -//Контроль свойств - Фон таблицы циклограммы -P8PCyclogramGrid.propTypes = { - tasks: PropTypes.array.isRequired, - columns: PropTypes.array.isRequired, - shift: PropTypes.number.isRequired, - maxWidth: PropTypes.number.isRequired, - maxHeight: PropTypes.number.isRequired, - lineHeight: PropTypes.number.isRequired -}; - -//Колонка заголовка циклограммы -const P8PCyclogramHeaderColumn = ({ column, start, shift, columnRenderer }) => { - //Рассчитываем ширину колонки - const columnWidth = column.end - column.start; - //Формируем собственное отображение, если требуется - const customView = columnRenderer ? columnRenderer({ column }) : null; - return ( - <> - - {customView ? ( - customView - ) : ( - - {column.name} - - )} - - - ); -}; - -//Контроль свойств - Колонка заголовка циклограммы -P8PCyclogramHeaderColumn.propTypes = { - column: PropTypes.object.isRequired, - start: PropTypes.number.isRequired, - shift: PropTypes.number.isRequired, - maxHeight: PropTypes.number.isRequired, - lastElement: PropTypes.bool, - columnRenderer: PropTypes.func -}; - -//Заголовок циклограммы -const P8PCyclogramHeader = ({ columns, shift, maxWidth, maxHeight, columnRenderer, headerBlock }) => { - //Инициализируем старт текущей колонки - let prevColumnEnd = 0; - return ( - - - {columns.map((column, index) => { - //Аккумулируем окончание последней колонки с учетом сдвига - prevColumnEnd = index !== 0 ? prevColumnEnd + (columns[index - 1].end - columns[index - 1].start) * shift : 0; - return ( - - ); - })} - - - - - ); -}; - -//Контроль свойств - Заголовок циклограммы -P8PCyclogramHeader.propTypes = { - columns: PropTypes.array.isRequired, - shift: PropTypes.number.isRequired, - maxWidth: PropTypes.number.isRequired, - maxHeight: PropTypes.number.isRequired, - columnRenderer: PropTypes.func, - headerBlock: PropTypes.object -}; - -//Задача циклограммы -const P8PCyclogramTask = ({ task, indexGrp, shift, lineHeight, openTaskEditor, taskRenderer }) => { - //Рассчитываем ширину задачи - const width = task.end !== 0 ? (task.end - task.start) * shift : 0; - //Формируем собственное отображение, если требуется - const customView = taskRenderer ? taskRenderer({ task, taskHeight: lineHeight, taskWidth: width }) || {} : {}; - return ( - - openTaskEditor(task)} - > - {customView.data ? ( - customView.data - ) : ( - - {task.name} - - )} - - - ); -}; - -//Контроль свойств - Группы циклограммы -P8PCyclogramTask.propTypes = { - task: PropTypes.object.isRequired, - indexGrp: PropTypes.number, - shift: PropTypes.number.isRequired, - lineHeight: PropTypes.number.isRequired, - openTaskEditor: PropTypes.func.isRequired, - taskRenderer: PropTypes.func -}; - -//Основная информация циклограммы -const P8PCyclogramMain = ({ - columns, - groups, - tasks, - shift, - lineHeight, - maxWidth, - maxHeight, - openTaskEditor, - groupHeaderRenderer, - taskRenderer, - columnRenderer, - headerBlock -}) => { - //Инициализируем коллекцию тасков с группами - const tasksWithGroup = tasks.filter(task => hasValue(task.groupName)); - //Инициализируем коллекцию тасков без групп - const tasksWithoutGroup = tasks.filter(task => !hasValue(task.groupName)); - //Инициализируем коллекцию отображаемых групп - const visibleGroups = groups ? groups.filter(group => group.visible) : []; - return ( - - - {visibleGroups.length !== 0 - ? visibleGroups.map((grp, indexGrp) => { - //Считываем задачи группы - let groupTasks = tasksWithGroup.filter(task => task.groupName === grp.name); - //Если по данной группе нет тасков - ничего не выводим - if (groupTasks.length === 0) { - return null; - } - return ( - - {groupTasks.map((task, index) => ( - - ))} - - - ); - }) - : null} - - {tasksWithoutGroup.map((task, index) => { - return ( - - ); - })} - - - - {visibleGroups.length !== 0 ? ( - - {visibleGroups.map((grp, indexGrp) => { - //Инициализируем параметры группы - let defaultView = null; - let customView = null; - let groupHeaderX = 0; - let groupHeaderY = 0; - let groupTasks = tasksWithGroup.filter(task => task.groupName === grp.name); - //Если по данной группе нет тасков - ничего не выводим - if (groupTasks.length === 0) { - return null; - } - //Если требуется отображать заголовок группы - if (grp.visible) { - //Формируем отображение по умолчанию - defaultView = ( - - {grp.name} - - ); - //Формируем собственное отображение, если требуется - customView = groupHeaderRenderer ? groupHeaderRenderer({ group: grp }) : null; - //Рассчитываем координаты заголовка группы - groupHeaderX = Math.min(...groupTasks.map(o => o.start)) * shift; - groupHeaderY = NDEFAULT_HEADER_HEIGHT + Math.min(...groupTasks.map(o => o.lineNumb)) * lineHeight - grp.height - 5; - } - return ( - - {customView ? customView : defaultView} - - ); - })} - - ) : null} - - ); -}; - -//Контроль свойств - Основная информация циклограммы -P8PCyclogramMain.propTypes = { - columns: PropTypes.array.isRequired, - groups: PropTypes.array, - tasks: PropTypes.array.isRequired, - shift: PropTypes.number.isRequired, - lineHeight: PropTypes.number.isRequired, - maxWidth: PropTypes.number.isRequired, - maxHeight: PropTypes.number.isRequired, - openTaskEditor: PropTypes.func.isRequired, - groupHeaderRenderer: PropTypes.func, - taskRenderer: PropTypes.func, - columnRenderer: PropTypes.func, - headerBlock: PropTypes.object -}; - -//Редактор задачи -const P8PCyclogramTaskEditor = ({ - task, - taskAttributes, - onOk, - onCancel, - taskAttributeRenderer, - taskDialogRenderer, - nameCaption, - okBtnCaption, - cancelBtnCaption -}) => { - //Собственное состояние - const [state] = useState({ - start: task.start, - end: task.end - }); - - //Отображаемые атрибуты - const dispTaskAttributes = - Array.isArray(taskAttributes) && taskAttributes.length > 0 ? taskAttributes.filter(attr => attr.visible && hasValue(task[attr.name])) : []; - - //При сохранении - const handleOk = () => (onOk && state.start && state.end ? onOk() : null); - - //При отмене - const handleCancel = () => (onCancel ? onCancel() : null); - - //Генерация содержимого - return ( - - {taskDialogRenderer ? ( - taskDialogRenderer({ task, taskAttributes, close: handleCancel }) - ) : ( - <> - - - - - - {dispTaskAttributes.length > 0 ? : null} - {dispTaskAttributes.length > 0 - ? dispTaskAttributes.map((attr, i) => { - const defaultView = task[attr.name]; - const customView = taskAttributeRenderer ? taskAttributeRenderer({ task, attribute: attr }) : null; - return ( - - - - - {i < dispTaskAttributes.length - 1 ? : null} - - ); - }) - : null} - - - - - - - - )} - - ); -}; - -//Контроль свойств - Редактор задачи -P8PCyclogramTaskEditor.propTypes = { - task: P8P_CYCLOGRAM_TASK_SHAPE, - taskAttributes: PropTypes.arrayOf(P8P_CYCLOGRAM_TASK_ATTRIBUTE_SHAPE), - onOk: PropTypes.func, - onCancel: PropTypes.func, - taskAttributeRenderer: PropTypes.func, - taskDialogRenderer: PropTypes.func, - nameCaption: PropTypes.string.isRequired, - okBtnCaption: PropTypes.string.isRequired, - cancelBtnCaption: PropTypes.string.isRequired -}; +//----------- +//Тело модуля +//----------- //Циклограмма const P8PCyclogram = ({ @@ -716,13 +144,14 @@ const P8PCyclogram = ({ {title ? ( {onTitleClick ? ( - onTitleClick()}> + onTitleClick()}> {title} ) : ( @@ -731,7 +160,7 @@ const P8PCyclogram = ({ ) : null} {zoomBar ? ( - + handleZoomChange(1)} disabled={state.zoom == P8P_CYCLOGRAM_ZOOM[P8P_CYCLOGRAM_ZOOM.length - 1]} @@ -743,7 +172,16 @@ const P8PCyclogram = ({ ) : null} - + - { + //Рассчитываем ширину колонки + const columnWidth = column.end - column.start; + //Формируем собственное отображение, если требуется + const customView = columnRenderer ? columnRenderer({ column }) : null; + return ( + <> + + {customView ? ( + customView + ) : ( + + {column.name} + + )} + + + ); +}; + +//Контроль свойств - Колонка +P8PCyclogramHeaderColumn.propTypes = { + column: PropTypes.object.isRequired, + start: PropTypes.number.isRequired, + shift: PropTypes.number.isRequired, + maxHeight: PropTypes.number.isRequired, + lastElement: PropTypes.bool, + columnRenderer: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PCyclogramHeaderColumn }; diff --git a/app/components/p8p_cyclogram/p8p_cyclogram_column_lines.js b/app/components/p8p_cyclogram/p8p_cyclogram_column_lines.js new file mode 100644 index 0000000..f45bbb3 --- /dev/null +++ b/app/components/p8p_cyclogram/p8p_cyclogram_column_lines.js @@ -0,0 +1,51 @@ +/* + Парус 8 - Панели мониторинга - Циклограмма + Компонент: Линии колонки таблицы +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента + +//----------- +//Тело модуля +//----------- + +//Линии колонок таблицы +const P8PCyclogramColumnsLines = ({ columns, shift, y1, y2 }) => { + //Инициализируем старт текущей колонки + let prevColumnEnd = 0; + return ( + + {columns.map((column, index) => { + //Аккумулируем окончание последней колонки с учетом сдвига + prevColumnEnd = index !== 0 ? prevColumnEnd + (columns[index - 1].end - columns[index - 1].start) * shift : 0; + return ; + })} + + + ); +}; + +//Контроль свойств - Линии колонок таблицы +P8PCyclogramColumnsLines.propTypes = { + columns: PropTypes.array.isRequired, + shift: PropTypes.number.isRequired, + y1: PropTypes.number.isRequired, + y2: PropTypes.number.isRequired +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PCyclogramColumnsLines }; diff --git a/app/components/p8p_cyclogram/p8p_cyclogram_constants.js b/app/components/p8p_cyclogram/p8p_cyclogram_constants.js new file mode 100644 index 0000000..5bbc1ab --- /dev/null +++ b/app/components/p8p_cyclogram/p8p_cyclogram_constants.js @@ -0,0 +1,80 @@ +/* + Парус 8 - Панели мониторинга - Циклограмма + Компонент: Константы +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import PropTypes from "prop-types"; //Контроль свойств компонента + +//----------- +//Тело модуля +//----------- + +//Уровни масштаба +const P8P_CYCLOGRAM_ZOOM = [0.2, 0.4, 0.7, 1, 1.5, 2, 2.5]; + +//Параметры элементов циклограммы +const NDEFAULT_LINE_HEIGHT = 20; +const NDEFAULT_HEADER_HEIGHT = 35; + +//Высота заголовка +const TITLE_HEIGHT = "44px"; + +//Высота панели масштабирования +const ZOOM_HEIGHT = "56px"; + +//Структура колонки +const P8P_CYCLOGRAM_COLUMN_SHAPE = PropTypes.shape({ + name: PropTypes.string.isRequired, + start: PropTypes.number.isRequired, + end: PropTypes.number.isRequired +}); + +//Структура группы +const P8P_CYCLOGRAM_GROUP_SHAPE = PropTypes.shape({ + name: PropTypes.string.isRequired, + height: PropTypes.number.isRequired, + width: PropTypes.number.isRequired, + visible: PropTypes.bool.isRequired +}); + +//Структура задачи +const P8P_CYCLOGRAM_TASK_SHAPE = PropTypes.shape({ + id: PropTypes.string.isRequired, + rn: PropTypes.number.isRequired, + name: PropTypes.string.isRequired, + fullName: PropTypes.string.isRequired, + lineNumb: PropTypes.number.isRequired, + start: PropTypes.number.isRequired, + end: PropTypes.number.isRequired, + group: PropTypes.string, + bgColor: PropTypes.string, + textColor: PropTypes.string, + highlightColor: PropTypes.string +}); + +//Структура динамического атрибута задачи +const P8P_CYCLOGRAM_TASK_ATTRIBUTE_SHAPE = PropTypes.shape({ + name: PropTypes.string.isRequired, + caption: PropTypes.string.isRequired, + visible: PropTypes.bool.isRequired +}); + +//---------------- +//Интерфейс модуля +//---------------- + +export { + P8P_CYCLOGRAM_ZOOM, + NDEFAULT_LINE_HEIGHT, + NDEFAULT_HEADER_HEIGHT, + TITLE_HEIGHT, + ZOOM_HEIGHT, + P8P_CYCLOGRAM_COLUMN_SHAPE, + P8P_CYCLOGRAM_GROUP_SHAPE, + P8P_CYCLOGRAM_TASK_SHAPE, + P8P_CYCLOGRAM_TASK_ATTRIBUTE_SHAPE +}; diff --git a/app/components/p8p_cyclogram/p8p_cyclogram_grid.js b/app/components/p8p_cyclogram/p8p_cyclogram_grid.js new file mode 100644 index 0000000..c3277ca --- /dev/null +++ b/app/components/p8p_cyclogram/p8p_cyclogram_grid.js @@ -0,0 +1,49 @@ +/* + Парус 8 - Панели мониторинга - Циклограмма + Компонент: Фон таблицы +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { P8PCyclogramRowsGrid } from "./p8p_cyclogram_rows_grid"; //Фон строк таблицы +import { P8PCyclogramRowsLines } from "./p8p_cyclogram_row_lines"; //Линии строк таблицы +import { P8PCyclogramColumnsLines } from "./p8p_cyclogram_column_lines"; //Линии колонок таблицы +import { NDEFAULT_HEADER_HEIGHT } from "./p8p_cyclogram_constants"; //Константы циклограммы + +//----------- +//Тело модуля +//----------- + +//Фон таблицы +const P8PCyclogramGrid = ({ tasks, columns, shift, maxWidth, maxHeight, lineHeight }) => { + //Формируем массив строк исходя из максимального значения строки задачи + const rows = Array.from(Array(Math.max(...tasks.map(o => o.lineNumb)) + 1).keys()); + return ( + + + + + + + ); +}; + +//Контроль свойств - Фон таблицы +P8PCyclogramGrid.propTypes = { + tasks: PropTypes.array.isRequired, + columns: PropTypes.array.isRequired, + shift: PropTypes.number.isRequired, + maxWidth: PropTypes.number.isRequired, + maxHeight: PropTypes.number.isRequired, + lineHeight: PropTypes.number.isRequired +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PCyclogramGrid }; diff --git a/app/components/p8p_cyclogram/p8p_cyclogram_header.js b/app/components/p8p_cyclogram/p8p_cyclogram_header.js new file mode 100644 index 0000000..8f1fab5 --- /dev/null +++ b/app/components/p8p_cyclogram/p8p_cyclogram_header.js @@ -0,0 +1,63 @@ +/* + Парус 8 - Панели мониторинга - Циклограмма + Компонент: Заголовок +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { NDEFAULT_HEADER_HEIGHT } from "./p8p_cyclogram_constants"; //Константы циклограммы +import { P8PCyclogramHeaderColumn } from "./p8p_cyclogram_column"; //Колонка циклограммы +import { P8PCyclogramColumnsLines } from "./p8p_cyclogram_column_lines"; //Линии колонок таблицы + +//----------- +//Тело модуля +//----------- + +//Заголовок +const P8PCyclogramHeader = ({ columns, shift, maxWidth, maxHeight, columnRenderer, headerBlock }) => { + //Инициализируем старт текущей колонки + let prevColumnEnd = 0; + return ( + + + {columns.map((column, index) => { + //Аккумулируем окончание последней колонки с учетом сдвига + prevColumnEnd = index !== 0 ? prevColumnEnd + (columns[index - 1].end - columns[index - 1].start) * shift : 0; + return ( + + ); + })} + + + + + ); +}; + +//Контроль свойств - Заголовок +P8PCyclogramHeader.propTypes = { + columns: PropTypes.array.isRequired, + shift: PropTypes.number.isRequired, + maxWidth: PropTypes.number.isRequired, + maxHeight: PropTypes.number.isRequired, + columnRenderer: PropTypes.func, + headerBlock: PropTypes.object +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PCyclogramHeader }; diff --git a/app/components/p8p_cyclogram/p8p_cyclogram_row_lines.js b/app/components/p8p_cyclogram/p8p_cyclogram_row_lines.js new file mode 100644 index 0000000..d286d9f --- /dev/null +++ b/app/components/p8p_cyclogram/p8p_cyclogram_row_lines.js @@ -0,0 +1,42 @@ +/* + Парус 8 - Панели мониторинга - Циклограмма + Компонент: Линии строк таблицы +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { NDEFAULT_HEADER_HEIGHT } from "./p8p_cyclogram_constants"; //Константы циклограммы + +//Линии строк таблицы +const P8PCyclogramRowsLines = ({ rows, maxWidth, lineHeight }) => { + return ( + + {rows.map((el, index) => ( + + ))} + + ); +}; + +//Контроль свойств - Линии строк таблицы +P8PCyclogramRowsLines.propTypes = { + rows: PropTypes.array.isRequired, + maxWidth: PropTypes.number.isRequired, + lineHeight: PropTypes.number.isRequired +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PCyclogramRowsLines }; diff --git a/app/components/p8p_cyclogram/p8p_cyclogram_rows_grid.js b/app/components/p8p_cyclogram/p8p_cyclogram_rows_grid.js new file mode 100644 index 0000000..b42e3f2 --- /dev/null +++ b/app/components/p8p_cyclogram/p8p_cyclogram_rows_grid.js @@ -0,0 +1,44 @@ +/* + Парус 8 - Панели мониторинга - Циклограмма + Компонент: Фон строк таблицы +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Box } from "@mui/material"; //Интерфейсные компоненты +import { P8P_BOX_CYCLOGRAM_ROW } from "../../theme/styles/box"; //Стили контейнеров +import { NDEFAULT_HEADER_HEIGHT } from "./p8p_cyclogram_constants"; //Константы циклограммы + +//----------- +//Тело модуля +//----------- + +//Фон строк таблицы +const P8PCyclogramRowsGrid = ({ rows, maxWidth, lineHeight }) => { + return ( + + {rows.map((el, index) => ( + + + + ))} + + ); +}; + +//Контроль свойств - Фон строк таблицы +P8PCyclogramRowsGrid.propTypes = { + rows: PropTypes.array.isRequired, + maxWidth: PropTypes.number.isRequired, + lineHeight: PropTypes.number.isRequired +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PCyclogramRowsGrid }; diff --git a/app/components/p8p_cyclogram/p8p_cyclogram_task.js b/app/components/p8p_cyclogram/p8p_cyclogram_task.js new file mode 100644 index 0000000..ad28529 --- /dev/null +++ b/app/components/p8p_cyclogram/p8p_cyclogram_task.js @@ -0,0 +1,77 @@ +/* + Парус 8 - Панели мониторинга - Циклограмма + Компонент: Задача +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Box, Typography } from "@mui/material"; //Интерфейсные компоненты +import { hasValue } from "../../core/utils"; //Вспомогательный функции +import { P8P_TYPOGRAPHY_VARIANT } from "../../theme/p8p_typography"; //Варианты шрифтов +import { P8P_BOX_CYCLOGRAM_TASK } from "../../theme/styles/box"; //Стили контейнеров +import { NDEFAULT_HEADER_HEIGHT } from "./p8p_cyclogram_constants"; //Константы циклограммы +import { P8P_TYPOGRAPHY_CG_TASK } from "../../theme/styles/typography"; //Стили текста + +//----------- +//Тело модуля +//----------- + +//Задача +const P8PCyclogramTask = ({ task, indexGrp, shift, lineHeight, openTaskEditor, taskRenderer }) => { + //Рассчитываем ширину задачи + const width = task.end !== 0 ? (task.end - task.start) * shift : 0; + //Формируем собственное отображение, если требуется + const customView = taskRenderer ? taskRenderer({ task, taskHeight: lineHeight, taskWidth: width }) || {} : {}; + //Определение количества доступных строк + const availableLines = Math.floor(lineHeight / 18); + return ( + + openTaskEditor(task)} + > + {customView.data ? ( + customView.data + ) : ( + + {task.name} + + )} + + + ); +}; + +//Контроль свойств - Задача +P8PCyclogramTask.propTypes = { + task: PropTypes.object.isRequired, + indexGrp: PropTypes.number, + shift: PropTypes.number.isRequired, + lineHeight: PropTypes.number.isRequired, + openTaskEditor: PropTypes.func.isRequired, + taskRenderer: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PCyclogramTask }; diff --git a/app/components/p8p_cyclogram/p8p_cyclogram_task_editor.js b/app/components/p8p_cyclogram/p8p_cyclogram_task_editor.js new file mode 100644 index 0000000..4a099fb --- /dev/null +++ b/app/components/p8p_cyclogram/p8p_cyclogram_task_editor.js @@ -0,0 +1,117 @@ +/* + Парус 8 - Панели мониторинга - Циклограмма + Компонент: Редактор задачи +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Dialog, DialogActions, DialogContent, Button, List, ListItem, ListItemText, Divider } from "@mui/material"; //Интерфейсные компоненты +import { hasValue } from "../../core/utils"; //Вспомогательный функции +import { P8P_DIALOG_CONTENT_VARIANT } from "../../theme/variants/p8p_dialog_content_variants"; //Варианты диалогов (содержимое) +import { P8P_LIST_VARIANT } from "../../theme/variants/p8p_list_variants"; //Варианты списков +import { P8P_LIST_ITEM_TEXT_VARIANT } from "../../theme/variants/p8p_list_item_text_variants"; //Варианты значений списков +import { P8P_BUTTON_VARIANT } from "../../theme/variants/p8p_button_variants"; //Варианты кнопок +import { P8P_CYCLOGRAM_TASK_SHAPE, P8P_CYCLOGRAM_TASK_ATTRIBUTE_SHAPE } from "./p8p_cyclogram_constants"; //Константы циклограммы + +//----------- +//Тело модуля +//----------- + +//Редактор задачи +const P8PCyclogramTaskEditor = ({ + task, + taskAttributes, + onOk, + onCancel, + taskAttributeRenderer, + taskDialogRenderer, + nameCaption, + okBtnCaption, + cancelBtnCaption +}) => { + //Собственное состояние + const [state] = useState({ + start: task.start, + end: task.end + }); + + //Отображаемые атрибуты + const dispTaskAttributes = + Array.isArray(taskAttributes) && taskAttributes.length > 0 ? taskAttributes.filter(attr => attr.visible && hasValue(task[attr.name])) : []; + + //При сохранении + const handleOk = () => (onOk && state.start && state.end ? onOk() : null); + + //При отмене + const handleCancel = () => (onCancel ? onCancel() : null); + + //Генерация содержимого + return ( + + {taskDialogRenderer ? ( + taskDialogRenderer({ task, taskAttributes, close: handleCancel }) + ) : ( + <> + + + + + + {dispTaskAttributes.length > 0 ? : null} + {dispTaskAttributes.length > 0 + ? dispTaskAttributes.map((attr, i) => { + const defaultView = task[attr.name]; + const customView = taskAttributeRenderer ? taskAttributeRenderer({ task, attribute: attr }) : null; + return ( + + + + + {i < dispTaskAttributes.length - 1 ? : null} + + ); + }) + : null} + + + + + + + + )} + + ); +}; + +//Контроль свойств - Редактор задачи +P8PCyclogramTaskEditor.propTypes = { + task: P8P_CYCLOGRAM_TASK_SHAPE, + taskAttributes: PropTypes.arrayOf(P8P_CYCLOGRAM_TASK_ATTRIBUTE_SHAPE), + onOk: PropTypes.func, + onCancel: PropTypes.func, + taskAttributeRenderer: PropTypes.func, + taskDialogRenderer: PropTypes.func, + nameCaption: PropTypes.string.isRequired, + okBtnCaption: PropTypes.string.isRequired, + cancelBtnCaption: PropTypes.string.isRequired +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PCyclogramTaskEditor }; diff --git a/app/components/p8p_cyclogram/p8p_cyclogram_utils.js b/app/components/p8p_cyclogram/p8p_cyclogram_utils.js new file mode 100644 index 0000000..8a80e6a --- /dev/null +++ b/app/components/p8p_cyclogram/p8p_cyclogram_utils.js @@ -0,0 +1,34 @@ +/* + Парус 8 - Панели мониторинга - Циклограмма + Ядро: Вспомогательные функции +*/ + +//----------- +//Тело модуля +//----------- + +//Определение сдвига для максимальной ширины колонок +export const getShift = (columns, currentColumnsMaxWidth, maxCyclogramWidth) => { + //Определяем доступное пространство для расширения + let maxWidthDiff = maxCyclogramWidth - currentColumnsMaxWidth; + //Инициализируем значение сдвига + let shift = 1; + //Если доступно больше ширины и есть пространство для расширения + if (maxCyclogramWidth > currentColumnsMaxWidth && maxCyclogramWidth - maxWidthDiff > columns.length) { + //Определяем доступный сдвиг колонок + shift = maxCyclogramWidth / currentColumnsMaxWidth; + } + //Возвращаем сдвиг + return shift; +}; + +//Формирование стилей для группы +export const getGroupStyles = (indexGrp, highlightColor) => { + return `.main .TaskGrp${indexGrp}:hover .TaskGrp${indexGrp} { + ${highlightColor ? `background: ${highlightColor};` : `filter: brightness(1.15);`} + } + .main:has(.TaskGrp${indexGrp}:hover) .TaskGrpHeader${indexGrp} { + display: block; + } + `; +}; diff --git a/app/components/p8p_cyclogram/p8p_cyclogram_view.js b/app/components/p8p_cyclogram/p8p_cyclogram_view.js new file mode 100644 index 0000000..c8c2dbf --- /dev/null +++ b/app/components/p8p_cyclogram/p8p_cyclogram_view.js @@ -0,0 +1,171 @@ +/* + Парус 8 - Панели мониторинга - Циклограмма + Компонент: Представление циклограммы +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Box, Typography } from "@mui/material"; //Интерфейсные компоненты +import { hasValue } from "../../core/utils"; //Вспомогательный функции +import { P8P_TYPOGRAPHY_VARIANT } from "../../theme/p8p_typography"; //Варианты шрифтов +import { P8P_BOX_CYCLOGRAM_GROUP } from "../../theme/styles/box"; //Стили контейнеров +import { NDEFAULT_HEADER_HEIGHT } from "./p8p_cyclogram_constants"; //Константы циклограммы +import { P8PCyclogramTask } from "./p8p_cyclogram_task"; //Задача циклограммы +import { P8PCyclogramHeader } from "./p8p_cyclogram_header"; //Заголовок циклограммы +import { getGroupStyles } from "./p8p_cyclogram_utils"; //Вспомогательные функции циклограммы +import { P8P_TYPOGRAPHY_CG_GROUP } from "../../theme/styles/typography"; //Стили текста + +//----------- +//Тело модуля +//----------- + +//Представление циклограммы +const P8PCyclogramView = ({ + columns, + groups, + tasks, + shift, + lineHeight, + maxWidth, + maxHeight, + openTaskEditor, + groupHeaderRenderer, + taskRenderer, + columnRenderer, + headerBlock +}) => { + //Инициализируем коллекцию тасков с группами + const tasksWithGroup = tasks.filter(task => hasValue(task.groupName)); + //Инициализируем коллекцию тасков без групп + const tasksWithoutGroup = tasks.filter(task => !hasValue(task.groupName)); + //Инициализируем коллекцию отображаемых групп + const visibleGroups = groups ? groups.filter(group => group.visible) : []; + return ( + + + {visibleGroups.length !== 0 + ? visibleGroups.map((grp, indexGrp) => { + //Считываем задачи группы + let groupTasks = tasksWithGroup.filter(task => task.groupName === grp.name); + //Если по данной группе нет тасков - ничего не выводим + if (groupTasks.length === 0) { + return null; + } + return ( + + {groupTasks.map((task, index) => ( + + ))} + + + ); + }) + : null} + + {tasksWithoutGroup.map((task, index) => { + return ( + + ); + })} + + + + {visibleGroups.length !== 0 ? ( + + {visibleGroups.map((grp, indexGrp) => { + //Инициализируем параметры группы + let defaultView = null; + let customView = null; + let groupHeaderX = 0; + let groupHeaderY = 0; + let groupTasks = tasksWithGroup.filter(task => task.groupName === grp.name); + //Если по данной группе нет тасков - ничего не выводим + if (groupTasks.length === 0) { + return null; + } + //Если требуется отображать заголовок группы + if (grp.visible) { + //Формируем отображение по умолчанию + defaultView = ( + + + {grp.name} + + + ); + //Формируем собственное отображение, если требуется + customView = groupHeaderRenderer ? groupHeaderRenderer({ group: grp }) : null; + //Рассчитываем координаты заголовка группы + groupHeaderX = Math.min(...groupTasks.map(o => o.start)) * shift; + groupHeaderY = NDEFAULT_HEADER_HEIGHT + Math.min(...groupTasks.map(o => o.lineNumb)) * lineHeight - grp.height - 5; + } + return ( + + {customView ? customView : defaultView} + + ); + })} + + ) : null} + + ); +}; + +//Контроль свойств - Представление циклограммы +P8PCyclogramView.propTypes = { + columns: PropTypes.array.isRequired, + groups: PropTypes.array, + tasks: PropTypes.array.isRequired, + shift: PropTypes.number.isRequired, + lineHeight: PropTypes.number.isRequired, + maxWidth: PropTypes.number.isRequired, + maxHeight: PropTypes.number.isRequired, + openTaskEditor: PropTypes.func.isRequired, + groupHeaderRenderer: PropTypes.func, + taskRenderer: PropTypes.func, + columnRenderer: PropTypes.func, + headerBlock: PropTypes.object +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PCyclogramView }; diff --git a/app/components/p8p_data_grid.js b/app/components/p8p_data_grid.js index 69adf5b..1826b14 100644 --- a/app/components/p8p_data_grid.js +++ b/app/components/p8p_data_grid.js @@ -19,7 +19,7 @@ import { P8P_TABLE_FILTERS_HEIGHT, P8P_TABLE_PAGINATOR_ALIGN, P8P_TABLE_PAGINATOR_POSITION -} from "./p8p_table"; //Таблица +} from "./p8p_table/p8p_table"; //Таблица import { useP8PDataGrid } from "./p8p_data_grid_hooks"; //Хук для таблицы данных //--------- @@ -92,6 +92,7 @@ const P8PDataGrid = ({ valueFormatter, containerComponent, containerComponentProps, + headExpandCellStyle, onOrderChanged, onFilterChanged, onPagesCountChanged, @@ -187,6 +188,7 @@ const P8PDataGrid = ({ containerComponent={containerComponent} containerComponentProps={containerComponentProps} morePagesBtnProps={morePagesBtnProps} + headExpandCellStyle={headExpandCellStyle} onOrderChanged={handleOrderChanged} onFilterChanged={handleFilterChanged} onPagesCountChanged={handlePagesCountChanged} @@ -233,6 +235,7 @@ P8PDataGrid.propTypes = { valueFormatter: PropTypes.func, containerComponent: PropTypes.oneOfType([PropTypes.elementType, PropTypes.string]), containerComponentProps: PropTypes.object, + headExpandCellStyle: PropTypes.object, onOrderChanged: PropTypes.func, onFilterChanged: PropTypes.func, onPagesCountChanged: PropTypes.func, diff --git a/app/components/p8p_dialog.js b/app/components/p8p_dialog.js index 1bc2a4a..c453588 100644 --- a/app/components/p8p_dialog.js +++ b/app/components/p8p_dialog.js @@ -12,7 +12,9 @@ import PropTypes from "prop-types"; //Контроль свойств компо import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from "@mui/material"; //Интерфейсные компоненты import { BUTTONS } from "../../app.text"; //Общие текстовые ресурсы import { P8P_INPUT, P8PInput } from "./p8p_input"; //Поле ввода -import { APP_STYLES } from "../../app.styles"; //Типовые стили +import { P8P_DIALOG_CONTENT_VARIANT } from "../theme/variants/p8p_dialog_content_variants"; //Варианты содержимого диалога +import { P8P_BUTTON_VARIANT } from "../theme/variants/p8p_button_variants"; //Варианты кнопок +import { P8P_DIALOG_TITLE_VARIANT } from "../theme/variants/p8p_dialog_title_variants"; //Варианты заголовков диалога //--------- //Константы @@ -27,12 +29,6 @@ const P8P_DIALOG_WIDTH = { XL: "xl" }; -//Стили -const STYLES = { - SCROLL: display => - display === true ? { overflow: "auto", ...APP_STYLES.SCROLL } : { overflow: "hidden", display: "flex", flexDirection: "column" } -}; - //----------------------- //Вспомогательные функции //----------------------- @@ -93,22 +89,29 @@ const P8PDialog = ({ //Формирование представления return ( - {title} - + {title} + {inputsState.map((input, i) => ( ))} - {children} + {onClose && ( + + )} + {onCancel && ( + + )} {onOk && ( - )} - {onCancel && } - {onClose && } ); diff --git a/app/components/p8p_fullscreen_dialog.js b/app/components/p8p_fullscreen_dialog.js index b37fd3a..b874448 100644 --- a/app/components/p8p_fullscreen_dialog.js +++ b/app/components/p8p_fullscreen_dialog.js @@ -10,17 +10,10 @@ import React from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента import { Dialog, AppBar, Toolbar, IconButton, Typography, Icon, DialogContent, DialogTitle } from "@mui/material"; //Интерфейсные компоненты - -//--------- -//Константы -//--------- - -//Стили -const STYLES = { - DIALOG_TITLE: { padding: 0 }, - APP_BAR: { position: "relative" }, - TITLE_TYPOGRAPHY: { ml: 2, flex: 1 } -}; +import { P8P_APP_BAR_VARIANT } from "../theme/variants/p8p_app_bar_variants"; //Варианты областей заголовка +import { P8P_TYPOGRAPHY_VARIANT } from "../theme/p8p_typography"; //Варианты шрифтов +import { P8P_COMPONENT_ZERO_PADDING } from "../theme/styles/common"; //Стили - общие +import { P8P_TYPOGRAPHY_DIALOG_TITLE } from "../theme/styles/typography"; //Стили текста //----------- //Тело модуля @@ -33,14 +26,14 @@ const P8PFullScreenDialog = ({ title, onClose, contentProps, children }) => { }; return ( - - - + + + close - + {title} diff --git a/app/components/p8p_gantt.js b/app/components/p8p_gantt.js index 04b335b..97c3e62 100644 --- a/app/components/p8p_gantt.js +++ b/app/components/p8p_gantt.js @@ -9,324 +9,23 @@ import React, { useEffect, useState, useCallback, useRef } from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента -import { - Box, - IconButton, - Icon, - Typography, - Dialog, - DialogActions, - DialogContent, - TextField, - Button, - List, - ListItem, - ListItemText, - Divider, - Slider, - Link -} from "@mui/material"; //Интерфейсные компоненты +import { Box, IconButton, Icon, Typography, Link } from "@mui/material"; //Интерфейсные компоненты import { P8PAppInlineError } from "./p8p_app_message"; //Встраиваемое сообщение об ошибке -import { useP8PGantt } from "./p8p_gantt_hooks"; //Хук для диаграммы Ганта - -//--------- -//Константы -//--------- - -//Уровни масштаба -const P8P_GANTT_ZOOM = [0, 1, 2, 3, 4, 5]; - -//Уровни масштаба (строковые наименования в терминах библиотеки) -const P8P_GANTT_ZOOM_VIEW_MODES = { - 0: "Quarter Day", - 1: "Half Day", - 2: "Day", - 3: "Week", - 4: "Month", - 5: "Year" -}; - -//Структура задачи -const P8P_GANTT_TASK_SHAPE = PropTypes.shape({ - id: PropTypes.string.isRequired, - rn: PropTypes.number.isRequired, - numb: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - fullName: PropTypes.string.isRequired, - start: PropTypes.string.isRequired, - end: PropTypes.string.isRequired, - progress: PropTypes.number, - dependencies: PropTypes.array, - readOnly: PropTypes.bool, - readOnlyDates: PropTypes.bool, - readOnlyProgress: PropTypes.bool, - bgColor: PropTypes.string, - textColor: PropTypes.string, - bgProgressColor: PropTypes.string -}); - -//Структура динамического атрибута задачи -const P8P_GANTT_TASK_ATTRIBUTE_SHAPE = PropTypes.shape({ - name: PropTypes.string.isRequired, - caption: PropTypes.string.isRequired, - visible: PropTypes.bool.isRequired -}); - -//Структура описания цвета задачи -const P8P_GANTT_TASK_COLOR_SHAPE = PropTypes.shape({ - bgColor: PropTypes.string, - textColor: PropTypes.string, - bgProgressColor: PropTypes.string, - desc: PropTypes.string.isRequired -}); - -//Высота заголовка -const TITLE_HEIGHT = "44px"; - -//Высота панели масштабирования -const ZOOM_HEIGHT = "56px"; - -//Стили -const STYLES = { - TASK_EDITOR_CONTENT: { minWidth: 400, overflowX: "auto" }, - TASK_EDITOR_LIST: { width: "100%", minWidth: 300, maxWidth: 700, bgcolor: "background.paper" }, - GANTT_TITLE: { height: TITLE_HEIGHT }, - GANTT_ZOOM: { height: ZOOM_HEIGHT }, - GANTT: (noData, title, zoomBar) => ({ - height: `calc(100% - ${zoomBar ? ZOOM_HEIGHT : "0px"} - ${title ? TITLE_HEIGHT : "0px"})`, - display: noData ? "none" : "" - }) -}; - -//-------------------------------- -//Вспомогательные классы и функции -//-------------------------------- - -//Проверка существования значения -const hasValue = value => typeof value !== "undefined" && value !== null && value !== ""; - -//Формирование описания для легенды -const taskLegendDesc = ({ task, taskColors }) => { - if (Array.isArray(taskColors) && taskColors.length > 0) { - const colorDesc = taskColors.find( - color => task.bgColor === color.bgColor && task.textColor === color.textColor && task.bgProgressColor === color.bgProgressColor - ); - if (colorDesc) - return { - text: colorDesc.desc, - style: { - ...(colorDesc.bgProgressColor - ? { - background: `linear-gradient(to right, ${colorDesc.bgProgressColor} ,${ - colorDesc.bgColor ? colorDesc.bgColor : "transparent" - })` - } - : colorDesc.bgColor - ? { backgroundColor: colorDesc.bgColor } - : {}), - ...(colorDesc.textColor ? { color: colorDesc.textColor } : {}) - } - }; - else return null; - } else return null; -}; - -//Редактор задачи -const P8PGanttTaskEditor = ({ - task, - taskAttributes, - taskColors, - onOk, - onCancel, - taskAttributeRenderer, - taskDialogRenderer, - taskDialogProps, - numbCaption, - nameCaption, - startCaption, - endCaption, - progressCaption, - legendCaption, - okBtnCaption, - cancelBtnCaption -}) => { - //Собственное состояние - const [state, setState] = useState({ - start: task.start, - end: task.end, - progress: task.progress - }); - - //Отображаемые атрибуты - const dispTaskAttributes = - Array.isArray(taskAttributes) && taskAttributes.length > 0 ? taskAttributes.filter(attr => attr.visible && hasValue(task[attr.name])) : []; - - //При сохранении - const handleOk = () => (onOk && state.start && state.end ? onOk({ task, start: state.start, end: state.end, progress: state.progress }) : null); - - //При отмене - const handleCancel = () => (onCancel ? onCancel() : null); - - //При изменении сроков - const handlePeriodChanged = e => setState(prev => ({ ...prev, [e.target.name]: e.target.value })); - - //При изменении прогресса - const handleProgressChanged = (e, newValue) => setState(prev => ({ ...prev, progress: newValue })); - - //Описание легенды для задачи - const legendDesc = taskLegendDesc({ task, taskColors }); - let legend = legendDesc ? ( - - ) : null; - - //Генерация содержимого - return ( - - {taskDialogRenderer ? ( - taskDialogRenderer({ task, taskAttributes, taskColors, close: handleCancel }) - ) : ( - <> - - - - - - - - - - - - - } - /> - - - - - } - /> - - {hasValue(task.progress) || legend || dispTaskAttributes.length > 0 ? : null} - {hasValue(task.progress) ? ( - <> - - - } - /> - - {legend || dispTaskAttributes.length > 0 ? : null} - - ) : null} - {legend ? ( - <> - {legend} - {dispTaskAttributes.length > 0 ? : null} - - ) : null} - {dispTaskAttributes.length > 0 - ? dispTaskAttributes.map((attr, i) => { - const defaultView = task[attr.name]; - const customView = taskAttributeRenderer ? taskAttributeRenderer({ task, attribute: attr }) : null; - return ( - - - - - {i < dispTaskAttributes.length - 1 ? : null} - - ); - }) - : null} - - - - - - - - )} - - ); -}; - -//Контроль свойств - Редактор задачи -P8PGanttTaskEditor.propTypes = { - task: P8P_GANTT_TASK_SHAPE, - taskAttributes: PropTypes.arrayOf(P8P_GANTT_TASK_ATTRIBUTE_SHAPE), - taskColors: PropTypes.arrayOf(P8P_GANTT_TASK_COLOR_SHAPE), - onOk: PropTypes.func, - onCancel: PropTypes.func, - taskAttributeRenderer: PropTypes.func, - taskDialogRenderer: PropTypes.func, - taskDialogProps: PropTypes.object, - numbCaption: PropTypes.string.isRequired, - nameCaption: PropTypes.string.isRequired, - startCaption: PropTypes.string.isRequired, - endCaption: PropTypes.string.isRequired, - progressCaption: PropTypes.string.isRequired, - legendCaption: PropTypes.string.isRequired, - okBtnCaption: PropTypes.string.isRequired, - cancelBtnCaption: PropTypes.string.isRequired -}; +import { useP8PGantt } from "./p8p_gantt/p8p_gantt_hooks"; //Хук для диаграммы Ганта +import { + P8P_GANTT_ZOOM, + P8P_GANTT_ZOOM_VIEW_MODES, + P8P_GANTT_TASK_SHAPE, + P8P_GANTT_TASK_ATTRIBUTE_SHAPE, + P8P_GANTT_TASK_COLOR_SHAPE, + TITLE_HEIGHT, + ZOOM_HEIGHT +} from "./p8p_gantt/p8p_gantt_constants"; //Константы диаграммы Ганта +import { P8PGanttTaskEditor, taskLegendDesc } from "./p8p_gantt/p8p_gantt_task_editor"; //Редактор задачи +import { P8P_TYPOGRAPHY_VARIANT } from "../theme/p8p_typography"; //Варианты шрифтов +import { P8P_COMPONENT_HEIGHT } from "../theme/styles/common"; //Стили - общие +import { P8P_BOX_GANTT } from "../theme/styles/box"; //Стили контейнеров +import { P8P_TYPOGRAPHY_TITLE } from "../theme/styles/typography"; //Стили текста //----------- //Тело модуля @@ -359,7 +58,9 @@ const P8PGantt = ({ progressTaskEditorCaption, legendTaskEditorCaption, okTaskEditorBtnCaption, - cancelTaskEditorBtnCaption + cancelTaskEditorBtnCaption, + zoomBarStyle, + zoomBarHeight }) => { //Собственное состояние const [state, setState] = useState({ @@ -438,13 +139,14 @@ const P8PGantt = ({ {state.gantt && !state.noData && title ? ( {onTitleClick ? ( - onTitleClick()}> + onTitleClick()}> {title} ) : ( @@ -453,7 +155,7 @@ const P8PGantt = ({ ) : null} {state.gantt && !state.noData && zoomBar ? ( - + handleZoomChange(-1)} disabled={state.zoom == 0}> zoom_in @@ -482,7 +184,14 @@ const P8PGantt = ({ cancelBtnCaption={cancelTaskEditorBtnCaption} /> ) : null} -
+
@@ -516,7 +225,9 @@ P8PGantt.propTypes = { progressTaskEditorCaption: PropTypes.string.isRequired, legendTaskEditorCaption: PropTypes.string.isRequired, okTaskEditorBtnCaption: PropTypes.string.isRequired, - cancelTaskEditorBtnCaption: PropTypes.string.isRequired + cancelTaskEditorBtnCaption: PropTypes.string.isRequired, + zoomBarStyle: PropTypes.object, + zoomBarHeight: PropTypes.string }; //---------------- diff --git a/app/components/p8p_gantt/p8p_gantt_constants.js b/app/components/p8p_gantt/p8p_gantt_constants.js new file mode 100644 index 0000000..bb435fc --- /dev/null +++ b/app/components/p8p_gantt/p8p_gantt_constants.js @@ -0,0 +1,81 @@ +/* + Парус 8 - Панели мониторинга - Диаграмма Ганта + Компонент: Константы +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import PropTypes from "prop-types"; //Контроль свойств компонента + +//----------- +//Тело модуля +//----------- + +//Уровни масштаба +const P8P_GANTT_ZOOM = [0, 1, 2, 3, 4, 5]; + +//Уровни масштаба (строковые наименования в терминах библиотеки) +const P8P_GANTT_ZOOM_VIEW_MODES = { + 0: "Quarter Day", + 1: "Half Day", + 2: "Day", + 3: "Week", + 4: "Month", + 5: "Year" +}; + +//Структура задачи +const P8P_GANTT_TASK_SHAPE = PropTypes.shape({ + id: PropTypes.string.isRequired, + rn: PropTypes.number.isRequired, + numb: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + fullName: PropTypes.string.isRequired, + start: PropTypes.string.isRequired, + end: PropTypes.string.isRequired, + progress: PropTypes.number, + dependencies: PropTypes.array, + readOnly: PropTypes.bool, + readOnlyDates: PropTypes.bool, + readOnlyProgress: PropTypes.bool, + bgColor: PropTypes.string, + textColor: PropTypes.string, + bgProgressColor: PropTypes.string +}); + +//Структура динамического атрибута задачи +const P8P_GANTT_TASK_ATTRIBUTE_SHAPE = PropTypes.shape({ + name: PropTypes.string.isRequired, + caption: PropTypes.string.isRequired, + visible: PropTypes.bool.isRequired +}); + +//Структура описания цвета задачи +const P8P_GANTT_TASK_COLOR_SHAPE = PropTypes.shape({ + bgColor: PropTypes.string, + textColor: PropTypes.string, + bgProgressColor: PropTypes.string, + desc: PropTypes.string.isRequired +}); + +//Высота заголовка +const TITLE_HEIGHT = "44px"; + +//Высота панели масштабирования +const ZOOM_HEIGHT = "56px"; + +//---------------- +//Интерфейс модуля +//---------------- + +export { + P8P_GANTT_ZOOM, + P8P_GANTT_ZOOM_VIEW_MODES, + P8P_GANTT_TASK_SHAPE, + P8P_GANTT_TASK_ATTRIBUTE_SHAPE, + P8P_GANTT_TASK_COLOR_SHAPE, + TITLE_HEIGHT, + ZOOM_HEIGHT +}; diff --git a/app/components/p8p_gantt_hooks.js b/app/components/p8p_gantt/p8p_gantt_hooks.js similarity index 96% rename from app/components/p8p_gantt_hooks.js rename to app/components/p8p_gantt/p8p_gantt_hooks.js index cc560ab..9f9fd94 100644 --- a/app/components/p8p_gantt_hooks.js +++ b/app/components/p8p_gantt/p8p_gantt_hooks.js @@ -8,8 +8,8 @@ //--------------------- import { useState, useCallback, useEffect, useContext, useRef, useMemo } from "react"; //Классы React -import { BackEndCtx } from "../context/backend"; //Контекст взаимодействия с сервером -import { formatDateJSONDateOnly } from "../core/utils"; //Вспомогательные функции +import { BackEndCtx } from "../../context/backend"; //Контекст взаимодействия с сервером +import { formatDateJSONDateOnly } from "../../core/utils"; //Вспомогательные функции //--------- //Константы diff --git a/app/components/p8p_gantt/p8p_gantt_task_editor.js b/app/components/p8p_gantt/p8p_gantt_task_editor.js new file mode 100644 index 0000000..39807d1 --- /dev/null +++ b/app/components/p8p_gantt/p8p_gantt_task_editor.js @@ -0,0 +1,267 @@ +/* + Парус 8 - Панели мониторинга - Диаграмма Ганта + Компонент: Редактор задачи +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Dialog, DialogActions, DialogContent, TextField, Button, List, ListItem, ListItemText, Divider, Slider } from "@mui/material"; //Интерфейсные компоненты +import { P8P_GANTT_TASK_SHAPE, P8P_GANTT_TASK_ATTRIBUTE_SHAPE, P8P_GANTT_TASK_COLOR_SHAPE } from "./p8p_gantt_constants"; //Константы диаграммы Ганта +import { P8P_DIALOG_CONTENT_VARIANT } from "../../theme/variants/p8p_dialog_content_variants"; //Варианты диалогов (содержимое) +import { P8P_LIST_VARIANT } from "../../theme/variants/p8p_list_variants"; //Варианты списков +import { P8P_LIST_ITEM_TEXT_VARIANT } from "../../theme/variants/p8p_list_item_text_variants"; //Варианты значений списков +import { P8P_TEXT_FIELD_VARIANT } from "../../theme/variants/p8p_text_field_variants"; //Варианты полей ввода +import { P8P_BUTTON_VARIANT } from "../../theme/variants/p8p_button_variants"; //Варианты кнопок + +//-------------------------------- +//Вспомогательные классы и функции +//-------------------------------- + +//Проверка существования значения +const hasValue = value => typeof value !== "undefined" && value !== null && value !== ""; + +//Формирование описания для легенды +const taskLegendDesc = ({ task, taskColors }) => { + if (Array.isArray(taskColors) && taskColors.length > 0) { + const colorDesc = taskColors.find( + color => task.bgColor === color.bgColor && task.textColor === color.textColor && task.bgProgressColor === color.bgProgressColor + ); + if (colorDesc) + return { + text: colorDesc.desc, + style: { + ...(colorDesc.bgProgressColor + ? { + background: `linear-gradient(to right, ${colorDesc.bgProgressColor} ,${ + colorDesc.bgColor ? colorDesc.bgColor : "transparent" + })` + } + : colorDesc.bgColor + ? { backgroundColor: colorDesc.bgColor } + : {}), + ...(colorDesc.textColor ? { color: colorDesc.textColor } : {}) + } + }; + else return null; + } else return null; +}; + +//----------- +//Тело модуля +//----------- + +//Редактор задачи +const P8PGanttTaskEditor = ({ + task, + taskAttributes, + taskColors, + onOk, + onCancel, + taskAttributeRenderer, + taskDialogRenderer, + taskDialogProps, + numbCaption, + nameCaption, + startCaption, + endCaption, + progressCaption, + legendCaption, + okBtnCaption, + cancelBtnCaption +}) => { + //Собственное состояние + const [state, setState] = useState({ + start: task.start, + end: task.end, + progress: task.progress + }); + + //Отображаемые атрибуты + const dispTaskAttributes = + Array.isArray(taskAttributes) && taskAttributes.length > 0 ? taskAttributes.filter(attr => attr.visible && hasValue(task[attr.name])) : []; + + //При сохранении + const handleOk = () => (onOk && state.start && state.end ? onOk({ task, start: state.start, end: state.end, progress: state.progress }) : null); + + //При отмене + const handleCancel = () => (onCancel ? onCancel() : null); + + //При изменении сроков + const handlePeriodChanged = e => setState(prev => ({ ...prev, [e.target.name]: e.target.value })); + + //При изменении прогресса + const handleProgressChanged = (e, newValue) => setState(prev => ({ ...prev, progress: newValue })); + + //Описание легенды для задачи + const legendDesc = taskLegendDesc({ task, taskColors }); + let legend = legendDesc ? ( + + ) : null; + + //Генерация содержимого + return ( + + {taskDialogRenderer ? ( + taskDialogRenderer({ task, taskAttributes, taskColors, close: handleCancel }) + ) : ( + <> + + + + + + + + + + + + + } + /> + + + + + } + /> + + {hasValue(task.progress) || legend || dispTaskAttributes.length > 0 ? : null} + {hasValue(task.progress) ? ( + <> + + + } + /> + + {legend || dispTaskAttributes.length > 0 ? : null} + + ) : null} + {legend ? ( + <> + {legend} + {dispTaskAttributes.length > 0 ? : null} + + ) : null} + {dispTaskAttributes.length > 0 + ? dispTaskAttributes.map((attr, i) => { + const defaultView = task[attr.name]; + const customView = taskAttributeRenderer ? taskAttributeRenderer({ task, attribute: attr }) : null; + return ( + + + + + {i < dispTaskAttributes.length - 1 ? : null} + + ); + }) + : null} + + + + + + + + )} + + ); +}; + +//Контроль свойств - Редактор задачи +P8PGanttTaskEditor.propTypes = { + task: P8P_GANTT_TASK_SHAPE, + taskAttributes: PropTypes.arrayOf(P8P_GANTT_TASK_ATTRIBUTE_SHAPE), + taskColors: PropTypes.arrayOf(P8P_GANTT_TASK_COLOR_SHAPE), + onOk: PropTypes.func, + onCancel: PropTypes.func, + taskAttributeRenderer: PropTypes.func, + taskDialogRenderer: PropTypes.func, + taskDialogProps: PropTypes.object, + numbCaption: PropTypes.string.isRequired, + nameCaption: PropTypes.string.isRequired, + startCaption: PropTypes.string.isRequired, + endCaption: PropTypes.string.isRequired, + progressCaption: PropTypes.string.isRequired, + legendCaption: PropTypes.string.isRequired, + okBtnCaption: PropTypes.string.isRequired, + cancelBtnCaption: PropTypes.string.isRequired +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PGanttTaskEditor, taskLegendDesc }; diff --git a/app/components/p8p_indicator.js b/app/components/p8p_indicator.js index eab245f..7a9693d 100644 --- a/app/components/p8p_indicator.js +++ b/app/components/p8p_indicator.js @@ -12,15 +12,18 @@ import PropTypes from "prop-types"; //Контроль свойств компо import { IconButton, Icon, Typography, Paper, Stack } from "@mui/material"; //Интерфейсные компоненты MUI import { P8PHintDialog } from "./p8p_app_message"; //Диалог подсказки import { TEXTS, STATE } from "../../app.text"; //Типовые текстовые ресурсы и константы -import { APP_COLORS } from "../../app.styles"; //Типовые стили import { useP8PIndicator } from "./p8p_indicator_hooks"; //Хук для индикатора +import { P8P_TYPOGRAPHY_VARIANT } from "../theme/p8p_typography"; //Варианты шрифтов +import { P8P_TYPOGRAPHY_CLICKABLE } from "../theme/styles/typography"; //Стили текстовых полей +import { P8P_PAPER_INDICATOR } from "../theme/styles/paper"; //Стили выделяемого контейнера +import { P8P_STACK_INLINE_HIDDEN } from "../theme/styles/stack"; //Стили для групповой информации +import { P8P_ICON_INDICATOR } from "../theme/styles/icon"; //Стили иконок //--------- //Константы //--------- //Варианты исполнения - const P8P_INDICATOR_VARIANT = { ELEVATION: "elevation", OUTLINED: "outlined" @@ -33,70 +36,11 @@ const P8P_INDICATOR_STATE = { WARN: STATE.WARN, ERR: STATE.ERR }; -//Цвета заливки -const BG_COLOR = { - [STATE.OK]: APP_COLORS[STATE.OK].color, - [STATE.ERR]: APP_COLORS[STATE.ERR].color, - [STATE.WARN]: APP_COLORS[STATE.WARN].color -}; -//Цвета текста и иконок -const COLOR = { - [STATE.OK]: APP_COLORS[STATE.OK].contrColor, - [STATE.ERR]: APP_COLORS[STATE.ERR].contrColor, - [STATE.WARN]: APP_COLORS[STATE.WARN].contrColor -}; - -//Стили -const STYLES = { - CONTAINER: (state, clickable, userColor, userBackgroundColor) => ({ - padding: "10px", - width: "100%", - height: "100%", - overflow: "hidden", - ...getBackgroundColor(state, userBackgroundColor), - ...getColor(state, userColor), - display: "flex", - flexDirection: "column", - justifyContent: "center", - ...(clickable - ? { - cursor: "pointer", - "&:hover": { filter: "brightness(0.92) !important" }, - "&:active": { backgroundColor: APP_COLORS.ACTIVE.color } - } - : {}) - }), - ICON: (state, userColor) => ({ fontSize: "50px", ...getColor(state, userColor) }), - HINT_ICON: (state, userColor) => ({ fontSize: "1rem", ...getColor(state, userColor) }), - VALUE_CAPTION_STACK: { containerType: "inline-size", width: "100%", overflow: "hidden" }, - CAPTION_TYPOGRAPHY: clickable => ({ - width: "99cqw", - ...(clickable - ? { - cursor: "pointer" - } - : {}) - }), - VALUE_TYPOGRAPHY: clickable => ({ - ...(clickable - ? { - cursor: "pointer" - } - : {}) - }) -}; - -//----------------------- -//Вспомогательные функции -//----------------------- - -//Подбор цвета заливки -const getBackgroundColor = (state, userColor) => - userColor ? { backgroundColor: userColor } : BG_COLOR[state] ? { backgroundColor: BG_COLOR[state] } : {}; - -//Подбор цвета текста -const getColor = (state, userColor) => (userColor ? { color: userColor } : COLOR[state] ? { color: COLOR[state] } : {}); +//Ширина для элементов индикатора +const WIDTH_ICON_VALUE = "1rem"; //Иконка значения индикатора +const WIDTH_ICON = "50px"; //Иконка индикатора +const WIDTH_CAPTION = "99cqw"; //Заголовок индикатора //----------- //Тело модуля @@ -150,7 +94,7 @@ const P8PIndicator = ({ //Представление текста значения индикатора const valueTextView = ( - + {[undefined, null, ""].includes(value) ? TEXTS.NO_DATA_FOUND_SHORT : value} ); @@ -158,9 +102,10 @@ const P8PIndicator = ({ //Представление текста подписи индикатора const captionView = ( @@ -175,7 +120,7 @@ const P8PIndicator = ({ {valueTextView} - help_outline + help_outline @@ -190,17 +135,17 @@ const P8PIndicator = ({ return ( - + {valueView} {captionView} - {icon ? {icon} : null} + {icon ? {icon} : null} ); diff --git a/app/components/p8p_input.js b/app/components/p8p_input.js index 5f83bb5..e8282d9 100644 --- a/app/components/p8p_input.js +++ b/app/components/p8p_input.js @@ -10,6 +10,12 @@ import React, { useState, useEffect } from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента import { Box, Icon, Input, InputAdornment, FormControl, Select, InputLabel, MenuItem, IconButton, Autocomplete, TextField } from "@mui/material"; //Интерфейсные компоненты +import { P8P_AUTOCOMPLETE_VARIANT } from "../theme/variants/p8p_autocomplete_variants"; //Варианты полей выбора (Autocomplete) +import { P8P_INPUT_LABEL_VARIANT } from "../theme/variants/p8p_input_label_variants"; //Варианты меток выбора +import { P8P_TEXT_FIELD_VARIANT } from "../theme/variants/p8p_text_field_variants"; //Варианты полей ввода +import { P8P_SELECT_VARIANT } from "../theme/variants/p8p_select_variants"; //Варианты полей выбора (Select) +import { P8P_MENU_ITEM_VARIANT } from "../theme/variants/p8p_menu_item_variants"; //Варианты элементов меню +import { P8P_INPUT_VARIANT } from "../theme/variants/p8p_input_variants"; //Варианты полей ввода (Input) //--------- //Константы @@ -58,7 +64,7 @@ const P8PInput = ({ name, value, label, onChange, dictionary, list, type, freeSo //Генерация содержимого return ( - + {list ? ( freeSolo ? ( @@ -71,53 +77,70 @@ const P8PInput = ({ name, value, label, onChange, dictionary, list, type, freeSo onChange={(event, newValue) => handleChangeByName(name, newValue)} onInputChange={(event, newInputValue) => handleChangeByName(name, newInputValue)} options={list} - renderInput={params => } + variant={P8P_AUTOCOMPLETE_VARIANT.PRIMARY} + renderInput={params => ( + + )} /> ) : ( <> - - {label} - - + + + {label} + + + ) ) : ( <> - - {label} - - - - list - - - ) : null - } - {...(current.type ? { type: current.type } : {})} - onChange={handleChange} - disabled={disabled} - /> + + + {label} + + + + list + + + ) : null + } + {...(current.type ? { type: current.type } : {})} + onChange={handleChange} + disabled={disabled} + variant={P8P_INPUT_VARIANT.PRIMARY} + /> + )} diff --git a/app/components/p8p_panels_menu.js b/app/components/p8p_panels_menu.js index 15b8c97..2d0856f 100644 --- a/app/components/p8p_panels_menu.js +++ b/app/components/p8p_panels_menu.js @@ -27,6 +27,16 @@ import { ListItemIcon, ListItemText } from "@mui/material"; //Интерфейсные компоненты +import { P8P_BOX_PANELS_MENU_CONTAINER } from "../theme/styles/box"; //Стили контейнеров +import { P8P_GRID_VARIANT } from "../theme/variants/p8p_grid_variants"; //Варианты сеток +import { P8P_CARD_VARIANT } from "../theme/variants/p8p_card_variants"; //Варианты карточек +import { P8P_COMPONENT_HEIGHT } from "../theme/styles/common"; //Стили - общие +import { P8P_ICON_VARIANT } from "../theme/variants/p8p_icon_variants"; //Варианты иконок +import { P8P_CARD_ACTIONS_VARIANT } from "../theme/variants/p8p_card_actions_variants"; //Варианты действий карточки +import { P8P_TYPOGRAPHY_VARIANT } from "../theme/p8p_typography"; //Варианты шрифтов +import { P8P_BUTTON_VARIANT } from "../theme/variants/p8p_button_variants"; //Варианты кнопок +import { P8P_LIST_ITEM_TEXT_VARIANT } from "../theme/variants/p8p_list_item_text_variants"; //Варианты значений списков +import { P8P_TYPOGRAPHY_PANEL_DESK } from "../theme/styles/typography"; //Стили текста //--------- //Константы @@ -52,37 +62,6 @@ const P8P_PANELS_MENU_PANEL_SHAPE = PropTypes.shape({ url: PropTypes.string.isRequired }); -//Стили -const STYLES = { - GRID_CONTAINER: { display: "flex", justifyContent: "center", alignItems: "flex-start", minHeight: "100vh" }, - GRID: { maxWidth: 1200, direction: "row", justifyContent: "left", alignItems: "stretch" }, - GRID_PANEL_CARD: { maxWidth: 400, height: "100%", flexDirection: "column", display: "flex" }, - GRID_PANEL_CARD_MEDIA: { height: 140 }, - GRID_PANEL_CARD_CONTENT_TITLE: { alignItems: "flex-start" }, - GRID_PANEL_CARD_CONTENT_TITLE_ICON: { paddingTop: "4px" }, - GRID_PANEL_CARD_ACTIONS: { marginTop: "auto", display: "flex", justifyContent: "flex-end", alignItems: "flex-start" }, - DESKTOP_GROUP_HEADER: { fontWeight: "bold", fontFamily: "tahoma, arial, verdana, sans-serif!important", fontSize: "13px!important" }, - DESKTOP_ITEM_BUTTON: { - fontSize: "12px", - textTransform: "none", - "&:hover": { backgroundColor: "#c3e1ff" }, - width: "150px", - height: "90px", - flexDirection: "column", - justifyContent: "flex-start" - }, - DESKTOP_ITEM_ICON: { width: "48px", height: "48px", fontSize: "48px" }, - DESKTOP_ITEM_CATION: { - display: "-webkit-box", - overflow: "hidden", - WebkitBoxOrient: "vertical", - WebkitLineClamp: 2, - fontSize: "12px", - maxWidth: "140px", - lineHeight: "1.2" - } -}; - //-------------------------------- //Вспомогательные классы и функции //-------------------------------- @@ -113,7 +92,7 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, group, defaultGroupTyt panelsLinks.push( variant === P8P_PANELS_MENU_VARIANT.GRID ? ( - + {grp ? grp : defaultGroupTytle} @@ -121,9 +100,7 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, group, defaultGroupTyt ) : ( - - {grp ? grp : defaultGroupTytle} - + {grp ? grp : defaultGroupTytle} ) ); @@ -132,28 +109,30 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, group, defaultGroupTyt panelsLinks.push( variant === P8P_PANELS_MENU_VARIANT.GRID ? ( - + {panel.preview ? ( - + ) : ( )} - - {panel.icon ? {panel.icon} : null} - {panel.caption} + + {panel.icon ? {panel.icon} : null} + {panel.caption} - - {panel.desc} - + {panel.desc} - - @@ -168,7 +147,7 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, group, defaultGroupTyt {panel.icon} - + ) : ( @@ -176,11 +155,11 @@ const getPanelsLinks = ({ variant, panels, selectedPanel, group, defaultGroupTyt p={3} key={panel.name} onClick={() => (onItemNavigate ? onItemNavigate(panel) : null)} - sx={STYLES.DESKTOP_ITEM_BUTTON} + variant={P8P_BUTTON_VARIANT.DESKTOP_PANEL} title={panel.caption} > - {panel.icon} - + {panel.icon} + {panel.caption} @@ -216,12 +195,18 @@ P8PPanelsMenuDrawer.propTypes = { //Меню панелей - грид const P8PPanelsMenuGrid = ({ onItemNavigate, navigateCaption, panels = [], defaultGroupTytle } = {}) => { //Формируем ссылки на панели - const panelsLinks = getPanelsLinks({ variant: P8P_PANELS_MENU_VARIANT.GRID, panels, defaultGroupTytle, navigateCaption, onItemNavigate }); + const panelsLinks = getPanelsLinks({ + variant: P8P_PANELS_MENU_VARIANT.GRID, + panels, + defaultGroupTytle, + navigateCaption, + onItemNavigate + }); //Генерация содержимого return ( - - + + {panelsLinks} diff --git a/app/components/p8p_settings_dialog.js b/app/components/p8p_settings_dialog.js index fc657ac..6757f99 100644 --- a/app/components/p8p_settings_dialog.js +++ b/app/components/p8p_settings_dialog.js @@ -11,21 +11,12 @@ import React, { useState, useContext, useMemo } from "react"; //Классы Rea import PropTypes from "prop-types"; //Контроль свойств компонента import { Stack, List, ListItem, ListItemButton, ListItemText, Typography, Box, Divider } from "@mui/material"; //Интерфейсные элементы import { P8PDialog, P8P_DIALOG_WIDTH } from "./p8p_dialog"; //Типовой диалог -import { APP_STYLES } from "../../app.styles"; //Типовые стили import { P8PSettingsList } from "./p8p_settings_list"; //Список параметров import { ApplicationCtx } from "../context/application"; //Контекст приложения import { deepCopyObject, hasValue } from "../core/utils"; //Вспомогательные функции - -//--------- -//Константы -//--------- - -//Стили -const STYLES = { - CONTAINER: { display: "flex", flexDirection: "row", alignItems: "flex-start" }, - BOX_PANELS: { width: "300px", height: "500px", overflow: "auto", ...APP_STYLES.SCROLL }, - BOX_SETTINGS: { width: "520px", height: "500px", overflow: "auto", ...APP_STYLES.SCROLL } -}; +import { P8P_BOX_SETTINGS_CONTAINER, P8P_BOX_SETTINGS_LIST, P8P_BOX_SETTINGS_PANELS } from "../theme/styles/box"; //Стили контейнеров +import { P8P_LIST_ITEM_TEXT_VARIANT } from "../theme/variants/p8p_list_item_text_variants"; //Варианты значений списков +import { P8P_TYPOGRAPHY_VARIANT } from "../theme/p8p_typography"; //Варианты шрифтов //----------- //Тело модуля @@ -73,7 +64,7 @@ const P8PSettingsDialog = ({ settings, panel = null, onOk, onClose }) => { //Генерация содержимого return ( <> - + {Object.keys(panelSettings).map((panel, i) => { //Считываем информацию о панели @@ -83,12 +74,13 @@ const P8PSettingsDialog = ({ settings, panel = null, onOk, onClose }) => { handlePanelSelect(panel)} selected={panel === selectedPanel}> + {`${panelInfo.desc ? panelInfo.desc : "Описание отсутствует"}`} @@ -117,20 +109,22 @@ const P8PSettingsDialog = ({ settings, panel = null, onOk, onClose }) => { okDisabled={Object.keys(panelSettings).length === 0} > {isSettingsExists ? ( - + {!hasValue(panel) ? panelsListRender() : null} - + {hasValue(selectedPanel) ? ( ) : ( - - Выберите панель для отображения параметров - + <> + + Выберите панель для отображения параметров + + )} ) : ( - + Отсутствуют доступные параметры )} diff --git a/app/components/p8p_settings_list.js b/app/components/p8p_settings_list.js index 67fde11..8d937d9 100644 --- a/app/components/p8p_settings_list.js +++ b/app/components/p8p_settings_list.js @@ -14,17 +14,10 @@ import { P8PDialog } from "./p8p_dialog"; //Типовой диалог import { deepCopyObject } from "../core/utils"; //Вспомогательные функции import { ApplicationCtx } from "../context/application"; //Контекст приложения import { P8P_DATA_TYPES } from "../core/data_types"; //Типы данных - -//--------- -//Константы -//--------- - -//Стили -const STYLES = { - LIST: { width: "510px", bgcolor: "background.paper", overflowY: "auto" }, - TYPOGRAPHY_VALUE: { maxWidth: "200px" }, - TEXT_FIELD_STR_VALUE: { minWidth: "400px" } -}; +import { P8P_LIST_VARIANT } from "../theme/variants/p8p_list_variants"; //Варианты списков +import { P8P_COMPONENT_WIDTH } from "../theme/styles/common"; //Стили - общие +import { P8P_LIST_ITEM_TEXT_VARIANT } from "../theme/variants/p8p_list_item_text_variants"; //Варианты значений списков +import { P8P_TYPOGRAPHY_VARIANT } from "../theme/p8p_typography"; //Варианты шрифтов //-------------------------------- //Вспомогательные классы и функции @@ -129,24 +122,25 @@ const P8PSettingsList = ({ settings, onSettingChange }) => { //Формирование представления return ( <> - + {Object.keys(settings).map((setting, i) => { return ( handleSettingClick(setting)}> {`${settings[setting].desc ? settings[setting].desc : "Описание отсутствует"}`} {`${settings[setting].value}`} diff --git a/app/components/p8p_svg.js b/app/components/p8p_svg.js index 2348b84..3c38e4c 100644 --- a/app/components/p8p_svg.js +++ b/app/components/p8p_svg.js @@ -10,17 +10,13 @@ import React, { useEffect, useRef, useState } from "react"; //Классы React import { IconButton, Icon, Container, Grid } from "@mui/material"; //Интерфейсные элементы import PropTypes from "prop-types"; //Контроль свойств компонента +import { P8P_BOX_CENTER } from "../theme/styles/box"; //Стили контейнеров +import { P8P_GRID_VARIANT } from "../theme/variants/p8p_grid_variants"; //Стили сеток //--------- //Константы //--------- -//Стили -const STYLES = { - GRID_ITEM_CANVAS: { width: "100%", height: "100%" }, - CONTROLS: { justifyContent: "center", alignItems: "center", display: "flex" } -}; - //Структура элемента изображения const P8P_SVG_ITEM_SHAPE = PropTypes.shape({ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, @@ -129,8 +125,8 @@ const P8PSVG = ({ data, items, onClick, onItemClick, canvasStyle, fillOpacity }) ? 0 : pv.currentImage + 1 : pv.currentImage - 1 < 0 - ? pv.imagesCount - 1 - : pv.currentImage - 1 + ? pv.imagesCount - 1 + : pv.currentImage - 1 })); }; @@ -156,12 +152,12 @@ const P8PSVG = ({ data, items, onClick, onItemClick, canvasStyle, fillOpacity }) return ( - +
{state.imagesCount > 1 ? ( -
+
arrow_left diff --git a/app/components/p8p_table.js b/app/components/p8p_table/p8p_table.js similarity index 54% rename from app/components/p8p_table.js rename to app/components/p8p_table/p8p_table.js index ca9e2ce..f69252a 100644 --- a/app/components/p8p_table.js +++ b/app/components/p8p_table/p8p_table.js @@ -20,475 +20,38 @@ import { Paper, IconButton, Icon, - Menu, - MenuItem, - Divider, Stack, - Dialog, - DialogTitle, - DialogContent, - DialogActions, Button, - TextField, - Chip, Container, Link } from "@mui/material"; //Интерфейсные компоненты -import { useTheme } from "@mui/material/styles"; //Взаимодействие со стилями MUI -import { P8PAppInlineError, P8PHintDialog } from "./p8p_app_message"; //Встраиваемое сообщение об ошибке -import { P8P_TABLE_AT, HEADER_INITIAL_STATE, hasValue, p8pTableReducer } from "./p8p_table_reducer"; //Редьюсер состояния -import { P8P_DATA_TYPES } from "../core/data_types"; //Типы данных - -//--------- -//Константы -//--------- - -//Размеры отступов -const P8P_TABLE_SIZE = { - SMALL: "small", - MEDIUM: "medium" -}; - -//Типы данных -const P8P_TABLE_DATA_TYPE = { - STR: P8P_DATA_TYPES.STR, - NUMB: P8P_DATA_TYPES.NUMB, - DATE: P8P_DATA_TYPES.DATE -}; - -//Направления сортировки -const P8P_TABLE_COLUMN_ORDER_DIRECTIONS = { - ASC: "ASC", - DESC: "DESC" -}; - -//Действия панели инструментов столбца -const P8P_TABLE_COLUMN_TOOL_BAR_ACTIONS = { - ORDER_TOGGLE: "ORDER_TOGGLE", - FILTER_TOGGLE: "FILTER_TOGGLE", - EXPAND_TOGGLE: "EXPAND_TOGGLE" -}; - -//Действия меню столбца -const P8P_TABLE_COLUMN_MENU_ACTIONS = { - ORDER_ASC: "ORDER_ASC", - ORDER_DESC: "ORDER_DESC", - FILTER: "FILTER" -}; - -//Структура элемента описания фильтра -const P8P_TABLE_FILTER_SHAPE = PropTypes.shape({ - name: PropTypes.string.isRequired, - from: PropTypes.any, - to: PropTypes.any -}); - -//Структура элемента описания сортировки -const P8P_TABLE_ORDER_SHAPE = PropTypes.shape({ - direction: PropTypes.string.isRequired, - name: PropTypes.string.isRequired -}); - -//Размещение области страниц по вертикали -const P8P_TABLE_PAGINATOR_ALIGN = { - LEFT: "left", - RIGHT: "right", - CENTER: "center" -}; - -//Размещение области страниц по горизонтали -const P8P_TABLE_PAGINATOR_POSITION = { - TOP: "top", - BOTTOM: "bottom", - BOTH: "both" -}; - -//Высота кнопки догрузки данных -const P8P_TABLE_MORE_HEIGHT = "49px"; - -//Высота фильтров таблицы -const P8P_TABLE_FILTERS_HEIGHT = "48px"; - -//Стили -const STYLES = { - TABLE: {}, - TABLE_HEAD_STICKY: { - position: "sticky", - top: 0, - zIndex: 1000 - }, - TABLE_HEAD_CELL_STICKY: (theme, left) => ({ - position: "sticky", - left, - backgroundColor: theme.palette.background.default, - zIndex: 1000 - }), - TABLE_ROW: { - "&:last-child td, &:last-child th": { border: 0 } - }, - TABLE_CELL_STICKY: (theme, left) => ({ - position: "sticky", - left, - backgroundColor: theme.palette.background.default, - zIndex: 500 - }), - TABLE_CELL_EXPAND_CONTROL: { - minWidth: "60px", - maxWidth: "60px" - }, - TABLE_CELL_EXPAND_CONTAINER: { - paddingBottom: 0, - paddingTop: 0, - paddingLeft: 0, - paddingRight: 0 - }, - TABLE_CELL_GROUP_HEADER: { - backgroundColor: "lightgray" - }, - TABLE_CELL_GROUP_HEADER_STICKY: { - position: "sticky", - left: 0 - }, - TABLE_COLUMN_STACK: { - alignItems: "center" - }, - TABLE_COLUMN_MENU_ITEM_ICON: { - paddingRight: "10px" - }, - FILTER_CHIP: { - alignItems: "center" - }, - PAGINATION: (pagesAlign, position) => ({ - display: "flex", - justifyContent: - pagesAlign === P8P_TABLE_PAGINATOR_ALIGN.LEFT - ? "flex-start" - : pagesAlign === P8P_TABLE_PAGINATOR_ALIGN.CENTER - ? "space-around" - : "flex-end", - ...(position === P8P_TABLE_PAGINATOR_POSITION.TOP ? { paddingBottom: "10px" } : { paddingTop: "10px" }) - }), - MORE_BUTTON_CONTAINER: { - with: "100%", - textAlign: "center", - padding: "5px" - } -}; - -//-------------------------------- -//Вспомогательные классы и функции -//-------------------------------- - -//Панель инструментов столбца (левая) -const P8PTableColumnToolBarLeft = ({ columnDef, onItemClick }) => { - //Кнопка развёртывания/свёртывания - let expButton = null; - if (columnDef.expandable) - expButton = ( - (onItemClick ? onItemClick(P8P_TABLE_COLUMN_TOOL_BAR_ACTIONS.EXPAND_TOGGLE, columnDef.name) : null)}> - {columnDef.expanded ? "indeterminate_check_box" : "add_box"} - - ); - - //Генерация содержимого - return <>{expButton}; -}; - -//Контроль свойств - Панель инструментов столбца (левая) -P8PTableColumnToolBarLeft.propTypes = { - columnDef: PropTypes.object.isRequired, - onItemClick: PropTypes.func -}; - -//Панель инструментов столбца (правая) -const P8PTableColumnToolBarRight = ({ columnDef, orders, filters, onItemClick }) => { - //Кнопка сортировки - const order = orders.find(o => o.name == columnDef.name); - let orderButton = null; - if (order) - orderButton = ( - (onItemClick ? onItemClick(P8P_TABLE_COLUMN_TOOL_BAR_ACTIONS.ORDER_TOGGLE, columnDef.name) : null)}> - {order.direction === P8P_TABLE_COLUMN_ORDER_DIRECTIONS.ASC ? "arrow_upward" : "arrow_downward"} - - ); - - //Кнопка фильтрации - const filter = filters.find(f => f.name == columnDef.name); - let filterButton = null; - if (hasValue(filter?.from) || hasValue(filter?.to)) - filterButton = ( - (onItemClick ? onItemClick(P8P_TABLE_COLUMN_TOOL_BAR_ACTIONS.FILTER_TOGGLE, columnDef.name) : null)}> - filter_alt - - ); - - //Генерация содержимого - return ( - <> - {orderButton} - {filterButton} - - ); -}; - -//Контроль свойств - Панель инструментов столбца (правая) -P8PTableColumnToolBarRight.propTypes = { - columnDef: PropTypes.object.isRequired, - orders: PropTypes.array.isRequired, - filters: PropTypes.array.isRequired, - onItemClick: PropTypes.func -}; - -//Меню столбца -const P8PTableColumnMenu = ({ columnDef, orderAscItemCaption, orderDescItemCaption, filterItemCaption, onItemClick }) => { - //Собственное состояние - const [anchorEl, setAnchorEl] = useState(null); - - //Флаг отображения - const open = Boolean(anchorEl); - - //По нажатию на открытие меню - const handleMenuButtonClick = event => { - setAnchorEl(event.currentTarget); - }; - - //По нажатию на пункт меню - const handleMenuItemClick = (event, index, action, columnName) => { - if (onItemClick) onItemClick(action, columnName); - setAnchorEl(null); - }; - - //При закрытии меню - const handleMenuClose = () => { - setAnchorEl(null); - }; - - //Формирование списка элементов меню в зависимости от описания колонки таблицы - const menuItems = []; - if (columnDef.order === true) { - menuItems.push( - handleMenuItemClick(event, index, P8P_TABLE_COLUMN_MENU_ACTIONS.ORDER_ASC, columnDef.name)} - > - arrow_upward - {orderAscItemCaption} - - ); - menuItems.push( - handleMenuItemClick(event, index, P8P_TABLE_COLUMN_MENU_ACTIONS.ORDER_DESC, columnDef.name)} - > - arrow_downward - {orderDescItemCaption} - - ); - } - if (columnDef.filter === true) { - if (menuItems.length > 0) menuItems.push(); - menuItems.push( - handleMenuItemClick(event, index, P8P_TABLE_COLUMN_MENU_ACTIONS.FILTER, columnDef.name)} - > - filter_alt - {filterItemCaption} - - ); - } - - //Генерация содержимого - return menuItems.length > 0 ? ( - <> - - more_vert - - - {menuItems} - - - ) : null; -}; - -//Контроль свойств - Меню столбца -P8PTableColumnMenu.propTypes = { - columnDef: PropTypes.object.isRequired, - orderAscItemCaption: PropTypes.string.isRequired, - orderDescItemCaption: PropTypes.string.isRequired, - filterItemCaption: PropTypes.string.isRequired, - onItemClick: PropTypes.func -}; - -//Диалог фильтра -const P8PTableColumnFilterDialog = ({ - columnDef, - from, - to, - valueCaption, - valueFromCaption, - valueToCaption, - okBtnCaption, - clearBtnCaption, - cancelBtnCaption, - valueFormatter, - onOk, - onClear, - onCancel -}) => { - //Собственное состояние - значения с-по - const [filterValues, setFilterValues] = useState({ from, to }); - - //Отработка воода значения в фильтр - const handleFilterTextFieldChanged = e => { - setFilterValues(prev => ({ ...prev, [e.target.name]: e.target.value })); - }; - - //Элементы ввода значений фильтра - let inputs = null; - if (Array.isArray(columnDef.values) && columnDef.values.length > 0) { - inputs = ( - - {columnDef.values.map((v, i) => ( - - {valueFormatter ? valueFormatter({ value: v, columnDef }) : v} - - ))} - - ); - } else { - switch (columnDef.dataType) { - case P8P_TABLE_DATA_TYPE.STR: { - inputs = ( - - ); - break; - } - case P8P_TABLE_DATA_TYPE.NUMB: - case P8P_TABLE_DATA_TYPE.DATE: { - inputs = ( - <> - -   - - - ); - break; - } - } - } - - return ( - (onCancel ? onCancel(columnDef.name) : null)} - > - {columnDef.caption} - {inputs} - - - - - - - ); -}; - -//Контроль свойств - Диалог фильтра -P8PTableColumnFilterDialog.propTypes = { - columnDef: PropTypes.object.isRequired, - from: PropTypes.any, - to: PropTypes.any, - valueCaption: PropTypes.string.isRequired, - valueFromCaption: PropTypes.string.isRequired, - valueToCaption: PropTypes.string.isRequired, - okBtnCaption: PropTypes.string.isRequired, - clearBtnCaption: PropTypes.string.isRequired, - cancelBtnCaption: PropTypes.string.isRequired, - valueFormatter: PropTypes.func, - onOk: PropTypes.func, - onClear: PropTypes.func, - onCancel: PropTypes.func -}; - -//Сводный фильтр -const P8PTableFiltersChips = ({ filters, columnsDef, valueFromCaption, valueToCaption, onFilterChipClick, onFilterChipDelete, valueFormatter }) => { - return ( - - {filters.map((filter, i) => { - const columnDef = columnsDef.find(columnDef => columnDef.name == filter.name); - return ( - - {columnDef.caption}:  - {hasValue(filter.from) && !columnDef.values && columnDef.dataType != P8P_TABLE_DATA_TYPE.STR - ? `${valueFromCaption.toLowerCase()} ` - : null} - {hasValue(filter.from) ? (valueFormatter ? valueFormatter({ value: filter.from, columnDef }) : filter.from) : null} - {hasValue(filter.to) && !columnDef.values && columnDef.dataType != P8P_TABLE_DATA_TYPE.STR - ? ` ${valueToCaption.toLowerCase()} ` - : null} - {hasValue(filter.to) ? (valueFormatter ? valueFormatter({ value: filter.to, columnDef }) : filter.to) : null} - - } - variant="outlined" - onClick={() => (onFilterChipClick ? onFilterChipClick(columnDef.name) : null)} - onDelete={() => (onFilterChipDelete ? onFilterChipDelete(columnDef.name) : null)} - /> - ); - })} - - ); -}; - -//Контроль свойств - Сводный фильтр -P8PTableFiltersChips.propTypes = { - filters: PropTypes.array.isRequired, - columnsDef: PropTypes.array.isRequired, - valueFromCaption: PropTypes.string.isRequired, - valueToCaption: PropTypes.string.isRequired, - onFilterChipClick: PropTypes.func, - onFilterChipDelete: PropTypes.func, - valueFormatter: PropTypes.func -}; +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"; //Варианты шрифтов //----------- //Тело модуля @@ -530,6 +93,7 @@ const P8PTable = ({ groupCellRender, rowExpandRender, valueFormatter, + headExpandCellStyle, onOrderChanged, onFilterChanged, onPagesCountChanged, @@ -555,9 +119,6 @@ const P8PTable = ({ //Собственное состояние - колонка с отображаемой подсказкой const [displayHintColumn, setDisplayHintColumn] = useState(null); - //Стили - const theme = useTheme(); - //Описание фильтруемой колонки const filterColumnDef = filterColumn ? columnsDef.find(columnDef => columnDef.name == filterColumn) || null : null; @@ -597,8 +158,8 @@ const P8PTable = ({ 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; + ? null + : P8P_TABLE_COLUMN_ORDER_DIRECTIONS.ASC; if (onOrderChanged) onOrderChanged({ columnName, direction: newDirection }); break; } @@ -688,34 +249,33 @@ const P8PTable = ({ const renderGroupCell = group => { let customRender = {}; if (groupCellRender) customRender = groupCellRender({ columnsDef: header.columnsDef, group }) || {}; - return header.displayDataColumns.map((columnDef, i) => ( - - {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} - - )); + 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} + + ); + }); }; //Генерация области страниц @@ -731,7 +291,8 @@ const P8PTable = ({ <> {pagesCount && pagesCount > 0 && isVisible ? ( - - +
+ {header.displayLevels.map((level, i) => ( {expandable && rowExpandRender && i == 0 ? ( ) : null} @@ -797,11 +362,11 @@ const P8PTable = ({ if (headCellRender) customRender = headCellRender({ columnDef }) || {}; return ( @@ -820,7 +386,7 @@ const P8PTable = ({ ) : columnDef.hint ? ( handleColumnShowHintClick(columnDef.name)} @@ -856,15 +422,13 @@ const P8PTable = ({ 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"} @@ -876,11 +440,15 @@ const P8PTable = ({ if (dataCellRender) customRender = dataCellRender({ row, columnDef }) || {}; return ( ); })} @@ -897,10 +465,8 @@ const P8PTable = ({ {expandable && rowExpandRender && expanded[i] === true ? ( {rowExpandRender({ columnsDef, row })} @@ -931,7 +497,7 @@ const P8PTable = ({ {renderPagination(P8P_TABLE_PAGINATOR_POSITION.BOTTOM)} {morePages && (!pagesCount || pagesCount <= 0) ? ( - + @@ -998,6 +564,7 @@ P8PTable.propTypes = { groupCellRender: PropTypes.func, rowExpandRender: PropTypes.func, valueFormatter: PropTypes.func, + headExpandCellStyle: PropTypes.object, onOrderChanged: PropTypes.func, onFilterChanged: PropTypes.func, onPagesCountChanged: PropTypes.func, @@ -1015,7 +582,7 @@ export { P8P_TABLE_DATA_TYPE, P8P_TABLE_SIZE, P8P_TABLE_FILTER_SHAPE, - P8P_TABLE_ORDER_SHAPE, + P8P_TABLE_ORDER_SHAPE, P8P_TABLE_MORE_HEIGHT, P8P_TABLE_FILTERS_HEIGHT, P8P_TABLE_PAGINATOR_ALIGN, diff --git a/app/components/p8p_table/p8p_table_column_filter_dialog.js b/app/components/p8p_table/p8p_table_column_filter_dialog.js new file mode 100644 index 0000000..b1d31f4 --- /dev/null +++ b/app/components/p8p_table/p8p_table_column_filter_dialog.js @@ -0,0 +1,164 @@ +/* + Парус 8 - Панели мониторинга - Таблица + Компонент: Диалог фильтра +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { MenuItem, Dialog, DialogTitle, DialogContent, DialogActions, Button, TextField, Typography } from "@mui/material"; //Интерфейсные компоненты +import { P8P_TABLE_DATA_TYPE } from "./p8p_table_constants"; //Типы данных +import { P8P_TEXT_FIELD_VARIANT } from "../../theme/variants/p8p_text_field_variants"; //Варианты полей ввода +import { P8P_TYPOGRAPHY_VARIANT } from "../../theme/p8p_typography"; //Варианты шрифтов +import { P8P_BUTTON_VARIANT } from "../../theme/variants/p8p_button_variants"; //Варианты кнопок +import { P8P_MENU_ITEM_VARIANT } from "../../theme/variants/p8p_menu_item_variants"; //Варианты элементов меню + +//----------- +//Тело модуля +//----------- + +//Диалог фильтра +const P8PTableColumnFilterDialog = ({ + columnDef, + from, + to, + valueCaption, + valueFromCaption, + valueToCaption, + okBtnCaption, + clearBtnCaption, + cancelBtnCaption, + valueFormatter, + onOk, + onClear, + onCancel +}) => { + //Собственное состояние - значения с-по + const [filterValues, setFilterValues] = useState({ from, to }); + + //Отработка воода значения в фильтр + const handleFilterTextFieldChanged = e => { + setFilterValues(prev => ({ ...prev, [e.target.name]: e.target.value })); + }; + + //Элементы ввода значений фильтра + let inputs = null; + if (Array.isArray(columnDef.values) && columnDef.values.length > 0) { + inputs = ( + + {columnDef.values.map((v, i) => ( + + {valueFormatter ? valueFormatter({ value: v, columnDef }) : v} + + ))} + + ); + } else { + switch (columnDef.dataType) { + case P8P_TABLE_DATA_TYPE.STR: { + inputs = ( + + ); + break; + } + case P8P_TABLE_DATA_TYPE.NUMB: + case P8P_TABLE_DATA_TYPE.DATE: { + inputs = ( + <> + +   + + + ); + break; + } + } + } + + return ( + (onCancel ? onCancel(columnDef.name) : null)} + > + + {columnDef.caption} + + {inputs} + + + + + + + ); +}; + +//Контроль свойств - Диалог фильтра +P8PTableColumnFilterDialog.propTypes = { + columnDef: PropTypes.object.isRequired, + from: PropTypes.any, + to: PropTypes.any, + valueCaption: PropTypes.string.isRequired, + valueFromCaption: PropTypes.string.isRequired, + valueToCaption: PropTypes.string.isRequired, + okBtnCaption: PropTypes.string.isRequired, + clearBtnCaption: PropTypes.string.isRequired, + cancelBtnCaption: PropTypes.string.isRequired, + valueFormatter: PropTypes.func, + onOk: PropTypes.func, + onClear: PropTypes.func, + onCancel: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PTableColumnFilterDialog }; diff --git a/app/components/p8p_table/p8p_table_column_menu.js b/app/components/p8p_table/p8p_table_column_menu.js new file mode 100644 index 0000000..ef74bf2 --- /dev/null +++ b/app/components/p8p_table/p8p_table_column_menu.js @@ -0,0 +1,106 @@ +/* + Парус 8 - Панели мониторинга - Таблица + Компонент: Меню столбца +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useState } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { IconButton, Icon, Menu, MenuItem, Divider, Typography } from "@mui/material"; //Интерфейсные компоненты +import { P8P_TABLE_COLUMN_MENU_ACTIONS } from "./p8p_table_constants"; //Действия меню столбца +import { P8P_TYPOGRAPHY_VARIANT } from "../../theme/p8p_typography"; //Варианты шрифтов +import { P8P_ICON_VARIANT } from "../../theme/variants/p8p_icon_variants"; //Варианты иконок + +//----------- +//Тело модуля +//----------- + +//Меню столбца +const P8PTableColumnMenu = ({ columnDef, orderAscItemCaption, orderDescItemCaption, filterItemCaption, onItemClick }) => { + //Собственное состояние + const [anchorEl, setAnchorEl] = useState(null); + + //Флаг отображения + const open = Boolean(anchorEl); + + //По нажатию на открытие меню + const handleMenuButtonClick = event => { + setAnchorEl(event.currentTarget); + }; + + //По нажатию на пункт меню + const handleMenuItemClick = (event, index, action, columnName) => { + if (onItemClick) onItemClick(action, columnName); + setAnchorEl(null); + }; + + //При закрытии меню + const handleMenuClose = () => { + setAnchorEl(null); + }; + + //Формирование списка элементов меню в зависимости от описания колонки таблицы + const menuItems = []; + if (columnDef.order === true) { + menuItems.push( + handleMenuItemClick(event, index, P8P_TABLE_COLUMN_MENU_ACTIONS.ORDER_ASC, columnDef.name)} + > + arrow_upward + {orderAscItemCaption} + + ); + menuItems.push( + handleMenuItemClick(event, index, P8P_TABLE_COLUMN_MENU_ACTIONS.ORDER_DESC, columnDef.name)} + > + arrow_downward + {orderDescItemCaption} + + ); + } + if (columnDef.filter === true) { + if (menuItems.length > 0) menuItems.push(); + menuItems.push( + handleMenuItemClick(event, index, P8P_TABLE_COLUMN_MENU_ACTIONS.FILTER, columnDef.name)} + > + filter_alt + {filterItemCaption} + + ); + } + + //Генерация содержимого + return menuItems.length > 0 ? ( + <> + + more_vert + + + {menuItems} + + + ) : null; +}; + +//Контроль свойств - Меню столбца +P8PTableColumnMenu.propTypes = { + columnDef: PropTypes.object.isRequired, + orderAscItemCaption: PropTypes.string.isRequired, + orderDescItemCaption: PropTypes.string.isRequired, + filterItemCaption: PropTypes.string.isRequired, + onItemClick: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PTableColumnMenu }; diff --git a/app/components/p8p_table/p8p_table_column_toolbar_left.js b/app/components/p8p_table/p8p_table_column_toolbar_left.js new file mode 100644 index 0000000..cc66429 --- /dev/null +++ b/app/components/p8p_table/p8p_table_column_toolbar_left.js @@ -0,0 +1,44 @@ +/* + Парус 8 - Панели мониторинга - Таблица + Компонент: Панель инструментов столбца (левая) +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { IconButton, Icon } from "@mui/material"; //Интерфейсные компоненты +import { P8P_TABLE_COLUMN_TOOL_BAR_ACTIONS } from "./p8p_table_constants"; //Действия панели инструментов столбца + +//----------- +//Тело модуля +//----------- + +//Панель инструментов столбца (левая) +const P8PTableColumnToolBarLeft = ({ columnDef, onItemClick }) => { + //Кнопка развёртывания/свёртывания + let expButton = null; + if (columnDef.expandable) + expButton = ( + (onItemClick ? onItemClick(P8P_TABLE_COLUMN_TOOL_BAR_ACTIONS.EXPAND_TOGGLE, columnDef.name) : null)}> + {columnDef.expanded ? "indeterminate_check_box" : "add_box"} + + ); + + //Генерация содержимого + return <>{expButton}; +}; + +//Контроль свойств - Панель инструментов столбца (левая) +P8PTableColumnToolBarLeft.propTypes = { + columnDef: PropTypes.object.isRequired, + onItemClick: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PTableColumnToolBarLeft }; diff --git a/app/components/p8p_table/p8p_table_column_toolbar_right.js b/app/components/p8p_table/p8p_table_column_toolbar_right.js new file mode 100644 index 0000000..13834bb --- /dev/null +++ b/app/components/p8p_table/p8p_table_column_toolbar_right.js @@ -0,0 +1,63 @@ +/* + Парус 8 - Панели мониторинга - Таблица + Компонент: Панель инструментов столбца (правая) +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { IconButton, Icon } from "@mui/material"; //Интерфейсные компоненты +import { hasValue } from "./p8p_table_reducer"; //Редьюсер состояния +import { P8P_TABLE_COLUMN_ORDER_DIRECTIONS, P8P_TABLE_COLUMN_TOOL_BAR_ACTIONS } from "./p8p_table_constants"; //Константы таблицы + +//----------- +//Тело модуля +//----------- + +//Панель инструментов столбца (правая) +const P8PTableColumnToolBarRight = ({ columnDef, orders, filters, onItemClick }) => { + //Кнопка сортировки + const order = orders.find(o => o.name == columnDef.name); + let orderButton = null; + if (order) + orderButton = ( + (onItemClick ? onItemClick(P8P_TABLE_COLUMN_TOOL_BAR_ACTIONS.ORDER_TOGGLE, columnDef.name) : null)}> + {order.direction === P8P_TABLE_COLUMN_ORDER_DIRECTIONS.ASC ? "arrow_upward" : "arrow_downward"} + + ); + + //Кнопка фильтрации + const filter = filters.find(f => f.name == columnDef.name); + let filterButton = null; + if (hasValue(filter?.from) || hasValue(filter?.to)) + filterButton = ( + (onItemClick ? onItemClick(P8P_TABLE_COLUMN_TOOL_BAR_ACTIONS.FILTER_TOGGLE, columnDef.name) : null)}> + filter_alt + + ); + + //Генерация содержимого + return ( + <> + {orderButton} + {filterButton} + + ); +}; + +//Контроль свойств - Панель инструментов столбца (правая) +P8PTableColumnToolBarRight.propTypes = { + columnDef: PropTypes.object.isRequired, + orders: PropTypes.array.isRequired, + filters: PropTypes.array.isRequired, + onItemClick: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PTableColumnToolBarRight }; diff --git a/app/components/p8p_table/p8p_table_constants.js b/app/components/p8p_table/p8p_table_constants.js new file mode 100644 index 0000000..fea3f2a --- /dev/null +++ b/app/components/p8p_table/p8p_table_constants.js @@ -0,0 +1,99 @@ +/* + Парус 8 - Панели мониторинга - Таблица + Компонент: Константы +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import PropTypes from "prop-types"; //Контроль свойств компонента +import { P8P_DATA_TYPES } from "../../core/data_types"; //Типы данных + +//----------- +//Тело модуля +//----------- + +//Размеры отступов +const P8P_TABLE_SIZE = { + SMALL: "small", + MEDIUM: "medium" +}; + +//Типы данных +const P8P_TABLE_DATA_TYPE = { + STR: P8P_DATA_TYPES.STR, + NUMB: P8P_DATA_TYPES.NUMB, + DATE: P8P_DATA_TYPES.DATE +}; + +//Направления сортировки +const P8P_TABLE_COLUMN_ORDER_DIRECTIONS = { + ASC: "ASC", + DESC: "DESC" +}; + +//Действия панели инструментов столбца +const P8P_TABLE_COLUMN_TOOL_BAR_ACTIONS = { + ORDER_TOGGLE: "ORDER_TOGGLE", + FILTER_TOGGLE: "FILTER_TOGGLE", + EXPAND_TOGGLE: "EXPAND_TOGGLE" +}; + +//Действия меню столбца +const P8P_TABLE_COLUMN_MENU_ACTIONS = { + ORDER_ASC: "ORDER_ASC", + ORDER_DESC: "ORDER_DESC", + FILTER: "FILTER" +}; + +//Структура элемента описания фильтра +const P8P_TABLE_FILTER_SHAPE = PropTypes.shape({ + name: PropTypes.string.isRequired, + from: PropTypes.any, + to: PropTypes.any +}); + +//Структура элемента описания сортировки +const P8P_TABLE_ORDER_SHAPE = PropTypes.shape({ + direction: PropTypes.string.isRequired, + name: PropTypes.string.isRequired +}); + +//Размещение области страниц по вертикали +const P8P_TABLE_PAGINATOR_ALIGN = { + LEFT: "left", + RIGHT: "right", + CENTER: "center" +}; + +//Размещение области страниц по горизонтали +const P8P_TABLE_PAGINATOR_POSITION = { + TOP: "top", + BOTTOM: "bottom", + BOTH: "both" +}; + +//Высота кнопки догрузки данных +const P8P_TABLE_MORE_HEIGHT = "49px"; + +//Высота фильтров таблицы +const P8P_TABLE_FILTERS_HEIGHT = "48px"; + +//---------------- +//Интерфейс модуля +//---------------- + +export { + 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 +}; diff --git a/app/components/p8p_table/p8p_table_filters_chips.js b/app/components/p8p_table/p8p_table_filters_chips.js new file mode 100644 index 0000000..b44e74e --- /dev/null +++ b/app/components/p8p_table/p8p_table_filters_chips.js @@ -0,0 +1,74 @@ +/* + Парус 8 - Панели мониторинга - Таблица + Компонент: Сводный фильтр +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import { Stack, Chip, Typography } from "@mui/material"; //Интерфейсные компоненты +import { hasValue } from "./p8p_table_reducer"; //Редьюсер состояния +import { P8P_TABLE_DATA_TYPE } from "./p8p_table_constants"; //Типы данных +import { P8P_TYPOGRAPHY_VARIANT } from "../../theme/p8p_typography"; //Варианты шрифтов + +//----------- +//Тело модуля +//----------- + +//Сводный фильтр +const P8PTableFiltersChips = ({ filters, columnsDef, valueFromCaption, valueToCaption, onFilterChipClick, onFilterChipDelete, valueFormatter }) => { + return ( + + {filters.map((filter, i) => { + const columnDef = columnsDef.find(columnDef => columnDef.name == filter.name); + return ( + + {columnDef.caption}:  + + {hasValue(filter.from) && !columnDef.values && columnDef.dataType != P8P_TABLE_DATA_TYPE.STR + ? `${valueFromCaption.toLowerCase()} ` + : null} + {hasValue(filter.from) + ? valueFormatter + ? valueFormatter({ value: filter.from, columnDef }) + : filter.from + : null} + {hasValue(filter.to) && !columnDef.values && columnDef.dataType != P8P_TABLE_DATA_TYPE.STR + ? ` ${valueToCaption.toLowerCase()} ` + : null} + {hasValue(filter.to) ? (valueFormatter ? valueFormatter({ value: filter.to, columnDef }) : filter.to) : null} + + + } + variant="outlined" + onClick={() => (onFilterChipClick ? onFilterChipClick(columnDef.name) : null)} + onDelete={() => (onFilterChipDelete ? onFilterChipDelete(columnDef.name) : null)} + /> + ); + })} + + ); +}; + +//Контроль свойств - Сводный фильтр +P8PTableFiltersChips.propTypes = { + filters: PropTypes.array.isRequired, + columnsDef: PropTypes.array.isRequired, + valueFromCaption: PropTypes.string.isRequired, + valueToCaption: PropTypes.string.isRequired, + onFilterChipClick: PropTypes.func, + onFilterChipDelete: PropTypes.func, + valueFormatter: PropTypes.func +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8PTableFiltersChips }; diff --git a/app/components/p8p_table_reducer.js b/app/components/p8p_table/p8p_table_reducer.js similarity index 100% rename from app/components/p8p_table_reducer.js rename to app/components/p8p_table/p8p_table_reducer.js diff --git a/app/config_wrapper.js b/app/config_wrapper.js index 6dea7fd..fcb4361 100644 --- a/app/config_wrapper.js +++ b/app/config_wrapper.js @@ -12,7 +12,7 @@ import { deepCopyObject } from "./core/utils"; //Вспомогательные import { TITLES, BUTTONS, TEXTS, CAPTIONS } from "../app.text"; //Текстовые ресурсы и константы import { P8PPanelsMenuGrid, P8P_PANELS_MENU_PANEL_SHAPE } from "./components/p8p_panels_menu"; //Меню панелей import { P8PAppWorkspace } from "./components/p8p_app_workspace"; //Рабочее пространство -import { P8PTable, P8P_TABLE_DATA_TYPE, P8P_TABLE_SIZE, P8P_TABLE_FILTER_SHAPE } from "./components/p8p_table"; //Таблица данных +import { P8PTable, P8P_TABLE_DATA_TYPE, P8P_TABLE_SIZE, P8P_TABLE_FILTER_SHAPE } from "./components/p8p_table/p8p_table"; //Таблица данных import { P8PDataGrid, P8P_DATA_GRID_DATA_TYPE, P8P_DATA_GRID_SIZE, P8P_DATA_GRID_FILTER_SHAPE } from "./components/p8p_data_grid"; //Таблица данных import { P8PGantt, P8P_GANTT_TASK_SHAPE, P8P_GANTT_TASK_ATTRIBUTE_SHAPE, P8P_GANTT_TASK_COLOR_SHAPE } from "./components/p8p_gantt"; //Диаграмма Ганта import { P8PCyclogram } from "./components/p8p_cyclogram"; //Циклограмма diff --git a/app/root.js b/app/root.js index b428c24..11a9dd1 100644 --- a/app/root.js +++ b/app/root.js @@ -10,6 +10,7 @@ import React from "react"; //Классы React import { MessagingContext } from "./context/messaging"; //Контекст сообщений import { BackEndContext } from "./context/backend"; //Контекст взаимодействия с сервером +import { SettingsContext } from "./context/settings"; //Контекст взаимодействия с параметрами import { ApplicationContext } from "./context/application"; //Контекст приложения import { App } from "./app"; //Приложение import { ERRORS, TITLES, TEXTS, BUTTONS } from "../app.text"; //Текстовые ресурсы и константы @@ -17,6 +18,9 @@ import { getDisplaySize, genGUID } from "./core/utils"; //Вспомогател import config from "../app.config"; //Настройки приложения import client from "./core/client"; //Клиент для взаимодействия с сервером +import { ThemeProvider } from "@mui/material/styles"; //Подключение темы +import { theme } from ".//theme/theme"; //Тема компонентов + //----------- //Тело модуля //----------- @@ -24,13 +28,17 @@ import client from "./core/client"; //Клиент для взаимодейст //Обёртка для контекста const Root = () => { return ( - - - - - - - + + + + + + + + + + + ); }; diff --git a/app/theme/colors/common.js b/app/theme/colors/common.js new file mode 100644 index 0000000..54ad981 --- /dev/null +++ b/app/theme/colors/common.js @@ -0,0 +1,42 @@ +/* + Парус 8 - Панели мониторинга + Дополнительные цвета: общие +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { STATE } from "../../../app.text"; //Типовые текстовые ресурсы и константы +import { P8P_COLOR_GREEN } from "../colors/green"; //Дополнительные цвета: зеленый +import { P8P_COLOR_RED } from "../colors/red"; //Дополнительные цвета: красный +import { P8P_COLOR_ORANGE } from "../colors/orange"; //Дополнительные цвета: оранжевый +import { P8P_COLOR_GREY } from "./grey"; //Дополнительные цвета: серый + +//---------------- +//Интерфейс модуля +//---------------- + +//Цвет - белый +export const P8P_COLOR_WHITE = "#FFF"; + +//Цвет - черный +export const P8P_COLOR_BLACK = "#000000"; + +//Цвет состояния +export const P8P_COLOR_STATE = { + [STATE.UNDEFINED]: P8P_COLOR_GREY[900], + [STATE.INFO]: P8P_COLOR_BLACK, + [STATE.OK]: P8P_COLOR_GREEN[900], + [STATE.ERR]: P8P_COLOR_RED[900], + [STATE.WARN]: P8P_COLOR_ORANGE[900] +}; + +//Цвет заливки состояния +export const P8P_COLOR_STATE_BG = { + [STATE.UNDEFINED]: P8P_COLOR_GREY[200], + [STATE.INFO]: P8P_COLOR_WHITE, + [STATE.OK]: P8P_COLOR_GREEN[200], + [STATE.ERR]: P8P_COLOR_RED[200], + [STATE.WARN]: P8P_COLOR_ORANGE[200] +}; diff --git a/app/theme/colors/green.js b/app/theme/colors/green.js new file mode 100644 index 0000000..45ed40c --- /dev/null +++ b/app/theme/colors/green.js @@ -0,0 +1,21 @@ +/* + Парус 8 - Панели мониторинга + Дополнительные цвета: зеленый +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +export const P8P_COLOR_GREEN = { + 50: "#e8f5e9", + 100: "#c8e6c9", + 200: "#a5d6a7", + 300: "#81c784", + 400: "#66bb6a", + 500: "#4caf50", + 600: "#43a047", + 700: "#388e3c", + 800: "#2e7d32", + 900: "#1b5e20" +}; diff --git a/app/theme/colors/grey.js b/app/theme/colors/grey.js new file mode 100644 index 0000000..931fba8 --- /dev/null +++ b/app/theme/colors/grey.js @@ -0,0 +1,21 @@ +/* + Парус 8 - Панели мониторинга + Дополнительные цвета: серый +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +export const P8P_COLOR_GREY = { + 50: "#fafafa", + 100: "#f5f5f5", + 200: "#eeeeee", + 300: "#e0e0e0", + 400: "#bdbdbd", + 500: "#9e9e9e", + 600: "#757575", + 700: "#616161", + 800: "#424242", + 900: "#212121" +}; diff --git a/app/theme/colors/orange.js b/app/theme/colors/orange.js new file mode 100644 index 0000000..4c533e7 --- /dev/null +++ b/app/theme/colors/orange.js @@ -0,0 +1,21 @@ +/* + Парус 8 - Панели мониторинга + Дополнительные цвета: оранжевый +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +export const P8P_COLOR_ORANGE = { + 50: "#fff3e0", + 100: "#ffe0b2", + 200: "#ffcc80", + 300: "#ffb74d", + 400: "#ffa726", + 500: "#ff9800", + 600: "#fb8c00", + 700: "#f57c00", + 800: "#ef6c00", + 900: "#e65100" +}; diff --git a/app/theme/colors/red.js b/app/theme/colors/red.js new file mode 100644 index 0000000..725f917 --- /dev/null +++ b/app/theme/colors/red.js @@ -0,0 +1,21 @@ +/* + Парус 8 - Панели мониторинга + Дополнительные цвета: красный +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +export const P8P_COLOR_RED = { + 50: "#ffebee", + 100: "#fecdd2", + 200: "#ef9a9a", + 300: "#e57373", + 400: "#ef5350", + 500: "#f44336", + 600: "#e53935", + 700: "#d32f2f", + 800: "#c62828", + 900: "#b71c1c" +}; diff --git a/app/theme/p8p_components.js b/app/theme/p8p_components.js new file mode 100644 index 0000000..bb1d27a --- /dev/null +++ b/app/theme/p8p_components.js @@ -0,0 +1,67 @@ +/* + Парус 8 - Панели мониторинга + Кастомные компоненты +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { P8P_BUTTON_OVERRIDES } from "./variants/p8p_button_variants"; //Расширение Button +import { P8P_TABLE_OVERRIDES } from "./variants/p8p_table_variants"; //Расширение Table +import { P8P_TABLE_HEAD_OVERRIDES } from "./variants/p8p_table_head_variants"; //Расширение TableHead +import { P8P_TABLE_CELL_OVERRIDES } from "./variants/p8p_table_cell_variants"; //Расширение TableCell +import { P8P_TABLE_ROW_OVERRIDES } from "./variants/p8p_table_row_variants"; //Расширение TableRow +import { P8P_PAGINATION_OVERRIDES } from "./variants/p8p_pagination_variants"; //Расширение Pagination +import { P8P_CONTAINER_OVERRIDES } from "./variants/p8p_container_variants"; //Расширение Container +import { P8P_ICON_OVERRIDES } from "./variants/p8p_icon_variants"; //Расширение Icon +import { P8P_ICON_BUTTON_OVERRIDES } from "./variants/p8p_icon_button_variants"; //Расширение IconButton +import { P8P_TEXT_FIELD_OVERRIDES } from "./variants/p8p_text_field_variants"; //Расширение TextField +import { P8P_LIST_OVERRIDES } from "./variants/p8p_list_variants"; //Расширение List +import { P8P_LIST_ITEM_TEXT_OVERRIDES } from "./variants/p8p_list_item_text_variants"; //Расширение ListItemText +import { P8P_DIALOG_TITLE_OVERRIDES } from "./variants/p8p_dialog_title_variants"; //Расширение DialogTitle +import { P8P_DIALOG_CONTENT_OVERRIDES } from "./variants/p8p_dialog_content_variants"; //Расширение DialogContent +import { P8P_DIALOG_CONTENT_TEXT_OVERRIDES } from "./variants/p8p_dialog_content_text_variants"; //Расширение DialogContentText +import { P8P_DRAWER_OVERRIDES } from "./variants/p8p_drawer_variants"; //Расширение Drawer +import { P8P_APP_BAR_OVERRIDES } from "./variants/p8p_app_bar_variants"; //Расширение AppBar +import { P8P_GRID_OVERRIDES } from "./variants/p8p_grid_variants"; //Расширение Grid +import { P8P_CARD_OVERRIDES } from "./variants/p8p_card_variants"; //Расширение Card +import { P8P_CARD_ACTIONS_OVERRIDES } from "./variants/p8p_card_actions_variants"; //Расширение CardActions +import { P8P_SELECT_OVERRIDES } from "./variants/p8p_select_variants"; //Расширение Select +import { P8P_INPUT_OVERRIDES } from "./variants/p8p_input_variants"; //Расширение Input +import { P8P_INPUT_LABEL_OVERRIDES } from "./variants/p8p_input_label_variants"; //Расширение InputLable +import { P8P_MENU_ITEM_OVERRIDES } from "./variants/p8p_menu_item_variants"; //Расширение MenuItem +import { P8P_AUTOCOMPLETE_OVERRIDES } from "./variants/p8p_autocomplete_variants"; //Расширение Autocomplete + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные компоненты +export const P8P_COMPONENTS = { + MuiButton: P8P_BUTTON_OVERRIDES, + MuiTable: P8P_TABLE_OVERRIDES, + MuiTableHead: P8P_TABLE_HEAD_OVERRIDES, + MuiTableRow: P8P_TABLE_ROW_OVERRIDES, + MuiTableCell: P8P_TABLE_CELL_OVERRIDES, + MuiPagination: P8P_PAGINATION_OVERRIDES, + MuiContainer: P8P_CONTAINER_OVERRIDES, + MuiIcon: P8P_ICON_OVERRIDES, + MuiIconButton: P8P_ICON_BUTTON_OVERRIDES, + MuiTextField: P8P_TEXT_FIELD_OVERRIDES, + MuiDialogTitle: P8P_DIALOG_TITLE_OVERRIDES, + MuiDialogContent: P8P_DIALOG_CONTENT_OVERRIDES, + MuiDialogContentText: P8P_DIALOG_CONTENT_TEXT_OVERRIDES, + MuiList: P8P_LIST_OVERRIDES, + MuiListItemText: P8P_LIST_ITEM_TEXT_OVERRIDES, + MuiDrawer: P8P_DRAWER_OVERRIDES, + MuiAppBar: P8P_APP_BAR_OVERRIDES, + MuiGrid: P8P_GRID_OVERRIDES, + MuiCard: P8P_CARD_OVERRIDES, + MuiCardActions: P8P_CARD_ACTIONS_OVERRIDES, + MuiSelect: P8P_SELECT_OVERRIDES, + MuiInputLabel: P8P_INPUT_LABEL_OVERRIDES, + MuiInput: P8P_INPUT_OVERRIDES, + MuiMenuItem: P8P_MENU_ITEM_OVERRIDES, + MuiAutocomplete: P8P_AUTOCOMPLETE_OVERRIDES +}; diff --git a/app/theme/p8p_palette.js b/app/theme/p8p_palette.js new file mode 100644 index 0000000..d4fcb21 --- /dev/null +++ b/app/theme/p8p_palette.js @@ -0,0 +1,72 @@ +/* + Парус 8 - Панели мониторинга + Кастомная палитра +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомная палитра +export const P8P_PALETTE = { + P8PText: { + primary: "#2a3039de", + secondary: "#2a303999", + disabled: "#2a303961" + }, + P8PPrimary: { + main: "#0F71BD", + dark: "#005EA6", + light: "#8DC2E2", + contrastText: "#FFF", + hover: "#0f71bd0a", + selected: "#0f71bd14", + focus: "#0f71bd1f", + focusVisible: "#0f71bd4d", + outlinedBorder: "#2a30391f" + }, + P8PSecondary: { + main: "#37474F", + dark: "#2A3039", + light: "#607D8B", + contrastText: "#FFF", + outlinedBorder: "#2a303980" + }, + P8PError: { + main: "#D32F2F", + dark: "#C62828", + light: "#EF5350" + }, + P8PWarning: { + main: "#EF6C00", + dark: "#E65100", + light: "#FF9800" + }, + P8PInfo: { + main: "#0F71BD", + dark: "#005EA6", + light: "#03A9F4" + }, + P8PSuccess: { + main: "#2E7D32", + dark: "#1B5E20", + light: "#4CAF50" + }, + P8PBackground: { + primary: "#FFF", + secondary: "#F8FAFD", + tableHeader: "#E6EAF3" + }, + P8PAction: { + active: "#0000008a" + }, + P8PCyclogram: { + group: "#e6eaf3", + task: "#cfd8dc" + }, + P8PDesktop: { + main: "#1976d2", + hover: "#c3e1ff" + }, + P8PPurple: "#5e35b1" +}; diff --git a/app/theme/p8p_typography.js b/app/theme/p8p_typography.js new file mode 100644 index 0000000..5651507 --- /dev/null +++ b/app/theme/p8p_typography.js @@ -0,0 +1,217 @@ +/* + Парус 8 - Панели мониторинга + Кастомные шрифты +*/ + +//--------- +//Константы +//--------- + +//Шрифт "Montserrat" +const P8PFontMontserrat = { + fontFamily: "Montserrat", + fontStyle: "normal" +}; + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные шрифты +export const P8P_TYPOGRAPHY = { + P8PFontMontserrat: "Montserrat", + P8PH1: { + ...P8PFontMontserrat, + fontSize: "96px", + fontWeight: "500", + lineHeight: "117%", + letterSpacing: "-1.5px" + }, + P8PH2: { + ...P8PFontMontserrat, + fontSize: "60px", + fontWeight: "500", + lineHeight: "120%", + letterSpacing: "-0.5px" + }, + P8PH3: { + ...P8PFontMontserrat, + fontSize: "48px", + fontWeight: "500", + lineHeight: "118%" + }, + P8PH4: { + ...P8PFontMontserrat, + fontSize: "32px", + fontWeight: "600", + lineHeight: "120%", + letterSpacing: "0.25px" + }, + P8PH5: { + ...P8PFontMontserrat, + fontSize: "24px", + fontWeight: "600", + lineHeight: "130%" + }, + P8PH6: { + ...P8PFontMontserrat, + fontSize: "20px", + fontWeight: "600", + lineHeight: "140%", + letterSpacing: "0.15px" + }, + P8PH6Light: { + ...P8PFontMontserrat, + fontSize: "20px", + fontWeight: "500", + lineHeight: "140%", + letterSpacing: "0.15px" + }, + P8PH7: { + ...P8PFontMontserrat, + fontSize: "18px", + fontWeight: "600", + lineHeight: "130%", + letterSpacing: "0.15px" + }, + P8PSubtitle1: { + ...P8PFontMontserrat, + fontSize: "16px", + fontWeight: "600", + lineHeight: "130%", + letterSpacing: "0.15px" + }, + P8PSubtitle2: { + ...P8PFontMontserrat, + fontSize: "14px", + fontWeight: "600", + lineHeight: "140%", + letterSpacing: "0.1px" + }, + P8PBody1: { + ...P8PFontMontserrat, + fontSize: "16px", + fontWeight: "500", + lineHeight: "150%", + letterSpacing: "0.15px" + }, + P8PBody2: { + ...P8PFontMontserrat, + fontSize: "12px", + fontWeight: "500", + lineHeight: "143%", + letterSpacing: "0.17px" + }, + P8PBody2Light: { + ...P8PFontMontserrat, + fontSize: "12px", + fontWeight: "400", + lineHeight: "143%", + letterSpacing: "0.17px" + }, + P8PBody3: { + ...P8PFontMontserrat, + fontSize: "14px", + fontWeight: "500", + lineHeight: "143%", + letterSpacing: "0.17px" + }, + P8PBody3Light: { + ...P8PFontMontserrat, + fontSize: "14px", + fontWeight: "400", + lineHeight: "143%", + letterSpacing: "0.17px" + }, + P8PBody4: { + ...P8PFontMontserrat, + fontSize: "15px", + fontWeight: "500", + lineHeight: "143%", + letterSpacing: "0.17px" + }, + P8PCaption: { + ...P8PFontMontserrat, + fontSize: "12px", + fontWeight: "500", + lineHeight: "166%", + letterSpacing: "0.4px" + }, + P8POverline: { + ...P8PFontMontserrat, + fontSize: "12px", + fontWeight: "400", + lineHeight: "266%", + letterSpacing: "1px" + }, + P8PButton: { + ...P8PFontMontserrat, + fontSize: "15px", + fontWeight: "600", + lineHeight: "26px", + letterSpacing: "0.46px" + }, + P8PColumn: { + ...P8PFontMontserrat, + fontSize: "12px", + fontWeight: "600", + lineHeight: "24px", + letterSpacing: "0.17px" + }, + P8PInputLabel: { + ...P8PFontMontserrat, + fontSize: "16px" + }, + P8PBody2Bold: { + ...P8PFontMontserrat, + fontSize: "12px", + fontWeight: "700", + lineHeight: "143%", + letterSpacing: "0.17px" + }, + P8PTitle: { + ...P8PFontMontserrat, + fontSize: "15px", + fontWeight: "500", + lineHeight: "140%", + letterSpacing: "0.1px" + }, + P8PDesktopGroup: { + fontFamily: "tahoma, arial, verdana, sans-serif!important", + fontSize: "13px !important", + fontWeight: "bold" + }, + P8PDesktopCaption: { + fontFamily: "tahoma, arial, verdana, sans-serif!important", + fontSize: "12px", + lineHeight: "1.2" + } +}; + +//Наименование кастомных шрифтов +export const P8P_TYPOGRAPHY_VARIANT = { + H1: "P8PH1", + H2: "P8PH2", + H3: "P8PH3", + H4: "P8PH4", + H5: "P8PH5", + H6: "P8PH6", + H6_LIGHT: "P8PH6Light", + H7: "P8PH7", + SUBTITLE1: "P8PSubtitle1", + SUBTITLE2: "P8PSubtitle2", + BODY1: "P8PBody1", + BODY2: "P8PBody2", + BODY2_LIGHT: "P8PBody2Light", + BODY3: "P8PBody3", + BODY3_LIGHT: "P8PBody3Light", + CAPTION: "P8PCaption", + OVERLINE: "P8POverline", + BUTTON: "P8PButton", + COLUMN: "P8PColumn", + INPUT_LABEL: "P8PInputLabel", + BODY2_BOLD: "P8PBody2Bold", + TITLE: "P8PTitle", + DESKTOP_GROUP: "P8PDesktopGroup", + DESKTOP_CAPTION: "P8PDesktopCaption" +}; diff --git a/app/theme/styles/box.js b/app/theme/styles/box.js new file mode 100644 index 0000000..5fa3a27 --- /dev/null +++ b/app/theme/styles/box.js @@ -0,0 +1,152 @@ +/* + Парус 8 - Панели мониторинга + Вспомогательные стили Box +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { P8P_COMPONENT_WIDTH, P8P_COMPONENT_HEIGHT, P8P_SCROLL_AUTO } from "./common"; //Стили - общие +import { useTheme } from "@mui/material/styles"; //Хук темы приложения + +//--------- +//Общие стили контейнеров +//--------- + +//Контейнер плавающий +export const P8P_BOX_FLEX = { display: "flex" }; + +//Контейнер плавающий, элементы по центру, к началу +export const P8P_BOX_CENTER_START = { + display: "flex", + alignItems: "center", + justifyContent: "flex-start" +}; + +//Контейнер плавающий, элементы по центру, к концу +export const P8P_BOX_CENTER_END = { + display: "flex", + alignItems: "center", + justifyContent: "flex-end" +}; + +//Контейнер плавающий, элементы по центру, равномерно +export const P8P_BOX_CENTER_BETWEEN = { + display: "flex", + alignItems: "center", + justifyContent: "space-between" +}; + +//Контейнер плавающий, элементы по центру +export const P8P_BOX_CENTER = { display: "flex", alignItems: "center", justifyContent: "center" }; + +//--------- +//Стили приложения +//--------- + +//Контейнер рабочего пространства +export const P8P_BOX_APP_WORKSPACE = { + ...P8P_COMPONENT_WIDTH({ width: "100vw" }), + //width: "100vw", + ...P8P_BOX_CENTER_BETWEEN +}; + +//Меню панелей - контейнер грида +export const P8P_BOX_PANELS_MENU_CONTAINER = { + ...P8P_BOX_CENTER_START, + ...P8P_COMPONENT_HEIGHT({ minHeight: "100vh" }) +}; + +//--------- +//Стили настроек панелей +//--------- + +//Список настроек панели +export const P8P_BOX_SETTINGS_LIST = { + ...P8P_COMPONENT_WIDTH({ width: "520px" }), + ...P8P_COMPONENT_HEIGHT({ height: "500px" }), + ...P8P_SCROLL_AUTO +}; + +//Список панелей настроек панелей +export const P8P_BOX_SETTINGS_PANELS = { + ...P8P_COMPONENT_WIDTH({ width: "300px" }), + ...P8P_COMPONENT_HEIGHT({ height: "500px" }), + ...P8P_SCROLL_AUTO +}; + +//Контейнер настроек панелей +export const P8P_BOX_SETTINGS_CONTAINER = { + display: "flex", + flexDirection: "row", + alignItems: "flex-start" +}; + +//--------- +//Стили Ганта +//--------- + +//Контейнер диаграмы Ганта +export const P8P_BOX_GANTT = ({ noData, zoomBarHeight, titleHeight }) => ({ + ...P8P_COMPONENT_HEIGHT({ height: `calc(100% - ${zoomBarHeight ? zoomBarHeight : "0px"} - ${titleHeight ? titleHeight : "0px"})` }), + //height: `calc(100% - ${zoomBarHeight ? zoomBarHeight : "0px"} - ${titleHeight ? titleHeight : "0px"})`, + display: noData ? "none" : "" +}); + +//--------- +//Стили циклограммы +//--------- + +//Контейнер циклограммы +export const P8P_BOX_CYCLOGRAM = ({ noData, zoomBarHeight, titleHeight }) => ({ + position: "relative", + overflow: "auto", + padding: "0px 8px", + ...P8P_COMPONENT_HEIGHT({ height: `calc(100% - ${zoomBarHeight ? zoomBarHeight : "0px"} - ${titleHeight ? titleHeight : "0px"})` }), + //height: `calc(100% - ${zoomBarHeight ? zoomBarHeight : "0px"} - ${titleHeight ? titleHeight : "0px"})`, + display: noData ? "none" : "" +}); + +//Контейнер строки циклограммы +export const P8P_BOX_CYCLOGRAM_ROW = ({ index }) => { + //Определение темы приложения + const theme = useTheme(); + //Возвращаем стиль + return { + ...(index % 2 === 0 ? { backgroundColor: theme.palette.P8PBackground.primary } : { backgroundColor: theme.palette.P8PBackground.secondary }) + }; +}; + +//Контейнер заголовка группы циклограммы +export const P8P_BOX_CYCLOGRAM_GROUP = ({ height }) => { + //Определение темы приложения + const theme = useTheme(); + //Возвращаем стиль + return { + border: "1px solid", + backgroundColor: theme.palette.P8PCyclogram.group, + ...P8P_BOX_CENTER, + height + }; +}; + +//Контейнер задачи циклограммы +export const P8P_BOX_CYCLOGRAM_TASK = ({ lineHeight, bgColor, textColor, highlightColor }) => { + //Определение темы приложения + const theme = useTheme(); + //Возвращаем стиль + return { + display: "flex", + alignItems: "center", + backgroundColor: bgColor ? bgColor : theme.palette.P8PCyclogram.task, + ...(textColor ? { color: textColor } : {}), + height: lineHeight, + "&:hover": { + ...(highlightColor + ? { backgroundColor: `${highlightColor} !important`, filter: "brightness(1) !important" } + : { filter: `brightness(${bgColor ? "1.25" : "1.1"}) !important` }), + cursor: "pointer !important" + } + }; +}; diff --git a/app/theme/styles/common.js b/app/theme/styles/common.js new file mode 100644 index 0000000..475aa84 --- /dev/null +++ b/app/theme/styles/common.js @@ -0,0 +1,58 @@ +/* + Парус 8 - Панели мониторинга + Вспомогательные стили - общие +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { APP_STYLES } from "../../../app.styles"; //Типовые стили + +//---------------- +//Интерфейс модуля +//---------------- + +//Ширина компонента +export const P8P_COMPONENT_WIDTH = ({ width, minWidth, maxWidth }) => ({ + ...(width ? { width } : {}), + ...(minWidth ? { minWidth } : {}), + ...(maxWidth ? { maxWidth } : {}) +}); + +//Высота компонента +export const P8P_COMPONENT_HEIGHT = ({ height, minHeight, maxHeight }) => ({ + ...(height ? { height } : {}), + ...(minHeight ? { minHeight } : {}), + ...(maxHeight ? { maxHeight } : {}) +}); + +//Отображаемый скролл +export const P8P_SCROLL_AUTO = { + overflow: "auto", + ...APP_STYLES.SCROLL +}; + +//Отсутствие отступов +export const P8P_COMPONENT_ZERO_PADDING = { + padding: "0px" +}; + +//Стили +export const P8P_SCROLL = { + "&::-webkit-scrollbar": { + height: "8px", + width: "8px" + }, + "&::-webkit-scrollbar-track": { + borderRadius: "8px", + backgroundColor: "#EBEBEB" + }, + "&::-webkit-scrollbar-thumb": { + borderRadius: "8px", + backgroundColor: "#b4b4b4" + }, + "&::-webkit-scrollbar-thumb:hover": { + backgroundColor: "#808080" + } +}; diff --git a/app/theme/styles/icon.js b/app/theme/styles/icon.js new file mode 100644 index 0000000..444f09c --- /dev/null +++ b/app/theme/styles/icon.js @@ -0,0 +1,32 @@ +/* + Парус 8 - Панели мониторинга + Вспомогательные стили Icon +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { STATE } from "../../../app.text"; //Типовые текстовые ресурсы и константы +import { P8P_COLOR_STATE } from "../colors/common"; //Дополнительные цвета - общие + +//--------- +//Константы +//--------- + +//Цвета текста и иконок индикатора +const P8P_INDICATOR_COLOR = { + [STATE.OK]: P8P_COLOR_STATE[STATE.OK], + [STATE.ERR]: P8P_COLOR_STATE[STATE.ERR], + [STATE.WARN]: P8P_COLOR_STATE[STATE.WARN] +}; + +//---------------- +//Интерфейс модуля +//---------------- + +//Иконка индикатора +export const P8P_ICON_INDICATOR = ({ state, fontSize, color }) => ({ + fontSize, + color: color || P8P_INDICATOR_COLOR[state] +}); diff --git a/app/theme/styles/main.js b/app/theme/styles/main.js new file mode 100644 index 0000000..eb86829 --- /dev/null +++ b/app/theme/styles/main.js @@ -0,0 +1,13 @@ +/* + Парус 8 - Панели мониторинга + Вспомогательные стили main +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Содержимое рабочего пространства +export const P8P_MAIN_APP_WORKSPACE = { + flexGrow: 1 +}; diff --git a/app/theme/styles/paper.js b/app/theme/styles/paper.js new file mode 100644 index 0000000..223350c --- /dev/null +++ b/app/theme/styles/paper.js @@ -0,0 +1,66 @@ +/* + Парус 8 - Панели мониторинга + Вспомогательные стили Paper +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { useTheme } from "@mui/material/styles"; //Хук темы приложения +import { STATE } from "../../../app.text"; //Типовые текстовые ресурсы и константы +import { P8P_COLOR_STATE, P8P_COLOR_STATE_BG } from "../colors/common"; //Дополнительные цвета - общие +import { P8P_COMPONENT_WIDTH, P8P_COMPONENT_HEIGHT } from "./common"; //Стили - общие + +//--------- +//Константы +//--------- + +//Цвета заливки индикатора +const P8P_INDICATOR_BG_COLOR = { + [STATE.OK]: P8P_COLOR_STATE_BG[STATE.OK], + [STATE.ERR]: P8P_COLOR_STATE_BG[STATE.ERR], + [STATE.WARN]: P8P_COLOR_STATE_BG[STATE.WARN] +}; + +//Цвета текста и иконок индикатора +const P8P_INDICATOR_COLOR = { + [STATE.OK]: P8P_COLOR_STATE[STATE.OK], + [STATE.ERR]: P8P_COLOR_STATE[STATE.ERR], + [STATE.WARN]: P8P_COLOR_STATE[STATE.WARN] +}; + +//----------------------- +//Вспомогательные функции +//----------------------- + +//---------------- +//Интерфейс модуля +//---------------- + +//Стиль для контейнера индикатора +export const P8P_PAPER_INDICATOR = ({ state, color, backgroundColor, clickable }) => { + //Определение темы приложения + const theme = useTheme(); + //Возвращаем стиль + return { + padding: "10px", + ...P8P_COMPONENT_WIDTH({ width: "100%" }), + ...P8P_COMPONENT_HEIGHT({ height: "100%" }), + //width: "100%", + //height: "100%", + overflow: "hidden", + backgroundColor: backgroundColor || P8P_INDICATOR_BG_COLOR[state], + color: color || P8P_INDICATOR_COLOR[state], + display: "flex", + flexDirection: "column", + justifyContent: "center", + ...(clickable + ? { + cursor: "pointer", + "&:hover": { filter: "brightness(0.92) !important" }, + "&:active": { backgroundColor: theme.palette.P8PAction.active } + } + : {}) + }; +}; diff --git a/app/theme/styles/stack.js b/app/theme/styles/stack.js new file mode 100644 index 0000000..1fb2f8f --- /dev/null +++ b/app/theme/styles/stack.js @@ -0,0 +1,22 @@ +/* + Парус 8 - Панели мониторинга + Вспомогательные стили Stack +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { P8P_COMPONENT_WIDTH } from "./common"; //Стили - общие + +//---------------- +//Интерфейс модуля +//---------------- + +//Адаптивный с сокрытием +export const P8P_STACK_INLINE_HIDDEN = { + ...P8P_COMPONENT_WIDTH({ width: "100%" }), + containerType: "inline-size", + //width: "100%", + overflow: "hidden" +}; diff --git a/app/theme/styles/typography.js b/app/theme/styles/typography.js new file mode 100644 index 0000000..4ed4e9a --- /dev/null +++ b/app/theme/styles/typography.js @@ -0,0 +1,86 @@ +/* + Парус 8 - Панели мониторинга + Вспомогательные стили Typography +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { P8P_COMPONENT_WIDTH, P8P_COMPONENT_HEIGHT } from "./common"; //Стили - общие + +//---------------- +//Интерфейс модуля +//---------------- + +//--------- +//Общие стили текста +//--------- + +//Текст с возможностью нажатия +export const P8P_TYPOGRAPHY_CLICKABLE = { + cursor: "pointer" +}; + +//Заголовок полноэкранного диалога +export const P8P_TYPOGRAPHY_DIALOG_TITLE = { + marginLeft: "16px", + flex: 1 +}; + +//Заголовок +export const P8P_TYPOGRAPHY_TITLE = { + ...P8P_COMPONENT_HEIGHT({ height: "44px" }) + //height: "44px" +}; + +//--------- +//Стили приложения +//--------- + +//Описание панели на рабочем столе +export const P8P_TYPOGRAPHY_PANEL_DESK = { + display: "-webkit-box", + overflow: "hidden", + WebkitBoxOrient: "vertical", + WebkitLineClamp: 2, + ...P8P_COMPONENT_WIDTH({ maxWidth: "140px" }) + //maxWidth: "140px" +}; + +//--------- +//Стили циклограммы +//--------- + +//Колонка циклограммы +export const P8P_TYPOGRAPHY_CG_HEADER = { + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "pre", + textAlign: "center", + lineHeight: "35px", + padding: "0px 5px" +}; + +//Группа циклограммы +export const P8P_TYPOGRAPHY_CG_GROUP = ({ maxWidth, maxHeight }) => ({ + maxWidth, + maxHeight, + textAlign: "center", + wordWrap: "break-word" +}); + +//Задача циклограммы +export const P8P_TYPOGRAPHY_CG_TASK = ({ maxHeight, availableLines }) => ({ + maxHeight, + ...P8P_COMPONENT_WIDTH({ width: "100%" }), + //width: "100%", + padding: "0px 5px", + overflowWrap: "break-word", + wordBreak: "break-all", + overflow: "hidden", + textOverflow: "ellipsis", + display: "-webkit-box", + WebkitBoxOrient: "vertical", + WebkitLineClamp: availableLines < 1 ? 1 : availableLines +}); diff --git a/app/theme/theme.js b/app/theme/theme.js new file mode 100644 index 0000000..7172140 --- /dev/null +++ b/app/theme/theme.js @@ -0,0 +1,24 @@ +/* + Парус 8 - Панели мониторинга + Стилистика компонентов +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { createTheme } from "@mui/material/styles"; //Функция создания темы MUI +import { P8P_TYPOGRAPHY } from "./p8p_typography"; //Кастомные шрифты +import { P8P_PALETTE } from "./p8p_palette"; //Кастомная палитра +import { P8P_COMPONENTS } from "./p8p_components"; //Кастомные компоненты + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомная тема MUI +export const theme = createTheme({ + palette: P8P_PALETTE, + typography: P8P_TYPOGRAPHY, + components: P8P_COMPONENTS +}); diff --git a/app/theme/utils.js b/app/theme/utils.js new file mode 100644 index 0000000..25dbebe --- /dev/null +++ b/app/theme/utils.js @@ -0,0 +1,43 @@ +//--------------------- +//Подключение библиотек +//--------------------- + +import { alpha } from "@mui/material/styles"; //Интерфейсные стили компонентов + +//---------------- +//Интерфейс модуля +//---------------- + +//Универсальное определение цвета кнопки +export const getButtonColorStyles = (color, theme, isOutlined = false) => { + //Определяем цвет + const colorObj = !color ? theme.palette.primary : theme.palette[color]; + //Если цвет неопределен или нет разбивки на оттенки + if (!colorObj || typeof colorObj !== "object" || !colorObj.main) { + return {}; + } + //Возвращаем результат + return { + backgroundColor: !isOutlined ? colorObj.main : null, + color: !isOutlined ? colorObj.contrastText || "#FFF" : colorObj.main, + ...(isOutlined ? { border: `1px solid ${colorObj.main}` } : {}), + "&:hover": { + textDecoration: "none", + backgroundColor: !isOutlined ? colorObj.dark || colorObj.main : alpha(colorObj.dark, 0.04), + ...(!isOutlined + ? { boxShadow: "0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12)" } + : {}) + }, + "&:active": { + backgroundColor: !isOutlined ? colorObj.light || colorObj.main : null, + ...(!isOutlined + ? { boxShadow: "0px 5px 5px -3px rgba(0, 0, 0, 0.2) 0px 8px 10px 1px rgba(0, 0, 0, 0.14) 0px 3px 14px 2px rgba(0, 0, 0, 0.12)" } + : {}) + }, + "&.Mui-disabled": { + boxShadow: "none", + backgroundColor: "rgba(0, 0, 0, 0.12)", + color: "rgba(0, 0, 0, 0.26)" + } + }; +}; diff --git a/app/theme/variants/p8p_app_bar_variants.js b/app/theme/variants/p8p_app_bar_variants.js new file mode 100644 index 0000000..5b98e3f --- /dev/null +++ b/app/theme/variants/p8p_app_bar_variants.js @@ -0,0 +1,33 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики AppBar +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные области заголовка +export const P8P_APP_BARS = { + primary: {}, + P8PFixed: { position: "fixed" }, + P8PRelative: { position: "relative" } +}; + +//Наименование кастомных областей заголовка +export const P8P_APP_BAR_VARIANT = { + FIXED: "P8PFixed", + RELATIVE: "P8PRelative" +}; + +//Кастомная стилистика компонента +export const P8P_APP_BAR_OVERRIDES = { + styleOverrides: { + root: ({ ownerState }) => { + //Определяем вариант + const variant = ownerState["variant"] || "primary"; + //Возвращаем стили варианта + return P8P_APP_BARS[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_autocomplete_variants.js b/app/theme/variants/p8p_autocomplete_variants.js new file mode 100644 index 0000000..e5e6efa --- /dev/null +++ b/app/theme/variants/p8p_autocomplete_variants.js @@ -0,0 +1,31 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики Autocomplete +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные поля выбора +export const P8P_AUTOCOMPLETES = theme => ({ + primary: {}, + P8PPrimary: { ...theme.typography.P8PBody1 } +}); + +//Наименование кастомных полей выбора +export const P8P_AUTOCOMPLETE_VARIANT = { + PRIMARY: "P8PPrimary" +}; + +//Кастомная стилистика компонента +export const P8P_AUTOCOMPLETE_OVERRIDES = { + styleOverrides: { + option: ({ ownerState, theme }) => { + //Определяем вариант + const variant = ownerState["variant"] || "primary"; + //Возвращаем стили варианта + return P8P_AUTOCOMPLETES(theme)[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_button_variants.js b/app/theme/variants/p8p_button_variants.js new file mode 100644 index 0000000..809b471 --- /dev/null +++ b/app/theme/variants/p8p_button_variants.js @@ -0,0 +1,106 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики Button +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { getButtonColorStyles } from "../utils"; //Дополнительные функции стилизации +import { P8P_COMPONENT_HEIGHT, P8P_COMPONENT_WIDTH } from "../styles/common"; //Стили - общие + +//--------- +//Константы +//--------- + +//Размеры кнопок +const P8P_BUTTON_SIZE = { + small: { + fontSize: "13px", + padding: "4px 22px" + }, + medium: { + fontSize: "14px", + padding: "6px 22px" + }, + large: { + fontSize: "15px", + padding: "8px 22px" + }, + default: { + fontSize: "15px", + padding: "6px 22px" + } +}; + +//Общие стили +const defaultStyles = (theme, size) => ({ + borderRadius: "4px", + ...theme.typography.P8PButton, + ...(size ? P8P_BUTTON_SIZE[size] : P8P_BUTTON_SIZE.default) +}); + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные кнопки +export const P8P_BUTTONS = ({ theme, color, size }) => ({ + P8PPrimary: { + ...defaultStyles(theme, size), + ...getButtonColorStyles(color === "primary" ? "P8PInfo" : color, theme) + }, + P8PSecondary: { + ...defaultStyles(theme, size), + ...getButtonColorStyles(color === "primary" ? "P8PSecondary" : color, theme) + }, + P8POutlined: { + ...defaultStyles(theme, size), + ...getButtonColorStyles(color === "primary" ? "P8PSecondary" : color, theme, true) + }, + P8PText: { + ...theme.typography.P8PSubtitle2, + ...getButtonColorStyles(color === "primary" ? "P8PInfo" : color, theme, true), + ...(size ? P8P_BUTTON_SIZE[size] : P8P_BUTTON_SIZE.default), + border: "none" + }, + P8PDesktopPanel: { + fontSize: "12px", + textTransform: "none", + ...P8P_COMPONENT_WIDTH({ width: "150px" }), + ...P8P_COMPONENT_HEIGHT({ width: "90px" }), + // width: "150px", + // height: "90px", + flexDirection: "column", + justifyContent: "flex-start", + color: theme.palette.P8PDesktop.main, + "&:hover": { backgroundColor: theme.palette.P8PDesktop.hover } + } +}); + +//Наименование кастомных кнопок +export const P8P_BUTTON_VARIANT = { + PRIMARY: "P8PPrimary", + SECONDARY: "P8PSecondary", + OUTLINED: "P8POutlined", + TEXT: "P8PText", + DESKTOP_PANEL: "P8PDesktopPanel" +}; + +//Кастомная стилистика компонента +export const P8P_BUTTON_OVERRIDES = { + styleOverrides: { + root: ({ ownerState, theme }) => { + //Определяем цвет + const { color, size } = ownerState; + //Возвращаем варианты + return { + variants: Object.entries(P8P_BUTTONS({ theme, color, size })).map(([name, styles]) => ({ + props: { variant: name }, + style: styles + })) + }; + } + } +}; diff --git a/app/theme/variants/p8p_card_actions_variants.js b/app/theme/variants/p8p_card_actions_variants.js new file mode 100644 index 0000000..ce784ab --- /dev/null +++ b/app/theme/variants/p8p_card_actions_variants.js @@ -0,0 +1,31 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики CardActions +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные действия карточки +export const P8P_CARD_ACTIONS = { + primary: {}, + P8PPanelCard: { marginTop: "auto", display: "flex", justifyContent: "flex-end", alignItems: "flex-start" } +}; + +//Наименование кастомных действий карточек +export const P8P_CARD_ACTIONS_VARIANT = { + PANEL_CARD: "P8PPanelCard" +}; + +//Кастомная стилистика компонента +export const P8P_CARD_ACTIONS_OVERRIDES = { + styleOverrides: { + root: ({ ownerState }) => { + //Определяем вариант + const variant = ownerState["variant"] || "primary"; + //Возвращаем стили варианта + return P8P_CARD_ACTIONS[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_card_variants.js b/app/theme/variants/p8p_card_variants.js new file mode 100644 index 0000000..a06aa23 --- /dev/null +++ b/app/theme/variants/p8p_card_variants.js @@ -0,0 +1,44 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики Card +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { P8P_COMPONENT_HEIGHT, P8P_COMPONENT_WIDTH } from "../styles/common"; //Стили - общие + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные карточки +export const P8P_CARDS = { + primary: {}, + P8PPanelInfo: { + ...P8P_COMPONENT_WIDTH({ maxWidth: 400 }), + ...P8P_COMPONENT_HEIGHT({ height: "100%" }), + //maxWidth: 400, + //height: "100%", + flexDirection: "column", + display: "flex" + } +}; + +//Наименование кастомных карточек +export const P8P_CARD_VARIANT = { + PANEL_INFO: "P8PPanelInfo" +}; + +//Кастомная стилистика компонента +export const P8P_CARD_OVERRIDES = { + styleOverrides: { + root: ({ ownerState }) => { + //Определяем вариант + const variant = ownerState["variant"] || "primary"; + //Возвращаем стили варианта + return P8P_CARDS[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_container_variants.js b/app/theme/variants/p8p_container_variants.js new file mode 100644 index 0000000..af17825 --- /dev/null +++ b/app/theme/variants/p8p_container_variants.js @@ -0,0 +1,48 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики Container +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { P8P_COMPONENT_WIDTH } from "../styles/common"; //Стили - общие + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные контейнеры +export const P8P_CONTAINERS = { + primary: {}, + P8PTableMoreButton: { + ...P8P_COMPONENT_WIDTH({ width: "100%" }), + //width: "100%", + textAlign: "center", + padding: "5px" + }, + P8PInlineMessage: { + ...P8P_COMPONENT_WIDTH({ width: "100%" }), + //width: "100%", + textAlign: "center" + } +}; + +//Наименование кастомных контейнеров +export const P8P_CONTAINER_VARIANT = { + TABLE_MORE_BUTTON: "P8PTableMoreButton", + INLINE_MSG: "P8PInlineMessage" +}; + +//Кастомная стилистика компонента +export const P8P_CONTAINER_OVERRIDES = { + styleOverrides: { + root: ({ ownerState }) => { + //Определяем вариант + const variant = ownerState?.variant || "primary"; + //Возвращаем стили варианта + return P8P_CONTAINERS[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_dialog_content_text_variants.js b/app/theme/variants/p8p_dialog_content_text_variants.js new file mode 100644 index 0000000..335d1e1 --- /dev/null +++ b/app/theme/variants/p8p_dialog_content_text_variants.js @@ -0,0 +1,37 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики DialogContentText +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные диалоги (текстовое содержимое) +export const P8P_DIALOG_CONTENT_TEXTS = theme => ({ + primary: {}, + P8PPrimary: { ...theme.typography.P8PBody1 }, + P8PInfo: { color: theme.palette.P8PText.primary, ...theme.typography.P8PBody1 }, + P8PWarn: { color: theme.palette.P8PWarning.main, ...theme.typography.P8PBody1 }, + P8PError: { color: theme.palette.P8PError.main, ...theme.typography.P8PBody1 } +}); + +//Наименование кастомных диалогов (текстовое содержимое) +export const P8P_DIALOG_CONTENT_TEXT_VARIANT = { + PRIMARY: "P8PPrimary", + INFO: "P8PInfo", + WARN: "P8PWarn", + ERROR: "P8PError" +}; + +//Кастомная стилистика компонента +export const P8P_DIALOG_CONTENT_TEXT_OVERRIDES = { + styleOverrides: { + root: ({ ownerState, theme }) => { + //Определяем вариант + const variant = ownerState["variant"] || "primary"; + //Возвращаем стили варианта + return P8P_DIALOG_CONTENT_TEXTS(theme)[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_dialog_content_variants.js b/app/theme/variants/p8p_dialog_content_variants.js new file mode 100644 index 0000000..2e04b53 --- /dev/null +++ b/app/theme/variants/p8p_dialog_content_variants.js @@ -0,0 +1,48 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики DialogContent +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { APP_STYLES } from "../../../app.styles"; //Типовые стили +import { P8P_COMPONENT_WIDTH } from "../styles/common"; //Стили - общие + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные диалоги (содержимое) +export const P8P_DIALOG_CONTENTS = theme => ({ + primary: {}, + P8PPrimary: { overflow: "auto", ...APP_STYLES.SCROLL }, + P8PTask: { + ...P8P_COMPONENT_WIDTH({ minWidth: 400 }), + //minWidth: 400, + overflowX: "auto" + }, + P8PHint: { ...theme.typography.P8PBody4, color: theme.palette.P8PText.primary }, + P8PHidden: { display: "flex", flexDirection: "column", overflow: "hidden" } +}); + +//Наименование кастомных диалогов (содержимое) +export const P8P_DIALOG_CONTENT_VARIANT = { + PRIMARY: "P8PPrimary", + TASK: "P8PTask", + HINT: "P8PHint", + HIDDEN: "P8PHidden" +}; + +//Кастомная стилистика компонента +export const P8P_DIALOG_CONTENT_OVERRIDES = { + styleOverrides: { + root: ({ ownerState, theme }) => { + //Определяем вариант + const variant = ownerState["variant"] || "primary"; + //Возвращаем стили варианта + return P8P_DIALOG_CONTENTS(theme)[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_dialog_title_variants.js b/app/theme/variants/p8p_dialog_title_variants.js new file mode 100644 index 0000000..8aaecd1 --- /dev/null +++ b/app/theme/variants/p8p_dialog_title_variants.js @@ -0,0 +1,37 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики DialogTitle +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные диалоги (заголовок) +export const P8P_DIALOG_TITLES = theme => ({ + primary: {}, + P8PPrimary: { ...theme.typography.P8PH6, color: theme.palette.P8PText.primary }, + P8PInfo: { ...theme.typography.P8PH6, color: theme.palette.P8PText.primary }, + P8PWarn: { ...theme.typography.P8PH6, color: theme.palette.P8PWarning.main }, + P8PError: { ...theme.typography.P8PH6, color: theme.palette.P8PError.main } +}); + +//Наименование кастомных диалогов (заголовок) +export const P8P_DIALOG_TITLE_VARIANT = { + PRIMARY: "P8PPrimary", + INFO: "P8PInfo", + WARN: "P8PWarn", + ERROR: "P8PError" +}; + +//Кастомная стилистика компонента +export const P8P_DIALOG_TITLE_OVERRIDES = { + styleOverrides: { + root: ({ ownerState, theme }) => { + //Определяем вариант + const variant = ownerState["variant"] || "primary"; + //Возвращаем стили варианта + return P8P_DIALOG_TITLES(theme)[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_drawer_variants.js b/app/theme/variants/p8p_drawer_variants.js new file mode 100644 index 0000000..4c88009 --- /dev/null +++ b/app/theme/variants/p8p_drawer_variants.js @@ -0,0 +1,37 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики Drawer +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { APP_STYLES } from "../../../app.styles"; //Типовые стили + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные выезжающие области +export const P8P_DRAWERS = { + primary: {}, + P8PPrimary: { [`& .MuiDrawer-paper`]: { ...APP_STYLES.SCROLL } } +}; + +//Наименование кастомных выезжающих областей +export const P8P_DRAWER_VARIANT = { + PRIMARY: "P8PPrimary" +}; + +//Кастомная стилистика компонента +export const P8P_DRAWER_OVERRIDES = { + styleOverrides: { + root: ({ ownerState }) => { + //Определяем вариант + const variant = ownerState["data-variant"] || "primary"; + //Возвращаем стили варианта + return P8P_DRAWERS[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_grid_variants.js b/app/theme/variants/p8p_grid_variants.js new file mode 100644 index 0000000..b6110c9 --- /dev/null +++ b/app/theme/variants/p8p_grid_variants.js @@ -0,0 +1,52 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики Grid +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { P8P_COMPONENT_HEIGHT, P8P_COMPONENT_WIDTH } from "../styles/common"; //Стили - общие + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные сетки +export const P8P_GRIDS = { + primary: {}, + P8PPrimary: {}, + P8PSvgContainer: { + ...P8P_COMPONENT_WIDTH({ width: "100%" }), + ...P8P_COMPONENT_HEIGHT({ height: "100%" }) + //width: "100%", + //height: "100%" + }, + P8PPanelsMenu: { + ...P8P_COMPONENT_WIDTH({ maxWidth: 1200 }), + //maxWidth: 1200, + direction: "row", + justifyContent: "left", + alignItems: "stretch" + } +}; + +//Наименование кастомных сеток +export const P8P_GRID_VARIANT = { + PRIMARY: "P8PPrimary", + SVG_CONTAINER: "P8PSvgContainer", + PANELS_MENU: "P8PPanelsMenu" +}; + +//Кастомная стилистика компонента +export const P8P_GRID_OVERRIDES = { + styleOverrides: { + root: ({ ownerState }) => { + //Определяем вариант + const variant = ownerState["variant"] || "primary"; + //Возвращаем стили варианта + return P8P_GRIDS[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_icon_button_variants.js b/app/theme/variants/p8p_icon_button_variants.js new file mode 100644 index 0000000..6bcc9ad --- /dev/null +++ b/app/theme/variants/p8p_icon_button_variants.js @@ -0,0 +1,33 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики IconButton +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные кнопки-иконки +export const P8P_ICON_BUTTONS = { + primary: {}, + P8PAppBarButton: { + marginRight: "16px" + } +}; + +//Наименования кастомных кнопок-иконкок +export const P8P_ICON_BUTTON_VARIANT = { + APP_BAR_BUTTON: "P8PAppBarButton" +}; + +//Кастомная стилистика компонента +export const P8P_ICON_BUTTON_OVERRIDES = { + styleOverrides: { + root: ({ ownerState }) => { + //Определяем вариант + const variant = ownerState?.variant || "primary"; + //Возвращаем стили варианта + return P8P_ICON_BUTTONS[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_icon_variants.js b/app/theme/variants/p8p_icon_variants.js new file mode 100644 index 0000000..f69dae4 --- /dev/null +++ b/app/theme/variants/p8p_icon_variants.js @@ -0,0 +1,51 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики Icon +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { P8P_COMPONENT_HEIGHT, P8P_COMPONENT_WIDTH } from "../styles/common"; //Стили - общие + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные иконки +export const P8P_ICONS = { + primary: {}, + P8PTableColumnMenu: { + marginRight: "10px" + }, + P8PPanelMenuTitle: { + paddingTop: "4px" + }, + P8PDesktopPanel: { + ...P8P_COMPONENT_WIDTH({ width: "48px" }), + ...P8P_COMPONENT_HEIGHT({ height: "48px" }), + // width: "48px", + // height: "48px", + fontSize: "48px" + } +}; + +//Наименование кастомных иконок +export const P8P_ICON_VARIANT = { + TABLE_COLUMN_MENU: "P8PTableColumnMenu", + PANEL_MENU_TITLE: "P8PPanelMenuTitle", + DESKTOP_PANEL: "P8PDesktopPanel" +}; + +//Кастомная стилистика компонента +export const P8P_ICON_OVERRIDES = { + styleOverrides: { + root: ({ ownerState }) => { + //Определяем вариант + const variant = ownerState?.variant || "primary"; + //Возвращаем стили варианта + return P8P_ICONS[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_input_label_variants.js b/app/theme/variants/p8p_input_label_variants.js new file mode 100644 index 0000000..b9a223e --- /dev/null +++ b/app/theme/variants/p8p_input_label_variants.js @@ -0,0 +1,31 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики InputLabel +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные метки ввода +export const P8P_INPUT_LABELS = theme => ({ + primary: {}, + P8PPrimary: { ...theme.typography.P8PInputLabel } +}); + +//Наименование кастомных меток ввода +export const P8P_INPUT_LABEL_VARIANT = { + PRIMARY: "P8PPrimary" +}; + +//Кастомная стилистика компонента +export const P8P_INPUT_LABEL_OVERRIDES = { + styleOverrides: { + root: ({ ownerState, theme }) => { + //Определяем вариант + const variant = ownerState["data-variant"] || "primary"; + //Возвращаем стили варианта + return P8P_INPUT_LABELS(theme)[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_input_variants.js b/app/theme/variants/p8p_input_variants.js new file mode 100644 index 0000000..667b8e2 --- /dev/null +++ b/app/theme/variants/p8p_input_variants.js @@ -0,0 +1,40 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики Input +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные поля ввода +export const P8P_INPUTS = theme => ({ + primary: {}, + P8PPrimary: { + "& .MuiOutlinedInput-notchedOutline": { + "& legend": { + fontFamily: theme.typography.P8PFontMontserrat + } + }, + "& .MuiInputBase-input": { + ...theme.typography.P8PBody1 + } + } +}); + +//Наименование кастомных полей ввода +export const P8P_INPUT_VARIANT = { + PRIMARY: "P8PPrimary" +}; + +//Кастомная стилистика компонента +export const P8P_INPUT_OVERRIDES = { + styleOverrides: { + root: ({ ownerState, theme }) => { + //Определяем вариант + const variant = ownerState["variant"] || "primary"; + //Возвращаем стили варианта + return P8P_INPUTS(theme)[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_list_item_text_variants.js b/app/theme/variants/p8p_list_item_text_variants.js new file mode 100644 index 0000000..d27e4c8 --- /dev/null +++ b/app/theme/variants/p8p_list_item_text_variants.js @@ -0,0 +1,38 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики ListItemText +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные значения списков +export const P8P_LIST_ITEM_TEXTS = theme => ({ + primary: {}, + P8PPrimary: { + "& .MuiListItemText-primary": { + ...theme.typography.P8PBody1 + }, + "& .MuiListItemText-secondary": { + ...theme.typography.P8PBody3 + } + } +}); + +//Наименование кастомных значений списков +export const P8P_LIST_ITEM_TEXT_VARIANT = { + PRIMARY: "P8PPrimary" +}; + +//Кастомная стилистика компонента +export const P8P_LIST_ITEM_TEXT_OVERRIDES = { + styleOverrides: { + root: ({ ownerState, theme }) => { + //Определяем вариант + const variant = ownerState["variant"] || "primary"; + //Возвращаем стили варианта + return P8P_LIST_ITEM_TEXTS(theme)[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_list_variants.js b/app/theme/variants/p8p_list_variants.js new file mode 100644 index 0000000..9cef5a7 --- /dev/null +++ b/app/theme/variants/p8p_list_variants.js @@ -0,0 +1,46 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики List +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { P8P_COMPONENT_WIDTH } from "../styles/common"; //Стили - общие + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные списки +export const P8P_LISTS = { + primary: {}, + P8PGanttTask: { + ...P8P_COMPONENT_WIDTH({ width: "100%", minWidth: 300, maxWidth: 700 }) + //width: "100%", minWidth: 300, maxWidth: 700 + }, + P8PSettings: { + ...P8P_COMPONENT_WIDTH({ width: "510px" }), + //width: "510px", + overflowY: "auto" + } +}; + +//Наименование кастомных списков +export const P8P_LIST_VARIANT = { + GANTT_TASK: "P8PGanttTask", + SETTINGS: "P8PSettings" +}; + +//Кастомная стилистика компонента +export const P8P_LIST_OVERRIDES = { + styleOverrides: { + root: ({ ownerState }) => { + //Определяем вариант + const variant = ownerState["variant"] || "primary"; + //Возвращаем стили варианта + return P8P_LISTS[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_menu_item_variants.js b/app/theme/variants/p8p_menu_item_variants.js new file mode 100644 index 0000000..c69c273 --- /dev/null +++ b/app/theme/variants/p8p_menu_item_variants.js @@ -0,0 +1,31 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики MenuItem +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные элементы меню +export const P8P_MENU_ITEMS = theme => ({ + primary: {}, + P8PPrimary: { ...theme.typography.P8PBody1 } +}); + +//Наименование кастомных элементов меню +export const P8P_MENU_ITEM_VARIANT = { + PRIMARY: "P8PPrimary" +}; + +//Кастомная стилистика компонента +export const P8P_MENU_ITEM_OVERRIDES = { + styleOverrides: { + root: ({ ownerState, theme }) => { + //Определяем вариант + const variant = ownerState["variant"] || "primary"; + //Возвращаем стили варианта + return P8P_MENU_ITEMS(theme)[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_pagination_variants.js b/app/theme/variants/p8p_pagination_variants.js new file mode 100644 index 0000000..70fa702 --- /dev/null +++ b/app/theme/variants/p8p_pagination_variants.js @@ -0,0 +1,51 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики Pagination +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import { P8P_TABLE_PAGINATOR_ALIGN, P8P_TABLE_PAGINATOR_POSITION } from "../../components/p8p_table/p8p_table_constants"; //Константы таблиц + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные пагинаторы +export const P8P_PAGINATIONS = ({ theme, pagesAlign, position }) => ({ + primary: {}, + P8PTablePagination: { + display: "flex", + justifyContent: + pagesAlign === P8P_TABLE_PAGINATOR_ALIGN.LEFT + ? "flex-start" + : pagesAlign === P8P_TABLE_PAGINATOR_ALIGN.CENTER + ? "space-around" + : "flex-end", + ...(position === P8P_TABLE_PAGINATOR_POSITION.TOP ? { paddingBottom: "10px" } : { paddingTop: "10px" }), + "& .MuiPaginationItem-root": { + ...theme.typography.P8PBody2 + } + } +}); + +//Наименование кастомных пагинаторов +export const P8P_PAGINATION_VARIANT = { + TABLE_PAGINATION: "P8PTablePagination" +}; + +//Кастомная стилистика компонента +export const P8P_PAGINATION_OVERRIDES = { + styleOverrides: { + root: ({ ownerState, theme }) => { + //Определяем вариант + const variant = ownerState?.variant || "primary"; + //Определяем доп. параметры + const { pagesAlign, position } = ownerState["data-variant-props"] || {}; + //Возвращаем стили варианта + return P8P_PAGINATIONS({ theme, pagesAlign, position })[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_select_variants.js b/app/theme/variants/p8p_select_variants.js new file mode 100644 index 0000000..9d35712 --- /dev/null +++ b/app/theme/variants/p8p_select_variants.js @@ -0,0 +1,38 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики Select +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные поля выбора +export const P8P_SELECTS = theme => ({ + primary: {}, + P8PPrimary: { + "& .MuiOutlinedInput-notchedOutline": { + "& legend": { + fontFamily: theme.typography.P8PFontMontserrat + } + }, + "& .MuiSelect-select": { ...theme.typography.P8PBody1 } + } +}); + +//Наименование кастомных полей выбора +export const P8P_SELECT_VARIANT = { + PRIMARY: "P8PPrimary" +}; + +//Кастомная стилистика компонента +export const P8P_SELECT_OVERRIDES = { + styleOverrides: { + root: ({ ownerState, theme }) => { + //Определяем вариант + const variant = ownerState["data-variant"] || "primary"; + //Возвращаем стили варианта + return P8P_SELECTS(theme)[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_table_cell_variants.js b/app/theme/variants/p8p_table_cell_variants.js new file mode 100644 index 0000000..7120506 --- /dev/null +++ b/app/theme/variants/p8p_table_cell_variants.js @@ -0,0 +1,93 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики TableCell +*/ + +//--------- +//Константы +//--------- + +//Общие стили +const STYLES = { + CELL_WIDTH: width => ({ + ...(width ? { minWidth: width, maxWidth: width } : {}) + }), + CELL_DEF: (fixed, theme, left, isHeader = false) => ({ + ...(fixed + ? { + position: "sticky", + left, + zIndex: isHeader ? 1000 : 500 + } + : {}) + }) +}; + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные ячейки таблицы +export const P8P_TABLE_CELLS = ({ theme, width, fixed, left }) => ({ + primary: {}, + P8PGroupHeader: { + ...theme.typography.P8PBody2, + ...STYLES.CELL_WIDTH(width), + ...(fixed ? { position: "sticky", left: 0 } : {}) + }, + P8PHeaderExpand: { + ...theme.typography.P8PColumn, + ...STYLES.CELL_WIDTH("60px"), + ...STYLES.CELL_DEF(fixed, theme, 0, true), + backgroundColor: theme.palette.P8PBackground.tableHeader + }, + P8PHeaderCell: { + ...theme.typography.P8PColumn, + ...STYLES.CELL_WIDTH(width), + ...STYLES.CELL_DEF(fixed, theme, left, true), + backgroundColor: theme.palette.P8PBackground.tableHeader + }, + P8PExpand: { + ...theme.typography.P8PBody2, + ...STYLES.CELL_WIDTH("60px"), + ...STYLES.CELL_DEF(fixed, theme, 0), + backgroundColor: "inherit" + }, + P8PCell: { + ...theme.typography.P8PBody2, + ...STYLES.CELL_WIDTH(width), + ...STYLES.CELL_DEF(fixed, theme, left), + backgroundColor: "inherit" + }, + P8PExpandContainer: { + paddingBottom: 0, + paddingTop: 0, + paddingLeft: 0, + paddingRight: 0, + ...STYLES.CELL_DEF(fixed, theme, left) + } +}); + +//Наименование кастомных ячеек таблиц +export const P8P_TABLE_CELL_VARIANT = { + GROUP_HEADER: "P8PGroupHeader", + HEADER_EXPAND: "P8PHeaderExpand", + HEADER_CELL: "P8PHeaderCell", + EXPAND: "P8PExpand", + CELL: "P8PCell", + EXPAND_CONTAINER: "P8PExpandContainer" +}; + +//Кастомная стилистика компонента +export const P8P_TABLE_CELL_OVERRIDES = { + styleOverrides: { + root: ({ ownerState, theme }) => { + //Определяем вариант + const variant = ownerState?.variant || "primary"; + //Определяем доп. параметры + const { width, fixed, left } = ownerState["data-variant-props"] || {}; + //Возвращаем стили варианта + return P8P_TABLE_CELLS({ theme, width, fixed, left })[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_table_head_variants.js b/app/theme/variants/p8p_table_head_variants.js new file mode 100644 index 0000000..15fe169 --- /dev/null +++ b/app/theme/variants/p8p_table_head_variants.js @@ -0,0 +1,35 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики TableHead +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные заголовки таблицы +export const P8P_TABLE_HEADS = { + primary: {}, + P8PSticky: { + position: "sticky", + top: 0, + zIndex: 1000 + } +}; + +//Наименование кастомных заголовков таблиц +export const P8P_TABLE_HEAD_VARIANT = { + STICKY: "P8PSticky" +}; + +//Кастомная стилистика компонента +export const P8P_TABLE_HEAD_OVERRIDES = { + styleOverrides: { + root: ({ ownerState }) => { + //Определяем вариант + const variant = ownerState?.variant || "primary"; + //Возвращаем стили варианта + return P8P_TABLE_HEADS[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_table_row_variants.js b/app/theme/variants/p8p_table_row_variants.js new file mode 100644 index 0000000..cfb6780 --- /dev/null +++ b/app/theme/variants/p8p_table_row_variants.js @@ -0,0 +1,33 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики TableRow +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные строки таблицы +export const P8P_TABLE_ROWS = { + primary: {}, + P8PPrimary: { + "&:last-child td, &:last-child th": { border: 0 } + } +}; + +//Наименование кастомных строк таблиц +export const P8P_TABLE_ROW_VARIANT = { + PRIMARY: "P8PPrimary" +}; + +//Кастомная стилистика компонента +export const P8P_TABLE_ROW_OVERRIDES = { + styleOverrides: { + root: ({ ownerState }) => { + //Определяем вариант + const variant = ownerState?.variant || "primary"; + //Возвращаем стили варианта + return P8P_TABLE_ROWS[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_table_variants.js b/app/theme/variants/p8p_table_variants.js new file mode 100644 index 0000000..06e2b1b --- /dev/null +++ b/app/theme/variants/p8p_table_variants.js @@ -0,0 +1,38 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики Table +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные таблицы +export const P8P_TABLES = theme => ({ + primary: {}, + P8PPrimary: { + ".MuiTableRow-root:nth-of-type(even)": { + backgroundColor: theme.palette.P8PBackground.primary + }, + ".MuiTableRow-root:nth-of-type(odd)": { + backgroundColor: theme.palette.P8PBackground.secondary + } + } +}); + +//Наименование кастомных таблиц +export const P8P_TABLE_VARIANT = { + PRIMARY: "P8PPrimary" +}; + +//Кастомная стилистика компонента +export const P8P_TABLE_OVERRIDES = { + styleOverrides: { + root: ({ ownerState, theme }) => { + //Определяем вариант + const variant = ownerState?.variant || "primary"; + //Возвращаем стили варианта + return P8P_TABLES(theme)[variant]; + } + } +}; diff --git a/app/theme/variants/p8p_text_field_variants.js b/app/theme/variants/p8p_text_field_variants.js new file mode 100644 index 0000000..ee862a0 --- /dev/null +++ b/app/theme/variants/p8p_text_field_variants.js @@ -0,0 +1,56 @@ +/* + Парус 8 - Панели мониторинга + Расширение стилистики TextField +*/ + +//---------------- +//Интерфейс модуля +//---------------- + +//Кастомные поля ввода +export const P8P_TEXT_FIELDS = theme => ({ + primary: {}, + P8PPrimary: { + "& .MuiInputLabel-root": { + ...theme.typography.P8PInputLabel + }, + "& .MuiOutlinedInput-notchedOutline": { + "& legend": { + fontFamily: theme.typography.P8PFontMontserrat + } + }, + "& .MuiInputBase-input": { + ...theme.typography.P8PBody1 + } + } +}); + +//Кастомные списки выбора +export const P8P_TEXT_FIELD_OPTIONS = theme => ({ + primary: {}, + P8PPrimary: { ...theme.typography.P8PBody1 } +}); + +//Наименование кастомных полей ввода +export const P8P_TEXT_FIELD_VARIANT = { + PRIMARY: "P8PPrimary" +}; + +//Кастомная стилистика компонента +export const P8P_TEXT_FIELD_OVERRIDES = { + styleOverrides: { + root: ({ ownerState, theme }) => { + //Определяем вариант + const variant = ownerState["data-variant"] || "primary"; + //Возвращаем стили варианта + return P8P_TEXT_FIELDS(theme)[variant]; + }, + option: ({ ownerState, theme }) => { + //Определяем вариант + const variant = ownerState["data-variant"] || "primary"; + console.log({ option: P8P_TEXT_FIELD_OPTIONS(theme)[variant] }); + //Возвращаем стили варианта + return P8P_TEXT_FIELD_OPTIONS(theme)[variant]; + } + } +};