Skip to content

Commit

Permalink
feat(VPullToRefresh): add new component
Browse files Browse the repository at this point in the history
closes #4099
  • Loading branch information
yuwu9145 committed Apr 22, 2024
1 parent 245f6d3 commit 46b0a7c
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 0 deletions.
18 changes: 18 additions & 0 deletions packages/vuetify/src/labs/VPullToRefresh/VPullToRefresh.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.v-pull-to-refresh
overflow: hidden
&__pull-down
position: relative
display: flex
justify-content: center
align-items: flex-end
height: 64px
transition: top .3s ease-out
&--touching
transition: none


&__scroll-container
position: relative
transition: top .3s ease-out
&--touching
transition: none
143 changes: 143 additions & 0 deletions packages/vuetify/src/labs/VPullToRefresh/VPullToRefresh.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Styles
import './VPullToRefresh.sass'

// Components
import { VIcon } from '@/components/VIcon'
import { VProgressCircular } from '@/components/VProgressCircular'

// Composables
import { LoaderSlot } from '@/composables/loader'

// Utilities
import { computed, onMounted, ref, shallowRef } from 'vue'
import { convertToUnit, genericComponent, useRender } from '@/util'

const PULL_DOWN_HEIGHT_PX = 64

export type PullToRefreshStatus = 'ok' | 'error'

export const VPullToRefresh = genericComponent()({
name: 'VPullToRefresh',

props: {
},

emits: {
load: (options: { done: (status: PullToRefreshStatus) => void }) => true,
},

setup (props, { slots, emit }) {
let touchstartY = 0

const touchDiff = shallowRef(0)
const scrollContainerRef = ref<HTMLElement>()

const height = shallowRef(0)

const canRefresh = computed(() => touchDiff.value > PULL_DOWN_HEIGHT_PX * 2 / 3)
const refreshing = shallowRef(false)
const touching = shallowRef(false)

function onTouchstart (e: TouchEvent) {
if (refreshing.value) {
e.preventDefault()
return
}
touching.value = true
touchstartY = e.touches[0].clientY
}

function onTouchmove (e: TouchEvent) {
if (refreshing.value) {
e.preventDefault()
return
}
const touchY = e.touches[0].clientY
if (touchDiff.value < PULL_DOWN_HEIGHT_PX && window.scrollY === 0) {
touchDiff.value = touchY - touchstartY
}
}

function onTouchend (e: TouchEvent) {
if (refreshing.value) {
e.preventDefault()
return
}
touching.value = false
if (canRefresh.value) {
function done (status: PullToRefreshStatus) {
if (status === 'ok') {
touchDiff.value = 0
refreshing.value = false
}
}
emit('load', { done })
refreshing.value = true
}
}

onMounted(() => {
height.value = scrollContainerRef.value!.offsetHeight
})

useRender(() => {
return (
<div
class={[
'v-pull-to-refresh',
]}
onTouchstart={ onTouchstart }
onTouchmove={ onTouchmove }
onTouchend={ onTouchend }
style={{
height: convertToUnit(height.value),
}}
>
<div
class={[
'v-pull-to-refresh__pull-down',
{
'v-pull-to-refresh__pull-down--touching': touching.value,
},
]}
style={{
top: convertToUnit(-1 * PULL_DOWN_HEIGHT_PX + touchDiff.value),
}}
>
{
canRefresh.value || refreshing.value ? (
<LoaderSlot
name="v-pull-to-refresh"
active={ false }
>
<VProgressCircular
indeterminate
active={ false }
/>
</LoaderSlot>
) : (
<VIcon
icon="$sortDesc"
/>
)
}
</div>
<div
class={[
'v-pull-to-refresh__scroll-container',
{
'v-pull-to-refresh__scroll-container--touching': touching.value,
},
]}
ref={ scrollContainerRef }
style={{ top: convertToUnit(-1 * PULL_DOWN_HEIGHT_PX + touchDiff.value) }}
>
{ slots.default?.() }
</div>
</div>
)
})
},
})

export type VPullToRefresh = InstanceType<typeof VPullToRefresh>
1 change: 1 addition & 0 deletions packages/vuetify/src/labs/VPullToRefresh/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { VPullToRefresh } from './VPullToRefresh'
1 change: 1 addition & 0 deletions packages/vuetify/src/labs/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './VEmptyState'
export * from './VFab'
export * from './VNumberInput'
export * from './VPicker'
export * from './VPullToRefresh'
export * from './VSparkline'
export * from './VSpeedDial'
export * from './VTimePicker'
Expand Down

0 comments on commit 46b0a7c

Please sign in to comment.