iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 24
1
自我挑戰組

30 天來點 Design System系列 第 24

Day 24 建立 Radio & Checkbox

  • 分享至 

  • xImage
  •  

會有以下幾種狀態:

  • reqired 是否為必填
  • disabled 禁止輸入

Checkbox

實作:

import React, { Fragment } from "react";
import isNumber from 'lodash/isNumber';
import styled from "styled-jss";
import propTypes from "prop-types";
import FromLabel from "../FromLabel";
import FormGroup from "../FormGroup";
import breakpoint from "../constant/breakpoint";

const StyledCheck = styled("label")({
  position: "relative",
  display: "flex",
  paddingLeft: 30,
  opacity: ({ disabled }) => disabled ? 0.5 : 1,
  marginBottom: ({ theme }) => theme.getSpacing(1),
  paddingRight: ({ theme }) => theme.getSpacing(1),
  alignItems: "center",
  "&:before": {
    position: "absolute",
    content: '""',
    left: 0,
    width: 8,
    height: 8,
    margin: ({ theme }) => theme.getSpacing(1),
    border: ({ theme, checked }) =>
      `5px solid ${checked ? theme.colors.primary : theme.colors.grey1}`,
  },
  [breakpoint.mediaLG]: {
    marginBottom: ({ theme }) => theme.getSpacing(0),
  },
});

const CheckItems = ({ items, name, disabled, onChange, values: currentValues }) => {
  return items.map((item, index) => {
    const itemValue = isNumber(item.value) ? item.value.toString() : item.value;
    const checked = currentValues.includes(itemValue);
    const ID = `${name}_${item.value}_${index}`;

    return (
      <Fragment key={`radios_${index}`}>
        <StyledCheck htmlFor={ID} checked={checked} disabled={disabled}>
          {item.label}
        </StyledCheck>
        <input
          type='checkbox'
          id={ID}
          name={name}
          value={item.value}
          onChange={onChange}
          checked={checked}
          disabled={disabled}
          style={{ display: "none" }}
        />
      </Fragment>
    );
  });
};

const CheckGroup = ({
  name,
  items,
  label,
  values,
  onChange,
  required,
  disabled,
  ...props
}) => {
  return (
    <FormGroup>
      <FromLabel required={required}>{label}</FromLabel>
      <CheckItems values={values} items={items} name={name} disabled={disabled} onChange={onChange} />
    </FormGroup>
  );
};

CheckGroup.propTypes = {
  label: propTypes.string,
  disabled: propTypes.bool,
  items: propTypes.arrayOf(
    propTypes.shape({
      label: propTypes.string,
      value: propTypes.oneOfType([propTypes.number, propTypes.string]),
    })
  ),
  values: propTypes.arrayOf(
    propTypes.oneOfType([propTypes.number, propTypes.string])
  ),
  onChange: propTypes.func,
};

CheckGroup.defaultProps = {
  label: "",
  items: [],
  values: [],
  disabled: false,
  onChange: () => false,
};

export default CheckGroup;

Usage

import React, { Fragment, useState } from "react";
import theme from "../../lib/theme";
import FormControl from "../../lib/FormControl";
import ThemeProvider from "../../lib/ThemeProvider";
import CheckGroup from "../../lib/CheckGroup";

const CheckItems = [
  {
    label: "Radio 1",
    value: 1,
  },
  {
    label: "Radio 2",
    value: 2,
  },
  {
    label: "Radio 3",
    value: 3,
  },
];

const handleOnChange = (values, setValue) => ({ target: { value } }) => {
  const newValues = values.includes(value)
    ? values.filter((selectedValue) => selectedValue !== value)
    : [value, ...values];

    setValue(newValues);
};

const Provider = (props) => {
  return <ThemeProvider theme={theme}>{props.children}</ThemeProvider>;
};

const TemplateForm = (args) => {
  const [values, setValues] = useState([]);

  const onChange = handleOnChange(values, setValues);

  return (
    <Provider>
      <Fragment>
        <FormControl>
          <CheckGroup {...args} values={values} onChange={onChange} />
          <CheckGroup {...args} name='disabledTest' values={values} onChange={onChange} disabled/>
        </FormControl>
      </Fragment>
    </Provider>
  );
};


const Template = (args) => {
  const [values, setValues] = useState([]);

  const onChange = handleOnChange(values, setValues);

  return (
    <Provider>
      <Fragment>
        <FormControl>
          <CheckGroup {...args} values={values} onChange={onChange} />
        </FormControl>
      </Fragment>
    </Provider>
  );
};

export const Default = TemplateForm.bind({});
Default.args = {
  name: "username",
  items: CheckItems,
};

export const LabelCheck = Template.bind({});
LabelCheck.args = {
  name: "labelItems",
  items: CheckItems,
  label: "label",
  required: true,
};

