refactor: switch (#6236)

* refactor: switch style

* refactor: delete switch style
pull/6245/head
Zev Zhu 2 years ago committed by GitHub
parent a40816880a
commit 69c17dc255
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -24,7 +24,7 @@ import './carousel/style';
// import './message/style';
// import './spin/style';
import './select/style';
import './switch/style';
// import './switch/style';
import './auto-complete/style';
// import './affix/style';
import './cascader/style';

@ -11,7 +11,7 @@ import useConfigInject from '../config-provider/hooks/useConfigInject';
import { useInjectFormItemContext } from '../form/FormItemContext';
import omit from '../_util/omit';
import type { FocusEventHandler } from '../_util/EventInterface';
import useStyle from './style';
export const SwitchSizes = tuple('small', 'default');
type CheckedType = boolean | string | number;
export const switchProps = () => ({
@ -89,6 +89,7 @@ const Switch = defineComponent({
);
const { prefixCls, direction, size } = useConfigInject('switch', props);
const [wrapSSR, hashId] = useStyle(prefixCls);
const refSwitchNode = ref();
const focus = () => {
refSwitchNode.value?.focus();
@ -148,47 +149,52 @@ const Switch = defineComponent({
[`${prefixCls.value}-disabled`]: props.disabled,
[prefixCls.value]: true,
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
[hashId.value]: true,
}));
return () => (
<Wave insertExtraNode>
<button
{...omit(props, [
'prefixCls',
'checkedChildren',
'unCheckedChildren',
'checked',
'autofocus',
'checkedValue',
'unCheckedValue',
'id',
'onChange',
'onUpdate:checked',
])}
{...attrs}
id={props.id ?? formItemContext.id.value}
onKeydown={handleKeyDown}
onClick={handleClick}
onBlur={handleBlur}
onMouseup={handleMouseUp}
type="button"
role="switch"
aria-checked={checked.value as any}
disabled={props.disabled || props.loading}
class={[attrs.class, classNames.value]}
ref={refSwitchNode}
>
<div class={`${prefixCls.value}-handle`}>
{props.loading ? <LoadingOutlined class={`${prefixCls.value}-loading-icon`} /> : null}
</div>
<span class={`${prefixCls.value}-inner`}>
{checkedStatus.value
? getPropsSlot(slots, props, 'checkedChildren')
: getPropsSlot(slots, props, 'unCheckedChildren')}
</span>
</button>
</Wave>
);
return () =>
wrapSSR(
<Wave insertExtraNode>
<button
{...omit(props, [
'prefixCls',
'checkedChildren',
'unCheckedChildren',
'checked',
'autofocus',
'checkedValue',
'unCheckedValue',
'id',
'onChange',
'onUpdate:checked',
])}
{...attrs}
id={props.id ?? formItemContext.id.value}
onKeydown={handleKeyDown}
onClick={handleClick}
onBlur={handleBlur}
onMouseup={handleMouseUp}
type="button"
role="switch"
aria-checked={checked.value as any}
disabled={props.disabled || props.loading}
class={[attrs.class, classNames.value]}
ref={refSwitchNode}
>
<div class={`${prefixCls.value}-handle`}>
{props.loading ? <LoadingOutlined class={`${prefixCls.value}-loading-icon`} /> : null}
</div>
<span class={`${prefixCls.value}-inner`}>
<span class={`${prefixCls.value}-inner-checked`}>
{getPropsSlot(slots, props, 'checkedChildren')}
</span>
<span class={`${prefixCls.value}-inner-unchecked`}>
{getPropsSlot(slots, props, 'unCheckedChildren')}
</span>
</span>
</button>
</Wave>,
);
},
});

@ -1,155 +0,0 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@switch-prefix-cls: ~'@{ant-prefix}-switch';
@switch-duration: 0.2s;
@switch-pin-size: @switch-height - 4px;
@switch-sm-pin-size: @switch-sm-height - 4px;
.@{switch-prefix-cls} {
.reset-component();
position: relative;
display: inline-block;
box-sizing: border-box;
min-width: @switch-min-width;
height: @switch-height;
line-height: @switch-height;
vertical-align: middle;
background-image: linear-gradient(to right, @disabled-color, @disabled-color),
linear-gradient(to right, @component-background, @component-background);
border: 0;
border-radius: 100px;
cursor: pointer;
transition: all @switch-duration;
user-select: none;
&:focus {
outline: 0;
box-shadow: 0 0 0 2px fade(@disabled-color, 10%);
}
&-checked:focus {
box-shadow: 0 0 0 2px @primary-1;
}
&:focus:hover {
box-shadow: none;
}
&-checked {
background: @switch-color;
}
&-loading,
&-disabled {
cursor: not-allowed;
opacity: @switch-disabled-opacity;
* {
box-shadow: none;
cursor: not-allowed;
}
}
// ========================= Inner ==========================
&-inner {
display: block;
margin: 0 @switch-inner-margin-min 0 @switch-inner-margin-max;
color: @text-color-inverse;
font-size: @font-size-sm;
transition: margin @switch-duration;
}
&-checked &-inner {
margin: 0 @switch-inner-margin-max 0 @switch-inner-margin-min;
}
// ========================= Handle =========================
&-handle {
position: absolute;
top: @switch-padding;
left: @switch-padding;
width: @switch-pin-size;
height: @switch-pin-size;
transition: all @switch-duration ease-in-out;
&::before {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: @switch-bg;
border-radius: (@switch-pin-size / 2);
box-shadow: 0 2px 4px 0 @switch-shadow-color;
transition: all @switch-duration ease-in-out;
content: '';
}
}
&-checked &-handle {
left: calc(100% - @switch-pin-size - @switch-padding);
}
&:not(&-disabled):active {
.@{switch-prefix-cls}-handle::before {
right: -30%;
left: 0;
}
&.@{switch-prefix-cls}-checked {
.@{switch-prefix-cls}-handle::before {
right: 0;
left: -30%;
}
}
}
// ======================== Loading =========================
&-loading-icon.@{iconfont-css-prefix} {
position: relative;
top: ((@switch-pin-size - @font-size-base) / 2);
color: rgba(0, 0, 0, 0.65);
vertical-align: top;
}
&-checked &-loading-icon {
color: @switch-color;
}
// ========================== Size ==========================
&-small {
min-width: @switch-sm-min-width;
height: @switch-sm-height;
line-height: @switch-sm-height;
.@{switch-prefix-cls}-inner {
margin: 0 @switch-sm-inner-margin-min 0 @switch-sm-inner-margin-max;
font-size: @font-size-sm;
}
.@{switch-prefix-cls}-handle {
width: @switch-sm-pin-size;
height: @switch-sm-pin-size;
}
.@{switch-prefix-cls}-loading-icon {
top: ((@switch-sm-pin-size - 9px) / 2);
font-size: 9px;
}
&.@{switch-prefix-cls}-checked {
.@{switch-prefix-cls}-inner {
margin: 0 @switch-sm-inner-margin-max 0 @switch-sm-inner-margin-min;
}
.@{switch-prefix-cls}-handle {
left: calc(100% - @switch-sm-pin-size - @switch-padding);
}
}
}
}
@import './rtl';

@ -0,0 +1,353 @@
import type { CSSObject } from '../../_util/cssinjs';
import { TinyColor } from '@ctrl/tinycolor';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { genFocusStyle, resetComponent } from '../../_style';
interface SwitchToken extends FullToken<'Switch'> {
switchMinWidth: number;
switchHeight: number;
switchDuration: string;
switchColor: string;
switchDisabledOpacity: number;
switchInnerMarginMin: number;
switchInnerMarginMax: number;
switchPadding: number;
switchPinSize: number;
switchBg: string;
switchMinWidthSM: number;
switchHeightSM: number;
switchInnerMarginMinSM: number;
switchInnerMarginMaxSM: number;
switchPinSizeSM: number;
switchHandleShadow: string;
switchLoadingIconSize: number;
switchLoadingIconColor: string;
switchHandleActiveInset: string;
}
const genSwitchSmallStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
const { componentCls } = token;
const switchInnerCls = `${componentCls}-inner`;
return {
[componentCls]: {
[`&${componentCls}-small`]: {
minWidth: token.switchMinWidthSM,
height: token.switchHeightSM,
lineHeight: `${token.switchHeightSM}px`,
[`${componentCls}-inner`]: {
paddingInlineStart: token.switchInnerMarginMaxSM,
paddingInlineEnd: token.switchInnerMarginMinSM,
[`${switchInnerCls}-checked`]: {
marginInlineStart: `calc(-100% + ${
token.switchPinSizeSM + token.switchPadding * 2
}px - ${token.switchInnerMarginMaxSM * 2}px)`,
marginInlineEnd: `calc(100% - ${token.switchPinSizeSM + token.switchPadding * 2}px + ${
token.switchInnerMarginMaxSM * 2
}px)`,
},
[`${switchInnerCls}-unchecked`]: {
marginTop: -token.switchHeightSM,
marginInlineStart: 0,
marginInlineEnd: 0,
},
},
[`${componentCls}-handle`]: {
width: token.switchPinSizeSM,
height: token.switchPinSizeSM,
},
[`${componentCls}-loading-icon`]: {
top: (token.switchPinSizeSM - token.switchLoadingIconSize) / 2,
fontSize: token.switchLoadingIconSize,
},
[`&${componentCls}-checked`]: {
[`${componentCls}-inner`]: {
paddingInlineStart: token.switchInnerMarginMinSM,
paddingInlineEnd: token.switchInnerMarginMaxSM,
[`${switchInnerCls}-checked`]: {
marginInlineStart: 0,
marginInlineEnd: 0,
},
[`${switchInnerCls}-unchecked`]: {
marginInlineStart: `calc(100% - ${
token.switchPinSizeSM + token.switchPadding * 2
}px + ${token.switchInnerMarginMaxSM * 2}px)`,
marginInlineEnd: `calc(-100% + ${
token.switchPinSizeSM + token.switchPadding * 2
}px - ${token.switchInnerMarginMaxSM * 2}px)`,
},
},
[`${componentCls}-handle`]: {
insetInlineStart: `calc(100% - ${token.switchPinSizeSM + token.switchPadding}px)`,
},
},
[`&:not(${componentCls}-disabled):active`]: {
[`&:not(${componentCls}-checked) ${switchInnerCls}`]: {
[`${switchInnerCls}-unchecked`]: {
marginInlineStart: token.marginXXS / 2,
marginInlineEnd: -token.marginXXS / 2,
},
},
[`&${componentCls}-checked ${switchInnerCls}`]: {
[`${switchInnerCls}-checked`]: {
marginInlineStart: -token.marginXXS / 2,
marginInlineEnd: token.marginXXS / 2,
},
},
},
},
},
};
};
const genSwitchLoadingStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
const { componentCls } = token;
return {
[componentCls]: {
[`${componentCls}-loading-icon${token.iconCls}`]: {
position: 'relative',
top: (token.switchPinSize - token.fontSize) / 2,
color: token.switchLoadingIconColor,
verticalAlign: 'top',
},
[`&${componentCls}-checked ${componentCls}-loading-icon`]: {
color: token.switchColor,
},
},
};
};
const genSwitchHandleStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
const { componentCls } = token;
const switchHandleCls = `${componentCls}-handle`;
return {
[componentCls]: {
[switchHandleCls]: {
position: 'absolute',
top: token.switchPadding,
insetInlineStart: token.switchPadding,
width: token.switchPinSize,
height: token.switchPinSize,
transition: `all ${token.switchDuration} ease-in-out`,
'&::before': {
position: 'absolute',
top: 0,
insetInlineEnd: 0,
bottom: 0,
insetInlineStart: 0,
backgroundColor: token.colorWhite,
borderRadius: token.switchPinSize / 2,
boxShadow: token.switchHandleShadow,
transition: `all ${token.switchDuration} ease-in-out`,
content: '""',
},
},
[`&${componentCls}-checked ${switchHandleCls}`]: {
insetInlineStart: `calc(100% - ${token.switchPinSize + token.switchPadding}px)`,
},
[`&:not(${componentCls}-disabled):active`]: {
[`${switchHandleCls}::before`]: {
insetInlineEnd: token.switchHandleActiveInset,
insetInlineStart: 0,
},
[`&${componentCls}-checked ${switchHandleCls}::before`]: {
insetInlineEnd: 0,
insetInlineStart: token.switchHandleActiveInset,
},
},
},
};
};
const genSwitchInnerStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
const { componentCls } = token;
const switchInnerCls = `${componentCls}-inner`;
return {
[componentCls]: {
[switchInnerCls]: {
display: 'block',
overflow: 'hidden',
borderRadius: 100,
height: '100%',
paddingInlineStart: token.switchInnerMarginMax,
paddingInlineEnd: token.switchInnerMarginMin,
transition: `padding-inline-start ${token.switchDuration} ease-in-out, padding-inline-end ${token.switchDuration} ease-in-out`,
[`${switchInnerCls}-checked, ${switchInnerCls}-unchecked`]: {
display: 'block',
color: token.colorTextLightSolid,
fontSize: token.fontSizeSM,
transition: `margin-inline-start ${token.switchDuration} ease-in-out, margin-inline-end ${token.switchDuration} ease-in-out`,
pointerEvents: 'none',
},
[`${switchInnerCls}-checked`]: {
marginInlineStart: `calc(-100% + ${token.switchPinSize + token.switchPadding * 2}px - ${
token.switchInnerMarginMax * 2
}px)`,
marginInlineEnd: `calc(100% - ${token.switchPinSize + token.switchPadding * 2}px + ${
token.switchInnerMarginMax * 2
}px)`,
},
[`${switchInnerCls}-unchecked`]: {
marginTop: -token.switchHeight,
marginInlineStart: 0,
marginInlineEnd: 0,
},
},
[`&${componentCls}-checked ${switchInnerCls}`]: {
paddingInlineStart: token.switchInnerMarginMin,
paddingInlineEnd: token.switchInnerMarginMax,
[`${switchInnerCls}-checked`]: {
marginInlineStart: 0,
marginInlineEnd: 0,
},
[`${switchInnerCls}-unchecked`]: {
marginInlineStart: `calc(100% - ${token.switchPinSize + token.switchPadding * 2}px + ${
token.switchInnerMarginMax * 2
}px)`,
marginInlineEnd: `calc(-100% + ${token.switchPinSize + token.switchPadding * 2}px - ${
token.switchInnerMarginMax * 2
}px)`,
},
},
[`&:not(${componentCls}-disabled):active`]: {
[`&:not(${componentCls}-checked) ${switchInnerCls}`]: {
[`${switchInnerCls}-unchecked`]: {
marginInlineStart: token.switchPadding * 2,
marginInlineEnd: -token.switchPadding * 2,
},
},
[`&${componentCls}-checked ${switchInnerCls}`]: {
[`${switchInnerCls}-checked`]: {
marginInlineStart: -token.switchPadding * 2,
marginInlineEnd: token.switchPadding * 2,
},
},
},
},
};
};
const genSwitchStyle = (token: SwitchToken): CSSObject => {
const { componentCls } = token;
return {
[componentCls]: {
...resetComponent(token),
position: 'relative',
display: 'inline-block',
boxSizing: 'border-box',
minWidth: token.switchMinWidth,
height: token.switchHeight,
lineHeight: `${token.switchHeight}px`,
verticalAlign: 'middle',
background: token.colorTextQuaternary,
border: '0',
borderRadius: 100,
cursor: 'pointer',
transition: `all ${token.motionDurationMid}`,
userSelect: 'none',
[`&:hover:not(${componentCls}-disabled)`]: {
background: token.colorTextTertiary,
},
...genFocusStyle(token),
[`&${componentCls}-checked`]: {
background: token.switchColor,
[`&:hover:not(${componentCls}-disabled)`]: {
background: token.colorPrimaryHover,
},
},
[`&${componentCls}-loading, &${componentCls}-disabled`]: {
cursor: 'not-allowed',
opacity: token.switchDisabledOpacity,
'*': {
boxShadow: 'none',
cursor: 'not-allowed',
},
},
// rtl style
[`&${componentCls}-rtl`]: {
direction: 'rtl',
},
},
};
};
// ============================== Export ==============================
export default genComponentStyleHook('Switch', token => {
const switchHeight = token.fontSize * token.lineHeight;
const switchHeightSM = token.controlHeight / 2;
const switchPadding = 2; // This is magic
const switchPinSize = switchHeight - switchPadding * 2;
const switchPinSizeSM = switchHeightSM - switchPadding * 2;
const switchToken = mergeToken<SwitchToken>(token, {
switchMinWidth: switchPinSize * 2 + switchPadding * 4,
switchHeight,
switchDuration: token.motionDurationMid,
switchColor: token.colorPrimary,
switchDisabledOpacity: token.opacityLoading,
switchInnerMarginMin: switchPinSize / 2,
switchInnerMarginMax: switchPinSize + switchPadding + switchPadding * 2,
switchPadding,
switchPinSize,
switchBg: token.colorBgContainer,
switchMinWidthSM: switchPinSizeSM * 2 + switchPadding * 2,
switchHeightSM,
switchInnerMarginMinSM: switchPinSizeSM / 2,
switchInnerMarginMaxSM: switchPinSizeSM + switchPadding + switchPadding * 2,
switchPinSizeSM,
switchHandleShadow: `0 2px 4px 0 ${new TinyColor('#00230b').setAlpha(0.2).toRgbString()}`,
switchLoadingIconSize: token.fontSizeIcon * 0.75,
switchLoadingIconColor: `rgba(0, 0, 0, ${token.opacityLoading})`,
switchHandleActiveInset: '-30%',
});
return [
genSwitchStyle(switchToken),
// inner style
genSwitchInnerStyle(switchToken),
// handle style
genSwitchHandleStyle(switchToken),
// loading style
genSwitchLoadingStyle(switchToken),
// small style
genSwitchSmallStyle(switchToken),
];
});

