4848 - Update Checkbox component (#4883)
# Summary * Add hover state which defaults to **false** * Add disable state  closes #4848 --------- Co-authored-by: Charles Bochet <charles@twenty.com> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@ -22,39 +22,63 @@ export enum CheckboxSize {
|
|||||||
type CheckboxProps = {
|
type CheckboxProps = {
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
indeterminate?: boolean;
|
indeterminate?: boolean;
|
||||||
|
hoverable?: boolean;
|
||||||
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
onCheckedChange?: (value: boolean) => void;
|
onCheckedChange?: (value: boolean) => void;
|
||||||
variant?: CheckboxVariant;
|
variant?: CheckboxVariant;
|
||||||
size?: CheckboxSize;
|
size?: CheckboxSize;
|
||||||
shape?: CheckboxShape;
|
shape?: CheckboxShape;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledInputContainer = styled.div`
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type InputProps = {
|
type InputProps = {
|
||||||
checkboxSize: CheckboxSize;
|
checkboxSize: CheckboxSize;
|
||||||
variant: CheckboxVariant;
|
variant: CheckboxVariant;
|
||||||
indeterminate?: boolean;
|
indeterminate?: boolean;
|
||||||
|
hoverable?: boolean;
|
||||||
shape?: CheckboxShape;
|
shape?: CheckboxShape;
|
||||||
isChecked?: boolean;
|
isChecked?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const StyledInputContainer = styled.div<InputProps>`
|
||||||
|
--size: ${({ checkboxSize }) =>
|
||||||
|
checkboxSize === CheckboxSize.Large ? '32px' : '24px'};
|
||||||
|
align-items: center;
|
||||||
|
border-radius: ${({ theme, shape }) =>
|
||||||
|
shape === CheckboxShape.Rounded
|
||||||
|
? theme.border.radius.rounded
|
||||||
|
: theme.border.radius.sm};
|
||||||
|
|
||||||
|
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
|
||||||
|
display: flex;
|
||||||
|
padding: ${({ checkboxSize }) =>
|
||||||
|
checkboxSize === CheckboxSize.Large ? '6px' : '5px'};
|
||||||
|
position: relative;
|
||||||
|
${({ hoverable, isChecked, theme, indeterminate, disabled }) => {
|
||||||
|
if (!hoverable || disabled === true) return '';
|
||||||
|
return `&:hover{
|
||||||
|
background-color: ${
|
||||||
|
indeterminate || isChecked
|
||||||
|
? theme.color.blue10
|
||||||
|
: theme.background.transparent.light
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
}`;
|
||||||
|
}}
|
||||||
|
`;
|
||||||
|
|
||||||
const StyledInput = styled.input<InputProps>`
|
const StyledInput = styled.input<InputProps>`
|
||||||
cursor: pointer;
|
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
|
||||||
margin: 0;
|
margin: 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
|
||||||
& + label {
|
& + label {
|
||||||
--size: ${({ checkboxSize }) =>
|
--size: ${({ checkboxSize }) =>
|
||||||
checkboxSize === CheckboxSize.Large ? '18px' : '12px'};
|
checkboxSize === CheckboxSize.Large ? '18px' : '12px'};
|
||||||
cursor: pointer;
|
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
|
||||||
height: calc(var(--size) + 2px);
|
height: calc(var(--size) + 2px);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -66,8 +90,16 @@ const StyledInput = styled.input<InputProps>`
|
|||||||
checkboxSize === CheckboxSize.Large ? '18px' : '12px'};
|
checkboxSize === CheckboxSize.Large ? '18px' : '12px'};
|
||||||
background: ${({ theme, indeterminate, isChecked }) =>
|
background: ${({ theme, indeterminate, isChecked }) =>
|
||||||
indeterminate || isChecked ? theme.color.blue : 'transparent'};
|
indeterminate || isChecked ? theme.color.blue : 'transparent'};
|
||||||
border-color: ${({ theme, indeterminate, isChecked, variant }) => {
|
border-color: ${({
|
||||||
|
theme,
|
||||||
|
indeterminate,
|
||||||
|
isChecked,
|
||||||
|
variant,
|
||||||
|
disabled,
|
||||||
|
}) => {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
|
case disabled:
|
||||||
|
return theme.background.transparent.medium;
|
||||||
case indeterminate || isChecked:
|
case indeterminate || isChecked:
|
||||||
return theme.color.blue;
|
return theme.color.blue;
|
||||||
case variant === CheckboxVariant.Primary:
|
case variant === CheckboxVariant.Primary:
|
||||||
@ -83,21 +115,21 @@ const StyledInput = styled.input<InputProps>`
|
|||||||
? theme.border.radius.rounded
|
? theme.border.radius.rounded
|
||||||
: theme.border.radius.sm};
|
: theme.border.radius.sm};
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: ${({ variant }) =>
|
border-width: ${({ variant, checkboxSize }) =>
|
||||||
variant === CheckboxVariant.Tertiary ? '2px' : '1px'};
|
checkboxSize === CheckboxSize.Large ||
|
||||||
|
variant === CheckboxVariant.Tertiary
|
||||||
|
? '1.43px'
|
||||||
|
: '1px'};
|
||||||
content: '';
|
content: '';
|
||||||
cursor: pointer;
|
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: var(--size);
|
height: var(--size);
|
||||||
width: var(--size);
|
width: var(--size);
|
||||||
}
|
}
|
||||||
|
|
||||||
& + label > svg {
|
& + label > svg {
|
||||||
--padding: ${({ checkboxSize, variant }) =>
|
--padding: ${({ checkboxSize }) =>
|
||||||
checkboxSize === CheckboxSize.Large ||
|
checkboxSize === CheckboxSize.Large ? '2px' : '1px'};
|
||||||
variant === CheckboxVariant.Tertiary
|
|
||||||
? '2px'
|
|
||||||
: '1px'};
|
|
||||||
--size: ${({ checkboxSize }) =>
|
--size: ${({ checkboxSize }) =>
|
||||||
checkboxSize === CheckboxSize.Large ? '16px' : '12px'};
|
checkboxSize === CheckboxSize.Large ? '16px' : '12px'};
|
||||||
height: var(--size);
|
height: var(--size);
|
||||||
@ -117,7 +149,9 @@ export const Checkbox = ({
|
|||||||
variant = CheckboxVariant.Primary,
|
variant = CheckboxVariant.Primary,
|
||||||
size = CheckboxSize.Small,
|
size = CheckboxSize.Small,
|
||||||
shape = CheckboxShape.Squared,
|
shape = CheckboxShape.Squared,
|
||||||
|
hoverable = false,
|
||||||
className,
|
className,
|
||||||
|
disabled = false,
|
||||||
}: CheckboxProps) => {
|
}: CheckboxProps) => {
|
||||||
const [isInternalChecked, setIsInternalChecked] =
|
const [isInternalChecked, setIsInternalChecked] =
|
||||||
React.useState<boolean>(false);
|
React.useState<boolean>(false);
|
||||||
@ -135,7 +169,16 @@ export const Checkbox = ({
|
|||||||
const checkboxId = 'checkbox' + v4();
|
const checkboxId = 'checkbox' + v4();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledInputContainer className={className}>
|
<StyledInputContainer
|
||||||
|
checkboxSize={size}
|
||||||
|
variant={variant}
|
||||||
|
shape={shape}
|
||||||
|
isChecked={isInternalChecked}
|
||||||
|
hoverable={hoverable}
|
||||||
|
indeterminate={indeterminate}
|
||||||
|
className={className}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@ -149,6 +192,7 @@ export const Checkbox = ({
|
|||||||
shape={shape}
|
shape={shape}
|
||||||
isChecked={isInternalChecked}
|
isChecked={isInternalChecked}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
<label htmlFor={checkboxId}>
|
<label htmlFor={checkboxId}>
|
||||||
{indeterminate ? (
|
{indeterminate ? (
|
||||||
|
|||||||
@ -20,6 +20,8 @@ export const Default: Story = {
|
|||||||
args: {
|
args: {
|
||||||
checked: false,
|
checked: false,
|
||||||
indeterminate: false,
|
indeterminate: false,
|
||||||
|
hoverable: false,
|
||||||
|
disabled: false,
|
||||||
variant: CheckboxVariant.Primary,
|
variant: CheckboxVariant.Primary,
|
||||||
size: CheckboxSize.Small,
|
size: CheckboxSize.Small,
|
||||||
shape: CheckboxShape.Squared,
|
shape: CheckboxShape.Squared,
|
||||||
@ -34,6 +36,7 @@ export const Catalog: CatalogStory<Story, typeof Checkbox> = {
|
|||||||
size: { control: false },
|
size: { control: false },
|
||||||
indeterminate: { control: false },
|
indeterminate: { control: false },
|
||||||
checked: { control: false },
|
checked: { control: false },
|
||||||
|
hoverable: { control: false },
|
||||||
shape: { control: false },
|
shape: { control: false },
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
@ -41,12 +44,14 @@ export const Catalog: CatalogStory<Story, typeof Checkbox> = {
|
|||||||
dimensions: [
|
dimensions: [
|
||||||
{
|
{
|
||||||
name: 'state',
|
name: 'state',
|
||||||
values: ['unchecked', 'checked', 'indeterminate'],
|
values: ['disabled', 'unchecked', 'checked', 'indeterminate'],
|
||||||
props: (state: string) => {
|
props: (state: string) => {
|
||||||
|
if (state === 'disabled') {
|
||||||
|
return { disabled: true };
|
||||||
|
}
|
||||||
if (state === 'checked') {
|
if (state === 'checked') {
|
||||||
return { checked: true };
|
return { checked: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state === 'indeterminate') {
|
if (state === 'indeterminate') {
|
||||||
return { indeterminate: true };
|
return { indeterminate: true };
|
||||||
}
|
}
|
||||||
@ -59,9 +64,14 @@ export const Catalog: CatalogStory<Story, typeof Checkbox> = {
|
|||||||
props: (shape: CheckboxShape) => ({ shape }),
|
props: (shape: CheckboxShape) => ({ shape }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'variant',
|
name: 'isHoverable',
|
||||||
values: Object.values(CheckboxVariant),
|
values: ['default', 'hoverable'],
|
||||||
props: (variant: CheckboxVariant) => ({ variant }),
|
props: (isHoverable: string) => {
|
||||||
|
if (isHoverable === 'hoverable') {
|
||||||
|
return { hoverable: true };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'size',
|
name: 'size',
|
||||||
|
|||||||
Reference in New Issue
Block a user