@flipdish/portal-library 7.3.1 → 7.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/atoms/Radio/index.cjs.js +1 -1
- package/dist/components/atoms/Radio/index.cjs.js.map +1 -1
- package/dist/components/atoms/Radio/index.d.ts +23 -2
- package/dist/components/atoms/Radio/index.js +1 -1
- package/dist/components/atoms/Radio/index.js.map +1 -1
- package/dist/components/molecules/Modal/index.cjs.js +1 -1
- package/dist/components/molecules/Modal/index.cjs.js.map +1 -1
- package/dist/components/molecules/Modal/index.js +1 -1
- package/dist/components/molecules/Modal/index.js.map +1 -1
- package/dist/components/molecules/RadioGroup/index.cjs.js +1 -1
- package/dist/components/molecules/RadioGroup/index.cjs.js.map +1 -1
- package/dist/components/molecules/RadioGroup/index.d.ts +29 -2
- package/dist/components/molecules/RadioGroup/index.js +1 -1
- package/dist/components/molecules/RadioGroup/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("@mui/material/Radio");module.exports=e;
|
|
1
|
+
"use strict";var e=require("react/jsx-runtime");require("react");var i=require("@mui/material/Box"),l=require("@mui/material/FormControlLabel"),t=require("@mui/material/Radio"),s=require("@mui/material/styles");const r=s.styled("span")((({theme:e})=>({display:"inline-flex",borderRadius:"50%",position:"relative","input:focus ~ &":{outline:`2px solid ${e.palette.semantic.stroke["stroke-focus"]}`,outlineOffset:"2px"},"&::before":{content:'""',position:"absolute",top:"50%",left:"50%",transform:"translate(-50%, -50%)",width:"95%",height:"95%",borderRadius:"50%",pointerEvents:"none",opacity:0,transition:"opacity 150ms ease"}}))),a=({error:i=!1,disabled:l=!1,size:t="medium"})=>{const a=s.useTheme(),o=l?a.palette.semantic.fill["fill-disabled"]:i?a.palette.semantic.fill["fill-error-strong"]:a.palette.semantic.stroke["stroke-strong"],n="small"===t?24:32,d=i?a.palette.semantic.fill["fill-error-weak"]:a.palette.semantic.fill["fill-inverse-strong"];return e.jsx(r,{children:e.jsx("svg",{fill:"none",height:n,viewBox:"0 0 20 20",width:n,children:e.jsx("circle",{cx:"10",cy:"10",fill:d,r:"9",stroke:o,strokeWidth:"1"})})})},o=({error:i=!1,disabled:l=!1,size:t="medium"})=>{const a=s.useTheme(),o=l?a.palette.action.disabled:i?a.palette.semantic.fill["fill-error-strong"]:a.palette.semantic.fill["fill-selected"],n="small"===t?24:32;return e.jsx(r,{children:e.jsxs("svg",{fill:"none",height:n,viewBox:"0 0 20 20",width:n,children:[e.jsx("circle",{cx:"10",cy:"10",fill:o,r:"9",stroke:o,strokeWidth:"1"}),e.jsx("circle",{cx:"10",cy:"10",fill:a.palette.semantic.fill["fill-inverse-strong"],r:"3"})]})})},n=s.styled(t)((({theme:e})=>({padding:0,"&:hover:not(.Mui-disabled) span::before":{opacity:1,backgroundColor:e.palette.semantic.fill["fill-hover"]},"&:active:not(.Mui-disabled) span::before":{opacity:1,backgroundColor:e.palette.semantic.fill["fill-press"]}}))),d=s.styled(i)((({theme:e})=>({display:"flex",flexDirection:"column",gap:e.spacing(.5)}))),c=s.styled(l,{shouldForwardProp:e=>"disabled"!==e&&"error"!==e&&"size"!==e})((({theme:e,size:i="medium"})=>({columnGap:"small"===i?e.spacing(1):e.spacing(1.5),marginTop:e.spacing(2),marginLeft:e.spacing(0),"& .MuiFormControlLabel-label":{..."small"===i?{fontSize:"14px"}:{}}}))),p=s.styled(i)((({theme:e})=>({position:"relative",display:"flex",flexDirection:"column",gap:e.spacing(.5),marginTop:e.spacing(1.5),paddingBottom:e.spacing(1),marginLeft:e.spacing(1.75),paddingLeft:e.spacing(3.25),borderLeft:`4px solid ${e.palette.divider}`,height:"76px"})));module.exports=({disabled:i=!1,size:l="medium",label:t,fdKey:s="basic-radio",error:r=!1,children:m,...f})=>{const u=r&&!i;return e.jsxs(d,{children:[e.jsx(c,{control:e.jsx(n,{disableFocusRipple:!0,disableRipple:!0,disableTouchRipple:!0,checkedIcon:e.jsx(o,{disabled:i,error:u,size:l}),"data-fd":s,disabled:i,icon:e.jsx(a,{disabled:i,error:u,size:l}),size:l,...f}),disabled:i,error:u,label:t||null,size:l}),m&&e.jsx(p,{children:m})]})};
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../../../src/components/atoms/Radio/index.tsx"],"sourcesContent":["import { type ReactNode } from 'react';\n\nimport Box from '@mui/material/Box';\nimport MuiFormControlLabel from '@mui/material/FormControlLabel';\nimport MuiRadio, { type RadioProps as MuiRadioProps } from '@mui/material/Radio';\nimport { styled, useTheme } from '@mui/material/styles';\n\nconst IconWrapper = styled('span')(({ theme }) => ({\n display: 'inline-flex',\n borderRadius: '50%',\n position: 'relative',\n 'input:focus ~ &': {\n outline: `2px solid ${theme.palette.semantic.stroke['stroke-focus']}`,\n outlineOffset: '2px',\n },\n '&::before': {\n content: '\"\"',\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n width: '95%',\n height: '95%',\n borderRadius: '50%',\n pointerEvents: 'none',\n opacity: 0,\n transition: 'opacity 150ms ease',\n },\n}));\n\n// Custom unchecked icon - gray or red border circle\nconst RadioUncheckedIcon = ({\n error = false,\n disabled = false,\n size = 'medium'\n}: {\n error?: boolean;\n disabled?: boolean;\n size?: 'medium' | 'small';\n}): React.ReactElement => {\n const theme = useTheme();\n const strokeColor = disabled\n ? theme.palette.semantic.fill['fill-disabled']\n : error\n ? theme.palette.semantic.fill['fill-error-strong']\n : theme.palette.semantic.stroke['stroke-strong'];\n const svgSize = size === 'small' ? 24 : 32;\n const fillBackground = error ? theme.palette.semantic.fill['fill-error-weak'] : theme.palette.semantic.fill['fill-inverse-strong'];\n return (\n <IconWrapper>\n <svg fill=\"none\" height={svgSize} viewBox=\"0 0 20 20\" width={svgSize}>\n <circle\n cx=\"10\"\n cy=\"10\"\n fill={fillBackground}\n r=\"9\"\n stroke={strokeColor}\n strokeWidth=\"1\"\n />\n </svg>\n </IconWrapper>\n );\n};\n\n// Custom checked icon - blue or red filled circle with white center dot\nconst RadioCheckedIcon = ({\n error = false,\n disabled = false,\n size = 'medium'\n}: {\n error?: boolean;\n disabled?: boolean;\n size?: 'medium' | 'small';\n}): React.ReactElement => {\n const theme = useTheme();\n const fillColor = disabled\n ? theme.palette.action.disabled\n : error\n ? theme.palette.semantic.fill['fill-error-strong']\n : theme.palette.semantic.fill['fill-selected'];\n const svgSize = size === 'small' ? 24 : 32;\n return (\n <IconWrapper>\n <svg fill=\"none\" height={svgSize} viewBox=\"0 0 20 20\" width={svgSize}>\n <circle\n cx=\"10\"\n cy=\"10\"\n fill={fillColor}\n r=\"9\"\n stroke={fillColor}\n strokeWidth=\"1\"\n />\n <circle\n cx=\"10\"\n cy=\"10\"\n fill={theme.palette.semantic.fill['fill-inverse-strong']}\n r=\"3\"\n />\n </svg>\n </IconWrapper>\n );\n};\n\n// Styled Radio with custom icons\nconst StyledRadio = styled(MuiRadio)(({ theme }) => ({\n padding: 0,\n '&:hover:not(.Mui-disabled) span::before': {\n opacity: 1,\n backgroundColor: theme.palette.semantic.fill['fill-hover'],\n },\n '&:active:not(.Mui-disabled) span::before': {\n opacity: 1,\n backgroundColor: theme.palette.semantic.fill['fill-press'],\n },\n}));\n\n// Custom Radio props extending MUI Radio props\nexport type RadioProps = Omit<MuiRadioProps, 'size'> & {\n /** Whether the radio is disabled. */\n disabled?: boolean;\n /** The size of the radio. */\n size?: 'medium' | 'small';\n /** The label of the radio. */\n label?: ReactNode;\n /** Whether the radio is required. */\n required?: boolean;\n /** The identifier of the radio. */\n fdKey?: string;\n /** Whether the radio is in an error state. */\n error?: boolean;\n /** The children of the radio. */\n children?: ReactNode;\n};\n\nconst StyledContainer = styled(Box)(({ theme }) => ({\n display: 'flex',\n flexDirection: 'column',\n gap: theme.spacing(0.5),\n}));\n\n/** Styled FormControlLabel props */\ninterface StyledFormControlLabelProps {\n disabled?: boolean;\n error?: boolean;\n size?: 'medium' | 'small';\n}\n\nconst StyledFormControlLabel = styled(MuiFormControlLabel, {\n shouldForwardProp: (prop) => prop !== 'disabled' && prop !== 'error' && prop !== 'size',\n})<StyledFormControlLabelProps>(({ theme, size = 'medium' }) => ({\n columnGap: size === 'small' ? theme.spacing(1) : theme.spacing(1.5), // 8px for small, 12px for medium\n marginTop: theme.spacing(2),\n marginLeft: theme.spacing(0),\n '& .MuiFormControlLabel-label': {\n ...(size === 'small'\n ? { fontSize: '14px' }\n : {}),\n },\n}));\n\nconst StyledConditionalContainer = styled(Box)(({ theme }) => ({\n position: 'relative',\n display: 'flex',\n flexDirection: 'column',\n gap: theme.spacing(0.5),\n marginTop: theme.spacing(1.5),\n paddingBottom: theme.spacing(1),\n marginLeft: theme.spacing(1.75),\n paddingLeft: theme.spacing(3.25),\n borderLeft: `4px solid ${theme.palette.divider}`,\n height: '76px',\n}));\n\nconst Radio = ({\n disabled = false,\n size = 'medium',\n label,\n fdKey = 'basic-radio',\n error = false,\n children,\n ...props\n}: RadioProps): React.ReactElement => {\n const isError = error && !disabled;\n\n return (\n <StyledContainer>\n <StyledFormControlLabel\n control={\n <StyledRadio\n disableFocusRipple\n disableRipple\n disableTouchRipple\n checkedIcon={<RadioCheckedIcon disabled={disabled} error={isError} size={size} />}\n data-fd={fdKey}\n disabled={disabled}\n icon={<RadioUncheckedIcon disabled={disabled} error={isError} size={size} />}\n size={size}\n {...props}\n />\n }\n disabled={disabled}\n error={isError}\n label={label || null}\n size={size}\n />\n\n {children && (\n <StyledConditionalContainer>\n {children}\n </StyledConditionalContainer>\n )}\n </StyledContainer>\n );\n};\n\nexport default Radio;\n"],"names":["IconWrapper","styled","theme","display","borderRadius","position","outline","palette","semantic","stroke","outlineOffset","content","top","left","transform","width","height","pointerEvents","opacity","transition","RadioUncheckedIcon","error","disabled","size","useTheme","strokeColor","fill","svgSize","fillBackground","_jsx","children","viewBox","cx","cy","r","strokeWidth","RadioCheckedIcon","fillColor","action","_jsxs","StyledRadio","MuiRadio","padding","backgroundColor","StyledContainer","Box","flexDirection","gap","spacing","StyledFormControlLabel","MuiFormControlLabel","shouldForwardProp","prop","columnGap","marginTop","marginLeft","fontSize","StyledConditionalContainer","paddingBottom","paddingLeft","borderLeft","divider","label","fdKey","props","isError","control","disableFocusRipple","disableRipple","disableTouchRipple","checkedIcon","icon"],"mappings":"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"}
|
|
@@ -1,2 +1,23 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { RadioProps as RadioProps$1 } from '@mui/material/Radio';
|
|
3
|
+
|
|
4
|
+
type RadioProps = Omit<RadioProps$1, 'size'> & {
|
|
5
|
+
/** Whether the radio is disabled. */
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
/** The size of the radio. */
|
|
8
|
+
size?: 'medium' | 'small';
|
|
9
|
+
/** The label of the radio. */
|
|
10
|
+
label?: ReactNode;
|
|
11
|
+
/** Whether the radio is required. */
|
|
12
|
+
required?: boolean;
|
|
13
|
+
/** The identifier of the radio. */
|
|
14
|
+
fdKey?: string;
|
|
15
|
+
/** Whether the radio is in an error state. */
|
|
16
|
+
error?: boolean;
|
|
17
|
+
/** The children of the radio. */
|
|
18
|
+
children?: ReactNode;
|
|
19
|
+
};
|
|
20
|
+
declare const Radio: ({ disabled, size, label, fdKey, error, children, ...props }: RadioProps) => React.ReactElement;
|
|
21
|
+
|
|
22
|
+
export { Radio as default };
|
|
23
|
+
export type { RadioProps };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
1
|
+
import{jsxs as e,jsx as i}from"react/jsx-runtime";import"react";import l from"@mui/material/Box";import t from"@mui/material/FormControlLabel";import r from"@mui/material/Radio";import{styled as a,useTheme as o}from"@mui/material/styles";const s=a("span")((({theme:e})=>({display:"inline-flex",borderRadius:"50%",position:"relative","input:focus ~ &":{outline:`2px solid ${e.palette.semantic.stroke["stroke-focus"]}`,outlineOffset:"2px"},"&::before":{content:'""',position:"absolute",top:"50%",left:"50%",transform:"translate(-50%, -50%)",width:"95%",height:"95%",borderRadius:"50%",pointerEvents:"none",opacity:0,transition:"opacity 150ms ease"}}))),n=({error:e=!1,disabled:l=!1,size:t="medium"})=>{const r=o(),a=l?r.palette.semantic.fill["fill-disabled"]:e?r.palette.semantic.fill["fill-error-strong"]:r.palette.semantic.stroke["stroke-strong"],n="small"===t?24:32,d=e?r.palette.semantic.fill["fill-error-weak"]:r.palette.semantic.fill["fill-inverse-strong"];return i(s,{children:i("svg",{fill:"none",height:n,viewBox:"0 0 20 20",width:n,children:i("circle",{cx:"10",cy:"10",fill:d,r:"9",stroke:a,strokeWidth:"1"})})})},d=({error:l=!1,disabled:t=!1,size:r="medium"})=>{const a=o(),n=t?a.palette.action.disabled:l?a.palette.semantic.fill["fill-error-strong"]:a.palette.semantic.fill["fill-selected"],d="small"===r?24:32;return i(s,{children:e("svg",{fill:"none",height:d,viewBox:"0 0 20 20",width:d,children:[i("circle",{cx:"10",cy:"10",fill:n,r:"9",stroke:n,strokeWidth:"1"}),i("circle",{cx:"10",cy:"10",fill:a.palette.semantic.fill["fill-inverse-strong"],r:"3"})]})})},c=a(r)((({theme:e})=>({padding:0,"&:hover:not(.Mui-disabled) span::before":{opacity:1,backgroundColor:e.palette.semantic.fill["fill-hover"]},"&:active:not(.Mui-disabled) span::before":{opacity:1,backgroundColor:e.palette.semantic.fill["fill-press"]}}))),p=a(l)((({theme:e})=>({display:"flex",flexDirection:"column",gap:e.spacing(.5)}))),m=a(t,{shouldForwardProp:e=>"disabled"!==e&&"error"!==e&&"size"!==e})((({theme:e,size:i="medium"})=>({columnGap:"small"===i?e.spacing(1):e.spacing(1.5),marginTop:e.spacing(2),marginLeft:e.spacing(0),"& .MuiFormControlLabel-label":{..."small"===i?{fontSize:"14px"}:{}}}))),f=a(l)((({theme:e})=>({position:"relative",display:"flex",flexDirection:"column",gap:e.spacing(.5),marginTop:e.spacing(1.5),paddingBottom:e.spacing(1),marginLeft:e.spacing(1.75),paddingLeft:e.spacing(3.25),borderLeft:`4px solid ${e.palette.divider}`,height:"76px"}))),u=({disabled:l=!1,size:t="medium",label:r,fdKey:a="basic-radio",error:o=!1,children:s,...u})=>{const g=o&&!l;return e(p,{children:[i(m,{control:i(c,{disableFocusRipple:!0,disableRipple:!0,disableTouchRipple:!0,checkedIcon:i(d,{disabled:l,error:g,size:t}),"data-fd":a,disabled:l,icon:i(n,{disabled:l,error:g,size:t}),size:t,...u}),disabled:l,error:g,label:r||null,size:t}),s&&i(f,{children:s})]})};export{u as default};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/components/atoms/Radio/index.tsx"],"sourcesContent":["import { type ReactNode } from 'react';\n\nimport Box from '@mui/material/Box';\nimport MuiFormControlLabel from '@mui/material/FormControlLabel';\nimport MuiRadio, { type RadioProps as MuiRadioProps } from '@mui/material/Radio';\nimport { styled, useTheme } from '@mui/material/styles';\n\nconst IconWrapper = styled('span')(({ theme }) => ({\n display: 'inline-flex',\n borderRadius: '50%',\n position: 'relative',\n 'input:focus ~ &': {\n outline: `2px solid ${theme.palette.semantic.stroke['stroke-focus']}`,\n outlineOffset: '2px',\n },\n '&::before': {\n content: '\"\"',\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n width: '95%',\n height: '95%',\n borderRadius: '50%',\n pointerEvents: 'none',\n opacity: 0,\n transition: 'opacity 150ms ease',\n },\n}));\n\n// Custom unchecked icon - gray or red border circle\nconst RadioUncheckedIcon = ({\n error = false,\n disabled = false,\n size = 'medium'\n}: {\n error?: boolean;\n disabled?: boolean;\n size?: 'medium' | 'small';\n}): React.ReactElement => {\n const theme = useTheme();\n const strokeColor = disabled\n ? theme.palette.semantic.fill['fill-disabled']\n : error\n ? theme.palette.semantic.fill['fill-error-strong']\n : theme.palette.semantic.stroke['stroke-strong'];\n const svgSize = size === 'small' ? 24 : 32;\n const fillBackground = error ? theme.palette.semantic.fill['fill-error-weak'] : theme.palette.semantic.fill['fill-inverse-strong'];\n return (\n <IconWrapper>\n <svg fill=\"none\" height={svgSize} viewBox=\"0 0 20 20\" width={svgSize}>\n <circle\n cx=\"10\"\n cy=\"10\"\n fill={fillBackground}\n r=\"9\"\n stroke={strokeColor}\n strokeWidth=\"1\"\n />\n </svg>\n </IconWrapper>\n );\n};\n\n// Custom checked icon - blue or red filled circle with white center dot\nconst RadioCheckedIcon = ({\n error = false,\n disabled = false,\n size = 'medium'\n}: {\n error?: boolean;\n disabled?: boolean;\n size?: 'medium' | 'small';\n}): React.ReactElement => {\n const theme = useTheme();\n const fillColor = disabled\n ? theme.palette.action.disabled\n : error\n ? theme.palette.semantic.fill['fill-error-strong']\n : theme.palette.semantic.fill['fill-selected'];\n const svgSize = size === 'small' ? 24 : 32;\n return (\n <IconWrapper>\n <svg fill=\"none\" height={svgSize} viewBox=\"0 0 20 20\" width={svgSize}>\n <circle\n cx=\"10\"\n cy=\"10\"\n fill={fillColor}\n r=\"9\"\n stroke={fillColor}\n strokeWidth=\"1\"\n />\n <circle\n cx=\"10\"\n cy=\"10\"\n fill={theme.palette.semantic.fill['fill-inverse-strong']}\n r=\"3\"\n />\n </svg>\n </IconWrapper>\n );\n};\n\n// Styled Radio with custom icons\nconst StyledRadio = styled(MuiRadio)(({ theme }) => ({\n padding: 0,\n '&:hover:not(.Mui-disabled) span::before': {\n opacity: 1,\n backgroundColor: theme.palette.semantic.fill['fill-hover'],\n },\n '&:active:not(.Mui-disabled) span::before': {\n opacity: 1,\n backgroundColor: theme.palette.semantic.fill['fill-press'],\n },\n}));\n\n// Custom Radio props extending MUI Radio props\nexport type RadioProps = Omit<MuiRadioProps, 'size'> & {\n /** Whether the radio is disabled. */\n disabled?: boolean;\n /** The size of the radio. */\n size?: 'medium' | 'small';\n /** The label of the radio. */\n label?: ReactNode;\n /** Whether the radio is required. */\n required?: boolean;\n /** The identifier of the radio. */\n fdKey?: string;\n /** Whether the radio is in an error state. */\n error?: boolean;\n /** The children of the radio. */\n children?: ReactNode;\n};\n\nconst StyledContainer = styled(Box)(({ theme }) => ({\n display: 'flex',\n flexDirection: 'column',\n gap: theme.spacing(0.5),\n}));\n\n/** Styled FormControlLabel props */\ninterface StyledFormControlLabelProps {\n disabled?: boolean;\n error?: boolean;\n size?: 'medium' | 'small';\n}\n\nconst StyledFormControlLabel = styled(MuiFormControlLabel, {\n shouldForwardProp: (prop) => prop !== 'disabled' && prop !== 'error' && prop !== 'size',\n})<StyledFormControlLabelProps>(({ theme, size = 'medium' }) => ({\n columnGap: size === 'small' ? theme.spacing(1) : theme.spacing(1.5), // 8px for small, 12px for medium\n marginTop: theme.spacing(2),\n marginLeft: theme.spacing(0),\n '& .MuiFormControlLabel-label': {\n ...(size === 'small'\n ? { fontSize: '14px' }\n : {}),\n },\n}));\n\nconst StyledConditionalContainer = styled(Box)(({ theme }) => ({\n position: 'relative',\n display: 'flex',\n flexDirection: 'column',\n gap: theme.spacing(0.5),\n marginTop: theme.spacing(1.5),\n paddingBottom: theme.spacing(1),\n marginLeft: theme.spacing(1.75),\n paddingLeft: theme.spacing(3.25),\n borderLeft: `4px solid ${theme.palette.divider}`,\n height: '76px',\n}));\n\nconst Radio = ({\n disabled = false,\n size = 'medium',\n label,\n fdKey = 'basic-radio',\n error = false,\n children,\n ...props\n}: RadioProps): React.ReactElement => {\n const isError = error && !disabled;\n\n return (\n <StyledContainer>\n <StyledFormControlLabel\n control={\n <StyledRadio\n disableFocusRipple\n disableRipple\n disableTouchRipple\n checkedIcon={<RadioCheckedIcon disabled={disabled} error={isError} size={size} />}\n data-fd={fdKey}\n disabled={disabled}\n icon={<RadioUncheckedIcon disabled={disabled} error={isError} size={size} />}\n size={size}\n {...props}\n />\n }\n disabled={disabled}\n error={isError}\n label={label || null}\n size={size}\n />\n\n {children && (\n <StyledConditionalContainer>\n {children}\n </StyledConditionalContainer>\n )}\n </StyledContainer>\n );\n};\n\nexport default Radio;\n"],"names":["IconWrapper","styled","theme","display","borderRadius","position","outline","palette","semantic","stroke","outlineOffset","content","top","left","transform","width","height","pointerEvents","opacity","transition","RadioUncheckedIcon","error","disabled","size","useTheme","strokeColor","fill","svgSize","fillBackground","_jsx","children","viewBox","cx","cy","r","strokeWidth","RadioCheckedIcon","fillColor","action","_jsxs","StyledRadio","MuiRadio","padding","backgroundColor","StyledContainer","Box","flexDirection","gap","spacing","StyledFormControlLabel","MuiFormControlLabel","shouldForwardProp","prop","columnGap","marginTop","marginLeft","fontSize","StyledConditionalContainer","paddingBottom","paddingLeft","borderLeft","divider","Radio","label","fdKey","props","isError","control","disableFocusRipple","disableRipple","disableTouchRipple","checkedIcon","icon"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("react/jsx-runtime"),t=require("react"),a=require("@mui/material/Modal"),i=require("@mui/material/Box"),r=require("../../atoms/Button/index.cjs.js"),o=require("@mui/material/Typography"),n=require("@mui/material/Stack"),s=require("@mui/material/styles"),l=require("@mui/material/useMediaQuery"),d=require("@mui/material/styles/useTheme"),c=require("../../../themes/tokens/breakpoints/breakpoints.cjs.js"),u=require("../../atoms/IconContainer/index.cjs.js"),m=require("../../Spacer/index.cjs.js");const p={iconContainer:{default:()=>"neutral",destructive:()=>"destructive"}},x=e=>p.iconContainer[e](),h=s.styled(i,{shouldForwardProp:e=>!["tone","size"].includes(e)})((({theme:e,size:t})=>({position:"absolute",backgroundColor:e.palette.semantic.background["background-overlay"],border:"none",borderRadius:e.radius["radius-16"],padding:e.spacing(4),boxShadow:"0px 1px 6px 0px rgba(0, 0, 0, 0.10), 0px 8px 16px 0px rgba(0, 0, 0, 0.15)",outline:"none",maxHeight:"90vh",overflowY:"auto",top:"50%",left:"50%",transform:"translate(-50%, -50%)",maxWidth:"large"===t?"700px":"500px",width:`calc(100% - ${e.spacing(4)})`,[e.breakpoints.down(c.breakpointValues.tablet)]:{..."small"===t&&{maxWidth:`calc(100% - ${e.spacing(4)})`,width:`calc(100% - ${e.spacing(4)})`,top:"auto",bottom:e.spacing(2),left:e.spacing(2),right:e.spacing(2),transform:"none",maxHeight:"70vh",overflowY:"auto",display:"flex",flexDirection:"column"},..."large"===t&&{height:`calc(100% - ${e.spacing(4)})`,maxHeight:`calc(100% - ${e.spacing(4)})`,overflowY:"auto",display:"flex",flexDirection:"column"}}}))),g=s.styled("img")((({theme:e})=>({width:"100%",height:"auto",borderRadius:e.radius["radius-8"]}))),b=s.styled(n)({justifyContent:"flex-start",alignItems:"center"}),f=s.styled(i)((({theme:e})=>({[e.breakpoints.down(c.breakpointValues.tablet)]:{flexGrow:1,display:"flex",flexDirection:"column",minHeight:"min-content",paddingBottom:e.spacing(3)}}))),j=t.memo((({open:t,onClose:i,tone:n="default",size:s="large",title:p,description:j,property:y,actions:
|
|
1
|
+
"use strict";var e=require("react/jsx-runtime"),t=require("react"),a=require("@mui/material/Modal"),i=require("@mui/material/Box"),r=require("../../atoms/Button/index.cjs.js"),o=require("@mui/material/Typography"),n=require("@mui/material/Stack"),s=require("@mui/material/styles"),l=require("@mui/material/useMediaQuery"),d=require("@mui/material/styles/useTheme"),c=require("../../../themes/tokens/breakpoints/breakpoints.cjs.js"),u=require("../../atoms/IconContainer/index.cjs.js"),m=require("../../Spacer/index.cjs.js");const p={iconContainer:{default:()=>"neutral",destructive:()=>"destructive"}},x=e=>p.iconContainer[e](),h=s.styled(i,{shouldForwardProp:e=>!["tone","size"].includes(e)})((({theme:e,size:t})=>({position:"absolute",backgroundColor:e.palette.semantic.background["background-overlay"],border:"none",borderRadius:e.radius["radius-16"],padding:e.spacing(4),boxShadow:"0px 1px 6px 0px rgba(0, 0, 0, 0.10), 0px 8px 16px 0px rgba(0, 0, 0, 0.15)",outline:"none",maxHeight:"90vh",overflowY:"auto",top:"50%",left:"50%",transform:"translate(-50%, -50%)",maxWidth:"large"===t?"700px":"500px",width:`calc(100% - ${e.spacing(4)})`,[e.breakpoints.down(c.breakpointValues.tablet)]:{..."small"===t&&{maxWidth:`calc(100% - ${e.spacing(4)})`,width:`calc(100% - ${e.spacing(4)})`,top:"auto",bottom:e.spacing(2),left:e.spacing(2),right:e.spacing(2),transform:"none",maxHeight:"70vh",overflowY:"auto",display:"flex",flexDirection:"column"},..."large"===t&&{height:`calc(100% - ${e.spacing(4)})`,maxHeight:`calc(100% - ${e.spacing(4)})`,overflowY:"auto",display:"flex",flexDirection:"column"}}}))),g=s.styled("img")((({theme:e})=>({width:"100%",height:"auto",borderRadius:e.radius["radius-8"]}))),b=s.styled(n)({justifyContent:"flex-start",alignItems:"center"}),f=s.styled(i)((({theme:e})=>({[e.breakpoints.down(c.breakpointValues.tablet)]:{flexGrow:1,display:"flex",flexDirection:"column",minHeight:"min-content",paddingBottom:e.spacing(3)}}))),j=t.memo((({open:t,onClose:i,tone:n="default",size:s="large",title:p,description:j,property:y,actions:w=[],className:v,"data-testid":k,children:q})=>{const z=d(),$=l(z.breakpoints.down(c.breakpointValues.tablet)),C="string"==typeof y,B=w.length>0;return e.jsx(a,{"aria-describedby":`modal-description-${k??"default"}`,"aria-labelledby":`modal-title-${k??"default"}`,className:v,"data-testid":k,onClose:i,open:t,children:e.jsxs(h,{size:s,tone:n,children:[e.jsxs(f,{children:[y&&e.jsxs(e.Fragment,{children:[C?e.jsx(g,{alt:p??"Modal image",src:y}):e.jsx(u,{icon:y,style:"filled",tone:x(n)}),e.jsx(m,{size:16,variant:"horizontal"})]}),p&&e.jsx(o,{component:"h2",id:`modal-title-${k??"default"}`,variant:"h3Strong",children:p}),p&&j&&e.jsx(m,{size:8,variant:"horizontal"}),j&&e.jsx(o,{color:z.palette.semantic.text["text-weak"],id:`modal-description-${k??"default"}`,variant:"b1Weak",children:j}),q&&e.jsxs(e.Fragment,{children:[e.jsx(m,{size:24,variant:"horizontal"}),q]})]}),B&&e.jsxs(e.Fragment,{children:[e.jsx(m,{size:24,variant:"horizontal"}),e.jsx(b,{direction:$?"column":"row-reverse",spacing:2,children:e.jsx(b,{direction:$?"column-reverse":"row",spacing:2,width:$?"100%":"fit-content",children:w.map(((t,a)=>{const{label:i,id:o,...n}=t;return e.jsx(r.Button,{fdKey:o??`modal-action-${i.replace(/\s+/g,"-").toLowerCase()}`,fullWidth:$,...n,children:i},o??`modal-action-${a}-${i.replace(/\s+/g,"-").toLowerCase()}`)}))})})]})]})})}));j.displayName="Modal",module.exports=j;
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../../../../src/components/molecules/Modal/index.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/explicit-function-return-type */\nimport { memo } from 'react';\n\nimport MuiModal from '@mui/material/Modal';\n\nimport Box from '@fd/components/atoms/Box';\nimport Button, { type ButtonProps } from '@fd/components/atoms/Button';\nimport Typography from '@fd/components/atoms/Typography';\nimport Stack from '@fd/components/molecules/Stack';\nimport styled from '@fd/utilities/styledUtilities';\nimport useMediaQuery from '@fd/utilities/useMediaQuery';\nimport useTheme from '@fd/utilities/useTheme';\n\nimport { breakpointValues } from '../../../themes/tokens/breakpoints/breakpoints';\nimport IconContainer, { type IconContainerTones } from '../../atoms/IconContainer';\nimport Spacer from '../../Spacer';\n\n/** Visual tone of the modal */\nexport type ModalTones = 'default' | 'destructive';\n\n/** Size variants for the modal */\nexport type ModalSizes = 'large' | 'small';\n\n/** Action button configuration for Modal */\nexport interface ModalAction extends Omit<ButtonProps, 'children' | 'fdKey' | 'fullWidth'> {\n /** Label text for the button */\n label: string;\n /** Optional unique identifier for the button */\n id?: string;\n}\n\n/** Props for the Modal component */\nexport interface ModalProps {\n /** Whether the modal is open */\n open: boolean;\n /** Callback function when the modal is closed */\n onClose: () => void;\n /** Visual tone of the modal */\n tone?: ModalTones;\n /** Size variant of the modal */\n size?: ModalSizes;\n /** Main heading text of the modal */\n title?: string;\n /** Detailed description text of the modal */\n description?: string;\n /** Property element to display - can be an image URL (string) or an icon component (ReactNode) */\n property?: React.ReactNode | string;\n /** Array of action buttons to display */\n actions?: ModalAction[];\n /** Additional CSS class names */\n className?: string;\n /** Test ID for testing and automation */\n 'data-testid'?: string;\n /** Content to display in the modal */\n children?: React.ReactNode;\n}\n\nconst COLOURS = {\n iconContainer: {\n default: (): IconContainerTones => 'neutral',\n destructive: (): IconContainerTones => 'destructive',\n },\n button: {\n default: (): ButtonProps['color'] => 'primary',\n destructive: (): ButtonProps['color'] => 'error',\n },\n};\n\nconst getIconContainerTone = (tone: ModalTones): IconContainerTones => {\n return COLOURS.iconContainer[tone]();\n};\n\ninterface StyledModalBoxProps {\n tone: ModalTones;\n size: ModalSizes;\n}\n\nconst StyledModalBox = styled(Box, {\n shouldForwardProp: (prop) => !['tone', 'size'].includes(prop as string),\n})<StyledModalBoxProps>(({ theme, size }) => ({\n position: 'absolute',\n backgroundColor: theme.palette.semantic.background['background-overlay'],\n border: 'none',\n borderRadius: theme.radius['radius-16'],\n padding: theme.spacing(4),\n boxShadow: '0px 1px 6px 0px rgba(0, 0, 0, 0.10), 0px 8px 16px 0px rgba(0, 0, 0, 0.15)', // TODO: Pull shadow from tokens when setup\n outline: 'none',\n maxHeight: '90vh',\n overflowY: 'auto',\n\n // Desktop styles (default)\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n maxWidth: size === 'large' ? '700px' : '500px',\n width: `calc(100% - ${theme.spacing(4)})`,\n\n // Tablet & Mobile styles\n [theme.breakpoints.down(breakpointValues.tablet)]: {\n // Small size at bottom of screen\n ...(size === 'small' && {\n maxWidth: `calc(100% - ${theme.spacing(4)})`,\n width: `calc(100% - ${theme.spacing(4)})`,\n top: 'auto',\n bottom: theme.spacing(2),\n left: theme.spacing(2),\n right: theme.spacing(2),\n transform: 'none',\n maxHeight: '70vh',\n overflowY: 'auto',\n // Flex layout to push buttons to bottom when content is small\n display: 'flex',\n flexDirection: 'column',\n }),\n\n // Large size takes full screen\n ...(size === 'large' && {\n height: `calc(100% - ${theme.spacing(4)})`,\n maxHeight: `calc(100% - ${theme.spacing(4)})`,\n overflowY: 'auto',\n // Flex layout to push buttons to bottom when content is small\n display: 'flex',\n flexDirection: 'column',\n }),\n },\n}));\n\nconst StyledImage = styled('img')(({ theme }) => ({\n width: '100%',\n height: 'auto',\n borderRadius: theme.radius['radius-8'],\n}));\n\nconst StyledButtonStack = styled(Stack)({\n justifyContent: 'flex-start',\n alignItems: 'center',\n});\n\nconst StyledContentContainer = styled(Box)(({ theme }) => ({\n [theme.breakpoints.down(breakpointValues.tablet)]: {\n flexGrow: 1,\n display: 'flex',\n flexDirection: 'column',\n minHeight: 'min-content',\n paddingBottom: theme.spacing(3),\n },\n}));\n\n/**\n * Modal component is used to display content that temporarily blocks interaction with the main view.\n * It creates a focused mode for completing tasks or viewing important information without leaving the current page.\n *\n * The component is wrapped with React.memo to optimize performance by preventing unnecessary\n * re-renders when the component's props haven't changed.\n */\nconst Modal = memo(\n ({\n open,\n onClose,\n tone = 'default',\n size = 'large',\n title,\n description,\n property,\n actions = [],\n className,\n 'data-testid': dataTestId,\n children,\n }: ModalProps) => {\n const theme = useTheme();\n const isTabletOrMobile = useMediaQuery(theme.breakpoints.down(breakpointValues.tablet));\n\n const isImageUrl = typeof property === 'string';\n const showButtonSection = actions.length > 0;\n\n return (\n <MuiModal\n aria-describedby={`modal-description-${dataTestId ?? 'default'}`}\n aria-labelledby={`modal-title-${dataTestId ?? 'default'}`}\n className={className}\n data-testid={dataTestId}\n onClose={onClose}\n open={open}\n >\n <StyledModalBox size={size} tone={tone}>\n <StyledContentContainer>\n {property && (\n <>\n {isImageUrl ? (\n <StyledImage alt={title ?? 'Modal image'} src={property} />\n ) : (\n <IconContainer icon={property} style=\"filled\" tone={getIconContainerTone(tone)} />\n )}\n <Spacer size={16} variant=\"horizontal\" />\n </>\n )}\n {title && (\n <Typography component=\"h2\" id={`modal-title-${dataTestId ?? 'default'}`} variant=\"h3Strong\">\n {title}\n </Typography>\n )}\n {title && description && <Spacer size={8} variant=\"horizontal\" />}\n {description && (\n <Typography\n color={theme.palette.semantic.text['text-weak']}\n id={`modal-description-${dataTestId ?? 'default'}`}\n variant=\"b1Weak\"\n >\n {description}\n </Typography>\n )}\n {children && (\n <>\n <Spacer size={24} variant=\"horizontal\" />\n {children}\n </>\n )}\n </StyledContentContainer>\n {showButtonSection && (\n <>\n <Spacer size={24} variant=\"horizontal\" />\n <StyledButtonStack direction={isTabletOrMobile ? 'column' : 'row-reverse'} spacing={2}>\n <StyledButtonStack\n direction={isTabletOrMobile ? 'column-reverse' : 'row-reverse'}\n spacing={2}\n width={isTabletOrMobile ? '100%' : 'fit-content'}\n >\n {actions.map((action, index) => {\n const { label, id, ...buttonProps } = action;\n return (\n <Button\n key={id ?? `modal-action-${index}-${label.replace(/\\s+/g, '-').toLowerCase()}`}\n fdKey={id ?? `modal-action-${label.replace(/\\s+/g, '-').toLowerCase()}`}\n fullWidth={isTabletOrMobile}\n {...buttonProps}\n >\n {label}\n </Button>\n );\n })}\n </StyledButtonStack>\n </StyledButtonStack>\n </>\n )}\n </StyledModalBox>\n </MuiModal>\n );\n },\n);\n\nModal.displayName = 'Modal';\n\nexport default Modal;\n"],"names":["COLOURS","iconContainer","default","destructive","getIconContainerTone","tone","StyledModalBox","styled","Box","shouldForwardProp","prop","includes","theme","size","position","backgroundColor","palette","semantic","background","border","borderRadius","radius","padding","spacing","boxShadow","outline","maxHeight","overflowY","top","left","transform","maxWidth","width","breakpoints","down","breakpointValues","tablet","bottom","right","display","flexDirection","height","StyledImage","StyledButtonStack","Stack","justifyContent","alignItems","StyledContentContainer","flexGrow","minHeight","paddingBottom","Modal","memo","open","onClose","title","description","property","actions","className","dataTestId","children","useTheme","isTabletOrMobile","useMediaQuery","isImageUrl","showButtonSection","length","_jsx","MuiModal","_jsxs","alt","src","IconContainer","icon","style","Spacer","variant","Typography","component","id","color","text","_Fragment","direction","map","action","index","label","buttonProps","Button","fdKey","replace","toLowerCase","fullWidth","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../../../src/components/molecules/Modal/index.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/explicit-function-return-type */\nimport { memo } from 'react';\n\nimport MuiModal from '@mui/material/Modal';\n\nimport Box from '@fd/components/atoms/Box';\nimport Button, { type ButtonProps } from '@fd/components/atoms/Button';\nimport Typography from '@fd/components/atoms/Typography';\nimport Stack from '@fd/components/molecules/Stack';\nimport styled from '@fd/utilities/styledUtilities';\nimport useMediaQuery from '@fd/utilities/useMediaQuery';\nimport useTheme from '@fd/utilities/useTheme';\n\nimport { breakpointValues } from '../../../themes/tokens/breakpoints/breakpoints';\nimport IconContainer, { type IconContainerTones } from '../../atoms/IconContainer';\nimport Spacer from '../../Spacer';\n\n/** Visual tone of the modal */\nexport type ModalTones = 'default' | 'destructive';\n\n/** Size variants for the modal */\nexport type ModalSizes = 'large' | 'small';\n\n/** Action button configuration for Modal */\nexport interface ModalAction extends Omit<ButtonProps, 'children' | 'fdKey' | 'fullWidth'> {\n /** Label text for the button */\n label: string;\n /** Optional unique identifier for the button */\n id?: string;\n}\n\n/** Props for the Modal component */\nexport interface ModalProps {\n /** Whether the modal is open */\n open: boolean;\n /** Callback function when the modal is closed */\n onClose: () => void;\n /** Visual tone of the modal */\n tone?: ModalTones;\n /** Size variant of the modal */\n size?: ModalSizes;\n /** Main heading text of the modal */\n title?: string;\n /** Detailed description text of the modal */\n description?: string;\n /** Property element to display - can be an image URL (string) or an icon component (ReactNode) */\n property?: React.ReactNode | string;\n /** Array of action buttons to display */\n actions?: ModalAction[];\n /** Additional CSS class names */\n className?: string;\n /** Test ID for testing and automation */\n 'data-testid'?: string;\n /** Content to display in the modal */\n children?: React.ReactNode;\n}\n\nconst COLOURS = {\n iconContainer: {\n default: (): IconContainerTones => 'neutral',\n destructive: (): IconContainerTones => 'destructive',\n },\n button: {\n default: (): ButtonProps['color'] => 'primary',\n destructive: (): ButtonProps['color'] => 'error',\n },\n};\n\nconst getIconContainerTone = (tone: ModalTones): IconContainerTones => {\n return COLOURS.iconContainer[tone]();\n};\n\ninterface StyledModalBoxProps {\n tone: ModalTones;\n size: ModalSizes;\n}\n\nconst StyledModalBox = styled(Box, {\n shouldForwardProp: (prop) => !['tone', 'size'].includes(prop as string),\n})<StyledModalBoxProps>(({ theme, size }) => ({\n position: 'absolute',\n backgroundColor: theme.palette.semantic.background['background-overlay'],\n border: 'none',\n borderRadius: theme.radius['radius-16'],\n padding: theme.spacing(4),\n boxShadow: '0px 1px 6px 0px rgba(0, 0, 0, 0.10), 0px 8px 16px 0px rgba(0, 0, 0, 0.15)', // TODO: Pull shadow from tokens when setup\n outline: 'none',\n maxHeight: '90vh',\n overflowY: 'auto',\n\n // Desktop styles (default)\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n maxWidth: size === 'large' ? '700px' : '500px',\n width: `calc(100% - ${theme.spacing(4)})`,\n\n // Tablet & Mobile styles\n [theme.breakpoints.down(breakpointValues.tablet)]: {\n // Small size at bottom of screen\n ...(size === 'small' && {\n maxWidth: `calc(100% - ${theme.spacing(4)})`,\n width: `calc(100% - ${theme.spacing(4)})`,\n top: 'auto',\n bottom: theme.spacing(2),\n left: theme.spacing(2),\n right: theme.spacing(2),\n transform: 'none',\n maxHeight: '70vh',\n overflowY: 'auto',\n // Flex layout to push buttons to bottom when content is small\n display: 'flex',\n flexDirection: 'column',\n }),\n\n // Large size takes full screen\n ...(size === 'large' && {\n height: `calc(100% - ${theme.spacing(4)})`,\n maxHeight: `calc(100% - ${theme.spacing(4)})`,\n overflowY: 'auto',\n // Flex layout to push buttons to bottom when content is small\n display: 'flex',\n flexDirection: 'column',\n }),\n },\n}));\n\nconst StyledImage = styled('img')(({ theme }) => ({\n width: '100%',\n height: 'auto',\n borderRadius: theme.radius['radius-8'],\n}));\n\nconst StyledButtonStack = styled(Stack)({\n justifyContent: 'flex-start',\n alignItems: 'center',\n});\n\nconst StyledContentContainer = styled(Box)(({ theme }) => ({\n [theme.breakpoints.down(breakpointValues.tablet)]: {\n flexGrow: 1,\n display: 'flex',\n flexDirection: 'column',\n minHeight: 'min-content',\n paddingBottom: theme.spacing(3),\n },\n}));\n\n/**\n * Modal component is used to display content that temporarily blocks interaction with the main view.\n * It creates a focused mode for completing tasks or viewing important information without leaving the current page.\n *\n * The component is wrapped with React.memo to optimize performance by preventing unnecessary\n * re-renders when the component's props haven't changed.\n */\nconst Modal = memo(\n ({\n open,\n onClose,\n tone = 'default',\n size = 'large',\n title,\n description,\n property,\n actions = [],\n className,\n 'data-testid': dataTestId,\n children,\n }: ModalProps) => {\n const theme = useTheme();\n const isTabletOrMobile = useMediaQuery(theme.breakpoints.down(breakpointValues.tablet));\n\n const isImageUrl = typeof property === 'string';\n const showButtonSection = actions.length > 0;\n\n return (\n <MuiModal\n aria-describedby={`modal-description-${dataTestId ?? 'default'}`}\n aria-labelledby={`modal-title-${dataTestId ?? 'default'}`}\n className={className}\n data-testid={dataTestId}\n onClose={onClose}\n open={open}\n >\n <StyledModalBox size={size} tone={tone}>\n <StyledContentContainer>\n {property && (\n <>\n {isImageUrl ? (\n <StyledImage alt={title ?? 'Modal image'} src={property} />\n ) : (\n <IconContainer icon={property} style=\"filled\" tone={getIconContainerTone(tone)} />\n )}\n <Spacer size={16} variant=\"horizontal\" />\n </>\n )}\n {title && (\n <Typography component=\"h2\" id={`modal-title-${dataTestId ?? 'default'}`} variant=\"h3Strong\">\n {title}\n </Typography>\n )}\n {title && description && <Spacer size={8} variant=\"horizontal\" />}\n {description && (\n <Typography\n color={theme.palette.semantic.text['text-weak']}\n id={`modal-description-${dataTestId ?? 'default'}`}\n variant=\"b1Weak\"\n >\n {description}\n </Typography>\n )}\n {children && (\n <>\n <Spacer size={24} variant=\"horizontal\" />\n {children}\n </>\n )}\n </StyledContentContainer>\n {showButtonSection && (\n <>\n <Spacer size={24} variant=\"horizontal\" />\n <StyledButtonStack direction={isTabletOrMobile ? 'column' : 'row-reverse'} spacing={2}>\n <StyledButtonStack\n direction={isTabletOrMobile ? 'column-reverse' : 'row'}\n spacing={2}\n width={isTabletOrMobile ? '100%' : 'fit-content'}\n >\n {actions.map((action, index) => {\n const { label, id, ...buttonProps } = action;\n return (\n <Button\n key={id ?? `modal-action-${index}-${label.replace(/\\s+/g, '-').toLowerCase()}`}\n fdKey={id ?? `modal-action-${label.replace(/\\s+/g, '-').toLowerCase()}`}\n fullWidth={isTabletOrMobile}\n {...buttonProps}\n >\n {label}\n </Button>\n );\n })}\n </StyledButtonStack>\n </StyledButtonStack>\n </>\n )}\n </StyledModalBox>\n </MuiModal>\n );\n },\n);\n\nModal.displayName = 'Modal';\n\nexport default Modal;\n"],"names":["COLOURS","iconContainer","default","destructive","getIconContainerTone","tone","StyledModalBox","styled","Box","shouldForwardProp","prop","includes","theme","size","position","backgroundColor","palette","semantic","background","border","borderRadius","radius","padding","spacing","boxShadow","outline","maxHeight","overflowY","top","left","transform","maxWidth","width","breakpoints","down","breakpointValues","tablet","bottom","right","display","flexDirection","height","StyledImage","StyledButtonStack","Stack","justifyContent","alignItems","StyledContentContainer","flexGrow","minHeight","paddingBottom","Modal","memo","open","onClose","title","description","property","actions","className","dataTestId","children","useTheme","isTabletOrMobile","useMediaQuery","isImageUrl","showButtonSection","length","_jsx","MuiModal","_jsxs","alt","src","IconContainer","icon","style","Spacer","variant","Typography","component","id","color","text","_Fragment","direction","map","action","index","label","buttonProps","Button","fdKey","replace","toLowerCase","fullWidth","displayName"],"mappings":"2gBAyDA,MAAMA,EAAU,CACdC,cAAe,CACbC,QAAS,IAA0B,UACnCC,YAAa,IAA0B,gBAQrCC,EAAwBC,GACrBL,EAAQC,cAAcI,KAQzBC,EAAiBC,EAAAA,OAAOC,EAAK,CACjCC,kBAAoBC,IAAU,CAAC,OAAQ,QAAQC,SAASD,IADnCH,EAEC,EAAGK,QAAOC,WAAM,CACtCC,SAAU,WACVC,gBAAiBH,EAAMI,QAAQC,SAASC,WAAW,sBACnDC,OAAQ,OACRC,aAAcR,EAAMS,OAAO,aAC3BC,QAASV,EAAMW,QAAQ,GACvBC,UAAW,4EACXC,QAAS,OACTC,UAAW,OACXC,UAAW,OAGXC,IAAK,MACLC,KAAM,MACNC,UAAW,wBACXC,SAAmB,UAATlB,EAAmB,QAAU,QACvCmB,MAAO,eAAepB,EAAMW,QAAQ,MAGpC,CAACX,EAAMqB,YAAYC,KAAKC,EAAAA,iBAAiBC,SAAU,IAEpC,UAATvB,GAAoB,CACtBkB,SAAU,eAAenB,EAAMW,QAAQ,MACvCS,MAAO,eAAepB,EAAMW,QAAQ,MACpCK,IAAK,OACLS,OAAQzB,EAAMW,QAAQ,GACtBM,KAAMjB,EAAMW,QAAQ,GACpBe,MAAO1B,EAAMW,QAAQ,GACrBO,UAAW,OACXJ,UAAW,OACXC,UAAW,OAEXY,QAAS,OACTC,cAAe,aAIJ,UAAT3B,GAAoB,CACtB4B,OAAQ,eAAe7B,EAAMW,QAAQ,MACrCG,UAAW,eAAed,EAAMW,QAAQ,MACxCI,UAAW,OAEXY,QAAS,OACTC,cAAe,eAKfE,EAAcnC,EAAAA,OAAO,MAAPA,EAAc,EAAGK,YAAO,CAC1CoB,MAAO,OACPS,OAAQ,OACRrB,aAAcR,EAAMS,OAAO,gBAGvBsB,EAAoBpC,EAAAA,OAAOqC,EAAPrC,CAAc,CACtCsC,eAAgB,aAChBC,WAAY,WAGRC,EAAyBxC,EAAAA,OAAOC,EAAPD,EAAY,EAAGK,YAAO,CACnD,CAACA,EAAMqB,YAAYC,KAAKC,EAAAA,iBAAiBC,SAAU,CACjDY,SAAU,EACVT,QAAS,OACTC,cAAe,SACfS,UAAW,cACXC,cAAetC,EAAMW,QAAQ,QAW3B4B,EAAQC,EAAAA,MACZ,EACEC,OACAC,UACAjD,OAAO,UACPQ,OAAO,QACP0C,QACAC,cACAC,WACAC,UAAU,GACVC,YACA,cAAeC,EACfC,eAEA,MAAMjD,EAAQkD,IACRC,EAAmBC,EAAcpD,EAAMqB,YAAYC,KAAKC,EAAAA,iBAAiBC,SAEzE6B,EAAiC,iBAAbR,EACpBS,EAAoBR,EAAQS,OAAS,EAE3C,OACEC,EAAAA,IAACC,EAAQ,CAAA,mBACW,qBAAqBT,GAAc,YAAW,kBAC/C,eAAeA,GAAc,YAC9CD,UAAWA,EAAS,cACPC,EACbN,QAASA,EACTD,KAAMA,EAAIQ,SAEVS,EAAAA,KAAChE,EAAc,CAACO,KAAMA,EAAMR,KAAMA,EAAIwD,SAAA,CACpCS,EAAAA,KAACvB,EAAsB,CAAAc,SAAA,CACpBJ,GACCa,6BACGL,EACCG,EAAAA,IAAC1B,EAAW,CAAC6B,IAAKhB,GAAS,cAAeiB,IAAKf,IAE/CW,MAACK,EAAa,CAACC,KAAMjB,EAAUkB,MAAM,SAAStE,KAAMD,EAAqBC,KAE3E+D,EAAAA,IAACQ,EAAM,CAAC/D,KAAM,GAAIgE,QAAQ,kBAG7BtB,GACCa,MAACU,EAAU,CAACC,UAAU,KAAKC,GAAI,eAAepB,GAAc,YAAaiB,QAAQ,WAAUhB,SACxFN,IAGJA,GAASC,GAAeY,EAAAA,IAACQ,EAAM,CAAC/D,KAAM,EAAGgE,QAAQ,eACjDrB,GACCY,EAAAA,IAACU,EAAU,CACTG,MAAOrE,EAAMI,QAAQC,SAASiE,KAAK,aACnCF,GAAI,qBAAqBpB,GAAc,YACvCiB,QAAQ,SAAQhB,SAEfL,IAGJK,GACCS,OAAAa,EAAAA,SAAA,CAAAtB,SAAA,CACEO,MAACQ,GAAO/D,KAAM,GAAIgE,QAAQ,eACzBhB,QAINK,GACCI,EAAAA,2BACEF,EAAAA,IAACQ,EAAM,CAAC/D,KAAM,GAAIgE,QAAQ,eAC1BT,EAAAA,IAACzB,EAAiB,CAACyC,UAAWrB,EAAmB,SAAW,cAAexC,QAAS,EAACsC,SACnFO,EAAAA,IAACzB,EAAiB,CAChByC,UAAWrB,EAAmB,iBAAmB,MACjDxC,QAAS,EACTS,MAAO+B,EAAmB,OAAS,uBAElCL,EAAQ2B,KAAI,CAACC,EAAQC,KACpB,MAAMC,MAAEA,EAAKR,GAAEA,KAAOS,GAAgBH,EACtC,OACElB,EAAAA,IAACsB,EAAAA,OAAM,CAELC,MAAOX,GAAM,gBAAgBQ,EAAMI,QAAQ,OAAQ,KAAKC,gBACxDC,UAAW/B,KACP0B,WAEHD,GALIR,GAAM,gBAAgBO,KAASC,EAAMI,QAAQ,OAAQ,KAAKC,mCAmBvF1C,EAAM4C,YAAc"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsx as e,jsxs as t,Fragment as i}from"react/jsx-runtime";import{memo as a}from"react";import o from"@mui/material/Modal";import r from"@mui/material/Box";import{Button as n}from"../../atoms/Button/index.js";import l from"@mui/material/Typography";import s from"@mui/material/Stack";import{styled as d}from"@mui/material/styles";import m from"@mui/material/useMediaQuery";import c from"@mui/material/styles/useTheme";import{breakpointValues as p}from"../../../themes/tokens/breakpoints/breakpoints.js";import u from"../../atoms/IconContainer/index.js";import h from"../../Spacer/index.js";const f={iconContainer:{default:()=>"neutral",destructive:()=>"destructive"}},g=e=>f.iconContainer[e](),x=d(r,{shouldForwardProp:e=>!["tone","size"].includes(e)})((({theme:e,size:t})=>({position:"absolute",backgroundColor:e.palette.semantic.background["background-overlay"],border:"none",borderRadius:e.radius["radius-16"],padding:e.spacing(4),boxShadow:"0px 1px 6px 0px rgba(0, 0, 0, 0.10), 0px 8px 16px 0px rgba(0, 0, 0, 0.15)",outline:"none",maxHeight:"90vh",overflowY:"auto",top:"50%",left:"50%",transform:"translate(-50%, -50%)",maxWidth:"large"===t?"700px":"500px",width:`calc(100% - ${e.spacing(4)})`,[e.breakpoints.down(p.tablet)]:{..."small"===t&&{maxWidth:`calc(100% - ${e.spacing(4)})`,width:`calc(100% - ${e.spacing(4)})`,top:"auto",bottom:e.spacing(2),left:e.spacing(2),right:e.spacing(2),transform:"none",maxHeight:"70vh",overflowY:"auto",display:"flex",flexDirection:"column"},..."large"===t&&{height:`calc(100% - ${e.spacing(4)})`,maxHeight:`calc(100% - ${e.spacing(4)})`,overflowY:"auto",display:"flex",flexDirection:"column"}}}))),b=d("img")((({theme:e})=>({width:"100%",height:"auto",borderRadius:e.radius["radius-8"]}))),w=d(s)({justifyContent:"flex-start",alignItems:"center"}),
|
|
1
|
+
import{jsx as e,jsxs as t,Fragment as i}from"react/jsx-runtime";import{memo as a}from"react";import o from"@mui/material/Modal";import r from"@mui/material/Box";import{Button as n}from"../../atoms/Button/index.js";import l from"@mui/material/Typography";import s from"@mui/material/Stack";import{styled as d}from"@mui/material/styles";import m from"@mui/material/useMediaQuery";import c from"@mui/material/styles/useTheme";import{breakpointValues as p}from"../../../themes/tokens/breakpoints/breakpoints.js";import u from"../../atoms/IconContainer/index.js";import h from"../../Spacer/index.js";const f={iconContainer:{default:()=>"neutral",destructive:()=>"destructive"}},g=e=>f.iconContainer[e](),x=d(r,{shouldForwardProp:e=>!["tone","size"].includes(e)})((({theme:e,size:t})=>({position:"absolute",backgroundColor:e.palette.semantic.background["background-overlay"],border:"none",borderRadius:e.radius["radius-16"],padding:e.spacing(4),boxShadow:"0px 1px 6px 0px rgba(0, 0, 0, 0.10), 0px 8px 16px 0px rgba(0, 0, 0, 0.15)",outline:"none",maxHeight:"90vh",overflowY:"auto",top:"50%",left:"50%",transform:"translate(-50%, -50%)",maxWidth:"large"===t?"700px":"500px",width:`calc(100% - ${e.spacing(4)})`,[e.breakpoints.down(p.tablet)]:{..."small"===t&&{maxWidth:`calc(100% - ${e.spacing(4)})`,width:`calc(100% - ${e.spacing(4)})`,top:"auto",bottom:e.spacing(2),left:e.spacing(2),right:e.spacing(2),transform:"none",maxHeight:"70vh",overflowY:"auto",display:"flex",flexDirection:"column"},..."large"===t&&{height:`calc(100% - ${e.spacing(4)})`,maxHeight:`calc(100% - ${e.spacing(4)})`,overflowY:"auto",display:"flex",flexDirection:"column"}}}))),b=d("img")((({theme:e})=>({width:"100%",height:"auto",borderRadius:e.radius["radius-8"]}))),w=d(s)({justifyContent:"flex-start",alignItems:"center"}),y=d(r)((({theme:e})=>({[e.breakpoints.down(p.tablet)]:{flexGrow:1,display:"flex",flexDirection:"column",minHeight:"min-content",paddingBottom:e.spacing(3)}}))),v=a((({open:a,onClose:r,tone:s="default",size:d="large",title:f,description:v,property:k,actions:z=[],className:$,"data-testid":C,children:j})=>{const H=c(),M=m(H.breakpoints.down(p.tablet)),S="string"==typeof k,W=z.length>0;return e(o,{"aria-describedby":`modal-description-${C??"default"}`,"aria-labelledby":`modal-title-${C??"default"}`,className:$,"data-testid":C,onClose:r,open:a,children:t(x,{size:d,tone:s,children:[t(y,{children:[k&&t(i,{children:[S?e(b,{alt:f??"Modal image",src:k}):e(u,{icon:k,style:"filled",tone:g(s)}),e(h,{size:16,variant:"horizontal"})]}),f&&e(l,{component:"h2",id:`modal-title-${C??"default"}`,variant:"h3Strong",children:f}),f&&v&&e(h,{size:8,variant:"horizontal"}),v&&e(l,{color:H.palette.semantic.text["text-weak"],id:`modal-description-${C??"default"}`,variant:"b1Weak",children:v}),j&&t(i,{children:[e(h,{size:24,variant:"horizontal"}),j]})]}),W&&t(i,{children:[e(h,{size:24,variant:"horizontal"}),e(w,{direction:M?"column":"row-reverse",spacing:2,children:e(w,{direction:M?"column-reverse":"row",spacing:2,width:M?"100%":"fit-content",children:z.map(((t,i)=>{const{label:a,id:o,...r}=t;return e(n,{fdKey:o??`modal-action-${a.replace(/\s+/g,"-").toLowerCase()}`,fullWidth:M,...r,children:a},o??`modal-action-${i}-${a.replace(/\s+/g,"-").toLowerCase()}`)}))})})]})]})})}));v.displayName="Modal";export{v as default};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/components/molecules/Modal/index.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/explicit-function-return-type */\nimport { memo } from 'react';\n\nimport MuiModal from '@mui/material/Modal';\n\nimport Box from '@fd/components/atoms/Box';\nimport Button, { type ButtonProps } from '@fd/components/atoms/Button';\nimport Typography from '@fd/components/atoms/Typography';\nimport Stack from '@fd/components/molecules/Stack';\nimport styled from '@fd/utilities/styledUtilities';\nimport useMediaQuery from '@fd/utilities/useMediaQuery';\nimport useTheme from '@fd/utilities/useTheme';\n\nimport { breakpointValues } from '../../../themes/tokens/breakpoints/breakpoints';\nimport IconContainer, { type IconContainerTones } from '../../atoms/IconContainer';\nimport Spacer from '../../Spacer';\n\n/** Visual tone of the modal */\nexport type ModalTones = 'default' | 'destructive';\n\n/** Size variants for the modal */\nexport type ModalSizes = 'large' | 'small';\n\n/** Action button configuration for Modal */\nexport interface ModalAction extends Omit<ButtonProps, 'children' | 'fdKey' | 'fullWidth'> {\n /** Label text for the button */\n label: string;\n /** Optional unique identifier for the button */\n id?: string;\n}\n\n/** Props for the Modal component */\nexport interface ModalProps {\n /** Whether the modal is open */\n open: boolean;\n /** Callback function when the modal is closed */\n onClose: () => void;\n /** Visual tone of the modal */\n tone?: ModalTones;\n /** Size variant of the modal */\n size?: ModalSizes;\n /** Main heading text of the modal */\n title?: string;\n /** Detailed description text of the modal */\n description?: string;\n /** Property element to display - can be an image URL (string) or an icon component (ReactNode) */\n property?: React.ReactNode | string;\n /** Array of action buttons to display */\n actions?: ModalAction[];\n /** Additional CSS class names */\n className?: string;\n /** Test ID for testing and automation */\n 'data-testid'?: string;\n /** Content to display in the modal */\n children?: React.ReactNode;\n}\n\nconst COLOURS = {\n iconContainer: {\n default: (): IconContainerTones => 'neutral',\n destructive: (): IconContainerTones => 'destructive',\n },\n button: {\n default: (): ButtonProps['color'] => 'primary',\n destructive: (): ButtonProps['color'] => 'error',\n },\n};\n\nconst getIconContainerTone = (tone: ModalTones): IconContainerTones => {\n return COLOURS.iconContainer[tone]();\n};\n\ninterface StyledModalBoxProps {\n tone: ModalTones;\n size: ModalSizes;\n}\n\nconst StyledModalBox = styled(Box, {\n shouldForwardProp: (prop) => !['tone', 'size'].includes(prop as string),\n})<StyledModalBoxProps>(({ theme, size }) => ({\n position: 'absolute',\n backgroundColor: theme.palette.semantic.background['background-overlay'],\n border: 'none',\n borderRadius: theme.radius['radius-16'],\n padding: theme.spacing(4),\n boxShadow: '0px 1px 6px 0px rgba(0, 0, 0, 0.10), 0px 8px 16px 0px rgba(0, 0, 0, 0.15)', // TODO: Pull shadow from tokens when setup\n outline: 'none',\n maxHeight: '90vh',\n overflowY: 'auto',\n\n // Desktop styles (default)\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n maxWidth: size === 'large' ? '700px' : '500px',\n width: `calc(100% - ${theme.spacing(4)})`,\n\n // Tablet & Mobile styles\n [theme.breakpoints.down(breakpointValues.tablet)]: {\n // Small size at bottom of screen\n ...(size === 'small' && {\n maxWidth: `calc(100% - ${theme.spacing(4)})`,\n width: `calc(100% - ${theme.spacing(4)})`,\n top: 'auto',\n bottom: theme.spacing(2),\n left: theme.spacing(2),\n right: theme.spacing(2),\n transform: 'none',\n maxHeight: '70vh',\n overflowY: 'auto',\n // Flex layout to push buttons to bottom when content is small\n display: 'flex',\n flexDirection: 'column',\n }),\n\n // Large size takes full screen\n ...(size === 'large' && {\n height: `calc(100% - ${theme.spacing(4)})`,\n maxHeight: `calc(100% - ${theme.spacing(4)})`,\n overflowY: 'auto',\n // Flex layout to push buttons to bottom when content is small\n display: 'flex',\n flexDirection: 'column',\n }),\n },\n}));\n\nconst StyledImage = styled('img')(({ theme }) => ({\n width: '100%',\n height: 'auto',\n borderRadius: theme.radius['radius-8'],\n}));\n\nconst StyledButtonStack = styled(Stack)({\n justifyContent: 'flex-start',\n alignItems: 'center',\n});\n\nconst StyledContentContainer = styled(Box)(({ theme }) => ({\n [theme.breakpoints.down(breakpointValues.tablet)]: {\n flexGrow: 1,\n display: 'flex',\n flexDirection: 'column',\n minHeight: 'min-content',\n paddingBottom: theme.spacing(3),\n },\n}));\n\n/**\n * Modal component is used to display content that temporarily blocks interaction with the main view.\n * It creates a focused mode for completing tasks or viewing important information without leaving the current page.\n *\n * The component is wrapped with React.memo to optimize performance by preventing unnecessary\n * re-renders when the component's props haven't changed.\n */\nconst Modal = memo(\n ({\n open,\n onClose,\n tone = 'default',\n size = 'large',\n title,\n description,\n property,\n actions = [],\n className,\n 'data-testid': dataTestId,\n children,\n }: ModalProps) => {\n const theme = useTheme();\n const isTabletOrMobile = useMediaQuery(theme.breakpoints.down(breakpointValues.tablet));\n\n const isImageUrl = typeof property === 'string';\n const showButtonSection = actions.length > 0;\n\n return (\n <MuiModal\n aria-describedby={`modal-description-${dataTestId ?? 'default'}`}\n aria-labelledby={`modal-title-${dataTestId ?? 'default'}`}\n className={className}\n data-testid={dataTestId}\n onClose={onClose}\n open={open}\n >\n <StyledModalBox size={size} tone={tone}>\n <StyledContentContainer>\n {property && (\n <>\n {isImageUrl ? (\n <StyledImage alt={title ?? 'Modal image'} src={property} />\n ) : (\n <IconContainer icon={property} style=\"filled\" tone={getIconContainerTone(tone)} />\n )}\n <Spacer size={16} variant=\"horizontal\" />\n </>\n )}\n {title && (\n <Typography component=\"h2\" id={`modal-title-${dataTestId ?? 'default'}`} variant=\"h3Strong\">\n {title}\n </Typography>\n )}\n {title && description && <Spacer size={8} variant=\"horizontal\" />}\n {description && (\n <Typography\n color={theme.palette.semantic.text['text-weak']}\n id={`modal-description-${dataTestId ?? 'default'}`}\n variant=\"b1Weak\"\n >\n {description}\n </Typography>\n )}\n {children && (\n <>\n <Spacer size={24} variant=\"horizontal\" />\n {children}\n </>\n )}\n </StyledContentContainer>\n {showButtonSection && (\n <>\n <Spacer size={24} variant=\"horizontal\" />\n <StyledButtonStack direction={isTabletOrMobile ? 'column' : 'row-reverse'} spacing={2}>\n <StyledButtonStack\n direction={isTabletOrMobile ? 'column-reverse' : 'row-reverse'}\n spacing={2}\n width={isTabletOrMobile ? '100%' : 'fit-content'}\n >\n {actions.map((action, index) => {\n const { label, id, ...buttonProps } = action;\n return (\n <Button\n key={id ?? `modal-action-${index}-${label.replace(/\\s+/g, '-').toLowerCase()}`}\n fdKey={id ?? `modal-action-${label.replace(/\\s+/g, '-').toLowerCase()}`}\n fullWidth={isTabletOrMobile}\n {...buttonProps}\n >\n {label}\n </Button>\n );\n })}\n </StyledButtonStack>\n </StyledButtonStack>\n </>\n )}\n </StyledModalBox>\n </MuiModal>\n );\n },\n);\n\nModal.displayName = 'Modal';\n\nexport default Modal;\n"],"names":["COLOURS","iconContainer","default","destructive","getIconContainerTone","tone","StyledModalBox","styled","Box","shouldForwardProp","prop","includes","theme","size","position","backgroundColor","palette","semantic","background","border","borderRadius","radius","padding","spacing","boxShadow","outline","maxHeight","overflowY","top","left","transform","maxWidth","width","breakpoints","down","breakpointValues","tablet","bottom","right","display","flexDirection","height","StyledImage","StyledButtonStack","Stack","justifyContent","alignItems","StyledContentContainer","flexGrow","minHeight","paddingBottom","Modal","memo","open","onClose","title","description","property","actions","className","dataTestId","children","useTheme","isTabletOrMobile","useMediaQuery","isImageUrl","showButtonSection","length","_jsx","MuiModal","_jsxs","alt","src","IconContainer","icon","style","Spacer","variant","Typography","component","id","color","text","_Fragment","direction","map","action","index","label","buttonProps","Button","fdKey","replace","toLowerCase","fullWidth","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/components/molecules/Modal/index.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/explicit-function-return-type */\nimport { memo } from 'react';\n\nimport MuiModal from '@mui/material/Modal';\n\nimport Box from '@fd/components/atoms/Box';\nimport Button, { type ButtonProps } from '@fd/components/atoms/Button';\nimport Typography from '@fd/components/atoms/Typography';\nimport Stack from '@fd/components/molecules/Stack';\nimport styled from '@fd/utilities/styledUtilities';\nimport useMediaQuery from '@fd/utilities/useMediaQuery';\nimport useTheme from '@fd/utilities/useTheme';\n\nimport { breakpointValues } from '../../../themes/tokens/breakpoints/breakpoints';\nimport IconContainer, { type IconContainerTones } from '../../atoms/IconContainer';\nimport Spacer from '../../Spacer';\n\n/** Visual tone of the modal */\nexport type ModalTones = 'default' | 'destructive';\n\n/** Size variants for the modal */\nexport type ModalSizes = 'large' | 'small';\n\n/** Action button configuration for Modal */\nexport interface ModalAction extends Omit<ButtonProps, 'children' | 'fdKey' | 'fullWidth'> {\n /** Label text for the button */\n label: string;\n /** Optional unique identifier for the button */\n id?: string;\n}\n\n/** Props for the Modal component */\nexport interface ModalProps {\n /** Whether the modal is open */\n open: boolean;\n /** Callback function when the modal is closed */\n onClose: () => void;\n /** Visual tone of the modal */\n tone?: ModalTones;\n /** Size variant of the modal */\n size?: ModalSizes;\n /** Main heading text of the modal */\n title?: string;\n /** Detailed description text of the modal */\n description?: string;\n /** Property element to display - can be an image URL (string) or an icon component (ReactNode) */\n property?: React.ReactNode | string;\n /** Array of action buttons to display */\n actions?: ModalAction[];\n /** Additional CSS class names */\n className?: string;\n /** Test ID for testing and automation */\n 'data-testid'?: string;\n /** Content to display in the modal */\n children?: React.ReactNode;\n}\n\nconst COLOURS = {\n iconContainer: {\n default: (): IconContainerTones => 'neutral',\n destructive: (): IconContainerTones => 'destructive',\n },\n button: {\n default: (): ButtonProps['color'] => 'primary',\n destructive: (): ButtonProps['color'] => 'error',\n },\n};\n\nconst getIconContainerTone = (tone: ModalTones): IconContainerTones => {\n return COLOURS.iconContainer[tone]();\n};\n\ninterface StyledModalBoxProps {\n tone: ModalTones;\n size: ModalSizes;\n}\n\nconst StyledModalBox = styled(Box, {\n shouldForwardProp: (prop) => !['tone', 'size'].includes(prop as string),\n})<StyledModalBoxProps>(({ theme, size }) => ({\n position: 'absolute',\n backgroundColor: theme.palette.semantic.background['background-overlay'],\n border: 'none',\n borderRadius: theme.radius['radius-16'],\n padding: theme.spacing(4),\n boxShadow: '0px 1px 6px 0px rgba(0, 0, 0, 0.10), 0px 8px 16px 0px rgba(0, 0, 0, 0.15)', // TODO: Pull shadow from tokens when setup\n outline: 'none',\n maxHeight: '90vh',\n overflowY: 'auto',\n\n // Desktop styles (default)\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n maxWidth: size === 'large' ? '700px' : '500px',\n width: `calc(100% - ${theme.spacing(4)})`,\n\n // Tablet & Mobile styles\n [theme.breakpoints.down(breakpointValues.tablet)]: {\n // Small size at bottom of screen\n ...(size === 'small' && {\n maxWidth: `calc(100% - ${theme.spacing(4)})`,\n width: `calc(100% - ${theme.spacing(4)})`,\n top: 'auto',\n bottom: theme.spacing(2),\n left: theme.spacing(2),\n right: theme.spacing(2),\n transform: 'none',\n maxHeight: '70vh',\n overflowY: 'auto',\n // Flex layout to push buttons to bottom when content is small\n display: 'flex',\n flexDirection: 'column',\n }),\n\n // Large size takes full screen\n ...(size === 'large' && {\n height: `calc(100% - ${theme.spacing(4)})`,\n maxHeight: `calc(100% - ${theme.spacing(4)})`,\n overflowY: 'auto',\n // Flex layout to push buttons to bottom when content is small\n display: 'flex',\n flexDirection: 'column',\n }),\n },\n}));\n\nconst StyledImage = styled('img')(({ theme }) => ({\n width: '100%',\n height: 'auto',\n borderRadius: theme.radius['radius-8'],\n}));\n\nconst StyledButtonStack = styled(Stack)({\n justifyContent: 'flex-start',\n alignItems: 'center',\n});\n\nconst StyledContentContainer = styled(Box)(({ theme }) => ({\n [theme.breakpoints.down(breakpointValues.tablet)]: {\n flexGrow: 1,\n display: 'flex',\n flexDirection: 'column',\n minHeight: 'min-content',\n paddingBottom: theme.spacing(3),\n },\n}));\n\n/**\n * Modal component is used to display content that temporarily blocks interaction with the main view.\n * It creates a focused mode for completing tasks or viewing important information without leaving the current page.\n *\n * The component is wrapped with React.memo to optimize performance by preventing unnecessary\n * re-renders when the component's props haven't changed.\n */\nconst Modal = memo(\n ({\n open,\n onClose,\n tone = 'default',\n size = 'large',\n title,\n description,\n property,\n actions = [],\n className,\n 'data-testid': dataTestId,\n children,\n }: ModalProps) => {\n const theme = useTheme();\n const isTabletOrMobile = useMediaQuery(theme.breakpoints.down(breakpointValues.tablet));\n\n const isImageUrl = typeof property === 'string';\n const showButtonSection = actions.length > 0;\n\n return (\n <MuiModal\n aria-describedby={`modal-description-${dataTestId ?? 'default'}`}\n aria-labelledby={`modal-title-${dataTestId ?? 'default'}`}\n className={className}\n data-testid={dataTestId}\n onClose={onClose}\n open={open}\n >\n <StyledModalBox size={size} tone={tone}>\n <StyledContentContainer>\n {property && (\n <>\n {isImageUrl ? (\n <StyledImage alt={title ?? 'Modal image'} src={property} />\n ) : (\n <IconContainer icon={property} style=\"filled\" tone={getIconContainerTone(tone)} />\n )}\n <Spacer size={16} variant=\"horizontal\" />\n </>\n )}\n {title && (\n <Typography component=\"h2\" id={`modal-title-${dataTestId ?? 'default'}`} variant=\"h3Strong\">\n {title}\n </Typography>\n )}\n {title && description && <Spacer size={8} variant=\"horizontal\" />}\n {description && (\n <Typography\n color={theme.palette.semantic.text['text-weak']}\n id={`modal-description-${dataTestId ?? 'default'}`}\n variant=\"b1Weak\"\n >\n {description}\n </Typography>\n )}\n {children && (\n <>\n <Spacer size={24} variant=\"horizontal\" />\n {children}\n </>\n )}\n </StyledContentContainer>\n {showButtonSection && (\n <>\n <Spacer size={24} variant=\"horizontal\" />\n <StyledButtonStack direction={isTabletOrMobile ? 'column' : 'row-reverse'} spacing={2}>\n <StyledButtonStack\n direction={isTabletOrMobile ? 'column-reverse' : 'row'}\n spacing={2}\n width={isTabletOrMobile ? '100%' : 'fit-content'}\n >\n {actions.map((action, index) => {\n const { label, id, ...buttonProps } = action;\n return (\n <Button\n key={id ?? `modal-action-${index}-${label.replace(/\\s+/g, '-').toLowerCase()}`}\n fdKey={id ?? `modal-action-${label.replace(/\\s+/g, '-').toLowerCase()}`}\n fullWidth={isTabletOrMobile}\n {...buttonProps}\n >\n {label}\n </Button>\n );\n })}\n </StyledButtonStack>\n </StyledButtonStack>\n </>\n )}\n </StyledModalBox>\n </MuiModal>\n );\n },\n);\n\nModal.displayName = 'Modal';\n\nexport default Modal;\n"],"names":["COLOURS","iconContainer","default","destructive","getIconContainerTone","tone","StyledModalBox","styled","Box","shouldForwardProp","prop","includes","theme","size","position","backgroundColor","palette","semantic","background","border","borderRadius","radius","padding","spacing","boxShadow","outline","maxHeight","overflowY","top","left","transform","maxWidth","width","breakpoints","down","breakpointValues","tablet","bottom","right","display","flexDirection","height","StyledImage","StyledButtonStack","Stack","justifyContent","alignItems","StyledContentContainer","flexGrow","minHeight","paddingBottom","Modal","memo","open","onClose","title","description","property","actions","className","dataTestId","children","useTheme","isTabletOrMobile","useMediaQuery","isImageUrl","showButtonSection","length","_jsx","MuiModal","_jsxs","alt","src","IconContainer","icon","style","Spacer","variant","Typography","component","id","color","text","_Fragment","direction","map","action","index","label","buttonProps","Button","fdKey","replace","toLowerCase","fullWidth","displayName"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var r=require("@mui/material/RadioGroup");module.exports=r;
|
|
1
|
+
"use strict";var e=require("react/jsx-runtime"),r=require("react"),i=require("@mui/material/Box"),l=require("@mui/material/FormHelperText"),a=require("@mui/material/FormLabel"),t=require("@mui/material/RadioGroup"),n=require("@mui/material/styles"),s=require("../../../icons/CancelCircle/index.cjs.js"),o=require("../../atoms/Radio/index.cjs.js");const d=n.styled(i)((({theme:e})=>({marginBottom:e.spacing(0)}))),m=n.styled(i)((({theme:e})=>({display:"flex",flexDirection:"column",marginBottom:e.spacing(.5)}))),c=n.styled("span")((({theme:e})=>({color:e.palette.semantic.text["text-weak"],marginLeft:e.spacing(.5)})));module.exports=({fdKey:n="radio-default",value:u="",onChange:x,label:h,required:j=!1,helperText:p,errorText:v,options:q,...g})=>{const y=`${n}-label`,b=p?`${n}-helper`:void 0,f=v?`${n}-error`:void 0,B=[b,f].filter(Boolean).join(" ")||void 0;return e.jsxs(i,{children:[e.jsxs(m,{children:[e.jsxs(a,{id:y,children:[h," ",j&&e.jsx(c,{children:" *"})]}),p?e.jsx(l,{id:b,children:p}):null,!!v&&e.jsxs(l,{error:!0,id:f,children:[e.jsx(s,{size:"md"}),v]})]}),e.jsx(t,{"aria-describedby":B,"aria-invalid":!!v||void 0,"aria-labelledby":y,name:n,onChange:(e,r)=>x(r),value:u,...g,children:q.map((i=>{const l=u===i.value,{value:a,children:t,...s}=i,m=l&&t?r.isValidElement(t)?r.cloneElement(t,{required:j,error:!!v}):t:void 0;return e.jsx(d,{children:e.jsx(o,{...s,error:!!v,name:n,value:a,children:m})},a)}))})]})};
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../../../src/components/molecules/RadioGroup/index.tsx"],"sourcesContent":["import React, { isValidElement } from 'react';\n\nimport Box from '@mui/material/Box';\nimport MuiFormHelperText from '@mui/material/FormHelperText';\nimport FormLabel from '@mui/material/FormLabel';\nimport MuiRadioGroup, { type RadioGroupProps as MuiRadioGroupProps } from '@mui/material/RadioGroup';\nimport { styled } from '@mui/material/styles';\n\nimport CancelCircleIcon from '@fd/icons/CancelCircle';\n\nimport Radio, { type RadioProps } from '../../atoms/Radio';\n\nconst StyledBox = styled(Box)(({ theme }) => ({\n ...({ marginBottom: theme.spacing(0) }),\n}));\n\nconst StyledLabelContainer = styled(Box)(({ theme }) => ({\n display: 'flex',\n flexDirection: 'column',\n marginBottom: theme.spacing(0.5),\n}));\n\nconst WeakText = styled('span')(({ theme }) => ({\n color: theme.palette.semantic.text['text-weak'],\n marginLeft: theme.spacing(0.5),\n}));\n\nexport type RadioOption = Omit<\n RadioProps,\n 'aria-describedby' | 'checked' | 'name' | 'onChange' | 'required'\n> & {\n value: string;\n};\n\nexport interface RadioGroupProps\n extends Omit<MuiRadioGroupProps, 'name' | 'onChange' | 'value'> {\n /** Identifier applied to the radio group; also used for the radio name attribute. */\n fdKey?: string;\n /** Currently selected option value; use null or undefined when no option should be selected. */\n value?: string | null;\n /** Callback fired whenever the selection changes with the newly selected value. */\n onChange: (value: string) => void;\n /** Label rendered above the group; accepts plain text or a custom node. */\n label: React.ReactNode;\n /** Marks the radio group as required and surfaces the required indicator. */\n required?: boolean;\n /** Helper text displayed below the label to provide additional guidance. */\n helperText?: string;\n /** Error message shown below the helper text and marks the inputs as invalid. */\n errorText?: string;\n /** Collection of radio options to render; each item maps to a Radio component. */\n options: RadioOption[];\n}\n\nconst RadioGroup = ({\n fdKey = 'radio-default',\n value = '',\n onChange,\n label,\n required = false,\n helperText,\n errorText,\n options,\n ...groupProps\n}: RadioGroupProps): React.ReactElement => {\n\n const labelId = `${fdKey}-label`;\n const helperId = helperText ? `${fdKey}-helper` : undefined;\n const errorId = errorText ? `${fdKey}-error` : undefined;\n const describedBy = [helperId, errorId].filter(Boolean).join(' ') || undefined;\n\n const handleChange: MuiRadioGroupProps['onChange'] = (_e, newVal) => onChange(newVal);\n\n return (\n <Box>\n\n <StyledLabelContainer>\n <FormLabel id={labelId}>\n {label} {required && <WeakText>{' *'}</WeakText>}\n </FormLabel>\n {helperText ? (\n <MuiFormHelperText id={helperId}>\n {helperText}\n </MuiFormHelperText>\n ) : null}\n\n {!!errorText && (\n <MuiFormHelperText error id={errorId}>\n <CancelCircleIcon size={\"md\"} />\n {errorText}\n </MuiFormHelperText>\n )}\n\n </StyledLabelContainer>\n\n <MuiRadioGroup\n aria-describedby={describedBy}\n aria-invalid={errorText ? true : undefined}\n aria-labelledby={labelId}\n name={fdKey}\n onChange={handleChange}\n value={value}\n {...groupProps}\n >\n {options.map((opt) => {\n const isSelected = (value) === opt.value;\n const { value: optionValue, children, ...radioProps } = opt;\n\n // Clone children and pass required/error props when selected\n const enhancedChildren = isSelected && children\n ? isValidElement(children)\n ? React.cloneElement(children, { required, error: !!errorText } as Partial<unknown>)\n : children\n : undefined;\n\n return (\n <StyledBox key={optionValue}>\n <Radio\n {...radioProps}\n error={!!errorText}\n name={fdKey}\n value={optionValue}>\n {enhancedChildren}\n </Radio>\n </StyledBox>\n );\n })}\n </MuiRadioGroup>\n </Box>\n );\n}\n\nexport default RadioGroup;"],"names":["StyledBox","styled","Box","theme","marginBottom","spacing","StyledLabelContainer","display","flexDirection","WeakText","color","palette","semantic","text","marginLeft","fdKey","value","onChange","label","required","helperText","errorText","options","groupProps","labelId","helperId","undefined","errorId","describedBy","filter","Boolean","join","_jsxs","children","FormLabel","id","_jsx","MuiFormHelperText","error","CancelCircleIcon","size","MuiRadioGroup","name","_e","newVal","map","opt","isSelected","optionValue","radioProps","enhancedChildren","isValidElement","React","cloneElement","Radio"],"mappings":"2VAYA,MAAMA,EAAYC,EAAAA,OAAOC,EAAPD,EAAY,EAAGE,YAAO,CAChCC,aAAcD,EAAME,QAAQ,OAG9BC,EAAuBL,EAAAA,OAAOC,EAAPD,EAAY,EAAGE,YAAO,CACjDI,QAAS,OACTC,cAAe,SACfJ,aAAcD,EAAME,QAAQ,QAGxBI,EAAWR,EAAAA,OAAO,OAAPA,EAAe,EAAGE,YAAO,CACxCO,MAAOP,EAAMQ,QAAQC,SAASC,KAAK,aACnCC,WAAYX,EAAME,QAAQ,uBA8BT,EACjBU,QAAQ,gBACRC,QAAQ,GACRC,WACAC,QACAC,YAAW,EACXC,aACAC,YACAC,aACGC,MAGH,MAAMC,EAAU,GAAGT,UACbU,EAAWL,EAAa,GAAGL,gBAAiBW,EAC5CC,EAAUN,EAAY,GAAGN,eAAgBW,EACzCE,EAAc,CAACH,EAAUE,GAASE,OAAOC,SAASC,KAAK,WAAQL,EAIrE,OACEM,EAAAA,KAAC9B,EAAG,CAAA+B,SAAA,CAEFD,OAAC1B,EAAoB,CAAA2B,SAAA,CACnBD,EAAAA,KAACE,EAAS,CAACC,GAAIX,EAAOS,SAAA,CACnBf,EAAK,IAAGC,GAAYiB,EAAAA,IAAC3B,EAAQ,CAAAwB,SAAE,UAEjCb,EACCgB,EAAAA,IAACC,EAAiB,CAACF,GAAIV,EAAQQ,SAC5Bb,IAED,OAEDC,GACDW,EAAAA,KAACK,EAAiB,CAACC,OAAK,EAACH,GAAIR,EAAOM,SAAA,CAClCG,EAAAA,IAACG,EAAgB,CAACC,KAAM,OACvBnB,QAMPe,EAAAA,IAACK,EAAa,CAAA,mBACMb,mBACJP,QAAmBK,EAAS,kBACzBF,EACjBkB,KAAM3B,EACNE,SA7B+C,CAAC0B,EAAIC,IAAW3B,EAAS2B,GA8BxE5B,MAAOA,KACHO,EAAUU,SAEbX,EAAQuB,KAAKC,IACZ,MAAMC,EAAa,IAAYD,EAAI9B,OAC3BA,MAAOgC,EAAWf,SAAEA,KAAagB,GAAeH,EAGlDI,EAAmBH,GAAcd,EACnCkB,EAAAA,eAAelB,GACbmB,EAAMC,aAAapB,EAAU,CAAEd,WAAUmB,QAASjB,IAClDY,OACFP,EAEJ,OACEU,EAAAA,IAACpC,EAAS,CAAAiC,SACRG,EAAAA,IAACkB,EAAK,IACAL,EACJX,QAASjB,EACTqB,KAAM3B,EACNC,MAAOgC,EAAWf,SACjBiB,KANWF"}
|
|
@@ -1,2 +1,29 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import react__default from 'react';
|
|
2
|
+
import { RadioGroupProps as RadioGroupProps$1 } from '@mui/material/RadioGroup';
|
|
3
|
+
import { RadioProps } from '../../atoms/Radio/index.js';
|
|
4
|
+
|
|
5
|
+
type RadioOption = Omit<RadioProps, 'aria-describedby' | 'checked' | 'name' | 'onChange' | 'required'> & {
|
|
6
|
+
value: string;
|
|
7
|
+
};
|
|
8
|
+
interface RadioGroupProps extends Omit<RadioGroupProps$1, 'name' | 'onChange' | 'value'> {
|
|
9
|
+
/** Identifier applied to the radio group; also used for the radio name attribute. */
|
|
10
|
+
fdKey?: string;
|
|
11
|
+
/** Currently selected option value; use null or undefined when no option should be selected. */
|
|
12
|
+
value?: string | null;
|
|
13
|
+
/** Callback fired whenever the selection changes with the newly selected value. */
|
|
14
|
+
onChange: (value: string) => void;
|
|
15
|
+
/** Label rendered above the group; accepts plain text or a custom node. */
|
|
16
|
+
label: react__default.ReactNode;
|
|
17
|
+
/** Marks the radio group as required and surfaces the required indicator. */
|
|
18
|
+
required?: boolean;
|
|
19
|
+
/** Helper text displayed below the label to provide additional guidance. */
|
|
20
|
+
helperText?: string;
|
|
21
|
+
/** Error message shown below the helper text and marks the inputs as invalid. */
|
|
22
|
+
errorText?: string;
|
|
23
|
+
/** Collection of radio options to render; each item maps to a Radio component. */
|
|
24
|
+
options: RadioOption[];
|
|
25
|
+
}
|
|
26
|
+
declare const RadioGroup: ({ fdKey, value, onChange, label, required, helperText, errorText, options, ...groupProps }: RadioGroupProps) => react__default.ReactElement;
|
|
27
|
+
|
|
28
|
+
export { RadioGroup as default };
|
|
29
|
+
export type { RadioGroupProps, RadioOption };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import o from"@mui/material/RadioGroup";
|
|
1
|
+
import{jsxs as e,jsx as r}from"react/jsx-runtime";import i,{isValidElement as o}from"react";import a from"@mui/material/Box";import l from"@mui/material/FormHelperText";import t from"@mui/material/FormLabel";import m from"@mui/material/RadioGroup";import{styled as n}from"@mui/material/styles";import d from"../../../icons/CancelCircle/index.js";import c from"../../atoms/Radio/index.js";const p=n(a)((({theme:e})=>({marginBottom:e.spacing(0)}))),s=n(a)((({theme:e})=>({display:"flex",flexDirection:"column",marginBottom:e.spacing(.5)}))),u=n("span")((({theme:e})=>({color:e.palette.semantic.text["text-weak"],marginLeft:e.spacing(.5)}))),h=({fdKey:n="radio-default",value:h="",onChange:f,label:x,required:v=!1,helperText:g,errorText:b,options:y,...j})=>{const B=`${n}-label`,C=g?`${n}-helper`:void 0,T=b?`${n}-error`:void 0,$=[C,T].filter(Boolean).join(" ")||void 0;return e(a,{children:[e(s,{children:[e(t,{id:B,children:[x," ",v&&r(u,{children:" *"})]}),g?r(l,{id:C,children:g}):null,!!b&&e(l,{error:!0,id:T,children:[r(d,{size:"md"}),b]})]}),r(m,{"aria-describedby":$,"aria-invalid":!!b||void 0,"aria-labelledby":B,name:n,onChange:(e,r)=>f(r),value:h,...j,children:y.map((e=>{const a=h===e.value,{value:l,children:t,...m}=e,d=a&&t?o(t)?i.cloneElement(t,{required:v,error:!!b}):t:void 0;return r(p,{children:r(c,{...m,error:!!b,name:n,value:l,children:d})},l)}))})]})};export{h as default};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/components/molecules/RadioGroup/index.tsx"],"sourcesContent":["import React, { isValidElement } from 'react';\n\nimport Box from '@mui/material/Box';\nimport MuiFormHelperText from '@mui/material/FormHelperText';\nimport FormLabel from '@mui/material/FormLabel';\nimport MuiRadioGroup, { type RadioGroupProps as MuiRadioGroupProps } from '@mui/material/RadioGroup';\nimport { styled } from '@mui/material/styles';\n\nimport CancelCircleIcon from '@fd/icons/CancelCircle';\n\nimport Radio, { type RadioProps } from '../../atoms/Radio';\n\nconst StyledBox = styled(Box)(({ theme }) => ({\n ...({ marginBottom: theme.spacing(0) }),\n}));\n\nconst StyledLabelContainer = styled(Box)(({ theme }) => ({\n display: 'flex',\n flexDirection: 'column',\n marginBottom: theme.spacing(0.5),\n}));\n\nconst WeakText = styled('span')(({ theme }) => ({\n color: theme.palette.semantic.text['text-weak'],\n marginLeft: theme.spacing(0.5),\n}));\n\nexport type RadioOption = Omit<\n RadioProps,\n 'aria-describedby' | 'checked' | 'name' | 'onChange' | 'required'\n> & {\n value: string;\n};\n\nexport interface RadioGroupProps\n extends Omit<MuiRadioGroupProps, 'name' | 'onChange' | 'value'> {\n /** Identifier applied to the radio group; also used for the radio name attribute. */\n fdKey?: string;\n /** Currently selected option value; use null or undefined when no option should be selected. */\n value?: string | null;\n /** Callback fired whenever the selection changes with the newly selected value. */\n onChange: (value: string) => void;\n /** Label rendered above the group; accepts plain text or a custom node. */\n label: React.ReactNode;\n /** Marks the radio group as required and surfaces the required indicator. */\n required?: boolean;\n /** Helper text displayed below the label to provide additional guidance. */\n helperText?: string;\n /** Error message shown below the helper text and marks the inputs as invalid. */\n errorText?: string;\n /** Collection of radio options to render; each item maps to a Radio component. */\n options: RadioOption[];\n}\n\nconst RadioGroup = ({\n fdKey = 'radio-default',\n value = '',\n onChange,\n label,\n required = false,\n helperText,\n errorText,\n options,\n ...groupProps\n}: RadioGroupProps): React.ReactElement => {\n\n const labelId = `${fdKey}-label`;\n const helperId = helperText ? `${fdKey}-helper` : undefined;\n const errorId = errorText ? `${fdKey}-error` : undefined;\n const describedBy = [helperId, errorId].filter(Boolean).join(' ') || undefined;\n\n const handleChange: MuiRadioGroupProps['onChange'] = (_e, newVal) => onChange(newVal);\n\n return (\n <Box>\n\n <StyledLabelContainer>\n <FormLabel id={labelId}>\n {label} {required && <WeakText>{' *'}</WeakText>}\n </FormLabel>\n {helperText ? (\n <MuiFormHelperText id={helperId}>\n {helperText}\n </MuiFormHelperText>\n ) : null}\n\n {!!errorText && (\n <MuiFormHelperText error id={errorId}>\n <CancelCircleIcon size={\"md\"} />\n {errorText}\n </MuiFormHelperText>\n )}\n\n </StyledLabelContainer>\n\n <MuiRadioGroup\n aria-describedby={describedBy}\n aria-invalid={errorText ? true : undefined}\n aria-labelledby={labelId}\n name={fdKey}\n onChange={handleChange}\n value={value}\n {...groupProps}\n >\n {options.map((opt) => {\n const isSelected = (value) === opt.value;\n const { value: optionValue, children, ...radioProps } = opt;\n\n // Clone children and pass required/error props when selected\n const enhancedChildren = isSelected && children\n ? isValidElement(children)\n ? React.cloneElement(children, { required, error: !!errorText } as Partial<unknown>)\n : children\n : undefined;\n\n return (\n <StyledBox key={optionValue}>\n <Radio\n {...radioProps}\n error={!!errorText}\n name={fdKey}\n value={optionValue}>\n {enhancedChildren}\n </Radio>\n </StyledBox>\n );\n })}\n </MuiRadioGroup>\n </Box>\n );\n}\n\nexport default RadioGroup;"],"names":["StyledBox","styled","Box","theme","marginBottom","spacing","StyledLabelContainer","display","flexDirection","WeakText","color","palette","semantic","text","marginLeft","RadioGroup","fdKey","value","onChange","label","required","helperText","errorText","options","groupProps","labelId","helperId","undefined","errorId","describedBy","filter","Boolean","join","_jsxs","children","FormLabel","id","_jsx","MuiFormHelperText","error","CancelCircleIcon","size","MuiRadioGroup","name","_e","newVal","map","opt","isSelected","optionValue","radioProps","enhancedChildren","isValidElement","React","cloneElement","Radio"],"mappings":"oYAYA,MAAMA,EAAYC,EAAOC,EAAPD,EAAY,EAAGE,YAAO,CAChCC,aAAcD,EAAME,QAAQ,OAG9BC,EAAuBL,EAAOC,EAAPD,EAAY,EAAGE,YAAO,CACjDI,QAAS,OACTC,cAAe,SACfJ,aAAcD,EAAME,QAAQ,QAGxBI,EAAWR,EAAO,OAAPA,EAAe,EAAGE,YAAO,CACxCO,MAAOP,EAAMQ,QAAQC,SAASC,KAAK,aACnCC,WAAYX,EAAME,QAAQ,QA8BtBU,EAAa,EACjBC,QAAQ,gBACRC,QAAQ,GACRC,WACAC,QACAC,YAAW,EACXC,aACAC,YACAC,aACGC,MAGH,MAAMC,EAAU,GAAGT,UACbU,EAAWL,EAAa,GAAGL,gBAAiBW,EAC5CC,EAAUN,EAAY,GAAGN,eAAgBW,EACzCE,EAAc,CAACH,EAAUE,GAASE,OAAOC,SAASC,KAAK,WAAQL,EAIrE,OACEM,EAAC/B,EAAG,CAAAgC,SAAA,CAEFD,EAAC3B,EAAoB,CAAA4B,SAAA,CACnBD,EAACE,EAAS,CAACC,GAAIX,EAAOS,SAAA,CACnBf,EAAK,IAAGC,GAAYiB,EAAC5B,EAAQ,CAAAyB,SAAE,UAEjCb,EACCgB,EAACC,EAAiB,CAACF,GAAIV,EAAQQ,SAC5Bb,IAED,OAEDC,GACDW,EAACK,EAAiB,CAACC,OAAK,EAACH,GAAIR,EAAOM,SAAA,CAClCG,EAACG,EAAgB,CAACC,KAAM,OACvBnB,QAMPe,EAACK,EAAa,CAAA,mBACMb,mBACJP,QAAmBK,EAAS,kBACzBF,EACjBkB,KAAM3B,EACNE,SA7B+C,CAAC0B,EAAIC,IAAW3B,EAAS2B,GA8BxE5B,MAAOA,KACHO,EAAUU,SAEbX,EAAQuB,KAAKC,IACZ,MAAMC,EAAa,IAAYD,EAAI9B,OAC3BA,MAAOgC,EAAWf,SAAEA,KAAagB,GAAeH,EAGlDI,EAAmBH,GAAcd,EACnCkB,EAAelB,GACbmB,EAAMC,aAAapB,EAAU,CAAEd,WAAUmB,QAASjB,IAClDY,OACFP,EAEJ,OACEU,EAACrC,EAAS,CAAAkC,SACRG,EAACkB,EAAK,IACAL,EACJX,QAASjB,EACTqB,KAAM3B,EACNC,MAAOgC,EAAWf,SACjBiB,KANWF"}
|