Skip to content

Commit

Permalink
chore(Switch): complete refactoring (#527)
Browse files Browse the repository at this point in the history
* chore(Switch): complete refactoring

* test: update snapshots

* fix: fix cr
  • Loading branch information
anlyyao authored Oct 9, 2024
1 parent aaa3db6 commit 61c6afd
Show file tree
Hide file tree
Showing 21 changed files with 1,279 additions and 206 deletions.
2 changes: 1 addition & 1 deletion site/mobile/mobile.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export default {
title: 'Switch 开关',
name: 'switch',
path: '/switch',
component: () => import('tdesign-mobile-react/switch/_example/index.jsx'),
component: () => import('tdesign-mobile-react/switch/_example/index.tsx'),
},
{
title: 'Cell 单元格',
Expand Down
2 changes: 2 additions & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export type TreeOptionData<T = string | number> = {

export type SizeEnum = 'small' | 'medium' | 'large';

export type ShapeEnum = 'circle' | 'round';

export type HorizontalAlignEnum = 'left' | 'center' | 'right';

export type VerticalAlignEnum = 'top' | 'middle' | 'bottom';
Expand Down
99 changes: 54 additions & 45 deletions src/switch/Switch.tsx
Original file line number Diff line number Diff line change
@@ -1,75 +1,84 @@
import React, { forwardRef, useMemo } from 'react';
import classNames from 'classnames';
import { TdSwitchProps } from './type';
import isArray from 'lodash/isArray';
import Loading from '../loading';
import { TdSwitchProps, SwitchValue } from './type';
import { switchDefaultProps } from './defaultProps';
import { StyledProps } from '../common';
import useConfig from '../_util/useConfig';
import useDefault from '../_util/useDefault';
import parseTNode from '../_util/parseTNode';
import { usePrefixClass } from '../hooks/useClass';
import useDefaultProps from '../hooks/useDefaultProps';

export interface SwitchProps extends TdSwitchProps, StyledProps {}
export interface SwitchProps<T extends SwitchValue = SwitchValue> extends TdSwitchProps<T>, StyledProps {}

const Switch = forwardRef<HTMLButtonElement, SwitchProps>((props, ref) => {
const { customValue, label, disabled, className, colors, style } = props;
const Switch = forwardRef<HTMLDivElement, SwitchProps>((originalProps, ref) => {
const props = useDefaultProps<SwitchProps<SwitchValue>>(originalProps, switchDefaultProps);
const { className, style, value, defaultValue, customValue, disabled, label, loading, size, onChange } = props;

const { classPrefix } = useConfig();
const switchClass = usePrefixClass('switch');

const switchBaseClassName = `${classPrefix}-switch`;
const [activeValue = true, inactiveValue = false] = customValue || [];

const [activeValue, inactiveValue] = customValue;

const [value, onChange] = useDefault(props.value, props.defaultValue, props.onChange);
const [innerValue, setInnerValue] = useDefault(value, defaultValue, onChange);

const checked = useMemo(() => {
if (typeof value !== 'undefined') {
if (Array.isArray(customValue) && !customValue.includes(value)) {
throw `${value} is not in customValue: ${JSON.stringify(customValue)}`;
if (typeof innerValue !== 'undefined') {
if (Array.isArray(customValue) && !customValue.includes(innerValue)) {
throw `${innerValue} is not in customValue: ${JSON.stringify(customValue)}`;
}
return value === customValue[0];
return innerValue === activeValue;
}
}, [value, customValue]);
}, [innerValue, customValue, activeValue]);

const switchClasses = classNames(
`${switchClass}`,
`${switchClass}--${props.size}`,
{
[`${switchClass}--checked`]: checked,
[`${switchClass}--disabled`]: disabled || loading,
},
className,
);

const onInternalClick = () => {
if (disabled) return;
const dotClasses = classNames(`${switchClass}__dot`, `${switchClass}__dot--${props.size}`, {
[`${switchClass}__dot--checked`]: checked,
[`${switchClass}__dot--plain`]: isArray(label) && label.length !== 2 && !loading,
});

onChange?.(!checked ? activeValue : inactiveValue);
};
const labelClasses = classNames(`${switchClass}__label`, `${switchClass}__label--${size}`, {
[`${switchClass}__label--checked`]: checked,
});

const renderSwitchText = (checked: boolean, label: SwitchProps['label']) => {
if (typeof label === 'function') {
return label({ value: checked ? activeValue : inactiveValue });
const handleToggle: React.MouseEventHandler<HTMLDivElement> = (e) => {
if (disabled || loading) {
return;
}
const changedValue = !checked ? activeValue : inactiveValue;
setInnerValue(changedValue, { e });
};

if (typeof label === 'string') return label;
const readerContent = React.useMemo<React.ReactNode>(() => {
if (loading) return <Loading inheritColor size="small" />;

if (Array.isArray(label)) {
const [activeContent, inactiveContent] = label;
const [activeContent = '', inactiveContent = ''] = label;
const content = checked ? activeContent : inactiveContent;

if (typeof content === 'function') return content();

return content;
return parseTNode(content, { value });
}

return null;
};

const switchClassName = classNames(switchBaseClassName, className, {
[`${classPrefix}-is-checked`]: checked,
[`${classPrefix}-is-disabled`]: disabled,
});
return parseTNode(label, { value });
}, [loading, label, checked, value]);

return (
<button ref={ref} className={switchClassName} style={style} onClick={onInternalClick}>
<span className={`${classPrefix}-switch__text`}>{renderSwitchText(checked, label)}</span>
<span
className={`${classPrefix}-switch__node`}
style={{ backgroundColor: checked ? colors?.[0] : colors?.[1] }}
></span>
</button>
<div ref={ref} className={switchClasses} style={style} onClick={handleToggle}>
<div className={dotClasses}>
<div className={labelClasses}>{readerContent}</div>
</div>
</div>
);
});

Switch.defaultProps = {
customValue: [true, false],
};
Switch.displayName = 'Switch';

export default Switch;
12 changes: 0 additions & 12 deletions src/switch/_example/base.jsx

This file was deleted.

18 changes: 18 additions & 0 deletions src/switch/_example/base.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, { useState } from 'react';
import { Switch, Cell } from 'tdesign-mobile-react';
import type { SwitchValue } from 'tdesign-mobile-react';

export default function SwitchBase() {
const [checked, setChecked] = useState<SwitchValue>(1);

const onChange = (value: SwitchValue) => {
console.log('value', value);
setChecked(value);
};

return (
<>
<Cell title="基础开关" rightIcon={<Switch value={checked} customValue={[1, 0]} onChange={onChange} />}></Cell>
</>
);
}
10 changes: 10 additions & 0 deletions src/switch/_example/color.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react';
import { Switch, Cell } from 'tdesign-mobile-react';

export default function SwitchColor() {
return (
<>
<Cell title="自定义颜色开关" rightIcon={<Switch defaultValue={true} className="custom-color" />}></Cell>
</>
);
}
15 changes: 0 additions & 15 deletions src/switch/_example/desc.jsx

This file was deleted.

13 changes: 0 additions & 13 deletions src/switch/_example/disabled.jsx

This file was deleted.

23 changes: 0 additions & 23 deletions src/switch/_example/index.jsx

This file was deleted.

33 changes: 33 additions & 0 deletions src/switch/_example/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import TDemoHeader from '../../../site/mobile/components/DemoHeader';
import TDemoBlock from '../../../site/mobile/components/DemoBlock';
import Base from './base';
import Color from './color';
import Label from './label';
import Status from './status';
import Size from './size';

import './style/index.less';

export default function () {
return (
<div className="tdesign-mobile-demo">
<TDemoHeader title="Switch 开关" summary="开关用于切换当个设置项的状态,开启、关闭为两个互斥的操作" />
<TDemoBlock title="01 组件类型" summary="基础开关">
<Base />
</TDemoBlock>
<TDemoBlock summary="带描述开关">
<Label />
</TDemoBlock>
<TDemoBlock summary="自定义颜色开关">
<Color />
</TDemoBlock>
<TDemoBlock title="02 状态" summary="加载状态">
<Status />
</TDemoBlock>
<TDemoBlock title="03 组件样式" summary="开关尺寸">
<Size />
</TDemoBlock>
</div>
);
}
28 changes: 28 additions & 0 deletions src/switch/_example/label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { useState } from 'react';
import { Switch, Cell } from 'tdesign-mobile-react';
import { Icon } from 'tdesign-icons-react';

export default function SwitchLabel() {
const [checked, setChecked] = useState(true);

const onChange = (value: boolean) => {
console.log('value', value);
setChecked(value);
};

const renderActiveContent = () => <Icon name="check" />;
const renderInactiveContent = () => <Icon name="close" />;

return (
<>
<Cell
title="带文字开关"
rightIcon={<Switch value={checked} label={({ value }) => (value ? '开' : '关')} onChange={onChange} />}
></Cell>
<Cell
title="带图标开关"
rightIcon={<Switch defaultValue label={[renderActiveContent(), renderInactiveContent()]} />}
></Cell>
</>
);
}
12 changes: 12 additions & 0 deletions src/switch/_example/size.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import { Switch, Cell } from 'tdesign-mobile-react';

export default function SwitchSize() {
return (
<>
<Cell title="大尺寸 32" rightIcon={<Switch defaultValue size="large" />}></Cell>
<Cell title="中尺寸 28" rightIcon={<Switch defaultValue />}></Cell>
<Cell title="小尺寸 24" rightIcon={<Switch defaultValue size="small" />}></Cell>
</>
);
}
16 changes: 16 additions & 0 deletions src/switch/_example/status.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import { Switch, Cell } from 'tdesign-mobile-react';

export default function SwitchStatus() {
return (
<>
<Cell title="加载状态" rightIcon={<Switch loading />}></Cell>
<Cell title="开关开启禁用" note={<Switch defaultValue loading />}></Cell>

<div className="demo__title">禁用状态</div>

<Cell title="禁用状态" note={<Switch disabled />}></Cell>
<Cell title="禁用状态" note={<Switch disabled value={true} />}></Cell>
</>
);
}
14 changes: 14 additions & 0 deletions src/switch/_example/style/index.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.custom-color {
--td-switch-checked-color: #00a870;
}

.t-cell + .t-cell {
margin-top: 16px;
}

.demo__title {
margin: 24px 16px 16px;
color: var(--td-text-color-secondary, rgba(0, 0, 0, 0.6));
font-size: 14px;
line-height: 22px;
}
7 changes: 7 additions & 0 deletions src/switch/defaultProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* */

import { TdSwitchProps } from './type';

export const switchDefaultProps: TdSwitchProps = { disabled: undefined, label: [], loading: false, size: 'medium' };
2 changes: 1 addition & 1 deletion src/switch/style/index.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
import '../../_common/style/mobile/components/switch/_index.less';
import '../../_common/style/mobile/components/switch/v2/_index.less';
19 changes: 19 additions & 0 deletions src/switch/switch.en-US.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
:: BASE_DOC ::

## API

### Switch Props

name | type | default | description | required
-- | -- | -- | -- | --
className | String | - | className of component | N
style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N
colors | Array | - | `deprecated`。Typescript:`string[]` | N
customValue | Array | - | Typescript:`Array<SwitchValue>` | N
disabled | Boolean | undefined | \- | N
label | TNode | [] | Typescript:`Array<string \| TNode> \| TNode<{ value: SwitchValue }>`[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N
loading | Boolean | false | \- | N
size | String | medium | options: small/medium/large | N
value | String / Number / Boolean | - | Typescript:`T` `type SwitchValue = string \| number \| boolean`[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/switch/type.ts) | N
defaultValue | String / Number / Boolean | - | uncontrolled property。Typescript:`T` `type SwitchValue = string \| number \| boolean`[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/switch/type.ts) | N
onChange | Function | | Typescript:`(value: T, context: { e: MouseEvent }) => void`<br/> | N
Loading

0 comments on commit 61c6afd

Please sign in to comment.