1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-11-10 01:19:53 +01:00
unleash.unleash/frontend/src/component/admin/banners/BannerModal/BannerForm.tsx
Fredrik Strand Oseberg f381718fd6
fix: icon imports (#6499)
Based on this article:
https://mui.com/material-ui/guides/minimizing-bundle-size/ importing
from `'@mui/icons-material'` instead of specifying the actual path to
the icon like `import Delete from '@mui/icons-material/Delete';` can be
up to six time slower. This change changes all named imports in Unleash
referencing the `@mui/icons-material` to default imports.

This reduced the amount of modules we had to process when building the
frontend from 15206 to 4746

Before:
<img width="1016" alt="Skjermbilde 2024-03-11 kl 14 19 58"
src="https://github.com/Unleash/unleash/assets/16081982/f137d24a-6557-4183-a40f-f62a33524520">

After:
<img width="1237" alt="Skjermbilde 2024-03-11 kl 14 20 32"
src="https://github.com/Unleash/unleash/assets/16081982/05a27d6a-2c3f-4409-9862-7188ab4b9c72">

Build time locally decreased by around 50%

Before:
<img width="1504" alt="Skjermbilde 2024-03-11 kl 14 31 45"
src="https://github.com/Unleash/unleash/assets/16081982/bc931559-b022-47ed-9f8f-c87401578518">


After:
<img width="1219" alt="Skjermbilde 2024-03-11 kl 14 27 00"
src="https://github.com/Unleash/unleash/assets/16081982/3c3a8d6b-576d-45c3-aa40-cc5f95d9df2b">
2024-03-12 10:56:10 +01:00

433 lines
17 KiB
TypeScript

import { Button, Checkbox, FormControlLabel, styled } from '@mui/material';
import { Banner } from 'component/banners/Banner/Banner';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { FormSwitch } from 'component/common/FormSwitch/FormSwitch';
import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
import Input from 'component/common/Input/Input';
import { BannerVariant } from 'interfaces/banner';
import { ChangeEvent, Dispatch, SetStateAction, useState } from 'react';
import Visibility from '@mui/icons-material/Visibility';
import { BannerDialog } from 'component/banners/Banner/BannerDialog/BannerDialog';
const StyledForm = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(4),
}));
const StyledBannerPreview = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: theme.spacing(1.5),
gap: theme.spacing(1.5),
border: `1px solid ${theme.palette.divider}`,
borderRadius: theme.shape.borderRadiusMedium,
}));
const StyledBannerPreviewDescription = styled('p')(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
color: theme.palette.text.secondary,
}));
const StyledRaisedSection = styled('div')(({ theme }) => ({
background: theme.palette.background.elevation1,
padding: theme.spacing(2, 3),
borderRadius: theme.shape.borderRadiusLarge,
}));
const StyledSection = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(1.5),
}));
const StyledSectionLabel = styled('p')(({ theme }) => ({
fontWeight: theme.fontWeight.bold,
}));
const StyledFieldGroup = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(1),
}));
const StyledInputDescription = styled('p')(({ theme }) => ({
display: 'flex',
color: theme.palette.text.primary,
}));
const StyledInput = styled(Input)({
width: '100%',
});
const StyledTooltip = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
padding: theme.spacing(0.5),
gap: theme.spacing(0.5),
}));
const StyledSelect = styled(GeneralSelect)(({ theme }) => ({
width: '100%',
maxWidth: theme.spacing(50),
}));
const StyledPreviewButton = styled(Button)(({ theme }) => ({
marginRight: 'auto',
}));
const VARIANT_OPTIONS = [
{ key: 'info', label: 'Information' },
{ key: 'warning', label: 'Warning' },
{ key: 'error', label: 'Error' },
{ key: 'success', label: 'Success' },
];
type IconOption = 'Default' | 'Custom' | 'None';
type LinkOption = 'Link' | 'Dialog' | 'None';
interface IBannerFormProps {
enabled: boolean;
message: string;
variant: BannerVariant;
sticky: boolean;
icon: string;
link: string;
linkText: string;
dialogTitle: string;
dialog: string;
setEnabled: Dispatch<SetStateAction<boolean>>;
setMessage: Dispatch<SetStateAction<string>>;
setVariant: Dispatch<SetStateAction<BannerVariant>>;
setSticky: Dispatch<SetStateAction<boolean>>;
setIcon: Dispatch<SetStateAction<string>>;
setLink: Dispatch<SetStateAction<string>>;
setLinkText: Dispatch<SetStateAction<string>>;
setDialogTitle: Dispatch<SetStateAction<string>>;
setDialog: Dispatch<SetStateAction<string>>;
}
export const BannerForm = ({
enabled,
message,
variant,
sticky,
icon,
link,
linkText,
dialogTitle,
dialog,
setEnabled,
setMessage,
setVariant,
setSticky,
setIcon,
setLink,
setLinkText,
setDialogTitle,
setDialog,
}: IBannerFormProps) => {
const [previewDialogOpen, setPreviewDialogOpen] = useState(false);
const [iconOption, setIconOption] = useState<IconOption>(
icon === '' ? 'Default' : icon === 'none' ? 'None' : 'Custom',
);
const [linkOption, setLinkOption] = useState<LinkOption>(
link === '' ? 'None' : link === 'dialog' ? 'Dialog' : 'Link',
);
return (
<StyledForm>
<StyledBannerPreview>
<StyledBannerPreviewDescription>
Banner preview:
</StyledBannerPreviewDescription>
<Banner
banner={{
message:
message ||
'*No message set. Please enter a message below.*',
variant,
sticky: false,
icon,
link,
linkText,
dialogTitle,
dialog,
}}
inline
/>
</StyledBannerPreview>
<StyledRaisedSection>
<FormSwitch checked={enabled} setChecked={setEnabled}>
Banner status
</FormSwitch>
</StyledRaisedSection>
<StyledSection>
<StyledSectionLabel>Configuration</StyledSectionLabel>
<StyledFieldGroup>
<StyledInputDescription>
What type of banner is it?
</StyledInputDescription>
<StyledSelect
size='small'
value={variant}
onChange={(variant) =>
setVariant(variant as BannerVariant)
}
options={VARIANT_OPTIONS}
/>
</StyledFieldGroup>
<StyledFieldGroup>
<StyledInputDescription>
What icon should be displayed on the banner?
</StyledInputDescription>
<StyledSelect
size='small'
value={iconOption}
onChange={(iconOption) => {
setIconOption(iconOption as IconOption);
if (iconOption === 'None') {
setIcon('none');
} else {
setIcon('');
}
}}
options={['Default', 'Custom', 'None'].map(
(option) => ({
key: option,
label: option,
}),
)}
/>
</StyledFieldGroup>
<ConditionallyRender
condition={iconOption === 'Custom'}
show={
<StyledFieldGroup>
<StyledInputDescription>
Which custom icon?
<HelpIcon
htmlTooltip
tooltip={
<StyledTooltip>
<p>
Choose an icon from{' '}
<a
href='https://fonts.google.com/icons'
target='_blank'
rel='noreferrer'
>
Material Symbols
</a>
.
</p>
<p>
For example, if you want to
display the "Rocket Launch"
icon, you can enter
"rocket_launch" in the field
below.
</p>
</StyledTooltip>
}
/>
</StyledInputDescription>
<StyledInput
label='Banner icon'
value={icon}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
setIcon(e.target.value)
}
autoComplete='off'
/>
</StyledFieldGroup>
}
/>
<StyledFieldGroup>
<StyledInputDescription>
What is your banner message?
<HelpIcon
htmlTooltip
tooltip={
<StyledTooltip>
<p>
<a
href='https://www.markdownguide.org/basic-syntax/'
target='_blank'
rel='noreferrer'
>
Markdown
</a>{' '}
is supported.
</p>
</StyledTooltip>
}
/>
</StyledInputDescription>
<StyledInput
autoFocus
label='Banner message'
multiline
minRows={2}
maxRows={6}
value={message}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
setMessage(e.target.value)
}
autoComplete='off'
required
/>
</StyledFieldGroup>
</StyledSection>
<StyledSection>
<StyledSectionLabel>Banner action</StyledSectionLabel>
<StyledFieldGroup>
<StyledInputDescription>
What action should be available in the banner?
</StyledInputDescription>
<StyledSelect
size='small'
value={linkOption}
onChange={(linkOption) => {
setLinkOption(linkOption as LinkOption);
if (linkOption === 'Dialog') {
setLink('dialog');
} else {
setLink('');
}
setLinkText('');
setDialogTitle('');
setDialog('');
}}
options={['None', 'Link', 'Dialog'].map((option) => ({
key: option,
label: option,
}))}
/>
</StyledFieldGroup>
<ConditionallyRender
condition={linkOption === 'Link'}
show={
<StyledFieldGroup>
<StyledInputDescription>
What URL should be opened?
</StyledInputDescription>
<StyledInput
label='URL'
value={link}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
setLink(e.target.value)
}
onBlur={() => {
if (!linkText) setLinkText(link);
}}
autoComplete='off'
/>
</StyledFieldGroup>
}
/>
<ConditionallyRender
condition={linkOption !== 'None'}
show={
<StyledFieldGroup>
<StyledInputDescription>
What is the action text?
</StyledInputDescription>
<StyledInput
label='Action text'
value={linkText}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
setLinkText(e.target.value)
}
autoComplete='off'
/>
</StyledFieldGroup>
}
/>
<ConditionallyRender
condition={linkOption === 'Dialog'}
show={
<>
<StyledFieldGroup>
<StyledInputDescription>
What is the dialog title?
</StyledInputDescription>
<StyledInput
label='Dialog title'
value={dialogTitle}
onChange={(
e: ChangeEvent<HTMLInputElement>,
) => setDialogTitle(e.target.value)}
autoComplete='off'
/>
</StyledFieldGroup>
<StyledFieldGroup>
<StyledInputDescription>
What is the dialog content?
<HelpIcon
htmlTooltip
tooltip={
<StyledTooltip>
<p>
<a
href='https://www.markdownguide.org/basic-syntax/'
target='_blank'
rel='noreferrer'
>
Markdown
</a>{' '}
is supported.
</p>
</StyledTooltip>
}
/>
</StyledInputDescription>
<StyledInput
label='Dialog content'
multiline
minRows={4}
value={dialog}
onChange={(
e: ChangeEvent<HTMLInputElement>,
) => setDialog(e.target.value)}
autoComplete='off'
/>
</StyledFieldGroup>
<StyledPreviewButton
variant='outlined'
color='primary'
startIcon={<Visibility />}
onClick={() => setPreviewDialogOpen(true)}
>
Preview dialog
</StyledPreviewButton>
<BannerDialog
open={previewDialogOpen}
setOpen={setPreviewDialogOpen}
title={dialogTitle || linkText}
>
{dialog!}
</BannerDialog>
</>
}
/>
</StyledSection>
<StyledSection>
<StyledSectionLabel>Sticky banner</StyledSectionLabel>
<FormControlLabel
control={
<Checkbox
checked={sticky}
onChange={(e) => setSticky(e.target.checked)}
/>
}
label='Make the banner sticky on the screen when scrolling'
/>
</StyledSection>
</StyledForm>
);
};