@ -1,2 +0,0 @@
import '../../style/index.less';
import './index.less';

@ -1,52 +0,0 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@switch-prefix-cls: ~'@{ant-prefix}-switch';
@switch-pin-size: @switch-height - 4px;
@switch-sm-pin-size: @switch-sm-height - 4px;
.@{switch-prefix-cls}-rtl {
direction: rtl;
.@{switch-prefix-cls}-inner {
margin: 0 @switch-inner-margin-max 0 @switch-inner-margin-min;
}
.@{switch-prefix-cls}-handle {
right: @switch-padding;
left: auto;
}
&:not(&-disabled):active {
.@{switch-prefix-cls}-handle::before {
right: 0;
left: -30%;
}
&.@{switch-prefix-cls}-checked {
.@{switch-prefix-cls}-handle::before {
right: -30%;
left: 0;
}
}
}
&.@{switch-prefix-cls}-checked {
.@{switch-prefix-cls}-inner {
margin: 0 @switch-inner-margin-min 0 @switch-inner-margin-max;
}
.@{switch-prefix-cls}-handle {
right: calc(100% - @switch-pin-size - @switch-padding);
}
}
&.@{switch-prefix-cls}-small {
&.@{switch-prefix-cls}-checked {
.@{switch-prefix-cls}-handle {
right: calc(100% - @switch-sm-pin-size - @switch-padding);
}
}
}
}
Loading…
Cancel
Save