Skip to content

Commit

Permalink
Merge pull request #178 from reaviz/feature/GOOD-181
Browse files Browse the repository at this point in the history
Checkbox Component styling
  • Loading branch information
amcdnl authored Jun 21, 2024
2 parents dd7bd54 + 1d4124b commit edb75a4
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 58 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ module.exports = {
'no-relative-import-paths/no-relative-import-paths': [
'warn',
{ 'allowSameFolder': true, 'prefix': '@', 'rootDir': 'src' }
]
],
'arrow-body-style': ['warn', 'as-needed'],
}
};
44 changes: 43 additions & 1 deletion src/form/Checkbox/Checkbox.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,30 @@ export const Simple = () => {
return <Checkbox checked={state} label="Check me" onChange={setState} />;
};

export const WithoutLabel = () => {
const [state, setState] = useState(true);
return <Checkbox checked={state} onChange={setState} />;
};

export const LabelPosition = () => {
const [state, setState] = useState(true);
return (
<Fragment>
<div style={{ padding: 20 }}>
<Checkbox
checked={state}
label="Start label"
labelPosition="start"
onChange={setState}
/>
</div>
<div style={{ padding: 20 }}>
<Checkbox checked={state} label="End label" onChange={setState} />
</div>
</Fragment>
);
};

export const Intermediate = () => {
const [state, setState] = useState(true);
return (
Expand Down Expand Up @@ -81,8 +105,26 @@ export const Sizes = () => {

export const Disabled = () => {
const [state, setState] = useState(true);
const [state2, setState2] = useState(false);
return (
<Checkbox checked={state} label="Disabled" onChange={setState} disabled />
<>
<div style={{ padding: 20 }}>
<Checkbox
checked={state}
label="Disabled"
onChange={setState}
disabled
/>
</div>
<div style={{ padding: 20 }}>
<Checkbox
checked={state2}
label="Disabled"
onChange={setState2}
disabled
/>
</div>
</>
);
};

Expand Down
84 changes: 58 additions & 26 deletions src/form/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { FC, forwardRef, LegacyRef } from 'react';
import React, { FC, forwardRef, LegacyRef, useCallback } from 'react';
import { motion, useMotionValue, useTransform } from 'framer-motion';
import { twMerge } from 'tailwind-merge';
import { CheckboxTheme } from './CheckboxTheme';
import { useComponentTheme } from '@/utils';
import { CheckboxLabel } from './CheckboxLabel';

export interface CheckboxProps {
/**
Expand All @@ -20,6 +21,11 @@ export interface CheckboxProps {
*/
label?: string;

/**
* Label position of checkbox.
*/
labelPosition?: 'start' | 'end';

/**
* Whether the checkbox is disabled or not.
*/
Expand Down Expand Up @@ -91,13 +97,14 @@ export const Checkbox: FC<CheckboxProps & CheckboxRef> = forwardRef(
label,
disabled,
size = 'medium',
labelPosition = 'end',
onChange,
onBlur,
className,
containerClassName,
labelClassName,
borderPath = 'M 0 0 L 0 16 L 16 16 L 16 0 Z',
checkedPath = 'M 5.36396 8.17792 L 7.34236 9.91424 L 10.6044 5.832',
borderPath = 'M 1 0 L 16 0 C 16.552 0 17 0.448 17 1 L 17 15 C 17 15.552 16.552 16 16 16 L 1 16 C 0.448 16 0 15.552 0 15 L 0 1 C 0 0.448 0.448 0 1 0 Z',
checkedPath = 'M 4 8 L 8 12 L 12 4',
intermediatePath = 'M 5.36396 8.17792 L 10.6044 8.17792',
theme: customTheme,
...rest
Expand All @@ -114,15 +121,39 @@ export const Checkbox: FC<CheckboxProps & CheckboxRef> = forwardRef(
unchecked: { pathLength: 0 }
};

const handleOnChange = useCallback(() => {
if (!disabled && onChange) {
onChange(!checked);
}
}, [disabled, onChange, checked]);

return (
<div className={twMerge(theme.base, containerClassName)}>
<div
className={twMerge(
theme.base,
containerClassName,
checked && 'checked'
)}
>
{labelPosition === 'start' && label && (
<CheckboxLabel
label={label}
size={size}
checked={checked}
disabled={disabled}
onChange={handleOnChange}
labelClassName={twMerge('mr-2.5', labelClassName)}
theme={theme}
/>
)}
<motion.div
{...rest}
ref={ref}
tabIndex={disabled ? -1 : 0}
className={twMerge(
theme.checkbox,
disabled && theme.disabled,
theme.checkbox.base,
checked && theme.checkbox.checked,
disabled && theme.checkbox.disabled,
theme.sizes[size],
className
)}
Expand All @@ -148,7 +179,11 @@ export const Checkbox: FC<CheckboxProps & CheckboxRef> = forwardRef(
height={16}
>
<motion.path
className={theme.border}
className={twMerge(
theme.border.base,
checked && theme.border.checked,
disabled && theme.border.disabled
)}
d={borderPath}
variants={theme.boxVariants}
/>
Expand All @@ -157,7 +192,7 @@ export const Checkbox: FC<CheckboxProps & CheckboxRef> = forwardRef(
d={intermediatePath}
fill="transparent"
strokeWidth="1"
className={theme.check}
className={theme.check.base}
variants={checkVariants}
style={{ pathLength, opacity }}
custom={checked}
Expand All @@ -167,31 +202,28 @@ export const Checkbox: FC<CheckboxProps & CheckboxRef> = forwardRef(
d={checkedPath}
fill="transparent"
strokeWidth="1"
className={theme.check}
className={twMerge(
theme.check.base,
disabled && theme.check.disabled,
checked && theme.check.checked
)}
variants={checkVariants}
style={{ pathLength, opacity }}
custom={checked}
/>
)}
</motion.svg>
</motion.div>
{label && (
<span
className={twMerge(
theme.label.base,
theme.label.sizes[size],
disabled && theme.disabled,
!disabled && onChange && theme.label.clickable,
labelClassName
)}
onClick={() => {
if (!disabled && onChange) {
onChange?.(!checked);
}
}}
>
{label}
</span>
{labelPosition === 'end' && label && (
<CheckboxLabel
label={label}
size={size}
checked={checked}
disabled={disabled}
onChange={handleOnChange}
labelClassName={twMerge('ml-2.5', labelClassName)}
theme={theme}
/>
)}
</div>
);
Expand Down
41 changes: 41 additions & 0 deletions src/form/Checkbox/CheckboxLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { FC } from 'react';
import { twMerge } from 'tailwind-merge';
import { CheckboxTheme } from './CheckboxTheme';

interface CheckboxLabelProps {
label: string;
size: 'small' | 'medium' | 'large' | string;
disabled?: boolean;
checked?: boolean;
onChange?: () => void;
labelClassName?: string;
theme: CheckboxTheme;
}

export const CheckboxLabel: FC<CheckboxLabelProps> = ({
label,
size,
disabled,
checked,
onChange,
labelClassName,
theme
}) => (
<span
className={twMerge(
theme.label.base,
theme.label.sizes[size],
checked && theme.label.checked,
disabled && theme.label.disabled,
!disabled && onChange && theme.label.clickable,
labelClassName
)}
onClick={() => {
if (!disabled && onChange) {
onChange();
}
}}
>
{label}
</span>
);
Loading

0 comments on commit edb75a4

Please sign in to comment.