refactor: backtop
parent
6c735fee67
commit
e64a19a05a
|
@ -42,3 +42,7 @@ export const withInstall = <T>(comp: T) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MaybeRef<T> = T | Ref<T>;
|
export type MaybeRef<T> = T | Ref<T>;
|
||||||
|
|
||||||
|
export function eventType<T>() {
|
||||||
|
return { type: [Function, Array] as PropType<T | T[]> };
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ You can customize the style of the button, just note the size limit: no more tha
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="components-back-top-demo-custom">
|
<div id="components-back-top-demo-custom">
|
||||||
<a-back-top>
|
<a-back-top @click="handleClick">
|
||||||
<div class="ant-back-top-inner">UP</div>
|
<div class="ant-back-top-inner">UP</div>
|
||||||
</a-back-top>
|
</a-back-top>
|
||||||
Scroll down to see the bottom-right
|
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.
|
button.
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const handleClick = () => {
|
||||||
|
console.log('click');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
#components-back-top-demo-custom .ant-back-top {
|
:deep(#components-back-top-demo-custom) .ant-back-top {
|
||||||
bottom: 100px;
|
inset-block-end: 100px;
|
||||||
}
|
}
|
||||||
#components-back-top-demo-custom .ant-back-top-inner {
|
#components-back-top-demo-custom .ant-back-top-inner {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
|
|
|
@ -11,21 +11,21 @@ import {
|
||||||
onDeactivated,
|
onDeactivated,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import VerticalAlignTopOutlined from '@ant-design/icons-vue/VerticalAlignTopOutlined';
|
import VerticalAlignTopOutlined from '@ant-design/icons-vue/VerticalAlignTopOutlined';
|
||||||
import addEventListener from '../vc-util/Dom/addEventListener';
|
|
||||||
import getScroll from '../_util/getScroll';
|
import getScroll from '../_util/getScroll';
|
||||||
import { getTransitionProps, Transition } from '../_util/transition';
|
import { getTransitionProps, Transition } from '../_util/transition';
|
||||||
import scrollTo from '../_util/scrollTo';
|
import scrollTo from '../_util/scrollTo';
|
||||||
import { withInstall } from '../_util/type';
|
import { withInstall, eventType } from '../_util/type';
|
||||||
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
|
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
|
||||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||||
import type { MouseEventHandler } from '../_util/EventInterface';
|
import type { MouseEventHandler } from '../_util/EventInterface';
|
||||||
|
import useStyle from './style';
|
||||||
|
|
||||||
export const backTopProps = () => ({
|
export const backTopProps = () => ({
|
||||||
visibilityHeight: { type: Number, default: 400 },
|
visibilityHeight: { type: Number, default: 400 },
|
||||||
duration: { type: Number, default: 450 },
|
duration: { type: Number, default: 450 },
|
||||||
target: Function as PropType<() => HTMLElement | Window | Document>,
|
target: Function as PropType<() => HTMLElement | Window | Document>,
|
||||||
prefixCls: String,
|
prefixCls: String,
|
||||||
onClick: Function as PropType<MouseEventHandler>,
|
onClick: eventType<MouseEventHandler>(),
|
||||||
// visible: { type: Boolean, default: undefined }, // Only for test. Don't use it.
|
// visible: { type: Boolean, default: undefined }, // Only for test. Don't use it.
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ const BackTop = defineComponent({
|
||||||
// emits: ['click'],
|
// emits: ['click'],
|
||||||
setup(props, { slots, attrs, emit }) {
|
setup(props, { slots, attrs, emit }) {
|
||||||
const { prefixCls, direction } = useConfigInject('back-top', props);
|
const { prefixCls, direction } = useConfigInject('back-top', props);
|
||||||
|
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||||
const domRef = ref();
|
const domRef = ref();
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
visible: false,
|
visible: false,
|
||||||
|
@ -60,26 +61,23 @@ const BackTop = defineComponent({
|
||||||
const handleScroll = throttleByAnimationFrame((e: Event | { target: any }) => {
|
const handleScroll = throttleByAnimationFrame((e: Event | { target: any }) => {
|
||||||
const { visibilityHeight } = props;
|
const { visibilityHeight } = props;
|
||||||
const scrollTop = getScroll(e.target, true);
|
const scrollTop = getScroll(e.target, true);
|
||||||
state.visible = scrollTop > visibilityHeight;
|
state.visible = scrollTop >= visibilityHeight;
|
||||||
});
|
});
|
||||||
|
|
||||||
const bindScrollEvent = () => {
|
const bindScrollEvent = () => {
|
||||||
const { target } = props;
|
const { target } = props;
|
||||||
const getTarget = target || getDefaultTarget;
|
const getTarget = target || getDefaultTarget;
|
||||||
const container = getTarget();
|
const container = getTarget();
|
||||||
state.scrollEvent = addEventListener(container, 'scroll', (e: Event) => {
|
handleScroll({ target: container });
|
||||||
handleScroll(e);
|
container?.addEventListener('scroll', handleScroll);
|
||||||
});
|
|
||||||
handleScroll({
|
|
||||||
target: container,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const scrollRemove = () => {
|
const scrollRemove = () => {
|
||||||
if (state.scrollEvent) {
|
const { target } = props;
|
||||||
state.scrollEvent.remove();
|
const getTarget = target || getDefaultTarget;
|
||||||
}
|
const container = getTarget();
|
||||||
(handleScroll as any).cancel();
|
handleScroll.cancel();
|
||||||
|
container?.removeEventListener('scroll', handleScroll);
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
@ -124,6 +122,7 @@ const BackTop = defineComponent({
|
||||||
...attrs,
|
...attrs,
|
||||||
onClick: scrollToTop,
|
onClick: scrollToTop,
|
||||||
class: {
|
class: {
|
||||||
|
[hashId.value]: true,
|
||||||
[`${prefixCls.value}`]: true,
|
[`${prefixCls.value}`]: true,
|
||||||
[`${attrs.class}`]: attrs.class,
|
[`${attrs.class}`]: attrs.class,
|
||||||
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
||||||
|
@ -131,12 +130,12 @@ const BackTop = defineComponent({
|
||||||
};
|
};
|
||||||
|
|
||||||
const transitionProps = getTransitionProps('fade');
|
const transitionProps = getTransitionProps('fade');
|
||||||
return (
|
return wrapSSR(
|
||||||
<Transition {...transitionProps}>
|
<Transition {...transitionProps}>
|
||||||
<div v-show={state.visible} {...divProps} ref={domRef}>
|
<div v-show={state.visible} {...divProps} ref={domRef}>
|
||||||
{slots.default?.() || defaultElement}
|
{slots.default?.() || defaultElement}
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -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';
|
|
|
@ -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,
|
||||||
|
}),
|
||||||
|
);
|
|
@ -1,2 +0,0 @@
|
||||||
import '../../style/index.less';
|
|
||||||
import './index.less';
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -28,7 +28,7 @@ import './switch/style';
|
||||||
import './auto-complete/style';
|
import './auto-complete/style';
|
||||||
// import './affix/style';
|
// import './affix/style';
|
||||||
import './cascader/style';
|
import './cascader/style';
|
||||||
import './back-top/style';
|
// import './back-top/style';
|
||||||
import './modal/style';
|
import './modal/style';
|
||||||
// import './alert/style';
|
// import './alert/style';
|
||||||
import './time-picker/style';
|
import './time-picker/style';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { ComponentToken as AlertComponentToken } from '../../alert/style';
|
import type { ComponentToken as AlertComponentToken } from '../../alert/style';
|
||||||
import type { ComponentToken as AnchorComponentToken } from '../../anchor/style';
|
import type { ComponentToken as AnchorComponentToken } from '../../anchor/style';
|
||||||
import type { ComponentToken as AvatarComponentToken } from '../../avatar/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 ButtonComponentToken } from '../../button/style';
|
||||||
// import type { ComponentToken as FloatButtonComponentToken } from '../../float-button/style';
|
// import type { ComponentToken as FloatButtonComponentToken } from '../../float-button/style';
|
||||||
// import type { ComponentToken as CalendarComponentToken } from '../../calendar/style';
|
// import type { ComponentToken as CalendarComponentToken } from '../../calendar/style';
|
||||||
|
@ -55,32 +55,32 @@ export interface ComponentTokenMap {
|
||||||
Alert?: AlertComponentToken;
|
Alert?: AlertComponentToken;
|
||||||
Anchor?: AnchorComponentToken;
|
Anchor?: AnchorComponentToken;
|
||||||
Avatar?: AvatarComponentToken;
|
Avatar?: AvatarComponentToken;
|
||||||
// BackTop?: BackTopComponentToken;
|
BackTop?: BackTopComponentToken;
|
||||||
// Badge?: {};
|
Badge?: {};
|
||||||
// Button?: ButtonComponentToken;
|
// Button?: ButtonComponentToken;
|
||||||
// Breadcrumb?: {};
|
Breadcrumb?: {};
|
||||||
// Card?: CardComponentToken;
|
// Card?: CardComponentToken;
|
||||||
// Carousel?: CarouselComponentToken;
|
// Carousel?: CarouselComponentToken;
|
||||||
// Cascader?: CascaderComponentToken;
|
// Cascader?: CascaderComponentToken;
|
||||||
// Checkbox?: CheckboxComponentToken;
|
// Checkbox?: CheckboxComponentToken;
|
||||||
// Collapse?: CollapseComponentToken;
|
// Collapse?: CollapseComponentToken;
|
||||||
// DatePicker?: DatePickerComponentToken;
|
// DatePicker?: DatePickerComponentToken;
|
||||||
// Descriptions?: {};
|
Descriptions?: {};
|
||||||
// Divider?: DividerComponentToken;
|
// Divider?: DividerComponentToken;
|
||||||
// Drawer?: DrawerComponentToken;
|
// Drawer?: DrawerComponentToken;
|
||||||
// Dropdown?: DropdownComponentToken;
|
// Dropdown?: DropdownComponentToken;
|
||||||
// Empty?: EmptyComponentToken;
|
// Empty?: EmptyComponentToken;
|
||||||
// FloatButton?: FloatButtonComponentToken;
|
// FloatButton?: FloatButtonComponentToken;
|
||||||
// Form?: {};
|
Form?: {};
|
||||||
// Grid?: {};
|
Grid?: {};
|
||||||
// Image?: ImageComponentToken;
|
// Image?: ImageComponentToken;
|
||||||
// Input?: {};
|
Input?: {};
|
||||||
// InputNumber?: InputNumberComponentToken;
|
// InputNumber?: InputNumberComponentToken;
|
||||||
// Layout?: LayoutComponentToken;
|
// Layout?: LayoutComponentToken;
|
||||||
// List?: ListComponentToken;
|
// List?: ListComponentToken;
|
||||||
// Mentions?: MentionsComponentToken;
|
// Mentions?: MentionsComponentToken;
|
||||||
// Notification?: NotificationComponentToken;
|
// Notification?: NotificationComponentToken;
|
||||||
// Pagination?: {};
|
Pagination?: {};
|
||||||
// Popover?: PopoverComponentToken;
|
// Popover?: PopoverComponentToken;
|
||||||
// Popconfirm?: PopconfirmComponentToken;
|
// Popconfirm?: PopconfirmComponentToken;
|
||||||
// Rate?: RateComponentToken;
|
// Rate?: RateComponentToken;
|
||||||
|
@ -91,11 +91,11 @@ export interface ComponentTokenMap {
|
||||||
// Skeleton?: SkeletonComponentToken;
|
// Skeleton?: SkeletonComponentToken;
|
||||||
// Slider?: SliderComponentToken;
|
// Slider?: SliderComponentToken;
|
||||||
// Spin?: SpinComponentToken;
|
// Spin?: SpinComponentToken;
|
||||||
// Statistic?: {};
|
Statistic?: {};
|
||||||
// Switch?: {};
|
Switch?: {};
|
||||||
// Tag?: TagComponentToken;
|
// Tag?: TagComponentToken;
|
||||||
// Tree?: {};
|
Tree?: {};
|
||||||
// TreeSelect?: {};
|
TreeSelect?: {};
|
||||||
// Typography?: TypographyComponentToken;
|
// Typography?: TypographyComponentToken;
|
||||||
// Timeline?: TimelineComponentToken;
|
// Timeline?: TimelineComponentToken;
|
||||||
// Transfer?: TransferComponentToken;
|
// Transfer?: TransferComponentToken;
|
||||||
|
|
Loading…
Reference in New Issue