/* * QQQ - Low-code Application Framework for Engineers. * Copyright (C) 2021-2024. Kingsrook, LLC * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States * contact@kingsrook.com * https://github.com/Kingsrook/ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData"; import {Box, Skeleton} from "@mui/material"; import Card from "@mui/material/Card"; import Modal from "@mui/material/Modal"; import parse from "html-react-parser"; import {BlockData} from "qqq/components/widgets/blocks/BlockModels"; import WidgetBlock from "qqq/components/widgets/WidgetBlock"; import ProcessWidgetBlockUtils from "qqq/pages/processes/ProcessWidgetBlockUtils"; import React, {useEffect, useState} from "react"; export interface CompositeData { blockId: string; blocks: BlockData[]; styleOverrides?: any; layout?: string; overlayHtml?: string; overlayStyleOverrides?: any; modalMode: string; styles?: any; } interface CompositeWidgetProps { widgetMetaData: QWidgetMetaData; data: CompositeData; actionCallback?: (blockData: BlockData, eventValues?: { [name: string]: any }) => boolean; values?: { [key: string]: any }; } /******************************************************************************* ** Widget which is a list of Blocks. *******************************************************************************/ export default function CompositeWidget({widgetMetaData, data, actionCallback, values}: CompositeWidgetProps): JSX.Element { if (!data || !data.blocks) { return (); } //////////////////////////////////////////////////////////////////////////////////// // note - these layouts are defined in qqq in the CompositeWidgetData.Layout enum // //////////////////////////////////////////////////////////////////////////////////// let layout = data?.layout; let boxStyle: any = {}; if (layout == "FLEX_COLUMN") { boxStyle.display = "flex"; boxStyle.flexDirection = "column"; boxStyle.flexWrap = "wrap"; boxStyle.gap = "0.5rem"; } else if (layout == "FLEX_ROW_WRAPPED") { boxStyle.display = "flex"; boxStyle.flexDirection = "row"; boxStyle.flexWrap = "wrap"; boxStyle.gap = "0.5rem"; } else if (layout == "FLEX_ROW") { boxStyle.display = "flex"; boxStyle.flexDirection = "row"; boxStyle.gap = "0.5rem"; } else if (layout == "FLEX_ROW_SPACE_BETWEEN") { boxStyle.display = "flex"; boxStyle.flexDirection = "row"; boxStyle.justifyContent = "space-between"; boxStyle.gap = "0.25rem"; } else if (layout == "FLEX_ROW_CENTER") { boxStyle.display = "flex"; boxStyle.flexDirection = "row"; boxStyle.justifyContent = "center"; boxStyle.gap = "0.25rem"; boxStyle.flexWrap = "wrap"; } else if (layout == "TABLE_SUB_ROW_DETAILS") { boxStyle.display = "flex"; boxStyle.flexDirection = "column"; boxStyle.fontSize = "0.875rem"; boxStyle.fontWeight = 400; boxStyle.borderRight = "1px solid #D0D0D0"; } else if (layout == "BADGES_WRAPPER") { boxStyle.display = "flex"; boxStyle.gap = "0.25rem"; boxStyle.padding = "0 0.25rem"; boxStyle.fontSize = "0.875rem"; boxStyle.fontWeight = 400; boxStyle.border = "1px solid gray"; boxStyle.borderRadius = "0.5rem"; boxStyle.background = "#FFFFFF"; } if (data?.styleOverrides) { boxStyle = {...boxStyle, ...data.styleOverrides}; } if (data.styles?.backgroundColor) { boxStyle.backgroundColor = ProcessWidgetBlockUtils.processColorFromStyleMap(data.styles.backgroundColor); } if (data.styles?.padding) { boxStyle.paddingTop = data.styles?.padding.top + "px" boxStyle.paddingBottom = data.styles?.padding.bottom + "px" boxStyle.paddingLeft = data.styles?.padding.left + "px" boxStyle.paddingRight = data.styles?.padding.right + "px" } let overlayStyle: any = {}; if (data?.overlayStyleOverrides) { overlayStyle = {...overlayStyle, ...data.overlayStyleOverrides}; } const content = ( <> { data?.overlayHtml && {parse(data.overlayHtml)} } { data.blocks.map((block: BlockData, index) => ( )) } ); if (data.modalMode) { const [isModalOpen, setIsModalOpen] = useState(values && (values[data.blockId] == true)); /*************************************************************************** ** ***************************************************************************/ const controlCallback = (newValue: boolean) => { setIsModalOpen(newValue); }; /*************************************************************************** ** ***************************************************************************/ const modalOnClose = (event: object, reason: string) => { values[data.blockId] = false; setIsModalOpen(false); actionCallback({blockTypeName: "BUTTON", values: {}}, {controlCode: `hideModal:${data.blockId}`}); }; ////////////////////////////////////////////////////////////////////////////////////////// // register the control-callback function - so when buttons are clicked, we can be told // ////////////////////////////////////////////////////////////////////////////////////////// useEffect(() => { if (actionCallback) { actionCallback(null, { registerControlCallbackName: data.blockId, registerControlCallbackFunction: controlCallback }); } }, []); return ( {content} ); } else { return content; } }