diff --git a/packages/mini-demo/order.json b/packages/mini-demo/order.json index 8f23290e8..db246f103 100644 --- a/packages/mini-demo/order.json +++ b/packages/mini-demo/order.json @@ -1 +1 @@ -{"input":{"AutoHeight":"6","Basic":"1","Clearable":"2","Disabled":"5","Native":"3","ReadOnly":"4","ShowLength":"7","Vertical":"8","Readonly":"4","Disable":0},"swipe-action":{"Basic":0}} +{"input":{"AutoHeight":"6","Basic":"1","Clearable":"2","Disabled":"5","Native":"3","ReadOnly":"4","ShowLength":"7","Vertical":"8","Readonly":"4","Disable":0},"swipe-action":{"Basic":0},"checkbox":{"Controlled":"3","Custom":"6","Basic":"1","Buttton":"2","List":"4","CustomIcon":"6"}} diff --git a/packages/mini-demo/src/app.config.ts b/packages/mini-demo/src/app.config.ts index df9ea8bc8..908bdfdd6 100644 --- a/packages/mini-demo/src/app.config.ts +++ b/packages/mini-demo/src/app.config.ts @@ -8,6 +8,7 @@ export default defineAppConfig({ 'pages/tabs/index', 'pages/input/index', 'pages/swipe-action/index', + 'pages/checkbox/index', ], window: { backgroundTextStyle: 'light', diff --git a/packages/mini-demo/src/pages/checkbox/component/basic.tsx b/packages/mini-demo/src/pages/checkbox/component/basic.tsx new file mode 100644 index 000000000..db518eea6 --- /dev/null +++ b/packages/mini-demo/src/pages/checkbox/component/basic.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { Checkbox, List, Panel } from 'zarm/mini'; + +/* order: 1 */ + +function Demo() { + return ( + + + + console.log(e)}>普通 + + + + 默认选中 + + + + 禁用 + + + + ); +} + +export default Demo; diff --git a/packages/mini-demo/src/pages/checkbox/component/buttton.tsx b/packages/mini-demo/src/pages/checkbox/component/buttton.tsx new file mode 100644 index 000000000..4f697c882 --- /dev/null +++ b/packages/mini-demo/src/pages/checkbox/component/buttton.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { Checkbox, List, Panel } from 'zarm/mini'; + +/* order: 2 */ + +function Demo() { + return ( + + + + console.log(e)} block> + 选项一 + 选项二 + 选项三 + + + + + ); +} + +export default Demo; diff --git a/packages/mini-demo/src/pages/checkbox/component/controlled.tsx b/packages/mini-demo/src/pages/checkbox/component/controlled.tsx new file mode 100644 index 000000000..4108135bd --- /dev/null +++ b/packages/mini-demo/src/pages/checkbox/component/controlled.tsx @@ -0,0 +1,39 @@ +import React, { useState } from 'react'; +import { showModal } from '@tarojs/taro'; +import { Checkbox, List, Panel } from 'zarm/mini'; + +/* order: 3 */ + +const Demo = () => { + const [checked, setChecked] = useState(false); + + const onChange = (e) => { + if (!e.target.checked) { + showModal({ + content: '是否要取消选择', + cancelText: '不取消', + success: ({ confirm }) => { + if (confirm) { + setChecked(false); + } + }, + }); + return false; + } + setChecked(true); + }; + + return ( + + + + + 取消勾选前确认 + + + + + ); +}; + +export default Demo; diff --git a/packages/mini-demo/src/pages/checkbox/component/custom-icon.tsx b/packages/mini-demo/src/pages/checkbox/component/custom-icon.tsx new file mode 100644 index 000000000..b08b20e44 --- /dev/null +++ b/packages/mini-demo/src/pages/checkbox/component/custom-icon.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { List, Checkbox, Panel } from 'zarm/mini'; +import { Star, StarFill, Success, Close } from '@zarm-design/icons'; + +/* order: 6 */ + +function Demo() { + return ( + + + + + + checked ? : + } + > + 选项一 + + + checked ? : + } + > + 选项二 + + 选项三 + + + + + ) +} + +export default Demo; diff --git a/packages/mini-demo/src/pages/checkbox/component/custom.tsx b/packages/mini-demo/src/pages/checkbox/component/custom.tsx new file mode 100644 index 000000000..081d6538a --- /dev/null +++ b/packages/mini-demo/src/pages/checkbox/component/custom.tsx @@ -0,0 +1,73 @@ +import React, { useState } from 'react'; +import { List, Checkbox, Panel } from 'zarm/mini'; +import { Success } from '@zarm-design/icons'; +import { View } from '@tarojs/components'; + +/* order: 6 */ + +const items = ['选项一', '选项二', '选项三']; + +const Demo = () => { + const [value, setValue] = useState(['0']); + + const onChange = (v) => { + console.log('onChange', v); + setValue(v); + }; + + const CustomCheckbox = (props) => { + return ( + + {({ checked }) => ( + + + + + {props.label} + + )} + + ); + }; + return ( + + + + + {items.map((item, index) => ( + + ))} + + + + + ); +}; + +export default Demo; diff --git a/packages/mini-demo/src/pages/checkbox/component/list.tsx b/packages/mini-demo/src/pages/checkbox/component/list.tsx new file mode 100644 index 000000000..ff9444bf0 --- /dev/null +++ b/packages/mini-demo/src/pages/checkbox/component/list.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { Checkbox, Panel } from 'zarm/mini'; + +/* order: 4 */ + +function Demo() { + return ( + + console.log(e)}> + 选项一 + 选项二 + 选项三 + + + ); +} + +export default Demo; diff --git a/packages/mini-demo/src/pages/checkbox/index.config.ts b/packages/mini-demo/src/pages/checkbox/index.config.ts new file mode 100644 index 000000000..267fb2a4b --- /dev/null +++ b/packages/mini-demo/src/pages/checkbox/index.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: 'Checkbox', +}); diff --git a/packages/mini-demo/src/pages/checkbox/index.scss b/packages/mini-demo/src/pages/checkbox/index.scss new file mode 100644 index 000000000..649525820 --- /dev/null +++ b/packages/mini-demo/src/pages/checkbox/index.scss @@ -0,0 +1,3 @@ +view { + box-sizing: border-box; +} diff --git a/packages/mini-demo/src/pages/checkbox/index.tsx b/packages/mini-demo/src/pages/checkbox/index.tsx new file mode 100644 index 000000000..91f056537 --- /dev/null +++ b/packages/mini-demo/src/pages/checkbox/index.tsx @@ -0,0 +1,23 @@ + +import * as React from 'react'; +import Basic from './component/basic'; +import Buttton from './component/buttton'; +import Controlled from './component/controlled'; +import CustomIcon from './component/custom-icon'; +import Custom from './component/custom'; +import List from './component/list'; + +import './index.scss'; + +export default () => { + return ( + <> + + + + + + + + ) +} \ No newline at end of file diff --git a/packages/mini-demo/src/site.ts b/packages/mini-demo/src/site.ts index 4a1f85f37..aa4ada427 100644 --- a/packages/mini-demo/src/site.ts +++ b/packages/mini-demo/src/site.ts @@ -28,6 +28,17 @@ const siteMap = { page: '/pages/input/index', }, // { + { + key: 'Checkbox', + name: '复选框', + page: '/pages/checkbox/index', + }, + // { + // key: 'Input', + // name: '输入框', + // page: '/pages/input/index', + // }, + // { // key: 'Keyboard', // name: '键盘', // page: '/pages/keyboard/index', diff --git a/packages/zarm/src/button/Button.mini.tsx b/packages/zarm/src/button/Button.mini.tsx index 9990a439c..5f98c7d22 100644 --- a/packages/zarm/src/button/Button.mini.tsx +++ b/packages/zarm/src/button/Button.mini.tsx @@ -7,7 +7,7 @@ import { BaseButtonProps } from './interface'; export interface ButtonProps extends BaseButtonProps, Omit {} -const Button: React.FC = (props: ButtonProps) => { +const Button = (props: ButtonProps) => { const { className, theme, diff --git a/packages/zarm/src/checkbox/Checkbox.mini.tsx b/packages/zarm/src/checkbox/Checkbox.mini.tsx new file mode 100644 index 000000000..d3a7974e5 --- /dev/null +++ b/packages/zarm/src/checkbox/Checkbox.mini.tsx @@ -0,0 +1,185 @@ +import { BaseEventOrig, Label, Switch, SwitchProps, View } from '@tarojs/components'; +import { createBEM } from '@zarm-design/bem'; +import { Minus as MinusIcon, Success as SuccessIcon } from '@zarm-design/icons'; +import includes from 'lodash/includes'; +import React, { ReactNode, useContext, useMemo, } from 'react'; +import { useControllableEventValue } from '../utils/hooks'; +import Button from '../button/index.mini'; +import { ConfigContext } from '../config-provider'; +import List from '../list'; +import { nanoid } from '../utils'; +import { canUseDOM } from '../utils/dom'; +import { HTMLProps } from '../utils/utilityTypes'; +import { CheckboxGroupContext } from './context'; +import type { BaseCheckboxProps, CheckboxCssVars } from './interface'; + +export type CheckboxProps = BaseCheckboxProps & + HTMLProps & { + renderIcon?: (props: CheckboxProps) => ReactNode; + children?: React.ReactNode | ((props: CheckboxProps) => React.ReactNode); + onChange?: (value: BaseEventOrig) => void; + }; + +// const getChecked = (props: CheckboxProps, defaultChecked?: boolean) => { +// return props.checked ?? props.defaultChecked ?? defaultChecked; +// }; + +const Checkbox = (props: CheckboxProps) => { + // const defaultVal: Partial<{value: boolean, defaultValue: boolean}> = {}; + // if ('checked' in props) { + // defaultVal.value = props.checked; + // } + // if ('defaultChecked' in props) { + // defaultVal.defaultValue = props.defaultChecked; + // } + // let [checked, setChecked] = useControllableEventValue({ + // ...props, + // ...defaultVal, + // }); + + // console.log(checked); + let [checked, setChecked] = useControllableEventValue(props, { + valuePropName: 'checked', + defaultValuePropName: 'defaultChecked' + }); + checked = checked ?? false; + + let { disabled } = props; + + const groupContext = useContext(CheckboxGroupContext); + if (groupContext && props.value !== undefined) { + checked = includes(groupContext.value, props.value); + setChecked = (e: any) => { + if (e.target.checked === true) { + groupContext.check(props.value); + } else { + groupContext.uncheck(props.value); + } + }; + disabled = disabled || groupContext.disabled; + } + + const { prefixCls } = useContext(ConfigContext); + const bem = createBEM('checkbox', { prefixCls }); + const cls = bem([ + { + disabled, + checked: checked && !props.indeterminate, + untext: !props.children, + indeterminate: props.indeterminate, + }, + props.className, + ]); + + const currentProps = { ...props, checked }; + + const children = + typeof props.children === 'function' ? props.children(currentProps) : props.children; + const textRender = props.children && {children}; + + const iconRender = ( + + {props.renderIcon ? ( + props.renderIcon(currentProps) + ) : ( + {props.indeterminate ? : } + )} + + ); + + const id = useMemo(() => nanoid(), []); + + const passProps = canUseDOM + ? { + nativeProps: { + id, + }, + } + : { id }; + + const inputRender = ( + { + if (disabled) return; + setChecked({ + target: { + checked: !checked, + } + } as any); + // props.onChange?.(e); + }} + /> + ); + + if (groupContext?.type === 'button') { + return ( + + {inputRender} + + + + ); + } + + if (groupContext?.type === 'list') { + const tickRender = ( + <> + {inputRender} + {iconRender} + + ); + + return ( + + ); + } + + const contentRender = + typeof props.children === 'function' ? ( + props.children(currentProps) + ) : ( + <> + {iconRender} + {textRender} + + ); + + return ( + + + {inputRender} + + ); +}; + +Checkbox.displayName = 'Checkbox'; + +Checkbox.defaultProps = { + indeterminate: false, +}; + +export default Checkbox; diff --git a/packages/zarm/src/checkbox/Checkbox.tsx b/packages/zarm/src/checkbox/Checkbox.tsx index ca9b95dbe..73f172b66 100644 --- a/packages/zarm/src/checkbox/Checkbox.tsx +++ b/packages/zarm/src/checkbox/Checkbox.tsx @@ -6,68 +6,46 @@ import React, { forwardRef, ReactNode, useContext, - useEffect, useImperativeHandle, useRef, - useState, } from 'react'; import Button from '../button'; import { ConfigContext } from '../config-provider'; import List from '../list'; import type { HTMLProps } from '../utils/utilityTypes'; import { CheckboxGroupContext } from './context'; -import type { BaseCheckboxProps } from './interface'; - -export interface CheckboxCssVars { - '--icon-size'?: React.CSSProperties['height']; - '--icon-background'?: React.CSSProperties['background']; - '--icon-border-radius'?: React.CSSProperties['borderRadius']; - '--icon-border-width'?: React.CSSProperties['borderWidth']; - '--icon-border-color'?: React.CSSProperties['borderColor']; - '--tick-font-size'?: React.CSSProperties['fontSize']; - '--tick-color'?: React.CSSProperties['color']; - '--tick-transition'?: React.CSSProperties['transition']; - '--text-margin-horizontal'?: React.CSSProperties['marginLeft']; - '--active-opacity'?: React.CSSProperties['opacity']; - '--checked-icon-background'?: React.CSSProperties['background']; - '--checked-icon-border-color'?: React.CSSProperties['borderColor']; - '--checked-tick-color'?: React.CSSProperties['color']; - '--disabled-icon-background'?: React.CSSProperties['background']; - '--disabled-icon-border-color'?: React.CSSProperties['borderColor']; - '--disabled-text-color'?: React.CSSProperties['color']; - '--disabled-tick-color'?: React.CSSProperties['color']; - '--group-spacing-vertical'?: React.CSSProperties['marginBottom']; - '--group-spacing-horizontal'?: React.CSSProperties['marginRight']; -} +import type { BaseCheckboxProps, CheckboxCssVars } from './interface'; +import { useControllableEventValue } from '../utils/hooks'; + export type CheckboxProps = BaseCheckboxProps & HTMLProps & { renderIcon?: (props: CheckboxProps) => ReactNode; - render?: (props: CheckboxProps) => ReactNode; + children?: React.ReactNode | ((props: CheckboxProps) => React.ReactNode); onChange?: (e: ChangeEvent) => void; }; -const getChecked = (props: CheckboxProps, defaultChecked?: boolean) => { - return props.checked ?? props.defaultChecked ?? defaultChecked; -}; - export interface CheckboxRef { check: () => void; uncheck: () => void; toggle: () => void; -}; +} const Checkbox = forwardRef((props, ref) => { const inputRef = useRef(null); - let [checked, setChecked] = useState(getChecked(props, false)); + let [checked, setChecked] = useControllableEventValue(props, { + valuePropName: 'checked', + defaultValuePropName: 'defaultChecked' + }); + checked = checked ?? false; let { disabled } = props; const groupContext = useContext(CheckboxGroupContext); - if (groupContext && props.value !== undefined) { + if (groupContext && props.value !== undefined ) { checked = includes(groupContext.value, props.value); - setChecked = (changedChecked: boolean) => { - if (changedChecked) { + setChecked = (e: ChangeEvent) => { + if (e.target.checked === true) { groupContext.check(props.value); } else { groupContext.uncheck(props.value); @@ -89,8 +67,10 @@ const Checkbox = forwardRef((props, ref) => { ]); const currentProps = { ...props, checked }; + const children = + typeof props.children === 'function' ? props.children(currentProps) : props.children; - const textRender = props.children && {props.children}; + const textRender = props.children && {children}; const iconRender = ( @@ -114,11 +94,8 @@ const Checkbox = forwardRef((props, ref) => { checked={checked} onChange={(e: ChangeEvent) => { if (disabled) return; - - if (!('checked' in props)) { - setChecked(e.target.checked); - } - props.onChange?.(e); + setChecked(e); + // props.onChange?.(e); }} /> ); @@ -139,13 +116,6 @@ const Checkbox = forwardRef((props, ref) => { }; }); - useEffect(() => { - if (props.checked === undefined) return; - if (props.checked === checked) return; - - setChecked(getChecked({ checked: props.checked, defaultChecked: props.defaultChecked }, false)); - }, [props.checked, props.defaultChecked]); - if (groupContext?.type === 'button') { return ( ); @@ -193,14 +163,15 @@ const Checkbox = forwardRef((props, ref) => { ); } - const contentRender = props.render ? ( - props.render(currentProps) - ) : ( - <> - {iconRender} - {textRender} - - ); + const contentRender = + typeof props.children === 'function' ? ( + props.children(currentProps) + ) : ( + <> + {iconRender} + {textRender} + + ); return (