Skip to content

Commit

Permalink
feat(checkbox): add checkbox & checkbox Group (#1371)
Browse files Browse the repository at this point in the history
Co-authored-by: ZhaoChen <ittisennsinn@gmail.com>
  • Loading branch information
itiiss and itiisennsinn authored Oct 25, 2021
1 parent 992c742 commit 4135236
Show file tree
Hide file tree
Showing 18 changed files with 1,019 additions and 342 deletions.
154 changes: 87 additions & 67 deletions src/checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,89 +1,109 @@
/* eslint-disable react/jsx-props-no-spreading */
/* eslint-disable react-hooks/exhaustive-deps */
import * as React from 'react';
import React from 'react';
import classNames from 'classnames';
import RcCheckbox from 'rc-checkbox';
import { CheckOutlined } from '@gio-design/icons';
import { usePrefixCls } from '@gio-design/utils';
import CheckboxGroupContext from './CheckboxGroupContext';
import { CheckboxProps } from './interface';

export const Checkbox: React.FC<CheckboxProps> = ({
prefixCls: customizePrefixCls,
className,
children,
style,
indeterminate,
onChange,
...restProps
}) => {
const rcCheckbox = React.useRef(null);

const [check, setChecked] = React.useState(restProps.checked);

const checkGroup = React.useContext(CheckboxGroupContext);

const handleChange = React.useCallback(
(e) => {
if (!checkGroup) {
setChecked(!check);
}
if (onChange) onChange(e);
checkGroup?.toggleOption?.({ label: children, value: restProps.value });
},
[onChange, check]
);
import { CheckboxProps, CheckboxValueType } from './interface';
import WithRef from '../utils/withRef';
import usePrefixCls from '../utils/hooks/use-prefix-cls';
import useControlledState from '../utils/hooks/useControlledState';

const prefixCls = usePrefixCls('checkbox', customizePrefixCls);
const checkProps: CheckboxProps = { ...restProps };
const Checkbox = WithRef<HTMLInputElement, CheckboxProps>((props, ref) => {
const {
indeterminate = false,
defaultChecked = false,
checked,
disabled = false,
color = 'transparent',
value = '',
children,
className,
style,
onChange,
...restProps
} = props;

if (checkGroup) {
checkProps.name = checkGroup.name;
checkProps.onChange = handleChange;
checkProps.checked = !!checkGroup.selectedValues.find((_) => _ === restProps.value);
checkProps.disabled = checkProps.disabled || checkGroup.disabled;
}
const checkboxProps: CheckboxProps = { ...restProps };

React.useEffect(() => {
checkGroup?.registerValue(restProps.value);
return () => {
checkGroup?.unRegisterValue(restProps.value);
};
}, [restProps.value]);
const prefixCls = usePrefixCls('checkbox');

const classes = classNames([className, prefixCls], {
[`${prefixCls}-${checked ? 'checked' : ''}`]: checked,
[`${prefixCls}-${indeterminate ? 'indeterminate' : ''}`]: indeterminate,
[`${prefixCls}-${disabled ? 'disabled' : ''}`]: disabled,
});

const checkboxCls = classNames(className, {
[`${prefixCls}-wrapper`]: true,
[`${prefixCls}-wrapper-disabled`]: checkProps.disabled,
[`${prefixCls}-wrapper-disabled`]: checkboxProps.disabled,
});

const checkboxClass = classNames({
[`${prefixCls}-indeterminate`]: indeterminate,
[`${prefixCls}-cool`]: !(checkProps.disabled || checkProps.checked || checkProps.indeterminate),
});
const [checkedStatus, setChecked] = useControlledState(checked, defaultChecked);

const checkboxIconClass = classNames({
[`${prefixCls}-icon`]: true,
[`${prefixCls}-icon-indeterminate`]: indeterminate,
[`${prefixCls}-icon-cool`]: !(checkProps.disabled || checkProps.checked || checkProps.indeterminate),
[`${prefixCls}-icon-disabled`]: checkProps.disabled,
[`${prefixCls}-icon-checked`]: checkProps.checked !== undefined ? checkProps.checked : check,
});
const checkboxGroup = React.useContext(CheckboxGroupContext);

const prevValue = React.useRef<CheckboxValueType>(value);

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setChecked(e.target.checked);
onChange?.(e);
};

React.useEffect(() => {
checkboxGroup?.registerValue(value);
}, []);

React.useEffect(() => {
if (value !== prevValue.current) {
checkboxGroup?.unRegisterValue(prevValue.current);
checkboxGroup?.registerValue(value);
}
return () => checkboxGroup?.unRegisterValue(value);
}, [value]);

if (checkboxGroup) {
checkboxProps.onChange = (...args) => {
if (onChange) {
onChange(...args);
}
if (checkboxGroup.toggleOption) {
checkboxGroup.toggleOption({ label: children, value });
}
};
checkboxProps.name = checkboxGroup.name;
checkboxProps.checked = checkboxGroup.selectedValues.indexOf(value) !== -1;
checkboxProps.disabled = disabled || checkboxGroup.disabled;
}

return (
// eslint-disable-next-line jsx-a11y/label-has-associated-control
<label className={checkboxCls} style={style}>
<CheckOutlined className={checkboxIconClass} />
<RcCheckbox
// eslint-disable-next-line @typescript-eslint/no-explicit-any
{...(checkProps as any)}
prefixCls={prefixCls}
ref={rcCheckbox}
<label className={checkboxCls}>
<input
type="checkbox"
className={checkboxClass}
ref={ref}
disabled={disabled}
className={classes}
indeterminate={indeterminate}
value={value}
checked={checkedStatus}
onChange={handleChange}
style={style}
color={color}
defaultChecked={defaultChecked}
{...checkboxProps}
/>
{children !== undefined ? <span>{children}</span> : null}
<span>{children}</span>
</label>
);
});

Checkbox.displayName = 'Checkbox';

Checkbox.defaultProps = {
indeterminate: false,
defaultChecked: false,
disabled: false,
color: 'transparent',
};

export default React.memo(Checkbox);
export default Checkbox;
43 changes: 18 additions & 25 deletions src/checkbox/demos/Checkbox.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,12 @@
import React from 'react';
import { Story, Meta } from '@storybook/react/types-6-0';
import { withDesign } from 'storybook-addon-designs';
import Docs from './CheckboxPage';
import Checkbox from '../Checkbox';
import CheckboxGroup from '../group';
import Checkbox from '../index';
import { CheckboxProps, CheckboxGroupProps, CheckboxValueType } from '../interface';
import '../style';

export default {
title: 'Components/Checkbox',
title: 'Upgraded/Checkbox',
component: Checkbox,
subcomponents: { CheckboxGroup },
decorators: [withDesign],
parameters: {
design: {
type: 'figma',
url: 'https://www.figma.com/file/kP3A6S2fLUGVVMBgDuUx0f/GrowingIO-Design-Components?node-id=889%3A1248',
allowFullscreen: true,
},
docs: {
page: Docs,
},
},
} as Meta;

const Template: Story<CheckboxProps> = (args) => {
Expand All @@ -31,21 +16,29 @@ const Template: Story<CheckboxProps> = (args) => {
};
return (
<Checkbox {...args} checked={checked} onChange={handleChange}>
Normal
我已阅读以下条款
</Checkbox>
);
};
const TemplateGroup: Story<CheckboxGroupProps<CheckboxValueType>> = (args) => <CheckboxGroup {...args} />;

export const Default = Template.bind({});
const TemplateGroup: Story<CheckboxGroupProps<CheckboxValueType>> = (args) => <Checkbox.Group {...args} />;

export const Group = TemplateGroup.bind({});

Default.args = {};
Group.args = {
options: [
{ label: '1', value: 1 },
{ label: '2', value: 2 },
{ label: '3', value: 3 },
{ label: '4', value: 4, disabled: true },
{ label: '我已阅读以下条款一', value: 1 },
{ label: '我已阅读以下条款二', value: 2 },
{ label: '我已阅读以下条款三', value: 3, disabled: true },
],
};

export const Default = Template.bind({});

export const Indeterminate = Template.bind({});

Indeterminate.args = {
indeterminate: true,
};

Default.args = {};
Loading

1 comment on commit 4135236

@vercel
Copy link

@vercel vercel bot commented on 4135236 Oct 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.