Skip to content

Instantly share code, notes, and snippets.

@dimitur2204
Last active January 20, 2023 12:59
Show Gist options
  • Save dimitur2204/b9ea018fa71bcb6f0948483f70ef0cb1 to your computer and use it in GitHub Desktop.
Save dimitur2204/b9ea018fa71bcb6f0948483f70ef0cb1 to your computer and use it in GitHub Desktop.
MUI Radio Group using MUI Card as buttons (also accessible <3)
import React from 'react'
import {
Card,
CardProps,
FormControl,
FormControlLabel,
Radio,
RadioGroup,
RadioGroupProps,
Stack,
Unstable_Grid2 as Grid2,
} from '@mui/material'
import { styled, lighten } from '@mui/material/styles'
import { CardGiftcard, Check, Cyclone } from '@mui/icons-material'
// Theme is a MUI theme object with an added property of borders
// { borders: { semiRound: '4px' } }
import theme from 'common/theme'
export const StyledRadioCardItem = styled(Card)(() => ({
padding: theme.spacing(2),
margin: 0,
cursor: 'pointer',
border: `1px solid ${theme.palette.primary.main}`,
width: '100%',
'&:focus-within': {
outline: `2px solid ${theme.palette.common.black}`,
},
}))
interface StyledRadioCardItemProps extends CardProps {
control: React.ReactNode
icon: React.ReactNode
selected?: boolean
disabled?: boolean
}
// Temporarily here for testing until the components starts being used
export const testRadioOptions: Option[] = [
{
value: 'card',
label: 'Card',
icon: <Check sx={{ width: 80, height: 80 }} />,
},
{
value: 'bank',
label: 'Bank',
icon: <Cyclone sx={{ width: 80, height: 80 }} />,
},
{
value: 'paypal',
label: 'PayPal',
icon: <CardGiftcard sx={{ width: 80, height: 80 }} />,
},
]
function RadioCardItem({ control, icon, selected, disabled, ...rest }: StyledRadioCardItemProps) {
const selectedStyles = {
backgroundColor: selected ? lighten(theme.palette.primary.light, 0.7) : 'inherit',
}
const disabledStyles = {
opacity: 0.7,
backgroundColor: `${theme.palette.grey[300]} !important`,
pointerEvents: 'none',
}
let styles = {}
if (disabled) {
styles = disabledStyles
} else if (selected) {
styles = selectedStyles
}
return (
<StyledRadioCardItem
sx={styles}
{...rest}>
<Stack justifyContent="center" alignItems="center">
{icon}
{control}
</Stack>
</StyledRadioCardItem>
)
}
type Option = {
value: string
label: string
icon: React.ReactNode
}
export interface RadioCardGroupProps extends RadioGroupProps {
options: Option[]
defaultValue?: string
}
function RadioCardGroup({ options, defaultValue }: RadioCardGroupProps) {
const [value, setValue] = React.useState(defaultValue)
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value)
}
return (
<FormControl>
<RadioGroup
aria-labelledby="TODO: Label by the title"
name="controlled-radio-buttons-group"
value={value}
onChange={handleChange}>
<Grid2 spacing={2} container>
{options.map((option) => (
<Grid2 xs={4} key={option.value}>
<RadioCardItem
onClick={() => setValue(option.value)}
control={
<FormControlLabel
value={option.value}
disableTypography
sx={{ margin: 0, ...theme.typography.h6 }}
control={
<Radio
sx={{ opacity: 0, position: 'absolute', width: 0, height: 0 }}
inputProps={{
style: {
width: 0,
height: 0,
},
}}
/>
}
label={option.label}
/>
}
icon={option.icon}
selected={value === option.value}
/>
</Grid2>
))}
</Grid2>
</RadioGroup>
</FormControl>
)
}
export default RadioCardGroup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment