-
Notifications
You must be signed in to change notification settings - Fork 140
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
24 changed files
with
453 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
packages/components/back-top/__test__/__snapshots__/backTop.spec.ts.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`BackTop.vue render work 1`] = `"<transition-stub><div class=\\"ix-back-top\\" style=\\"display: none;\\"><i class=\\"ix-icon ix-icon-vertical-align-top\\" role=\\"img\\" arialabel=\\"vertical-align-top\\"></i></div></transition-stub>"`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import { mount } from '@vue/test-utils' | ||
import { nextTick } from 'vue' | ||
import BackTop from '../src/BackTop.vue' | ||
import { renderWork, waitRAF, isShow, wait } from '@tests' | ||
|
||
const mockFn = jest.fn() | ||
const warn = jest.spyOn(console, 'warn').mockImplementation() | ||
|
||
const backTopMount = (template: string, options = {}) => | ||
mount( | ||
{ | ||
components: { | ||
BackTop, | ||
}, | ||
template, | ||
...options, | ||
}, | ||
{ attachTo: document.body }, | ||
) | ||
|
||
describe('BackTop.vue', () => { | ||
renderWork(BackTop) | ||
|
||
test('scroll work', async () => { | ||
const wrapper = backTopMount( | ||
` | ||
<div class="ix-back-top-test" style="height: 300px; overflow: auto;"> | ||
<div style="height: 1000px;"> | ||
<back-top :duration="100" :visibilityHeight="200" target=".ix-back-top-test"></back-top> | ||
</div> | ||
</div> | ||
`, | ||
{ | ||
beforeUnmount() { | ||
mockFn() | ||
}, | ||
}, | ||
) | ||
|
||
expect(isShow(wrapper.find('.ix-back-top'))).toBe(false) | ||
|
||
wrapper.element.scrollTop = 600 | ||
await wrapper.trigger('scroll') | ||
await wait(1000) | ||
expect(isShow(wrapper.find('.ix-back-top'))).toBe(true) | ||
|
||
await wrapper.find('.ix-back-top').trigger('click') | ||
await wait(1000) | ||
expect(wrapper.element.scrollTop).toBe(0) | ||
|
||
wrapper.unmount() | ||
expect(mockFn).toBeCalled() | ||
}) | ||
|
||
test('props work: target is a HTMLElement', async () => { | ||
const wrapper = backTopMount( | ||
` | ||
<div style="height: 1000px;"> | ||
<back-top :target="target"></back-top> | ||
</div> | ||
`, | ||
{ | ||
data() { | ||
return { | ||
target: document.documentElement, | ||
} | ||
}, | ||
}, | ||
) | ||
|
||
expect(isShow(wrapper.find('.ix-back-top'))).toBe(false) | ||
|
||
document.documentElement.scrollTop = 600 | ||
window.dispatchEvent(new Event('scroll')) | ||
|
||
await wait(1000) | ||
|
||
expect(isShow(wrapper.find('.ix-back-top'))).toBe(true) | ||
}) | ||
|
||
test('props work: target does not exist', async () => { | ||
mount(BackTop, { | ||
props: { | ||
target: '#ix-back-top-test', | ||
}, | ||
}) | ||
|
||
await nextTick() | ||
expect(warn).toBeCalled() | ||
}) | ||
|
||
test('props work: target is the default value', async () => { | ||
window.scrollTo = mockFn | ||
const wrapper = backTopMount(` | ||
<div style="height: 1000px; overflow: auto;"> | ||
<back-top></back-top> | ||
</div> | ||
`) | ||
|
||
document.documentElement.scrollTop = 600 | ||
window.dispatchEvent(new Event('scroll')) | ||
await wait(1000) | ||
await wrapper.find('.ix-back-top').trigger('click') | ||
|
||
await waitRAF() | ||
expect(mockFn).toBeCalled() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
<template> | ||
Scroll down to see the bottom-right button. | ||
<ix-back-top /> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<template> | ||
<div ref="target" class="ix-back-top-demo-custom" style="height: 300px; overflow: auto"> | ||
<div style="height: 1000px"> | ||
<div>Scroll down to see the bottom-right button.</div> | ||
<div>Scroll down to see the bottom-right button.</div> | ||
<div>Scroll down to see the bottom-right button.</div> | ||
<div>Scroll down to see the bottom-right button.</div> | ||
<div>Scroll down to see the bottom-right button.</div> | ||
<ix-back-top style="bottom: 100px; border-radius: 4px" :duration="200" :target="target"> UP </ix-back-top> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { defineComponent, ref } from 'vue' | ||
export default defineComponent({ | ||
setup() { | ||
const target = ref<HTMLElement | null>(null) | ||
return { target } | ||
}, | ||
}) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
--- | ||
order: 0 | ||
title: | ||
zh: 基本 | ||
--- | ||
|
||
## zh | ||
|
||
基础用法,滑动页面即可看到右下方的按钮。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
--- | ||
order: 1 | ||
title: | ||
zh: 自定义 | ||
--- | ||
|
||
## zh | ||
|
||
自定义显示内容或样式。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
--- | ||
category: components | ||
type: 其他 | ||
title: BackTop | ||
subtitle: 回到顶部 | ||
cover: | ||
--- | ||
|
||
返回页面顶部的操作按钮 | ||
|
||
## 何时使用 | ||
|
||
- 当页面内容区域比较长时。 | ||
- 当用户需要频繁返回顶部查看相关内容时。 | ||
|
||
## API | ||
|
||
### `ix-back-top` | ||
|
||
#### Props | ||
|
||
| 参数 | 说明 | 类型 | 默认值 | 全局配置 | 备注 | | ||
| -- | -- | -- | -- | -- | -- | | ||
| `target` | 需要监听其滚动事件的元素 | `string \| HTMLElement` | `window` | - | 传入 string 类型,会在 mounted 的时候使用 querySelector 来获取元素 | | ||
| `duration` | 回到顶部所需时间(ms) | `number` | `450` | ✅ | - | | ||
| `visibility-height` | 滚动高度达到此参数值才出现 | `number` | `400` | ✅ | - | | ||
|
||
#### Slots | ||
|
||
| 名称 | 说明 | 参数类型 | 备注 | | ||
| -- | -- | -- | -- | | ||
| `defalut` | 自定义显示内容 | - | - | | ||
|
||
#### Emits | ||
|
||
| 名称 | 说明 | 参数类型 | 备注 | | ||
| -- | -- | -- | -- | | ||
| `click` | 点击按钮触发的事件 | `event` | - | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { installComponent } from '@idux/components/core/utils' | ||
import IxBackTop from './src/BackTop.vue' | ||
|
||
IxBackTop.install = installComponent(IxBackTop) | ||
|
||
export { IxBackTop } | ||
export * from './src/types' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
<template> | ||
<transition name="ix-fade"> | ||
<div v-show="visible" class="ix-back-top" @click.stop="handleClick"> | ||
<slot> | ||
<ix-icon name="vertical-align-top" /> | ||
</slot> | ||
</div> | ||
</transition> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import type { SetupContext } from 'vue' | ||
import type { BackTopProps } from './types' | ||
import { defineComponent, ref, onUnmounted, onMounted, nextTick } from 'vue' | ||
import { IxIcon } from '@idux/components/icon' | ||
import { PropTypes, withUndefined, isString, isHTMLElement, on, off, raf } from '@idux/cdk/utils' | ||
import { easeInOutQuad } from '@idux/components/core/utils' | ||
import { useGlobalConfig } from '@idux/components/core/config' | ||
import throttle from 'lodash/throttle' | ||
import { Logger } from '@idux/components/core/logger' | ||
const getTarget = (target: HTMLElement | string | undefined) => { | ||
const defaultTarget = window | ||
if (isString(target)) { | ||
const contaniner = document.querySelector<HTMLElement>(target) | ||
if (contaniner) { | ||
return contaniner | ||
} | ||
Logger.warn(`target does not exist: ${target}, default value are already used: window.`) | ||
return defaultTarget | ||
} | ||
if (isHTMLElement(target)) { | ||
return target as HTMLElement | ||
} | ||
return defaultTarget | ||
} | ||
const getCurrentScrollTop = (currTarget: Window | HTMLElement) => { | ||
if (currTarget === window) { | ||
return window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop | ||
} | ||
return (currTarget as HTMLElement).scrollTop | ||
} | ||
const scrollTo = (y: number, duration: number, container: HTMLElement | Window) => { | ||
const scrollTop = getCurrentScrollTop(container) | ||
const startTime = Date.now() | ||
const change = y - scrollTop | ||
const frameFunc = () => { | ||
const time = Date.now() - startTime | ||
const nextScrollTop = easeInOutQuad(time > duration ? duration : time, scrollTop, change, duration) | ||
if (container === window) { | ||
container.scrollTo(window.pageXOffset, nextScrollTop) | ||
} else { | ||
;(container as HTMLElement).scrollTop = nextScrollTop | ||
} | ||
if (time < duration) { | ||
raf(frameFunc) | ||
} | ||
} | ||
raf(frameFunc) | ||
} | ||
export default defineComponent({ | ||
name: 'IxBackTop', | ||
components: { IxIcon }, | ||
props: { | ||
target: withUndefined(PropTypes.oneOfType([PropTypes.string, HTMLElement])), | ||
duration: PropTypes.number, | ||
visibilityHeight: PropTypes.number, | ||
}, | ||
emits: ['click'], | ||
setup(props: BackTopProps, { emit }: SetupContext) { | ||
const backTopConfig = useGlobalConfig('backTop') | ||
const eventType = 'scroll' | ||
const visible = ref(false) | ||
const container = ref<Window | HTMLElement>((null as unknown) as HTMLElement) | ||
const handleScroll = () => { | ||
visible.value = getCurrentScrollTop(container.value) >= (props.visibilityHeight ?? backTopConfig.visibilityHeight) | ||
} | ||
const handleClick = (event: MouseEvent) => { | ||
scrollTo(0, props.duration ?? backTopConfig.duration, container.value) | ||
emit('click', event) | ||
} | ||
const throttledScrollHandler = throttle(handleScroll, 300) | ||
onMounted(async () => { | ||
await nextTick() | ||
container.value = getTarget(props.target) | ||
on(container.value, eventType, throttledScrollHandler) | ||
handleScroll() | ||
}) | ||
onUnmounted(() => { | ||
off(container.value, eventType, throttledScrollHandler) | ||
}) | ||
return { | ||
visible, | ||
handleClick, | ||
} | ||
}, | ||
}) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import type { DefineComponent } from 'vue' | ||
|
||
interface BackTopOriginalProps { | ||
target?: string | HTMLElement | ||
duration?: number | ||
visibilityHeight?: number | ||
} | ||
|
||
export type BackTopProps = Readonly<BackTopOriginalProps> | ||
|
||
export type BackTopComponent = InstanceType<DefineComponent<BackTopProps>> |
Oops, something went wrong.