// 你的頁面標題
export default {
  component: CheckGroup,
  title: "Form/Check",
};

結果如下:

Radio

實作

import React, { Fragment } from "react";
import styled from "styled-jss";
import propTypes from "prop-types";
import FromLabel from "../FromLabel";
import FormGroup from "../FormGroup";
import breakpoint from "../constant/breakpoint";

const StyledRadio = styled("label")({
  position: "relative",
  display: "flex",
  paddingLeft: 30,
  opacity: ({ disabled }) => disabled ? 0.5:1,
  marginBottom: ({ theme }) => theme.getSpacing(1),
  paddingRight: ({ theme }) => theme.getSpacing(1),
  alignItems: "center",
  "&:before": {
    position: "absolute",
    content: '""',
    left: 0,
    width: 8,
    height: 8,
    borderRadius: 10,
    margin: ({ theme }) => theme.getSpacing(1),
    border: ({ theme, checked }) =>
      `5px solid ${checked ? theme.colors.primary : theme.colors.grey1}`,
  },
  [breakpoint.mediaMD]: {
    marginBottom: ({ theme }) => theme.getSpacing(0),
  },
});

const RadioItems = ({
  items,
  name,
  onChange,
  disabled,
  value: currentValue,
}) => {
  return items.map((item, index) => {
    const checked = currentValue === item.value;
    const ID = `${name}_${item.value}_${index}`;

    return (
      <Fragment key={`radios_${index}`}>
        <StyledRadio htmlFor={ID} checked={checked} disabled={disabled}>
          {item.label}
        </StyledRadio>
        <input
          type='radio'
          id={ID}
          name={name}
          value={item.value}
          onChange={onChange}
          checked={checked}
          disabled={disabled}
          style={{ display: "none" }}
        />
      </Fragment>
    );
  });
};

const RadioGroup = ({
  name,
  items,
  label,
  value,
  onChange,
  required,
  disabled,
  ...props
}) => {
  return (
    <FormGroup>
      <FromLabel required={required}>{label}</FromLabel>
      <RadioItems
        name={name}
        value={value}
        items={items}
        onChange={onChange}
        disabled={disabled}
      />
    </FormGroup>
  );
};

RadioGroup.propTypes = {
  label: propTypes.string,
  disabled: propTypes.bool,
  items: propTypes.arrayOf(
    propTypes.shape({
      label: propTypes.string,
      value: propTypes.oneOfType([propTypes.number, propTypes.string]),
    })
  ),
  value: propTypes.oneOfType([propTypes.number, propTypes.string]),
  onChange: propTypes.func,
};

RadioGroup.defaultProps = {
  label: "",
  value: "",
  items: [],
  disabled: false,
  onChange: () => false,
};

export default RadioGroup;

Usage

import React, { Fragment, useState } from "react";
import theme from "../../lib/theme";
import FormControl from "../../lib/FormControl";
import ThemeProvider from "../../lib/ThemeProvider";
import RadioGroup from '../../lib/RadioGroup';

const RadioItems = [
  {
    label: 'Radio 1',
    value: 1
  },
  {
    label: 'Radio 2',
    value: 2
  },
  {
    label: 'Radio 3',
    value: 3
  }
]

const handleOnChange = (setValue) => ({ target: { value }}) => {
  setValue(parseInt(value, 10));
}

const Provider = (props) => {
  return <ThemeProvider theme={theme}>{props.children}</ThemeProvider>;
};

const TemplateForm = (args) => {

  const [value, setValue] = useState(1);

  const onChange = handleOnChange(setValue);

  return (
    <Provider>
      <Fragment>
        <FormControl>
            <RadioGroup {...args} value={value} onChange={onChange}/>
            <RadioGroup {...args} disabled name='disableTest' value={value} onChange={onChange}/>
        </FormControl>
      </Fragment>
    </Provider>
  );
};

const Template = (args) => {

  const [value, setValue] = useState(1);

  const onChange = handleOnChange(setValue);

  return (
    <Provider>
      <Fragment>
        <FormControl>
            <RadioGroup {...args} value={value} onChange={onChange}/>
        </FormControl>
      </Fragment>
    </Provider>
  );
};

export const Default = TemplateForm.bind({});
Default.args = {
  name: 'username',
  items: RadioItems
}

export const LabelRadio = Template.bind({});
LabelRadio.args = {
  name: 'labelItems',
  items: RadioItems,
  label: 'label',
  required: true,
}

// 你的頁面標題
export default {
  component: RadioGroup,
  title: "Form/Radio",
};

結果如下:


上一篇
Day 23 建立 Input
下一篇
Day 25 建立 下拉選單
系列文
30 天來點 Design System30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言