Merged main into feature/select-text-on-child-tables

This commit is contained in:
2023-12-18 10:37:08 -06:00
24 changed files with 708 additions and 315 deletions

View File

@ -29,8 +29,8 @@ import React, {SyntheticEvent} from "react";
import colors from "qqq/assets/theme/base/colors";
const AntSwitch = styled(Switch)(({theme}) => ({
width: 28,
height: 16,
width: 32,
height: 20,
padding: 0,
display: "flex",
"&:active": {
@ -54,18 +54,19 @@ const AntSwitch = styled(Switch)(({theme}) => ({
},
"& .MuiSwitch-thumb": {
boxShadow: "0 2px 4px 0 rgb(0 35 11 / 20%)",
width: 12,
height: 12,
borderRadius: 6,
width: 16,
height: 16,
borderRadius: 8,
transition: theme.transitions.create([ "width" ], {
duration: 200,
}),
},
"&.nullSwitch .MuiSwitch-thumb": {
width: 24,
width: 28,
},
"& .MuiSwitch-track": {
borderRadius: 16 / 2,
height: 20,
borderRadius: 20 / 2,
opacity: 1,
backgroundColor:
theme.palette.mode === "dark" ? "rgba(255,255,255,.35)" : "rgba(0,0,0,.25)",
@ -106,9 +107,9 @@ function BooleanFieldSwitch({name, label, value, isDisabled}: Props) : JSX.Eleme
return (
<Box bgcolor={isDisabled ? colors.grey[200] : ""}>
<InputLabel shrink={true}>{label}</InputLabel>
<Stack direction="row" spacing={1} alignItems="center">
<Stack direction="row" spacing={1} alignItems="center" height="37px">
<Typography
fontSize="0.875rem"
fontSize="1rem"
color={value === false ? "auto" : "#bfbfbf" }
onClick={(e) => setSwitch(e, false)}
sx={{cursor: value === false || isDisabled ? "inherit" : "pointer"}}>
@ -116,7 +117,7 @@ function BooleanFieldSwitch({name, label, value, isDisabled}: Props) : JSX.Eleme
</Typography>
<AntSwitch className={classNullSwitch} name={name} checked={value} onClick={toggleSwitch} disabled={isDisabled} />
<Typography
fontSize="0.875rem"
fontSize="1rem"
color={value === true ? "auto" : "#bfbfbf"}
onClick={(e) => setSwitch(e, true)}
sx={{cursor: value === true || isDisabled ? "inherit" : "pointer"}}>

View File

@ -32,6 +32,7 @@ import React, {useState} from "react";
import QDynamicFormField from "qqq/components/forms/DynamicFormField";
import DynamicSelect from "qqq/components/forms/DynamicSelect";
import MDTypography from "qqq/components/legacy/MDTypography";
import HelpContent from "qqq/components/misc/HelpContent";
import ValueUtils from "qqq/utils/qqq/ValueUtils";
interface Props
@ -41,16 +42,13 @@ interface Props
bulkEditMode?: boolean;
bulkEditSwitchChangeHandler?: any;
record?: QRecord;
helpRoles?: string[];
helpContentKeyPrefix?: string;
}
function QDynamicForm(props: Props): JSX.Element
function QDynamicForm({formData, formLabel, bulkEditMode, bulkEditSwitchChangeHandler, record, helpRoles, helpContentKeyPrefix}: Props): JSX.Element
{
const {
formData, formLabel, bulkEditMode, bulkEditSwitchChangeHandler,
} = props;
const {
formFields, values, errors, touched,
} = formData;
const {formFields, values, errors, touched} = formData;
const formikProps = useFormikContext();
const [fileName, setFileName] = useState(null as string);
@ -70,8 +68,8 @@ function QDynamicForm(props: Props): JSX.Element
{
setFileName(null);
formikProps.setFieldValue(fieldName, null);
props.record?.values.delete(fieldName)
props.record?.displayValues.delete(fieldName)
record?.values.delete(fieldName)
record?.displayValues.delete(fieldName)
};
const bulkEditSwitchChanged = (name: string, value: boolean) =>
@ -79,6 +77,7 @@ function QDynamicForm(props: Props): JSX.Element
bulkEditSwitchChangeHandler(name, value);
};
return (
<Box>
<Box lineHeight={0}>
@ -96,29 +95,38 @@ function QDynamicForm(props: Props): JSX.Element
&& Object.keys(formFields).map((fieldName: any) =>
{
const field = formFields[fieldName];
if (field.omitFromQDynamicForm)
{
return null;
}
if (values[fieldName] === undefined)
{
values[fieldName] = "";
}
if (field.omitFromQDynamicForm)
let formattedHelpContent = <HelpContent helpContents={field.fieldMetaData.helpContents} roles={helpRoles} helpContentKey={`${helpContentKeyPrefix ?? ""}field:${fieldName}`} />;
if(formattedHelpContent)
{
return null;
formattedHelpContent = <Box color="#757575" fontSize="0.875rem" mt="-0.25rem">{formattedHelpContent}</Box>
}
const labelElement = <Box fontSize="1rem" fontWeight="500" marginBottom="0.25rem">
<label htmlFor={field.name}>{field.label}</label>
</Box>
if (field.type === "file")
{
const pseudoField = new QFieldMetaData({name: fieldName, type: QFieldType.BLOB});
return (
<Grid item xs={12} sm={6} key={fieldName}>
<Box mb={1.5}>
<InputLabel shrink={true}>{field.label}</InputLabel>
{labelElement}
{
props.record && props.record.values.get(fieldName) && <Box fontSize="0.875rem" pb={1}>
record && record.values.get(fieldName) && <Box fontSize="0.875rem" pb={1}>
Current File:
<Box display="inline-flex" pl={1}>
{ValueUtils.getDisplayValue(pseudoField, props.record, "view")}
{ValueUtils.getDisplayValue(pseudoField, record, "view")}
<Tooltip placement="bottom" title="Remove current file">
<Icon className="blobIcon" fontSize="small" onClick={(e) => removeFile(fieldName)}>delete</Icon>
</Tooltip>
@ -162,18 +170,20 @@ function QDynamicForm(props: Props): JSX.Element
return (
<Grid item xs={12} sm={6} key={fieldName}>
{labelElement}
<DynamicSelect
tableName={field.possibleValueProps.tableName}
processName={field.possibleValueProps.processName}
fieldName={fieldName}
isEditable={field.isEditable}
fieldLabel={field.label}
fieldLabel=""
initialValue={values[fieldName]}
initialDisplayValue={field.possibleValueProps.initialDisplayValue}
bulkEditMode={bulkEditMode}
bulkEditSwitchChangeHandler={bulkEditSwitchChanged}
otherValues={otherValuesMap}
/>
{formattedHelpContent}
</Grid>
);
}
@ -182,9 +192,11 @@ function QDynamicForm(props: Props): JSX.Element
// todo? placeholder={password.placeholder}
return (
<Grid item xs={12} sm={6} key={fieldName}>
{labelElement}
<QDynamicFormField
id={field.name}
type={field.type}
label={field.label}
label=""
isEditable={field.isEditable}
name={fieldName}
displayFormat={field.displayFormat}
@ -195,6 +207,7 @@ function QDynamicForm(props: Props): JSX.Element
success={`${values[fieldName]}` !== "" && !errors[fieldName] && touched[fieldName]}
formFieldObject={field}
/>
{formattedHelpContent}
</Grid>
);
})}
@ -207,6 +220,7 @@ function QDynamicForm(props: Props): JSX.Element
QDynamicForm.defaultProps = {
formLabel: undefined,
bulkEditMode: false,
helpRoles: ["ALL_SCREENS"],
bulkEditSwitchChangeHandler: () =>
{
},

View File

@ -25,6 +25,7 @@ import Switch from "@mui/material/Switch";
import {ErrorMessage, Field, useFormikContext} from "formik";
import React, {useState} from "react";
import AceEditor from "react-ace";
import colors from "qqq/assets/theme/base/colors";
import BooleanFieldSwitch from "qqq/components/forms/BooleanFieldSwitch";
import MDInput from "qqq/components/legacy/MDInput";
import MDTypography from "qqq/components/legacy/MDTypography";
@ -52,6 +53,7 @@ function QDynamicFormField({
{
const [switchChecked, setSwitchChecked] = useState(false);
const [isDisabled, setIsDisabled] = useState(!isEditable || bulkEditMode);
const {inputBorderColor} = colors;
const {setFieldValue} = useFormikContext();
@ -122,7 +124,7 @@ function QDynamicFormField({
width="100%"
height="300px"
value={value}
style={{border: "1px solid gray"}}
style={{border: `1px solid ${inputBorderColor}`, borderRadius: "0.75rem"}}
/>
</>
);
@ -131,7 +133,7 @@ function QDynamicFormField({
{
field = (
<>
<Field {...rest} onWheel={handleOnWheel} name={name} type={type} as={MDInput} variant="standard" label={label} InputLabelProps={inputLabelProps} InputProps={inputProps} fullWidth disabled={isDisabled}
<Field {...rest} onWheel={handleOnWheel} name={name} type={type} as={MDInput} variant="outlined" label={label} InputLabelProps={inputLabelProps} InputProps={inputProps} fullWidth disabled={isDisabled}
onKeyPress={(e: any) =>
{
if (e.key === "Enter")
@ -171,6 +173,14 @@ function QDynamicFormField({
id={`bulkEditSwitch-${name}`}
checked={switchChecked}
onClick={bulkEditSwitchChanged}
sx={{top: "-4px",
"& .MuiSwitch-track": {
height: 20,
borderRadius: 10,
top: -3,
position: "relative"
}
}}
/>
</Box>
<Box width="100%" sx={{background: (type == "checkbox" && isDisabled) ? "#f0f2f5!important" : "initial"}}>

View File

@ -89,6 +89,7 @@ class DynamicFormUtils
label += field.isRequired ? " *" : "";
return ({
fieldMetaData: field,
name: field.name,
label: label,
isRequired: field.isRequired,

View File

@ -29,6 +29,7 @@ import Switch from "@mui/material/Switch";
import TextField from "@mui/material/TextField";
import {ErrorMessage, useFormikContext} from "formik";
import React, {useEffect, useState} from "react";
import colors from "qqq/assets/theme/base/colors";
import MDTypography from "qqq/components/legacy/MDTypography";
import Client from "qqq/utils/qqq/Client";
@ -49,6 +50,7 @@ interface Props
bulkEditMode?: boolean;
bulkEditSwitchChangeHandler?: any;
otherValues?: Map<string, any>;
variant: "standard" | "outlined";
}
DynamicSelect.defaultProps = {
@ -63,6 +65,7 @@ DynamicSelect.defaultProps = {
isMultiple: false,
bulkEditMode: false,
otherValues: new Map<string, any>(),
variant: "outlined",
bulkEditSwitchChangeHandler: () =>
{
},
@ -70,12 +73,13 @@ DynamicSelect.defaultProps = {
const qController = Client.getInstance();
function DynamicSelect({tableName, processName, fieldName, overrideId, fieldLabel, inForm, initialValue, initialDisplayValue, initialValues, onChange, isEditable, isMultiple, bulkEditMode, bulkEditSwitchChangeHandler, otherValues}: Props)
function DynamicSelect({tableName, processName, fieldName, overrideId, fieldLabel, inForm, initialValue, initialDisplayValue, initialValues, onChange, isEditable, isMultiple, bulkEditMode, bulkEditSwitchChangeHandler, otherValues, variant}: Props)
{
const [open, setOpen] = useState(false);
const [options, setOptions] = useState<readonly QPossibleValue[]>([]);
const [searchTerm, setSearchTerm] = useState(null);
const [firstRender, setFirstRender] = useState(true);
const {inputBorderColor} = colors;
////////////////////////////////////////////////////////////////////////////////////////////////
// default value - needs to be an array (from initialValues (array) prop) for multiple mode - //
@ -230,7 +234,7 @@ function DynamicSelect({tableName, processName, fieldName, overrideId, fieldLabe
// attributes. so, doing this, w/ key=id, seemed to fix it. //
///////////////////////////////////////////////////////////////////////////////////////////////
return (
<li {...props} key={option.id}>
<li {...props} key={option.id} style={{fontSize: "1rem"}}>
{content}
</li>
);
@ -244,13 +248,35 @@ function DynamicSelect({tableName, processName, fieldName, overrideId, fieldLabe
bulkEditSwitchChangeHandler(fieldName, newSwitchValue);
};
// console.log(`default value: ${JSON.stringify(defaultValue)}`);
////////////////////////////////////////////
// for outlined style, adjust some styles //
////////////////////////////////////////////
let autocompleteSX = {};
if (variant == "outlined")
{
autocompleteSX = {
"& .MuiOutlinedInput-root": {
borderRadius: "0.75rem",
},
"& .MuiInputBase-root": {
padding: "0.5rem",
background: isDisabled ? "#f0f2f5!important" : "initial",
},
"& .MuiOutlinedInput-root .MuiAutocomplete-input": {
padding: "0",
fontSize: "1rem"
},
"& .Mui-disabled .MuiOutlinedInput-notchedOutline": {
borderColor: inputBorderColor
}
}
}
const autocomplete = (
<Box>
<Autocomplete
id={overrideId ?? fieldName}
sx={{background: isDisabled ? "#f0f2f5!important" : "initial"}}
sx={autocompleteSX}
open={open}
fullWidth
onOpen={() =>
@ -305,7 +331,7 @@ function DynamicSelect({tableName, processName, fieldName, overrideId, fieldLabe
<TextField
{...params}
label={fieldLabel}
variant="standard"
variant={variant}
autoComplete="off"
type="search"
InputProps={{
@ -341,6 +367,14 @@ function DynamicSelect({tableName, processName, fieldName, overrideId, fieldLabe
id={`bulkEditSwitch-${fieldName}`}
checked={switchChecked}
onClick={bulkEditSwitchChanged}
sx={{top: "-4px",
"& .MuiSwitch-track": {
height: 20,
borderRadius: 10,
top: -3,
position: "relative"
}
}}
/>
</Box>
<Box width="100%">

View File

@ -37,10 +37,12 @@ import React, {useContext, useEffect, useReducer, useState} from "react";
import {useLocation, useNavigate, useParams} from "react-router-dom";
import * as Yup from "yup";
import QContext from "QContext";
import colors from "qqq/assets/theme/base/colors";
import {QCancelButton, QSaveButton} from "qqq/components/buttons/DefaultButtons";
import QDynamicForm from "qqq/components/forms/DynamicForm";
import DynamicFormUtils from "qqq/components/forms/DynamicFormUtils";
import MDTypography from "qqq/components/legacy/MDTypography";
import HelpContent from "qqq/components/misc/HelpContent";
import QRecordSidebar from "qqq/components/misc/RecordSidebar";
import HtmlUtils from "qqq/utils/HtmlUtils";
import Client from "qqq/utils/qqq/Client";
@ -79,6 +81,7 @@ function EntityForm(props: Props): JSX.Element
const [validations, setValidations] = useState({});
const [initialValues, setInitialValues] = useState({} as { [key: string]: any });
const [formFields, setFormFields] = useState(null as Map<string, any>);
const [t1section, setT1Section] = useState(null as QTableSection);
const [t1sectionName, setT1SectionName] = useState(null as string);
const [nonT1Sections, setNonT1Sections] = useState([] as QTableSection[]);
@ -151,7 +154,9 @@ function EntityForm(props: Props): JSX.Element
{
return <div>Loading...</div>;
}
return <QDynamicForm formData={formData} record={record} />;
const helpRoles = [props.id ? "EDIT_SCREEN" : "INSERT_SCREEN", "WRITE_SCREENS", "ALL_SCREENS"]
return <QDynamicForm formData={formData} record={record} helpRoles={helpRoles} helpContentKeyPrefix={`table:${tableName};`} />;
}
if (!asyncLoadInited)
@ -330,6 +335,7 @@ function EntityForm(props: Props): JSX.Element
/////////////////////////////////////
const dynamicFormFieldsBySection = new Map<string, any>();
let t1sectionName;
let t1section;
const nonT1Sections: QTableSection[] = [];
for (let i = 0; i < tableSections.length; i++)
{
@ -382,6 +388,7 @@ function EntityForm(props: Props): JSX.Element
if (section.tier === "T1")
{
t1sectionName = section.name;
t1section = section;
}
else
{
@ -389,6 +396,7 @@ function EntityForm(props: Props): JSX.Element
}
}
setT1SectionName(t1sectionName);
setT1Section(t1section);
setNonT1Sections(nonT1Sections);
setFormFields(dynamicFormFieldsBySection);
setValidations(Yup.object().shape(formValidations));
@ -552,6 +560,19 @@ function EntityForm(props: Props): JSX.Element
const formId = props.id != null ? `edit-${tableMetaData?.name}-form` : `create-${tableMetaData?.name}-form`;
let body;
const getSectionHelp = (section: QTableSection) =>
{
const helpRoles = [props.id ? "EDIT_SCREEN" : "INSERT_SCREEN", "WRITE_SCREENS", "ALL_SCREENS"]
const formattedHelpContent = <HelpContent helpContents={section.helpContents} roles={helpRoles} helpContentKey={`table:${tableMetaData.name};section:${section.name}`} />;
return formattedHelpContent && (
<Box px={"1.5rem"} fontSize={"0.875rem"}>
{formattedHelpContent}
</Box>
)
}
if (notAllowedError)
{
body = (
@ -573,23 +594,26 @@ function EntityForm(props: Props): JSX.Element
}
else
{
const cardElevation = props.isModal ? 3 : 1;
const cardElevation = props.isModal ? 3 : 0;
body = (
<Box mb={3}>
<Grid container spacing={3}>
<Grid item xs={12}>
{alertContent ? (
<Box mb={3}>
<Alert severity="error" onClose={() => setAlertContent(null)}>{alertContent}</Alert>
</Box>
) : ("")}
{warningContent ? (
<Box mb={3}>
<Alert severity="warning" onClose={() => setWarningContent(null)}>{warningContent}</Alert>
</Box>
) : ("")}
{
(alertContent || warningContent) &&
<Grid container spacing={3}>
<Grid item xs={12}>
{alertContent ? (
<Box mb={3}>
<Alert severity="error" onClose={() => setAlertContent(null)}>{alertContent}</Alert>
</Box>
) : ("")}
{warningContent ? (
<Box mb={3}>
<Alert severity="warning" onClose={() => setWarningContent(null)}>{warningContent}</Alert>
</Box>
) : ("")}
</Grid>
</Grid>
</Grid>
}
<Grid container spacing={3}>
{
!props.isModal &&
@ -627,10 +651,11 @@ function EntityForm(props: Props): JSX.Element
<MDTypography variant="h5">{formTitle}</MDTypography>
</Box>
</Box>
{t1section && getSectionHelp(t1section)}
{
t1sectionName && formFields ? (
<Box pb={1} px={3}>
<Box p={3} width="100%">
<Box px={3}>
<Box pb={"0.25rem"} width="100%">
{getFormSection(values, touched, formFields.get(t1sectionName), errors)}
</Box>
</Box>
@ -644,8 +669,9 @@ function EntityForm(props: Props): JSX.Element
<MDTypography variant="h6" p={3} pb={1}>
{section.label}
</MDTypography>
{getSectionHelp(section)}
<Box pb={1} px={3}>
<Box p={3} width="100%">
<Box pb={"0.75rem"} width="100%">
{getFormSection(values, touched, formFields.get(section.name), errors)}
</Box>
</Box>

View File

@ -69,7 +69,15 @@ export default styled(TextField)(({theme, ownerState}: { theme?: Theme; ownerSta
});
return {
backgroundColor: disabled ? `${grey[200]} !important` : transparent.main,
"& .MuiInputBase-root": {
backgroundColor: disabled ? `${grey[200]} !important` : transparent.main,
borderRadius: "0.75rem",
},
"& input": {
backgroundColor: `${transparent.main}!important`,
padding: "0.5rem",
fontSize: "1rem",
},
pointerEvents: disabled ? "none" : "auto",
...(error && errorStyles()),
...(success && successStyles()),

View File

@ -0,0 +1,139 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. 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 <https://www.gnu.org/licenses/>.
*/
import {QHelpContent} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QHelpContent";
import Box from "@mui/material/Box";
import parse from "html-react-parser";
import React, {useContext} from "react";
import Markdown from "react-markdown";
import QContext from "QContext";
interface Props
{
helpContents: QHelpContent[];
roles: string[];
heading?: string;
helpContentKey?: string;
}
HelpContent.defaultProps = {};
/*******************************************************************************
** format some content - meaning, change it from string to JSX element(s) or string.
** does a parse() for HTML, and a <Markdown> for markdown, else just text.
*******************************************************************************/
const formatHelpContent = (content: string, format: string): string | JSX.Element | JSX.Element[] =>
{
if (format == "HTML")
{
return parse(content);
}
else if (format == "MARKDOWN")
{
return (<Markdown>{content}</Markdown>)
}
return content;
}
/*******************************************************************************
** return the first help content from the list that matches the first role
** in the roles list.
*******************************************************************************/
const getMatchingHelpContent = (helpContents: QHelpContent[], roles: string[]): QHelpContent =>
{
if (helpContents)
{
if (helpContents.length == 1 && helpContents[0].roles.size == 0)
{
//////////////////////////////////////////////////////////////////////////////////////////////////
// if there's only 1 entry, and it has no roles, then assume user wanted it globally and use it //
//////////////////////////////////////////////////////////////////////////////////////////////////
return (helpContents[0]);
}
else
{
for (let i = 0; i < roles.length; i++)
{
for (let j = 0; j < helpContents.length; j++)
{
if (helpContents[j].roles.has(roles[i]))
{
return(helpContents[j])
}
}
}
}
}
return (null);
}
/*******************************************************************************
** test if a list of help contents would find any matches from a list of roles.
*******************************************************************************/
export const hasHelpContent = (helpContents: QHelpContent[], roles: string[]) =>
{
return getMatchingHelpContent(helpContents, roles) != null;
}
/*******************************************************************************
** component that renders a box of formatted help content, from a list of
** helpContents (from meta-data), and for a list of roles (based on what screen
*******************************************************************************/
function HelpContent({helpContents, roles, heading, helpContentKey}: Props): JSX.Element
{
const {helpHelpActive} = useContext(QContext);
let selectedHelpContent = getMatchingHelpContent(helpContents, roles);
let content = null;
if (helpHelpActive)
{
if (!selectedHelpContent)
{
selectedHelpContent = new QHelpContent({content: ""});
}
content = selectedHelpContent.content + ` [${helpContentKey ?? "?"}]`;
}
else if(selectedHelpContent)
{
content = selectedHelpContent.content;
}
///////////////////////////////////////////////////
// if content was found, format it and return it //
///////////////////////////////////////////////////
if (content)
{
return <Box display="inline" className="helpContent">
{heading && <span className="header">{heading}</span>}
{formatHelpContent(content, selectedHelpContent.format)}
</Box>;
}
return (null);
}
export default HelpContent;

View File

@ -76,7 +76,7 @@ function QRecordSidebar({tableSections, widgetMetaDataList, light, stickyTop}: P
return (
<Card sx={{borderRadius: ({borders: {borderRadius}}) => borderRadius.lg, position: "sticky", top: stickyTop}}>
<Card sx={{borderRadius: "0.75rem", position: "sticky", top: stickyTop, overflow: "auto", maxHeight: "calc(100vh - 200px)"}}>
<Box component="ul" display="flex" flexDirection="column" p={2} m={0} sx={{listStyle: "none"}}>
{
sidebarEntries ? sidebarEntries.map((entry: SidebarEntry, key: number) => (

View File

@ -215,6 +215,7 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
initialDisplayValue={selectedPossibleValue?.label}
inForm={false}
onChange={(value: any) => valueChangeHandler(null, 0, value)}
variant="standard"
/>
</Box>;
case ValueMode.PVS_MULTI:
@ -242,6 +243,7 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
initialValues={initialValues}
inForm={false}
onChange={(value: any) => valueChangeHandler(null, "all", value)}
variant="standard"
/>
</Box>;
}

View File

@ -56,6 +56,7 @@ interface Props
labelAdditionalComponentsLeft: LabelComponent[];
labelAdditionalElementsLeft: JSX.Element[];
labelAdditionalComponentsRight: LabelComponent[];
labelBoxAdditionalSx?: any;
widgetMetaData?: QWidgetMetaData;
widgetData?: WidgetData;
children: JSX.Element;
@ -75,6 +76,7 @@ Widget.defaultProps = {
labelAdditionalComponentsLeft: [],
labelAdditionalElementsLeft: [],
labelAdditionalComponentsRight: [],
labelBoxAdditionalSx: {},
omitPadding: false,
};
@ -174,7 +176,7 @@ export class AddNewRecordButton extends LabelComponent
render = (args: LabelComponentRenderArgs): JSX.Element =>
{
return (
<Typography variant="body2" p={2} pr={0} display="inline" position="relative" top="0.25rem">
<Typography variant="body2" p={2} pr={0} display="inline" position="relative" top="-0.5rem">
<Button sx={{mt: 0.75}} onClick={() => this.openEditForm(args.navigate, this.table, null, this.defaultValues, this.disabledFields)}>{this.label}</Button>
</Typography>
);
@ -552,7 +554,7 @@ function Widget(props: React.PropsWithChildren<Props>): JSX.Element
<Box sx={{width: "100%", height: "100%", minHeight: props.widgetMetaData?.minHeight ? props.widgetMetaData?.minHeight : "initial"}}>
{
needLabelBox &&
<Box display="flex" justifyContent="space-between" alignItems="flex-start" sx={{width: "100%"}} minHeight={"2.5rem"}>
<Box display="flex" justifyContent="space-between" alignItems="flex-start" sx={{width: "100%", ...props.labelBoxAdditionalSx}} minHeight={"2.5rem"}>
<Box>
{
hasPermission ?

View File

@ -138,7 +138,7 @@ function RecordGridWidget({widgetMetaData, data}: Props): JSX.Element
if(data && data.viewAllLink)
{
labelAdditionalElementsLeft.push(
<Typography variant="body2" p={2} display="inline" fontSize=".875rem" pt="0" position="relative" top="-0.25rem">
<Typography variant="body2" p={2} display="inline" fontSize=".875rem" pt="0" position="relative">
<Link to={data.viewAllLink}>View All</Link>
</Typography>
)
@ -178,8 +178,8 @@ function RecordGridWidget({widgetMetaData, data}: Props): JSX.Element
if(widgetMetaData?.showExportButton)
{
labelAdditionalElementsLeft.push(
<Typography key={1} variant="body2" py={2} px={0} display="inline" position="relative" top="-0.25rem">
<Tooltip title={tooltipTitle}><Button sx={{px: 1, py: 0, minWidth: "initial"}} onClick={onExportClick} disabled={isExportDisabled}><Icon sx={{color: "#757575", fontSize: 1.125}}>save_alt</Icon></Button></Tooltip>
<Typography key={1} variant="body2" py={2} px={0} display="inline" position="relative">
<Tooltip title={tooltipTitle}><Button sx={{px: 1, py: 0, minWidth: "initial"}} onClick={onExportClick} disabled={isExportDisabled}><Icon sx={{color: "#757575", fontSize: 1.25}}>save_alt</Icon></Button></Tooltip>
</Typography>
);
}
@ -250,6 +250,7 @@ function RecordGridWidget({widgetMetaData, data}: Props): JSX.Element
widgetData={data}
labelAdditionalElementsLeft={labelAdditionalElementsLeft}
labelAdditionalComponentsRight={labelAdditionalComponentsRight}
labelBoxAdditionalSx={{position: "relative", top: "-0.375rem"}}
>
<Box mx={-2} mb={-3}>
<DataGridPro

View File

@ -86,14 +86,18 @@ function TableWidget(props: Props): JSX.Element
}
const cell = rows[i][columns[j].accessor];
const text = htmlToText(cell,
{
selectors: [
{selector: "a", format: "inline"},
{selector: ".MuiIcon-root", format: "skip"},
{selector: ".button", format: "skip"}
]
});
let text = cell;
if(columns[j].type != "default")
{
text = htmlToText(cell,
{
selectors: [
{selector: "a", format: "inline"},
{selector: ".MuiIcon-root", format: "skip"},
{selector: ".button", format: "skip"}
]
});
}
csv += `"${ValueUtils.cleanForCsv(text)}"`;
}
csv += "\n";