refactor: backtop

pull/6213/head
tangjinzhou 2023-01-26 10:13:10 +08:00
parent 6c735fee67
commit e64a19a05a
9 changed files with 159 additions and 105 deletions

View File

@ -42,3 +42,7 @@ export const withInstall = <T>(comp: T) => {
};
export type MaybeRef<T> = T | Ref<T>;
export function eventType<T>() {
return { type: [Function, Array] as PropType<T | T[]> };
}

View File

@ -17,7 +17,7 @@ You can customize the style of the button, just note the size limit: no more tha
<template>
<div id="components-back-top-demo-custom">
<a-back-top>
<a-back-top @click="handleClick">
<div class="ant-back-top-inner">UP</div>
</a-back-top>
Scroll down to see the bottom-right
@ -25,10 +25,15 @@ You can customize the style of the button, just note the size limit: no more tha
button.
</div>
</template>
<script setup>
const handleClick = () => {
console.log('click');
};
</script>
<style scoped>
#components-back-top-demo-custom .ant-back-top {
bottom: 100px;
:deep(#components-back-top-demo-custom) .ant-back-top {
inset-block-end: 100px;
}
#components-back-top-demo-custom .ant-back-top-inner {
height: 40px;

View File

@ -11,21 +11,21 @@ import {
onDeactivated,
} from 'vue';
import VerticalAlignTopOutlined from '@ant-design/icons-vue/VerticalAlignTopOutlined';
import addEventListener from '../vc-util/Dom/addEventListener';
import getScroll from '../_util/getScroll';
import { getTransitionProps, Transition } from '../_util/transition';
import scrollTo from '../_util/scrollTo';
import { withInstall } from '../_util/type';
import { withInstall, eventType } from '../_util/type';
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
import useConfigInject from '../_util/hooks/useConfigInject';
import type { MouseEventHandler } from '../_util/EventInterface';
import useStyle from './style';
export const backTopProps = () => ({
visibilityHeight: { type: Number, default: 400 },
duration: { type: Number, default: 450 },
target: Function as PropType<() => HTMLElement | Window | Document>,
prefixCls: String,
onClick: Function as PropType<MouseEventHandler>,
onClick: eventType<MouseEventHandler>(),
// visible: { type: Boolean, default: undefined }, // Only for test. Don't use it.
});
@ -39,6 +39,7 @@ const BackTop = defineComponent({
// emits: ['click'],
setup(props, { slots, attrs, emit }) {
const { prefixCls, direction } = useConfigInject('back-top', props);
const [wrapSSR, hashId] = useStyle(prefixCls);
const domRef = ref();
const state = reactive({
visible: false,
@ -60,26 +61,23 @@ const BackTop = defineComponent({
const handleScroll = throttleByAnimationFrame((e: Event | { target: any }) => {
const { visibilityHeight } = props;
const scrollTop = getScroll(e.target, true);
state.visible = scrollTop > visibilityHeight;
state.visible = scrollTop >= visibilityHeight;
});
const bindScrollEvent = () => {
const { target } = props;
const getTarget = target || getDefaultTarget;
const container = getTarget();
state.scrollEvent = addEventListener(container, 'scroll', (e: Event) => {
handleScroll(e);
});
handleScroll({
target: container,
});
handleScroll({ target: container });
container?.addEventListener('scroll', handleScroll);
};
const scrollRemove = () => {
if (state.scrollEvent) {
state.scrollEvent.remove();
}
(handleScroll as any).cancel();
const { target } = props;
const getTarget = target || getDefaultTarget;
const container = getTarget();
handleScroll.cancel();
container?.removeEventListener('scroll', handleScroll);
};
watch(
@ -124,6 +122,7 @@ const BackTop = defineComponent({
...attrs,
onClick: scrollToTop,
class: {
[hashId.value]: true,
[`${prefixCls.value}`]: true,
[`${attrs.class}`]: attrs.class,
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
@ -131,12 +130,12 @@ const BackTop = defineComponent({
};
const transitionProps = getTransitionProps('fade');
return (
return wrapSSR(
<Transition {...transitionProps}>
<div v-show={state.visible} {...divProps} ref={domRef}>
{slots.default?.() || defaultElement}
</div>
</Transition>
</Transition>,
);
};
},

View File

@ -1,49 +0,0 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@backtop-prefix-cls: ~'@{ant-prefix}-back-top';
.@{backtop-prefix-cls} {
.reset-component();
position: fixed;
right: 100px;
bottom: 50px;
z-index: @zindex-back-top;
width: 40px;
height: 40px;
cursor: pointer;
&:empty {
display: none;
}
&-rtl {
right: auto;
left: 100px;
direction: rtl;
}
&-content {
width: 40px;
height: 40px;
overflow: hidden;
color: @back-top-color;
text-align: center;
background-color: @back-top-bg;
border-radius: 20px;
transition: all 0.3s;
&:hover {
background-color: @back-top-hover-bg;
transition: all 0.3s;
}
}
&-icon {
font-size: 24px;
line-height: 40px;
}
}
@import './responsive';

View File

@ -0,0 +1,118 @@
import type { CSSObject } from '../../_util/cssinjs';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { resetComponent } from '../../_style';
/** Component only token. Which will handle additional calculation of alias token */
export interface ComponentToken {
zIndexPopup: number;
}
type BackTopToken = FullToken<'BackTop'> & {
backTopBackground: string;
backTopColor: string;
backTopHoverBackground: string;
backTopFontSize: number;
backTopSize: number;
// Position
backTopBlockEnd: number;
backTopInlineEnd: number;
backTopInlineEndMD: number;
backTopInlineEndXS: number;
};
// ============================== Shared ==============================
const genSharedBackTopStyle: GenerateStyle<BackTopToken, CSSObject> = (token): CSSObject => {
const { componentCls, backTopFontSize, backTopSize, zIndexPopup } = token;
return {
[componentCls]: {
...resetComponent(token),
position: 'fixed',
insetInlineEnd: token.backTopInlineEnd,
insetBlockEnd: token.backTopBlockEnd,
zIndex: zIndexPopup,
width: 40,
height: 40,
cursor: 'pointer',
'&:empty': {
display: 'none',
},
[`${componentCls}-content`]: {
width: backTopSize,
height: backTopSize,
overflow: 'hidden',
color: token.backTopColor,
textAlign: 'center',
backgroundColor: token.backTopBackground,
borderRadius: backTopSize,
transition: `all ${token.motionDurationMid}`,
'&:hover': {
backgroundColor: token.backTopHoverBackground,
transition: `all ${token.motionDurationMid}`,
},
},
// change to .backtop .backtop-icon
[`${componentCls}-icon`]: {
fontSize: backTopFontSize,
lineHeight: `${backTopSize}px`,
},
},
};
};
const genMediaBackTopStyle: GenerateStyle<BackTopToken> = (token): CSSObject => {
const { componentCls } = token;
return {
[`@media (max-width: ${token.screenMD}px)`]: {
[componentCls]: {
insetInlineEnd: token.backTopInlineEndMD,
},
},
[`@media (max-width: ${token.screenXS}px)`]: {
[componentCls]: {
insetInlineEnd: token.backTopInlineEndXS,
},
},
};
};
// ============================== Export ==============================
export default genComponentStyleHook<'BackTop'>(
'BackTop',
token => {
const {
fontSizeHeading3,
colorTextDescription,
colorTextLightSolid,
colorText,
controlHeightLG,
} = token;
const backTopToken = mergeToken<BackTopToken>(token, {
backTopBackground: colorTextDescription,
backTopColor: colorTextLightSolid,
backTopHoverBackground: colorText,
backTopFontSize: fontSizeHeading3,
backTopSize: controlHeightLG,
backTopBlockEnd: controlHeightLG * 1.25,
backTopInlineEnd: controlHeightLG * 2.5,
backTopInlineEndMD: controlHeightLG * 1.5,
backTopInlineEndXS: controlHeightLG * 0.5,
});
return [genSharedBackTopStyle(backTopToken), genMediaBackTopStyle(backTopToken)];
},
token => ({
zIndexPopup: token.zIndexBase + 10,
}),
);

View File

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

View File

@ -1,21 +0,0 @@
@media screen and (max-width: @screen-md) {
.@{backtop-prefix-cls} {
right: 60px;
&-rtl {
right: auto;
left: 60px;
}
}
}
@media screen and (max-width: @screen-xs) {
.@{backtop-prefix-cls} {
right: 20px;
&-rtl {
right: auto;
left: 20px;
}
}
}

View File

@ -28,7 +28,7 @@ import './switch/style';
import './auto-complete/style';
// import './affix/style';
import './cascader/style';
import './back-top/style';
// import './back-top/style';
import './modal/style';
// import './alert/style';
import './time-picker/style';

View File

@ -1,7 +1,7 @@
import type { ComponentToken as AlertComponentToken } from '../../alert/style';
import type { ComponentToken as AnchorComponentToken } from '../../anchor/style';
import type { ComponentToken as AvatarComponentToken } from '../../avatar/style';
// import type { ComponentToken as BackTopComponentToken } from '../../back-top/style';
import type { ComponentToken as BackTopComponentToken } from '../../back-top/style';
// import type { ComponentToken as ButtonComponentToken } from '../../button/style';
// import type { ComponentToken as FloatButtonComponentToken } from '../../float-button/style';
// import type { ComponentToken as CalendarComponentToken } from '../../calendar/style';
@ -55,32 +55,32 @@ export interface ComponentTokenMap {
Alert?: AlertComponentToken;
Anchor?: AnchorComponentToken;
Avatar?: AvatarComponentToken;
// BackTop?: BackTopComponentToken;
// Badge?: {};
BackTop?: BackTopComponentToken;
Badge?: {};
// Button?: ButtonComponentToken;
// Breadcrumb?: {};
Breadcrumb?: {};
// Card?: CardComponentToken;
// Carousel?: CarouselComponentToken;
// Cascader?: CascaderComponentToken;
// Checkbox?: CheckboxComponentToken;
// Collapse?: CollapseComponentToken;
// DatePicker?: DatePickerComponentToken;
// Descriptions?: {};
Descriptions?: {};
// Divider?: DividerComponentToken;
// Drawer?: DrawerComponentToken;
// Dropdown?: DropdownComponentToken;
// Empty?: EmptyComponentToken;
// FloatButton?: FloatButtonComponentToken;
// Form?: {};
// Grid?: {};
Form?: {};
Grid?: {};
// Image?: ImageComponentToken;
// Input?: {};
Input?: {};
// InputNumber?: InputNumberComponentToken;
// Layout?: LayoutComponentToken;
// List?: ListComponentToken;
// Mentions?: MentionsComponentToken;
// Notification?: NotificationComponentToken;
// Pagination?: {};
Pagination?: {};
// Popover?: PopoverComponentToken;
// Popconfirm?: PopconfirmComponentToken;
// Rate?: RateComponentToken;
@ -91,11 +91,11 @@ export interface ComponentTokenMap {
// Skeleton?: SkeletonComponentToken;
// Slider?: SliderComponentToken;
// Spin?: SpinComponentToken;
// Statistic?: {};
// Switch?: {};
Statistic?: {};
Switch?: {};
// Tag?: TagComponentToken;
// Tree?: {};
// TreeSelect?: {};
Tree?: {};
TreeSelect?: {};
// Typography?: TypographyComponentToken;
// Timeline?: TimelineComponentToken;
// Transfer?: TransferComponentToken;