/
index.jsx
154 lines (144 loc) · 4.01 KB
/
index.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import React, { useState, useRef, useLayoutEffect } from 'react';
import PropTypes from 'prop-types';
import styled, { css } from 'styled-components';
import { useColor } from 'hooks/useColor';
const transitionStyle = css`
transition: left 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, right 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
`;
const SwitchButton = styled.div`
height: ${(props) => props.$thumbSize}px;
width: ${(props) => props.$switchWidth}px;
background: ${(props) => props.$switchColor};
display: inline-flex;
color: #FFF;
border-radius: 50px;
position: relative;
border: 3px solid ${(props) => props.$switchColor};
cursor: ${(props) => (props.$isDisabled ? 'not-allowed' : 'pointer')};
box-sizing: content-box;
`;
const Thumb = styled.div`
width: ${(props) => props.$thumbSize}px;
height: ${(props) => props.$thumbSize}px;
border-radius: 50px;
background: #FFF;
position: absolute;
${(props) => {
if (props.$isChecked) {
return `left: ${props.$switchWidth - props.$thumbSize}px;`;
}
return 'left: 0px;';
}}
${transitionStyle}
`;
const Label = styled.div`
position: absolute;
font-size: 14px;
white-space: nowrap;
top: 50%;
transform: translateY(-50%);
padding: 0px ${(props) => props.$padding}px;
${(props) => {
if (props.$isChecked) {
return `right: ${props.$switchWidth - props.$labelWidth}px;`;
}
return `
right: 0px;
`;
}}
${transitionStyle}
`;
/**
* `Switch` 元件是一個開關的選擇器。
* 在我們表示開關狀態,或兩種狀態之間的切換時,很適合使用。和 Checkbox 的區別是, Checkbox 一般只用來標記狀態是否被選取,
* 需要提交之後才會生效,而 Switch 則會在觸發的當下直接觸發狀態的改變。
*/
const Switch = ({
isChecked, isDisabled,
size, themeColor, onChange,
checkedChildren, unCheckedChildren,
...props
}) => {
const labelRef = useRef(null);
const { makeColor } = useColor();
const [labelWidth, setLabelWidth] = useState(0);
const thumbSize = size === 'small' ? 12 : 18;
const switchWidth = thumbSize + labelWidth;
const switchColor = makeColor({ themeColor, isDisabled: !isChecked });
useLayoutEffect(() => {
const minLabelSize = thumbSize * 1.2;
const currentLabelWidth = labelRef?.current?.clientWidth;
if (currentLabelWidth) {
setLabelWidth(currentLabelWidth < minLabelSize ? minLabelSize : currentLabelWidth);
}
}, [labelRef?.current?.clientWidth, thumbSize]);
return (
<SwitchButton
$switchWidth={switchWidth}
$thumbSize={thumbSize}
$switchColor={switchColor}
$isDisabled={isDisabled}
onClick={isDisabled ? null : onChange}
{...props}
>
<Thumb
$isChecked={isChecked}
$thumbSize={thumbSize}
$switchWidth={switchWidth}
/>
<Label
ref={labelRef}
$padding={thumbSize / 3}
$labelWidth={labelWidth}
$switchWidth={switchWidth}
$isChecked={isChecked}
>
{
isChecked
? checkedChildren
: unCheckedChildren
}
</Label>
</SwitchButton>
);
};
Switch.propTypes = {
/**
* 開啟狀態的內容。若設置,則由外部參數控制;若不設置,則由內部 state 控制
*/
isChecked: PropTypes.bool,
/**
* 禁用狀態
*/
isDisabled: PropTypes.bool,
/**
* 主題配色,primary、secondary 或是自己傳入色票
*/
themeColor: PropTypes.oneOfType([PropTypes.oneOf(['primary', 'secondary']), PropTypes.string]),
/**
* 狀態改變的 callback function
*/
onChange: PropTypes.func,
/**
* 開關大小
*/
size: PropTypes.oneOf(['default', 'small']),
/**
* 開啟狀態的內容
*/
checkedChildren: PropTypes.string,
/**
* 關閉狀態的內容
*/
unCheckedChildren: PropTypes.string,
};
Switch.defaultProps = {
isChecked: null,
isDisabled: false,
themeColor: 'primary',
size: 'default',
checkedChildren: '',
unCheckedChildren: '',
onChange: () => {},
};
export default Switch;