Skip to content

Commit

Permalink
feat: switch组件初始化
Browse files Browse the repository at this point in the history
  • Loading branch information
1360151219 committed Aug 10, 2023
1 parent 3e7627e commit e45f4ab
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 21 deletions.
9 changes: 7 additions & 2 deletions packages/demo/src/components/Switch/demo/Base.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { Switch } from 'pivot-design';
import React, { useState } from 'react';
import React from 'react';

const App: React.FC = () => {
return <Switch />;
return (
<>
<Switch />
<Switch defaultValue={true} />
</>
);
};
export default App;
12 changes: 12 additions & 0 deletions packages/demo/src/components/Switch/demo/Controlled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Switch } from 'pivot-design';
import React, { useState } from 'react';

const App: React.FC = () => {
const [value, setValue] = useState(true);
return (
<>
<Switch value={value} onChange={(checked) => setValue(checked)} />
</>
);
};
export default App;
18 changes: 16 additions & 2 deletions packages/demo/src/components/Switch/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@ import Props from './props.mdx';

# Switch

<CodeBlock line={'5-7'}>
Switch 开关是用于切换两种互斥状态的常见控件。

## 基本用法

你可以直接通过`<Switch />`来渲染一个开关,此时该组件是一个非受控组件,你可以通过`defaultValue`来给开关控制它的初始值。

<CodeBlock>
<Base />
</CodeBlock>
</CodeBlock>

## 受控组件

你可以传入`value``onChange`属性来让开关变成受控组件。

<CodeBlock>
<Controlled />
</CodeBlock>
5 changes: 2 additions & 3 deletions packages/demo/src/components/_CodeBlock/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
margin: 30px 0;
overflow: hidden;
border: 1px solid rgba(243, 242, 242, 0.1);
border-radius: 6px;
border-radius: 12px;
&-actions {
display: flex;
flex-direction: row-reverse;
Expand All @@ -17,11 +17,10 @@
&-demo {
padding: 60px 30px;
display: flex;
border: 1px solid rgba(243, 242, 242, 0.1);
flex-wrap: wrap;
justify-content: center;
align-items: center;
background-color: #ffffff;
background-color: var(--semi-color-fill-0);
> :not(.PIVOT-draggable-item) {
margin: 0 24px;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/demo/src/pages/component/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { list as TransitionList } from '../../components/Transition/.catalog';
import './index.scss';

function Index() {
const [select, setSelect] = useState('Transition');
const [select, setSelect] = useState('Switch');
const demoSelect = () => {
return (
<div className="demo-container">
Expand Down
1 change: 1 addition & 0 deletions packages/demo/src/pages/home/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
}
.navigation-wrapper {
display: flex;
align-items: center;
.navigator {
margin: 0 20px;
padding: 4px 24px;
Expand Down
16 changes: 6 additions & 10 deletions packages/demo/src/pages/home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState } from 'react';
import './index.scss';
import Logo from '../../images/logo';
import { Link, Outlet } from 'react-router-dom';
import { Switch } from 'pivot-design';

const navigatorList = [
{
Expand All @@ -19,22 +20,17 @@ const navigatorList = [
];

const Home: React.FC = () => {
const [theme, setTheme] = useState<'dark' | 'light'>('dark');
const toggleTheme = () => {
if (theme === 'dark') {
document.body.setAttribute('pivot-theme', 'light');
setTheme('light');
} else {
document.body.setAttribute('pivot-theme', 'dark');
setTheme('dark');
}
const [isLight, setIsLight] = useState(false);
const onThemeChange = (isLight: boolean) => {
document.body.setAttribute('pivot-theme', isLight ? 'light' : 'dark');
setIsLight(isLight);
};
return (
<div className="pivot-design-home">
<div className="pivot-design-home-title">
<div className="title">Pivot Design</div>
<div className="navigation-wrapper">
<div onClick={toggleTheme}>切换主题</div>
<Switch value={isLight} onChange={onThemeChange} />
{navigatorList.map((nav) => (
<Link to={nav.path} className="navigator" key={nav.text}>
{nav.text}
Expand Down
35 changes: 34 additions & 1 deletion packages/design/components/Switch/index.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
@import '../constants.scss';

.#{$prefix}-switch {
color: black;
position: relative;
width: 42px;
height: 24px;
border-radius: 12px;
background-color: var(--semi-color-fill-2);
transition: background 200ms ease-in;

&.checked {
background-color: var(--semi-color-success);
.#{$prefix}-switch-pole {
transform: translateX(20px);
}
}

&-pole {
position: absolute;
top: 2px;
left: 0;
width: 20px;
height: 20px;
border-radius: 50%;
transform: translateX(2px);
background-color: var(--semi-color-white);
transition: transform 200ms ease-in;
}
&-input {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
opacity: 0;
z-index: 1;
}
}
29 changes: 27 additions & 2 deletions packages/design/components/Switch/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
import classnames from 'classnames';
import { prefix } from '../constants';
import './index.scss';
function Switch(props: any) {
return <div className={`${prefix}-switch`}>switch</div>;
import useControlled from '../hooks/useControlled';

interface SwitchProps {
value?: boolean;
defaultValue?: boolean;
onChange?: (e: boolean) => void;
}

function Switch(props: SwitchProps) {
const [value, setValue] = useControlled(props);

return (
<div
className={classnames(`${prefix}-switch`, {
checked: value,
})}
>
<div className={classnames(`${prefix}-switch-pole`)} />
<input
type="checkbox"
className={classnames(`${prefix}-switch-input`)}
defaultChecked={value}
onChange={(e) => setValue(e.target.checked)}
/>
</div>
);
}
export default Switch;
51 changes: 51 additions & 0 deletions packages/design/components/hooks/useControlled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* eslint-disable no-redeclare */
import { useCallback, useState, SetStateAction } from 'react';

export interface Options<T> {
defaultValue?: T;
defaultValuePropName?: string;
valuePropName?: string;
changeName?: string;
}

export interface StandardProps<T> {
value?: T;
defaultValue?: T;
onChange?: (val: T) => void;
}

type Props = Record<string, any>;

function useControlled<T = any>(props: StandardProps<T>): [T, (v: SetStateAction<T>) => void];
function useControlled<T = any>(
props?: StandardProps<T>,
options?: Options<T>
): [T, (v: SetStateAction<T>, ...args: any[]) => void];

function useControlled<T = any>(props: Props = {}, options: Options<T> = {}) {
const {
defaultValue: defaultOptionsValue,
defaultValuePropName = 'defaultValue',
valuePropName = 'value',
changeName = 'onChange',
} = options;

// 获取外部传入的value,判断是否受控
const value = props[valuePropName] as T;
const defaultPropsValue = props[defaultValuePropName] ?? defaultOptionsValue;
const propsOnChange = props[changeName];
const isControlled = Object.hasOwn(props, valuePropName);

const [state, setState] = useState(isControlled ? value : defaultPropsValue);

const setValue = useCallback((v: T, ...args: any[]) => {
if (isControlled) {
propsOnChange(v, args);
}
setState(v);
}, []);

return [state, setValue];
}

export default useControlled;

0 comments on commit e45f4ab

Please sign in to comment.