-
-
Notifications
You must be signed in to change notification settings - Fork 7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(VPullToRefresh): add new component (#19666)
Co-authored-by: John Leider <[email protected]>
- Loading branch information
1 parent
66880ce
commit 1816775
Showing
7 changed files
with
347 additions
and
0 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
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,92 @@ | ||
<template> | ||
<div class="scrollable-container bg-surface-light"> | ||
<v-pull-to-refresh | ||
:pull-down-threshold="pullDownThreshold" | ||
@load="load" | ||
> | ||
<v-list> | ||
<v-list-item | ||
v-for="item in items" | ||
:key="item.value" | ||
:title="item.title" | ||
></v-list-item> | ||
</v-list> | ||
</v-pull-to-refresh> | ||
</div> | ||
</template> | ||
|
||
<script setup> | ||
const pullDownThreshold = ref(64) | ||
let items = [ | ||
{ | ||
title: '1', | ||
value: 1, | ||
}, | ||
{ | ||
title: '2', | ||
value: 2, | ||
}, | ||
{ | ||
title: '3', | ||
value: 3, | ||
}, | ||
] | ||
let count = 2 | ||
async function load ({ done }) { | ||
console.log('loading') | ||
await new Promise(resolve => setTimeout(() => resolve(), 2000)) | ||
items = Array.from({ length: count * 3 }, (k, v) => ({ | ||
title: `${v + 1}`, | ||
value: v + 1, | ||
})) | ||
console.log('load finish') | ||
count++ | ||
done('ok') | ||
} | ||
</script> | ||
|
||
<script> | ||
export default { | ||
data: () => ({ | ||
pullDownThreshold: 64, | ||
items: [ | ||
{ | ||
title: '1', | ||
value: 1, | ||
}, | ||
{ | ||
title: '2', | ||
value: 2, | ||
}, | ||
{ | ||
title: '3', | ||
value: 3, | ||
}, | ||
], | ||
}), | ||
methods: { | ||
async load ({ done }) { | ||
// Perform API call | ||
console.log('loading') | ||
await new Promise(resolve => setTimeout(() => resolve(), 2000)) | ||
this.items = Array.from({ length: 3 }, (k, v) => ({ | ||
title: `${v + 1}`, | ||
value: v + 1, | ||
})) | ||
console.log('load finish') | ||
done('ok') | ||
}, | ||
}, | ||
} | ||
</script> | ||
|
||
<style> | ||
.scrollable-container { | ||
max-height: 300px; | ||
overflow-y: scroll; | ||
} | ||
</style> |
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,59 @@ | ||
--- | ||
emphasized: true | ||
meta: | ||
title: Pull To Refresh | ||
description: The PullToRefresh allows users to update content with a simple downward swipe on their screen. | ||
keywords: Pull to refresh, vuetify Pull to refresh component, vue pull to refresh component | ||
features: | ||
label: 'C: VPullToRefresh' | ||
github: /components/VPullToRefresh/ | ||
report: true | ||
--- | ||
|
||
# Pull To Refresh | ||
|
||
The PullToRefresh allows users to update content with a simple downward swipe on their screen. Works for Mobile and Desktop. | ||
|
||
<PageFeatures /> | ||
|
||
::: warning | ||
|
||
This feature requires [v3.6.0](/getting-started/release-notes/?version=v3.6.0) | ||
|
||
::: | ||
|
||
## Installation | ||
|
||
Labs components require a manual import and installation of the component. | ||
|
||
```js { resource="src/plugins/vuetify.js" } | ||
import { VPullToRefresh } from 'vuetify/labs/VPullToRefresh' | ||
|
||
export default createVuetify({ | ||
components: { | ||
VPullToRefresh, | ||
}, | ||
}) | ||
``` | ||
|
||
## Usage | ||
|
||
Drag the list downward to activate the pull-to-refresh feature. | ||
|
||
<ExamplesExample file="v-pull-to-refresh/usage" /> | ||
|
||
::: tip | ||
|
||
Pull down functionality is available as soon as its immediate scrollable parent has scrolled to the top. | ||
|
||
::: | ||
|
||
<PromotedEntry /> | ||
|
||
## API | ||
|
||
| Component | Description | | ||
| - | - | | ||
| [v-pull-to-refresh](/api/v-pull-to-refresh/) | Primary Component | | ||
|
||
<ApiInline hide-links /> |
23 changes: 23 additions & 0 deletions
23
packages/vuetify/src/labs/VPullToRefresh/VPullToRefresh.sass
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 @@ | ||
.v-pull-to-refresh | ||
overflow: hidden | ||
position: relative | ||
&__pull-down | ||
position: absolute | ||
width: 100% | ||
transition: top .3s ease-out | ||
&--touching | ||
transition: none | ||
|
||
&__pull-down-default | ||
display: flex | ||
width: 100% | ||
height: 100% | ||
justify-content: center | ||
align-items: flex-end | ||
padding-bottom: 10px | ||
|
||
&__scroll-container | ||
position: relative | ||
transition: top .3s ease-out | ||
&--touching | ||
transition: none |
167 changes: 167 additions & 0 deletions
167
packages/vuetify/src/labs/VPullToRefresh/VPullToRefresh.tsx
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,167 @@ | ||
// Styles | ||
import './VPullToRefresh.sass' | ||
|
||
// Components | ||
import { VIcon } from '@/components/VIcon' | ||
import { VProgressCircular } from '@/components/VProgressCircular' | ||
|
||
// Utilities | ||
import { computed, onMounted, ref, shallowRef, watch } from 'vue' | ||
import { clamp, convertToUnit, genericComponent, getScrollParents, useRender } from '@/util' | ||
|
||
export type VPullToRefreshSlots = { | ||
default: never | ||
pullDownPanel: { | ||
canRefresh: boolean | ||
goingUp: boolean | ||
refreshing: boolean | ||
} | ||
} | ||
|
||
export const VPullToRefresh = genericComponent<VPullToRefreshSlots>()({ | ||
name: 'VPullToRefresh', | ||
|
||
props: { | ||
pullDownThreshold: { | ||
type: Number, | ||
default: 64, | ||
}, | ||
}, | ||
|
||
emits: { | ||
load: (options: { done: () => void }) => true, | ||
}, | ||
|
||
setup (props, { slots, emit }) { | ||
let touchstartY = 0 | ||
let scrollParents: HTMLElement[] = [] | ||
|
||
const touchDiff = shallowRef(0) | ||
const containerRef = ref<HTMLElement>() | ||
|
||
const refreshing = shallowRef(false) | ||
const goingUp = shallowRef(false) | ||
const touching = shallowRef(false) | ||
|
||
const canRefresh = computed(() => touchDiff.value >= props.pullDownThreshold && !refreshing.value) | ||
const topOffset = computed(() => clamp(touchDiff.value, 0, props.pullDownThreshold)) | ||
|
||
function onTouchstart (e: TouchEvent | MouseEvent) { | ||
if (refreshing.value) return | ||
touching.value = true | ||
touchstartY = 'clientY' in e ? e.clientY : e.touches[0].clientY | ||
} | ||
|
||
function onTouchmove (e: TouchEvent | MouseEvent) { | ||
if (refreshing.value || !touching.value) return | ||
|
||
const touchY = 'clientY' in e ? e.clientY : e.touches[0].clientY | ||
|
||
if (scrollParents.length && !scrollParents[0].scrollTop) { | ||
touchDiff.value = touchY - touchstartY | ||
} | ||
} | ||
|
||
function onTouchend (e: TouchEvent | MouseEvent) { | ||
if (refreshing.value) return | ||
touching.value = false | ||
if (canRefresh.value) { | ||
function done () { | ||
if (!refreshing.value) return | ||
touchDiff.value = 0 | ||
refreshing.value = false | ||
} | ||
emit('load', { done }) | ||
refreshing.value = true | ||
} else { | ||
touchDiff.value = 0 | ||
} | ||
} | ||
|
||
onMounted(() => { | ||
scrollParents = getScrollParents(containerRef.value) | ||
}) | ||
|
||
watch([topOffset, refreshing], () => { | ||
if (scrollParents.length) { | ||
const stopScrolling = topOffset.value && !refreshing.value | ||
scrollParents.forEach(p => p.style.overflow = stopScrolling ? 'hidden' : 'auto') | ||
} | ||
}) | ||
|
||
watch(topOffset, (newVal, oldVal) => { | ||
goingUp.value = newVal < oldVal | ||
}) | ||
|
||
useRender(() => { | ||
return ( | ||
<div | ||
class={[ | ||
'v-pull-to-refresh', | ||
]} | ||
onTouchstart={ onTouchstart } | ||
onTouchmove={ onTouchmove } | ||
onTouchend={ onTouchend } | ||
onMousedown={ onTouchstart } | ||
onMouseup={ onTouchend } | ||
onMouseleave={ onTouchend } | ||
onMousemove={ onTouchmove } | ||
ref={ containerRef } | ||
> | ||
<div | ||
class={[ | ||
'v-pull-to-refresh__pull-down', | ||
{ | ||
'v-pull-to-refresh__pull-down--touching': touching.value, | ||
}, | ||
]} | ||
style={{ | ||
top: convertToUnit(-1 * props.pullDownThreshold + topOffset.value), | ||
height: convertToUnit(props.pullDownThreshold), | ||
}} | ||
> | ||
{ slots.pullDownPanel | ||
? slots.pullDownPanel({ | ||
canRefresh: canRefresh.value, | ||
goingUp: goingUp.value, | ||
refreshing: refreshing.value, | ||
}) : ( | ||
<div | ||
class={[ | ||
'v-pull-to-refresh__pull-down-default', | ||
]} | ||
> | ||
{ | ||
refreshing.value ? ( | ||
<VProgressCircular | ||
indeterminate | ||
active={ false } | ||
/> | ||
) : ( | ||
<VIcon | ||
icon={ canRefresh.value || goingUp.value ? '$sortAsc' : '$sortDesc' } | ||
/> | ||
) | ||
} | ||
</div> | ||
) | ||
} | ||
</div> | ||
<div | ||
class={[ | ||
'v-pull-to-refresh__scroll-container', | ||
{ | ||
'v-pull-to-refresh__scroll-container--touching': touching.value, | ||
}, | ||
]} | ||
style={{ top: convertToUnit(topOffset.value) }} | ||
> | ||
{ slots.default?.() } | ||
</div> | ||
</div> | ||
) | ||
}) | ||
}, | ||
}) | ||
|
||
export type VPullToRefresh = InstanceType<typeof VPullToRefresh> |
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 @@ | ||
export { VPullToRefresh } from './VPullToRefresh' |
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