diff --git a/packages/Checkbox/Checkbox.css b/packages/Checkbox/Checkbox.css new file mode 100644 index 0000000..6755cfb --- /dev/null +++ b/packages/Checkbox/Checkbox.css @@ -0,0 +1,71 @@ +.van-checkbox { + display: flex; + align-items: center; + overflow: hidden; + user-select: none; +} +.van-checkbox__icon-wrap, +.van-checkbox__label { + line-height: 20px; + line-height: var(--checkbox-size, 20px); +} +.van-checkbox__icon-wrap { + flex: none; +} +.van-checkbox__icon { + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + width: 1em; + height: 1em; + color: transparent; + text-align: center; + transition-property: color, border-color, background-color; + font-size: 20px; + font-size: var(--checkbox-size, 20px); + border: 1px solid #c8c9cc; + border: 1px solid var(--checkbox-border-color, #c8c9cc); + transition-duration: 0.2s; + transition-duration: var(--checkbox-transition-duration, 0.2s); +} +.van-checkbox__icon--round { + border-radius: 100%; +} +.van-checkbox__icon--checked { + color: #fff; + color: var(--white, #fff); + background-color: #1989fa; + background-color: var(--checkbox-checked-icon-color, #1989fa); + border-color: #1989fa; + border-color: var(--checkbox-checked-icon-color, #1989fa); +} +.van-checkbox__icon--disabled { + background-color: #ebedf0; + background-color: var(--checkbox-disabled-background-color, #ebedf0); + border-color: #c8c9cc; + border-color: var(--checkbox-disabled-icon-color, #c8c9cc); +} +.van-checkbox__icon--disabled.van-checkbox__icon--checked { + color: #c8c9cc; + color: var(--checkbox-disabled-icon-color, #c8c9cc); +} +.van-checkbox__label { + word-wrap: break-word; + margin-left: 10px; + margin-left: var(--checkbox-label-margin, 10px); + color: #323233; + color: var(--checkbox-label-color, #323233); +} +.van-checkbox__label--left { + float: left; + margin: 0 10px 0 0; + margin: 0 var(--checkbox-label-margin, 10px) 0 0; +} +.van-checkbox__label--disabled { + color: #c8c9cc; + color: var(--checkbox-disabled-label-color, #c8c9cc); +} +.van-checkbox__label:empty { + margin: 0; +} diff --git a/packages/Checkbox/Checkbox.tsx b/packages/Checkbox/Checkbox.tsx new file mode 100644 index 0000000..cbd1616 --- /dev/null +++ b/packages/Checkbox/Checkbox.tsx @@ -0,0 +1,156 @@ +// packages +import React, { + FunctionComponent, + useContext, + CSSProperties, + ReactElement, +} from 'react'; +import clsx from 'clsx'; +import { View } from 'remax/wechat'; +// internal +import Icon from '../Icon'; +import CheckboxGroupContext, { + CheckboxChangeEvent, +} from '../CheckboxGroup/CheckboxGroupContext'; +import { Switch, Case } from '../tools/Switch'; +import pickStyle from '../tools/pick-style'; +import withDefaultProps from '../tools/with-default-props-advance'; +import './Checkbox.css'; + +// 默认值填充属性 +interface NeutralCheckboxProps { + shape: 'round' | 'square'; + value: boolean; + disabled: boolean; + labelDisabled: boolean; + labelPosition: 'left' | 'right'; + checkedColor: string; + iconSize: string; +} + +interface ExogenousCheckboxProps { + // 自定义图标 + icon?: ReactElement; + // 唯一标示 + name: string; + // 容器类名,用以覆盖内部 + className?: string; + // 事件回调 + onChange?: (event: CheckboxChangeEvent) => void; +} + +type CheckboxProps = NeutralCheckboxProps & ExogenousCheckboxProps; + +const DefaultCheckboxProps: NeutralCheckboxProps = { + shape: 'round', + value: false, + disabled: false, + labelDisabled: false, + labelPosition: 'right', + checkedColor: '#1989fa', + iconSize: '20px;', +}; + +const Checkbox: FunctionComponent = (props) => { + const { + labelPosition, + labelDisabled, + checkedColor, + icon, + iconSize, + name, + shape, + className, + children, + value, + onChange, + disabled: _disabled1, + } = props; + // const { isChecked, onChange, disabled: _disabled2 } = useContext( + // CheckboxGroupContext + // ); + const context = useContext(CheckboxGroupContext); + + // derivation, fuse standalone and CheckboxGroup usage + const disabled = _disabled1 || context?.disabled; + const checked = context ? context?.isChecked(name) : value; + const color = checked && !disabled ? checkedColor : undefined; + + // binding + const classnames = { + container: clsx(className, 'van-checkbox'), + label: clsx( + 'van-checkbox__label', + `van-checkbox__label--${labelPosition}`, + { + 'van-checkbox__label--disabled': disabled, + } + ), + iconParent: clsx('van-checkbox__icon', `van-checkbox__icon--${shape}`, { + 'van-checkbox__icon--disabled': disabled, + 'van-checkbox__icon--checked': checked, + }), + }; + const stylesheet: Record<'iconParent' | 'icon', CSSProperties> = { + iconParent: pickStyle({ + fontSize: iconSize, + borderColor: color, + backgroundColor: color, + }), + icon: { + lineHeight: '1.25em', + }, + }; + const visibility = { + customIcon: !!icon, + }; + + // 事件绑定 + const onChangeWrap = context?.onChange || onChange; + const onClick = () => { + if (!disabled) { + if (typeof onChangeWrap === 'function') { + onChangeWrap({ + detail: { + name, + status: !checked, + }, + }); + } + } + }; + const onClickLabel = () => { + if (!disabled && !labelDisabled) { + if (typeof onChangeWrap === 'function') { + onChangeWrap({ + detail: { + name, + status: !checked, + }, + }); + } + } + }; + + return ( + + + + + {icon} + + + + + + + + {children} + + + ); +}; + +export default withDefaultProps( + DefaultCheckboxProps +)(Checkbox); diff --git a/packages/Checkbox/index.ts b/packages/Checkbox/index.ts new file mode 100644 index 0000000..9e951f7 --- /dev/null +++ b/packages/Checkbox/index.ts @@ -0,0 +1,4 @@ +/** + * @description - nothing but export component + */ +export { default } from './Checkbox'; diff --git a/packages/CheckboxGroup/CheckboxGroup.css b/packages/CheckboxGroup/CheckboxGroup.css new file mode 100644 index 0000000..e69de29 diff --git a/packages/CheckboxGroup/CheckboxGroup.tsx b/packages/CheckboxGroup/CheckboxGroup.tsx new file mode 100644 index 0000000..fcd8ca9 --- /dev/null +++ b/packages/CheckboxGroup/CheckboxGroup.tsx @@ -0,0 +1,47 @@ +// packages +import React, { FunctionComponent } from 'react'; +// internal +import withDefaultProps from '../tools/with-default-props-advance'; +import CheckboxGroupContext, { + CheckboxChangeEventDetail, +} from './CheckboxGroupContext'; +import './CheckboxGroup.css'; + +// 默认值填充属性 +interface NeutralCheckboxGroupProps { + disabled: boolean; +} + +interface ExogenousCheckboxGroupProps { + name?: string; + value: string[]; + max?: number; + onChange?: (event: { detail: CheckboxChangeEventDetail }) => void; +} + +type RadioGroupProps = NeutralCheckboxGroupProps & ExogenousCheckboxGroupProps; + +const DefaultRadioGroupProps: NeutralCheckboxGroupProps = { + disabled: false, +}; + +const CheckboxGroup: FunctionComponent = (props) => { + const { disabled, value, onChange, children } = props; + const payload = { + disabled, + value, + onChange, + isChecked: (name: string) => value.includes(name), + }; + + return ( + + {children} + + ); +}; + +export default withDefaultProps< + ExogenousCheckboxGroupProps, + NeutralCheckboxGroupProps +>(DefaultRadioGroupProps)(CheckboxGroup); diff --git a/packages/CheckboxGroup/CheckboxGroupContext.ts b/packages/CheckboxGroup/CheckboxGroupContext.ts new file mode 100644 index 0000000..e644b13 --- /dev/null +++ b/packages/CheckboxGroup/CheckboxGroupContext.ts @@ -0,0 +1,21 @@ +// packages +import { createContext } from 'react'; + +// interface +export interface CheckboxChangeEventDetail { + name: string; + status: boolean; +} + +export interface CheckboxChangeEvent { + detail: CheckboxChangeEventDetail; +} + +export interface CheckboxGroupContextAbstract { + disabled: boolean; + isChecked: (name: string) => boolean; + onChange?: (event: { detail: CheckboxChangeEventDetail }) => void; +} + +// 默认值需要保持一致,以防万一 +export default createContext(null); diff --git a/packages/CheckboxGroup/index.ts b/packages/CheckboxGroup/index.ts new file mode 100644 index 0000000..567c942 --- /dev/null +++ b/packages/CheckboxGroup/index.ts @@ -0,0 +1,4 @@ +/** + * @description - nothing but export component + */ +export { default } from './CheckboxGroup'; diff --git a/public/app.css b/public/app.css index 95c5915..97bc957 100644 --- a/public/app.css +++ b/public/app.css @@ -118,6 +118,7 @@ page { position: static !important; } -.demo-block__radio { +.demo-block__radio, +.demo-block__checkbox { margin-top: 6px; } diff --git a/public/pages/checkbox/index.tsx b/public/pages/checkbox/index.tsx new file mode 100644 index 0000000..0da1600 --- /dev/null +++ b/public/pages/checkbox/index.tsx @@ -0,0 +1,166 @@ +// packages +import React, { useState } from 'react'; +import { View, Text } from 'remax/wechat'; + +// internal +import Image from '../../../packages/Image'; +import Checkbox from '../../../packages/Checkbox'; +import CheckboxGroup from '../../../packages/CheckboxGroup'; +import CellGroup from '../../../packages/CellGroup'; +import Cell from '../../../packages/Cell'; +import { CheckboxChangeEvent } from '../../../packages/CheckboxGroup/CheckboxGroupContext'; + +export default () => { + const [value, setValue] = useState(false); + const onChangeSingle = (event: CheckboxChangeEvent) => { + setValue(event.detail.status); + }; + const [value1, setValue1] = useState([]); + const onChangeGroup = (event: CheckboxChangeEvent) => { + const { + detail: { name, status }, + } = event; + const next = status + ? value1.concat(name) + : value1.filter((item) => item !== name); + + setValue1(next); + }; + + const icons = { + activeIcon: ( + + ), + inactiveIcon: ( + + ), + }; + + return ( + + 基本用法 + + + Apple + + + + 禁用状态 + + + Banana + + + Cherry + + + + 自定义形状 + + + Lemon + + + + 自定义颜色 + + + Menon + + + + 自定义大小 + + + Olive + + + + 自定义图标 + + + Pear + + + + 禁用文本点击 + + + Pomelo + + + + 复选框组 + + + + Pomelo + + + Banana + + + Cherry + + + + + 搭配单元格组件使用 + + + + } + /> + } + /> + } + /> + + + + + ); +};