diff --git a/src/qqq/components/widgets/WidgetBlock.tsx b/src/qqq/components/widgets/WidgetBlock.tsx
index fab8f6d..1115741 100644
--- a/src/qqq/components/widgets/WidgetBlock.tsx
+++ b/src/qqq/components/widgets/WidgetBlock.tsx
@@ -22,6 +22,9 @@
import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData";
import {Alert, Skeleton} from "@mui/material";
+import ActionButtonBlock from "qqq/components/widgets/blocks/ActionButtonBlock";
+import AudioBlock from "qqq/components/widgets/blocks/AudioBlock";
+import InputFieldBlock from "qqq/components/widgets/blocks/InputFieldBlock";
import React from "react";
import BigNumberBlock from "qqq/components/widgets/blocks/BigNumberBlock";
import {BlockData} from "qqq/components/widgets/blocks/BlockModels";
@@ -32,19 +35,21 @@ import TableSubRowDetailRowBlock from "qqq/components/widgets/blocks/TableSubRow
import TextBlock from "qqq/components/widgets/blocks/TextBlock";
import UpOrDownNumberBlock from "qqq/components/widgets/blocks/UpOrDownNumberBlock";
import CompositeWidget from "qqq/components/widgets/CompositeWidget";
+import ImageBlock from "./blocks/ImageBlock";
interface WidgetBlockProps
{
widgetMetaData: QWidgetMetaData;
block: BlockData;
+ actionCallback?: (blockData: BlockData) => boolean;
}
/*******************************************************************************
** Component to render a single Block in the widget framework!
*******************************************************************************/
-export default function WidgetBlock({widgetMetaData, block}: WidgetBlockProps): JSX.Element
+export default function WidgetBlock({widgetMetaData, block, actionCallback}: WidgetBlockProps): JSX.Element
{
if(!block)
{
@@ -64,7 +69,7 @@ export default function WidgetBlock({widgetMetaData, block}: WidgetBlockProps):
if(block.blockTypeName == "COMPOSITE")
{
// @ts-ignore - special case for composite type block...
- return ();
+ return ();
}
switch(block.blockTypeName)
@@ -83,6 +88,14 @@ export default function WidgetBlock({widgetMetaData, block}: WidgetBlockProps):
return ();
case "BIG_NUMBER":
return ();
+ case "INPUT_FIELD":
+ return ();
+ case "ACTION_BUTTON":
+ return ();
+ case "AUDIO":
+ return ();
+ case "IMAGE":
+ return ();
default:
return (Unsupported block type: {block.blockTypeName})
}
diff --git a/src/qqq/components/widgets/blocks/ActionButtonBlock.tsx b/src/qqq/components/widgets/blocks/ActionButtonBlock.tsx
new file mode 100644
index 0000000..8b0e2ec
--- /dev/null
+++ b/src/qqq/components/widgets/blocks/ActionButtonBlock.tsx
@@ -0,0 +1,60 @@
+/*
+ * 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 Box from "@mui/material/Box";
+import Icon from "@mui/material/Icon";
+import {standardWidth} from "qqq/components/buttons/DefaultButtons";
+import MDButton from "qqq/components/legacy/MDButton";
+import BlockElementWrapper from "qqq/components/widgets/blocks/BlockElementWrapper";
+import {StandardBlockComponentProps} from "qqq/components/widgets/blocks/BlockModels";
+import React from "react";
+
+
+/*******************************************************************************
+ ** Block that renders ... an action button...
+ **
+ *******************************************************************************/
+export default function ActionButtonBlock({widgetMetaData, data, actionCallback}: StandardBlockComponentProps): JSX.Element
+{
+ const icon = data.values.iconName ? {data.values.iconName} : null;
+
+ function onClick()
+ {
+ if(actionCallback)
+ {
+ actionCallback(data, {actionCode: data.values?.actionCode})
+ }
+ else
+ {
+ console.log("ActionButtonBlock onClick with no actionCallback present, so, noop");
+ }
+ }
+
+ return (
+
+
+
+ {data.values.label ?? "Action"}
+
+
+
+ );
+}
diff --git a/src/qqq/components/widgets/blocks/AudioBlock.tsx b/src/qqq/components/widgets/blocks/AudioBlock.tsx
new file mode 100644
index 0000000..210eaaf
--- /dev/null
+++ b/src/qqq/components/widgets/blocks/AudioBlock.tsx
@@ -0,0 +1,40 @@
+/*
+ * 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 Box from "@mui/material/Box";
+import BlockElementWrapper from "qqq/components/widgets/blocks/BlockElementWrapper";
+import {StandardBlockComponentProps} from "qqq/components/widgets/blocks/BlockModels";
+import DumpJsonBox from "qqq/utils/DumpJsonBox";
+import React from "react";
+
+/*******************************************************************************
+ ** Block that renders ... an audio tag
+ **
+ **
+ *******************************************************************************/
+export default function AudioBlock({widgetMetaData, data}: StandardBlockComponentProps): JSX.Element
+{
+ return (
+
+
+
+ );
+}
diff --git a/src/qqq/components/widgets/blocks/ImageBlock.tsx b/src/qqq/components/widgets/blocks/ImageBlock.tsx
new file mode 100644
index 0000000..d6e60c5
--- /dev/null
+++ b/src/qqq/components/widgets/blocks/ImageBlock.tsx
@@ -0,0 +1,59 @@
+/*
+ * 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 Box from "@mui/material/Box";
+import BlockElementWrapper from "qqq/components/widgets/blocks/BlockElementWrapper";
+import {StandardBlockComponentProps} from "qqq/components/widgets/blocks/BlockModels";
+import DumpJsonBox from "qqq/utils/DumpJsonBox";
+import React from "react";
+
+/*******************************************************************************
+ ** Block that renders ... an image tag
+ **
+ **
+ *******************************************************************************/
+export default function AudioBlock({widgetMetaData, data}: StandardBlockComponentProps): JSX.Element
+{
+ let imageStyle: any = {};
+
+ if(data.styles?.width)
+ {
+ imageStyle.width = data.styles?.width;
+ }
+
+ if(data.styles?.height)
+ {
+ imageStyle.height = data.styles?.height;
+ }
+
+ if(data.styles?.bordered)
+ {
+ imageStyle.border = "1px solid #C0C0C0";
+ imageStyle.borderRadius = "0.5rem";
+ }
+
+
+ return (
+
+
+
+ );
+}
diff --git a/src/qqq/components/widgets/blocks/InputFieldBlock.tsx b/src/qqq/components/widgets/blocks/InputFieldBlock.tsx
new file mode 100644
index 0000000..8c16166
--- /dev/null
+++ b/src/qqq/components/widgets/blocks/InputFieldBlock.tsx
@@ -0,0 +1,128 @@
+/*
+ * 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 {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
+import Box from "@mui/material/Box";
+import QDynamicFormField from "qqq/components/forms/DynamicFormField";
+import DynamicFormUtils from "qqq/components/forms/DynamicFormUtils";
+import BlockElementWrapper from "qqq/components/widgets/blocks/BlockElementWrapper";
+import {StandardBlockComponentProps} from "qqq/components/widgets/blocks/BlockModels";
+import React, {SyntheticEvent, useState} from "react";
+
+/*******************************************************************************
+ ** Block that renders ... a text input
+ **
+ *******************************************************************************/
+export default function InputFieldBlock({widgetMetaData, data, actionCallback}: StandardBlockComponentProps): JSX.Element
+{
+ const [blurCount, setBlurCount] = useState(0)
+
+ const fieldMetaData = new QFieldMetaData(data.values.fieldMetaData);
+ const dynamicField = DynamicFormUtils.getDynamicField(fieldMetaData);
+
+ let autoFocus = data.values.autoFocus as boolean
+ let value = data.values.value;
+ if(value == null || value == undefined)
+ {
+ value = "";
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // for an autoFocus field... //
+ // we're finding that if we blur it when clicking an action button, that //
+ // an un-desirable "now it's been touched, so show an error" happens. //
+ // so let us remove the default blur handler, for the first (auto) focus/blur //
+ // cycle, and we seem to have a better time. //
+ ////////////////////////////////////////////////////////////////////////////////
+ let onBlurRest: {onBlur?: any} = {}
+ if(autoFocus && blurCount == 0)
+ {
+ onBlurRest.onBlur = (event: React.SyntheticEvent) =>
+ {
+ event.stopPropagation();
+ event.preventDefault();
+ setBlurCount(blurCount + 1);
+ }
+ }
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ function eventHandler(event: KeyboardEvent)
+ {
+ if(data.values.submitOnEnter && event.key == "Enter")
+ {
+ // @ts-ignore target.value...
+ const inputValue = event.target.value?.trim()
+
+ // todo - make this behavior opt-in for inputBlocks?
+ if(inputValue && `${inputValue}`.startsWith("->"))
+ {
+ const actionCode = inputValue.substring(2);
+ if(actionCallback)
+ {
+ actionCallback(data, {actionCode: actionCode, _fieldToClearIfError: fieldMetaData.name});
+
+ ///////////////////////////////////////////////////////
+ // return, so we don't submit the actionCode as text //
+ ///////////////////////////////////////////////////////
+ return;
+ }
+ }
+
+ if(fieldMetaData.isRequired && inputValue == "")
+ {
+ console.log("input field is required, but missing value, so not submitting");
+ return;
+ }
+
+ if(actionCallback)
+ {
+ console.log("InputFieldBlock calling actionCallback for submitOnEnter");
+
+ let values: {[name: string]: any} = {};
+ values[fieldMetaData.name] = inputValue;
+
+ actionCallback(data, values);
+ }
+ else
+ {
+ console.log("InputFieldBlock was set as submitOnEnter, but no actionCallback was present, so, noop");
+ }
+ }
+ }
+
+ const labelElement =
+
+
+
+ return (
+
+
+ <>
+ {labelElement}
+
+ >
+
+
+ );
+}
diff --git a/src/qqq/components/widgets/blocks/TextBlock.tsx b/src/qqq/components/widgets/blocks/TextBlock.tsx
index 55bb73e..2790918 100644
--- a/src/qqq/components/widgets/blocks/TextBlock.tsx
+++ b/src/qqq/components/widgets/blocks/TextBlock.tsx
@@ -19,8 +19,10 @@
* along with this program. If not, see .
*/
+import Box from "@mui/material/Box";
import BlockElementWrapper from "qqq/components/widgets/blocks/BlockElementWrapper";
import {StandardBlockComponentProps} from "qqq/components/widgets/blocks/BlockModels";
+import DumpJsonBox from "qqq/utils/DumpJsonBox";
/*******************************************************************************
** Block that renders ... just some text.
@@ -29,9 +31,54 @@ import {StandardBlockComponentProps} from "qqq/components/widgets/blocks/BlockMo
*******************************************************************************/
export default function TextBlock({widgetMetaData, data}: StandardBlockComponentProps): JSX.Element
{
+ let color = "rgba(0, 0, 0, 0.87)";
+ if (data.styles?.standardColor)
+ {
+ switch (data.styles?.standardColor)
+ {
+ case "SUCCESS":
+ color = "#2BA83F";
+ break;
+ case "WARNING":
+ color = "#FBA132";
+ break;
+ case "ERROR":
+ color = "#FB4141";
+ break;
+ case "INFO":
+ color = "#458CFF";
+ break;
+ case "MUTED":
+ color = "#7b809a";
+ break;
+ }
+ }
+
+ let boxStyle = {};
+ if (data.styles?.isAlert)
+ {
+ boxStyle =
+ {
+ border: `1px solid ${color}`,
+ background: `${color}40`,
+ padding: "0.5rem",
+ borderRadius: "0.5rem",
+ };
+ }
+
+ const text = data.values.interpolatedText ?? data.values.text;
+ const lines = text.split("\n");
+
return (
- {data.values.text}
+
+
+ {lines.map((line: string, index: number) =>
+ (
+