From b715dc8cf9575f2bf9c022e8277bab062278347d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=A2=A6=E8=89=AF?= Date: Mon, 28 Aug 2023 20:47:28 +0800 Subject: [PATCH 1/3] chore: radio mini --- packages/mini-demo/src/app.config.ts | 1 + .../mini-demo/src/pages/radio/index.config.ts | 3 + packages/mini-demo/src/pages/radio/index.scss | 22 ++ packages/mini-demo/src/pages/radio/index.tsx | 91 +++++++ packages/mini-demo/src/site.ts | 70 +++--- packages/zarm/src/index.mini.ts | 2 + packages/zarm/src/radio/Radio.mini.tsx | 228 ++++++++++++++++++ packages/zarm/src/radio/RadioGroup.mini.tsx | 76 ++++++ packages/zarm/src/radio/demo/basic.mini.tsx | 29 +++ packages/zarm/src/radio/demo/button.mini.tsx | 41 ++++ packages/zarm/src/radio/demo/group.mini.tsx | 29 +++ packages/zarm/src/radio/demo/list.mini.tsx | 27 +++ packages/zarm/src/radio/index.mini.ts | 10 + packages/zarm/src/radio/style/index.mini.ts | 4 + 14 files changed, 598 insertions(+), 35 deletions(-) create mode 100644 packages/mini-demo/src/pages/radio/index.config.ts create mode 100644 packages/mini-demo/src/pages/radio/index.scss create mode 100644 packages/mini-demo/src/pages/radio/index.tsx create mode 100644 packages/zarm/src/radio/Radio.mini.tsx create mode 100644 packages/zarm/src/radio/RadioGroup.mini.tsx create mode 100644 packages/zarm/src/radio/demo/basic.mini.tsx create mode 100644 packages/zarm/src/radio/demo/button.mini.tsx create mode 100644 packages/zarm/src/radio/demo/group.mini.tsx create mode 100644 packages/zarm/src/radio/demo/list.mini.tsx create mode 100644 packages/zarm/src/radio/index.mini.ts create mode 100644 packages/zarm/src/radio/style/index.mini.ts diff --git a/packages/mini-demo/src/app.config.ts b/packages/mini-demo/src/app.config.ts index 1624e4e0e..a14dfbc82 100644 --- a/packages/mini-demo/src/app.config.ts +++ b/packages/mini-demo/src/app.config.ts @@ -5,6 +5,7 @@ export default defineAppConfig({ 'pages/popup/index', 'pages/list/index', 'pages/collapse/index', + 'pages/radio/index', ], window: { backgroundTextStyle: 'light', diff --git a/packages/mini-demo/src/pages/radio/index.config.ts b/packages/mini-demo/src/pages/radio/index.config.ts new file mode 100644 index 000000000..90dd9fb8a --- /dev/null +++ b/packages/mini-demo/src/pages/radio/index.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: 'Radio', +}); diff --git a/packages/mini-demo/src/pages/radio/index.scss b/packages/mini-demo/src/pages/radio/index.scss new file mode 100644 index 000000000..eb5904078 --- /dev/null +++ b/packages/mini-demo/src/pages/radio/index.scss @@ -0,0 +1,22 @@ +// .popup-box { +// width: 100%; +// height: 200px; +// background-color: #fff; +// padding: 20px; +// } + +// .popup-box-top { +// width: 100%; +// background-color: rgba(0, 0, 0, 0.5); +// color: #fff; +// text-align: center; +// padding: 10px; +// } + +// .popup-box-left, +// .popup-box-right { +// width: 200px; +// height: 100%; +// background: #fff; +// padding: 20px; +// } diff --git a/packages/mini-demo/src/pages/radio/index.tsx b/packages/mini-demo/src/pages/radio/index.tsx new file mode 100644 index 000000000..17adfe7be --- /dev/null +++ b/packages/mini-demo/src/pages/radio/index.tsx @@ -0,0 +1,91 @@ +import { View } from '@tarojs/components'; +import * as React from 'react'; +import { List, Radio } from 'zarm/mini'; +import './index.scss'; + +const Demo = () => { + const [value, setValue] = React.useState([]); + + const onChange = (value) => { + console.log('onChange', value); + setValue(value); + }; + + return ( + + 基本用法 + + + 普通 + + + 默认选中 + + + 禁用 + + + + 选中且禁用 + + + + 组合使用 + + + + 选项一 + 选项二 + 选项三 + + + + 列表样式 + + 选项一 + 选项二 + + 选项三(禁止选择) + + + 通栏样式 + + + 选项一 + 选项二 + + + 按钮样式 + + + 选项一 + 选项二 + 选项三 + + + + + 选项一 + 选项二 + 选项三 + + + + + 选项一 + 选项二 + 选项三 + + + + + 选项一 + 选项二 + 选项三 + + + + ); +}; + +export default Demo; diff --git a/packages/mini-demo/src/site.ts b/packages/mini-demo/src/site.ts index 888077562..6b8520f0e 100644 --- a/packages/mini-demo/src/site.ts +++ b/packages/mini-demo/src/site.ts @@ -14,41 +14,41 @@ const siteMap = { // } ], }, - // input: { - // title: '数据录入', - // components: [ - // { - // key: 'Checkbox', - // name: '复选框', - // page: '/pages/checkbox/index', - // }, - // { - // key: 'Input', - // name: '输入框', - // page: '/pages/input/index', - // }, - // { - // key: 'Keyboard', - // name: '键盘', - // page: '/pages/keyboard/index', - // }, - // { - // key: 'Radio', - // name: '复选框', - // page: '/pages/radio/index', - // }, - // { - // key: 'Stepper', - // name: '步进器', - // page: '/pages/stepper/index', - // }, - // { - // key: 'Collapse', - // name: '折叠面板', - // page: '/pages/collapse/index', - // }, - // ] - // }, + input: { + title: '数据录入', + components: [ + // { + // key: 'Checkbox', + // name: '复选框', + // page: '/pages/checkbox/index', + // }, + // { + // key: 'Input', + // name: '输入框', + // page: '/pages/input/index', + // }, + // { + // key: 'Keyboard', + // name: '键盘', + // page: '/pages/keyboard/index', + // }, + { + key: 'Radio', + name: '复选框', + page: '/pages/radio/index', + }, + // { + // key: 'Stepper', + // name: '步进器', + // page: '/pages/stepper/index', + // }, + // { + // key: 'Collapse', + // name: '折叠面板', + // page: '/pages/collapse/index', + // }, + ], + }, view: { title: '数据展示', components: [ diff --git a/packages/zarm/src/index.mini.ts b/packages/zarm/src/index.mini.ts index 25da5da17..ecac13185 100644 --- a/packages/zarm/src/index.mini.ts +++ b/packages/zarm/src/index.mini.ts @@ -10,4 +10,6 @@ export { default as Panel } from './panel/index.mini'; export type { PanelProps } from './panel/index.mini'; export { default as Popup } from './popup/index.mini'; export type { PopupProps } from './popup/index.mini'; +export { default as Radio } from './radio/index.mini'; +export type { RadioGroupProps, RadioProps } from './radio/index.mini'; export { default as Select } from './select'; diff --git a/packages/zarm/src/radio/Radio.mini.tsx b/packages/zarm/src/radio/Radio.mini.tsx new file mode 100644 index 000000000..6f2da675c --- /dev/null +++ b/packages/zarm/src/radio/Radio.mini.tsx @@ -0,0 +1,228 @@ +import { View } from '@tarojs/components'; +import { createBEM } from '@zarm-design/bem'; +import { Minus as MinusIcon, Success as SuccessIcon } from '@zarm-design/icons'; +import React, { + ChangeEvent, + forwardRef, + ReactNode, + useContext, + useEffect, + useImperativeHandle, + useRef, + useState, +} from 'react'; +import Button from '../button/Button.mini'; +import { ConfigContext } from '../config-provider'; +import List from '../list/index.mini'; +import type { HTMLProps } from '../utils/utilityTypes'; +import { RadioGroupContext } from './context'; +import type { BaseRadioProps } from './interface'; + +export interface RadioCssVars { + '--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']; +} + +export type RadioProps = BaseRadioProps & + HTMLProps & { + renderIcon?: (props: RadioProps) => ReactNode; + render?: (props: RadioProps) => ReactNode; + onChange?: (e: ChangeEvent) => void; + }; + +const getChecked = (props: RadioProps, defaultChecked?: boolean) => { + return props.checked ?? props.defaultChecked ?? defaultChecked; +}; + +export interface RadioRef { + check: () => void; +} + +const Radio = forwardRef((props, ref) => { + const inputRef = useRef(null); + let [checked, setChecked] = useState(getChecked(props, false)); + let { disabled } = props; + + const groupContext = useContext(RadioGroupContext); + if (groupContext && props.value !== undefined) { + checked = groupContext.value === props.value; + setChecked = (changedChecked: boolean) => { + if (changedChecked) { + groupContext.check(props.value); + } + }; + disabled = disabled || groupContext.disabled; + } + + const { prefixCls } = useContext(ConfigContext); + const bem = createBEM('radio', { prefixCls }); + const cls = bem([ + { + disabled, + checked: checked && !props.indeterminate, + untext: !props.children, + indeterminate: props.indeterminate, + }, + props.className, + ]); + + const currentProps = { ...props, checked }; + + const textRender = props.children && {props.children}; + + const iconRender = ( + + {props.renderIcon ? ( + props.renderIcon(currentProps) + ) : ( + {props.indeterminate ? : } + )} + + ); + + const inputRender = ( + { + inputRef.current = node; + }} + id={props.id} + type="radio" + className={bem('input')} + aria-checked={checked} + disabled={disabled} + value={props.value} + checked={checked} + onChange={(e: ChangeEvent) => { + if (disabled) return; + if (!('checked' in props)) { + setChecked(true); + } + props.onChange?.(e); + }} + /> + ); + + useImperativeHandle(ref, () => { + return { + check: () => { + if (checked) return; + props.onChange?.({ + target: { value: props.value, checked: true }, + } as ChangeEvent); + setChecked(true); + }, + }; + }); + + 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 ( + + ); + } + + if (groupContext?.type === 'list') { + const tickRender = ( + <> + {inputRender} + {iconRender} + + ); + + return ( + { + if (disabled) return; + props.onChange?.({ + target: { value: props.value, checked: true }, + } as ChangeEvent); + setChecked(true); + } + : undefined + } + /> + ); + } + + const contentRender = props.render ? ( + props.render(currentProps) + ) : ( + { + if (disabled) return; + if (!('checked' in props)) { + setChecked(true); + } + props.onChange?.({ + target: { value: props.value, checked: true }, + } as ChangeEvent); + }} + > + {iconRender} + {textRender} + + ); + + return ( + + ); +}); + +Radio.displayName = 'Radio'; + +Radio.defaultProps = { + indeterminate: false, +}; + +export default Radio; diff --git a/packages/zarm/src/radio/RadioGroup.mini.tsx b/packages/zarm/src/radio/RadioGroup.mini.tsx new file mode 100644 index 000000000..1d97fd391 --- /dev/null +++ b/packages/zarm/src/radio/RadioGroup.mini.tsx @@ -0,0 +1,76 @@ +import { createBEM } from '@zarm-design/bem'; +import React, { FC, useContext, useEffect, useState } from 'react'; +import { ConfigContext } from '../config-provider'; +import List from '../list'; +import type { HTMLProps } from '../utils/utilityTypes'; +import { RadioGroupContext } from './context'; +import type { BaseRadioGroupProps, RadioValue } from './interface'; + +export interface RadioGroupCssVars { + '--group-spacing-vertical'?: React.CSSProperties['marginBottom']; + '--group-spacing-horizontal'?: React.CSSProperties['marginRight']; +} + +const getValue = (props: RadioGroupProps, defaultValue?: RadioValue) => { + return props.value ?? props.defaultValue ?? defaultValue; +}; + +export type RadioGroupProps = BaseRadioGroupProps & HTMLProps; + +const RadioGroup: FC = (props) => { + const [value, setValue] = useState(getValue(props)); + const { type, block, disabled, listIconAlign, compact, className, style } = props; + const { prefixCls } = useContext(ConfigContext); + + const bem = createBEM('radio-group', { prefixCls }); + const cls = bem([ + { + [`${type}`]: !!type, + block, + disabled, + 'button-compact': compact, + }, + className, + ]); + + useEffect(() => { + if (props.value === undefined) return; + if (props.value === value) return; + + setValue(getValue(props)); + }, [props.value]); + + return ( + { + setValue(v); + props.onChange?.(v); + }, + }} + > +
+
+ {type === 'list' ? {props.children} : props.children} +
+
+
+ ); +}; + +RadioGroup.displayName = 'RadioGroup'; + +RadioGroup.defaultProps = { + block: false, + disabled: false, + compact: false, + listIconAlign: 'before', +}; + +export default RadioGroup; diff --git a/packages/zarm/src/radio/demo/basic.mini.tsx b/packages/zarm/src/radio/demo/basic.mini.tsx new file mode 100644 index 000000000..f0d153bc2 --- /dev/null +++ b/packages/zarm/src/radio/demo/basic.mini.tsx @@ -0,0 +1,29 @@ +import { View } from '@tarojs/components'; +import * as React from 'react'; +import { List, Radio } from 'zarm/mini'; + +const Demo = () => { + return ( + + 基本用法 + + + 普通 + + + 默认选中 + + + 禁用 + + + + 选中且禁用 + + + + + ); +}; + +export default Demo; diff --git a/packages/zarm/src/radio/demo/button.mini.tsx b/packages/zarm/src/radio/demo/button.mini.tsx new file mode 100644 index 000000000..009ed2df3 --- /dev/null +++ b/packages/zarm/src/radio/demo/button.mini.tsx @@ -0,0 +1,41 @@ +import { View } from '@tarojs/components'; +import * as React from 'react'; +import { List, Radio } from 'zarm/mini'; + +const Demo = () => { + return ( + + 按钮样式 + + + 选项一 + 选项二 + 选项三 + + + + + 选项一 + 选项二 + 选项三 + + + + + 选项一 + 选项二 + 选项三 + + + + + 选项一 + 选项二 + 选项三 + + + + ); +}; + +export default Demo; diff --git a/packages/zarm/src/radio/demo/group.mini.tsx b/packages/zarm/src/radio/demo/group.mini.tsx new file mode 100644 index 000000000..65496e26e --- /dev/null +++ b/packages/zarm/src/radio/demo/group.mini.tsx @@ -0,0 +1,29 @@ +import { View } from '@tarojs/components'; +import * as React from 'react'; +import { List, Radio } from 'zarm/mini'; + +const Demo = () => { + const [value, setValue] = React.useState([]); + + const onChange = (val) => { + console.log('onChange', val); + setValue(val); + }; + + return ( + + 组合使用 + + + + 选项一 + 选项二 + 选项三 + + + + + ); +}; + +export default Demo; diff --git a/packages/zarm/src/radio/demo/list.mini.tsx b/packages/zarm/src/radio/demo/list.mini.tsx new file mode 100644 index 000000000..a71ecd784 --- /dev/null +++ b/packages/zarm/src/radio/demo/list.mini.tsx @@ -0,0 +1,27 @@ +import { View } from '@tarojs/components'; +import * as React from 'react'; +import { List, Radio } from 'zarm/mini'; + +const Demo = () => { + return ( + + 列表样式 + + 选项一 + 选项二 + + 选项三(禁止选择) + + + 通栏样式 + + + 选项一 + 选项二 + + + + ); +}; + +export default Demo; diff --git a/packages/zarm/src/radio/index.mini.ts b/packages/zarm/src/radio/index.mini.ts new file mode 100644 index 000000000..2ae91791a --- /dev/null +++ b/packages/zarm/src/radio/index.mini.ts @@ -0,0 +1,10 @@ +import attachPropertiesToComponent from '../utils/attachPropertiesToComponent'; +import Radio from './Radio.mini'; +import Group from './RadioGroup.mini'; + +export type { RadioCssVars, RadioProps, RadioRef } from './Radio.mini'; +export type { RadioGroupCssVars, RadioGroupProps } from './RadioGroup.mini'; + +export default attachPropertiesToComponent(Radio, { + Group, +}); diff --git a/packages/zarm/src/radio/style/index.mini.ts b/packages/zarm/src/radio/style/index.mini.ts new file mode 100644 index 000000000..c2e730a0c --- /dev/null +++ b/packages/zarm/src/radio/style/index.mini.ts @@ -0,0 +1,4 @@ +import '../../icon/style'; +import '../../list/style'; +import '../../style'; +import './index.scss'; From d181aa685891df63121858f84ac283336af0d20f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=A2=A6=E8=89=AF?= Date: Tue, 29 Aug 2023 10:52:55 +0800 Subject: [PATCH 2/3] chore: radio mini demo --- .../src/pages/radio/component/basic.tsx | 27 ++++++ .../src/pages/radio/component/button.tsx | 39 ++++++++ .../src/pages/radio/component/group.tsx | 27 ++++++ .../src/pages/radio/component/list.tsx | 28 ++++++ packages/mini-demo/src/pages/radio/index.tsx | 96 +++---------------- packages/zarm/src/radio/demo/basic.mini.tsx | 8 +- packages/zarm/src/radio/demo/button.mini.tsx | 8 +- packages/zarm/src/radio/demo/group.mini.tsx | 8 +- packages/zarm/src/radio/demo/list.mini.tsx | 33 +++---- packages/zarm/src/radio/style/index.mini.ts | 3 +- 10 files changed, 160 insertions(+), 117 deletions(-) create mode 100644 packages/mini-demo/src/pages/radio/component/basic.tsx create mode 100644 packages/mini-demo/src/pages/radio/component/button.tsx create mode 100644 packages/mini-demo/src/pages/radio/component/group.tsx create mode 100644 packages/mini-demo/src/pages/radio/component/list.tsx diff --git a/packages/mini-demo/src/pages/radio/component/basic.tsx b/packages/mini-demo/src/pages/radio/component/basic.tsx new file mode 100644 index 000000000..8d7d0f900 --- /dev/null +++ b/packages/mini-demo/src/pages/radio/component/basic.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { List, Panel, Radio } from 'zarm/mini'; + +const Demo = () => { + return ( + + + + 普通 + + + 默认选中 + + + 禁用 + + + + 选中且禁用 + + + + + ); +}; + +export default Demo; diff --git a/packages/mini-demo/src/pages/radio/component/button.tsx b/packages/mini-demo/src/pages/radio/component/button.tsx new file mode 100644 index 000000000..41fccbfc5 --- /dev/null +++ b/packages/mini-demo/src/pages/radio/component/button.tsx @@ -0,0 +1,39 @@ +import * as React from 'react'; +import { List, Panel, Radio } from 'zarm/mini'; + +const Demo = () => { + return ( + + + + 选项一 + 选项二 + 选项三 + + + + + 选项一 + 选项二 + 选项三 + + + + + 选项一 + 选项二 + 选项三 + + + + + 选项一 + 选项二 + 选项三 + + + + ); +}; + +export default Demo; diff --git a/packages/mini-demo/src/pages/radio/component/group.tsx b/packages/mini-demo/src/pages/radio/component/group.tsx new file mode 100644 index 000000000..78c0f2b32 --- /dev/null +++ b/packages/mini-demo/src/pages/radio/component/group.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { List, Panel, Radio } from 'zarm/mini'; + +const Demo = () => { + const [value, setValue] = React.useState([]); + + const onChange = (val) => { + console.log('onChange', val); + setValue(val); + }; + + return ( + + + + + 选项一 + 选项二 + 选项三 + + + + + ); +}; + +export default Demo; diff --git a/packages/mini-demo/src/pages/radio/component/list.tsx b/packages/mini-demo/src/pages/radio/component/list.tsx new file mode 100644 index 000000000..4d6c30e9b --- /dev/null +++ b/packages/mini-demo/src/pages/radio/component/list.tsx @@ -0,0 +1,28 @@ +import * as React from 'react'; +import { List, Panel, Radio } from 'zarm/mini'; + +const Demo = () => { + return ( + <> + + + 选项一 + 选项二 + + 选项三(禁止选择) + + + + + + + 选项一 + 选项二 + + + + + ); +}; + +export default Demo; diff --git a/packages/mini-demo/src/pages/radio/index.tsx b/packages/mini-demo/src/pages/radio/index.tsx index 17adfe7be..c833f9816 100644 --- a/packages/mini-demo/src/pages/radio/index.tsx +++ b/packages/mini-demo/src/pages/radio/index.tsx @@ -1,91 +1,17 @@ -import { View } from '@tarojs/components'; import * as React from 'react'; -import { List, Radio } from 'zarm/mini'; +import Basic from './component/basic'; +import Button from './component/button'; +import Group from './component/group'; +import List from './component/list'; import './index.scss'; -const Demo = () => { - const [value, setValue] = React.useState([]); - - const onChange = (value) => { - console.log('onChange', value); - setValue(value); - }; - +export default () => { return ( - - 基本用法 - - - 普通 - - - 默认选中 - - - 禁用 - - - - 选中且禁用 - - - - 组合使用 - - - - 选项一 - 选项二 - 选项三 - - - - 列表样式 - - 选项一 - 选项二 - - 选项三(禁止选择) - - - 通栏样式 - - - 选项一 - 选项二 - - - 按钮样式 - - - 选项一 - 选项二 - 选项三 - - - - - 选项一 - 选项二 - 选项三 - - - - - 选项一 - 选项二 - 选项三 - - - - - 选项一 - 选项二 - 选项三 - - - + <> + + + +