add loading footer
This commit is contained in:
parent
0d6cf813a8
commit
924e803d16
|
@ -0,0 +1,26 @@
|
||||||
|
<div class="loading-footer">
|
||||||
|
<LoadingSpinner size={{48}} />
|
||||||
|
<span class="loading-footer-info">
|
||||||
|
Loading more...
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.loading-footer {
|
||||||
|
padding: 20px 0 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.loading-footer-info {
|
||||||
|
margin-left: 20px;
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import LoadingSpinner from './LoadingSpinner.html'
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
LoadingSpinner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,10 +1,10 @@
|
||||||
<svg class="loading-spinner-icon {{maskStyle ? 'mask-style' : ''}}">
|
<svg class="loading-spinner-icon {{maskStyle ? 'mask-style' : ''}}"
|
||||||
|
style="width: {{size || 64}}px; height: {{size || 64}}px;"
|
||||||
|
>
|
||||||
<use xlink:href="#fa-spinner" />
|
<use xlink:href="#fa-spinner" />
|
||||||
</svg>
|
</svg>
|
||||||
<style>
|
<style>
|
||||||
.loading-spinner-icon {
|
.loading-spinner-icon {
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
fill: var(--svg-fill);
|
fill: var(--svg-fill);
|
||||||
animation: spin 2s infinite linear;
|
animation: spin 2s infinite linear;
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 503 B After Width: | Height: | Size: 533 B |
|
@ -4,6 +4,8 @@
|
||||||
items="{{keyedStatuses}}"
|
items="{{keyedStatuses}}"
|
||||||
on:scrollToBottom="onScrollToBottom()"
|
on:scrollToBottom="onScrollToBottom()"
|
||||||
shown="{{initialized}}"
|
shown="{{initialized}}"
|
||||||
|
footerComponent="{{LoadingFooter}}"
|
||||||
|
showFooter="{{initialized && runningUpdate}}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<style>
|
<style>
|
||||||
|
@ -15,6 +17,7 @@
|
||||||
import { store } from '../_utils/store'
|
import { store } from '../_utils/store'
|
||||||
import { getTimeline } from '../_utils/mastodon/timelines'
|
import { getTimeline } from '../_utils/mastodon/timelines'
|
||||||
import StatusListItem from './StatusListItem.html'
|
import StatusListItem from './StatusListItem.html'
|
||||||
|
import LoadingFooter from './LoadingFooter.html'
|
||||||
import VirtualList from './VirtualList.html'
|
import VirtualList from './VirtualList.html'
|
||||||
import { splice, push } from 'svelte-extras'
|
import { splice, push } from 'svelte-extras'
|
||||||
import worker from 'workerize-loader!../_utils/database/database'
|
import worker from 'workerize-loader!../_utils/database/database'
|
||||||
|
@ -40,6 +43,7 @@
|
||||||
},
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
StatusListItem: StatusListItem,
|
StatusListItem: StatusListItem,
|
||||||
|
LoadingFooter: LoadingFooter,
|
||||||
statuses: [],
|
statuses: [],
|
||||||
runningUpdate: false,
|
runningUpdate: false,
|
||||||
initialized: false
|
initialized: false
|
||||||
|
@ -69,8 +73,8 @@
|
||||||
mark('onScrollToBottom')
|
mark('onScrollToBottom')
|
||||||
this.set({ runningUpdate: true })
|
this.set({ runningUpdate: true })
|
||||||
let newStatuses = await this.fetchStatusesAndPossiblyFallBack()
|
let newStatuses = await this.fetchStatusesAndPossiblyFallBack()
|
||||||
this.addStatuses(newStatuses)
|
|
||||||
this.set({ runningUpdate: false })
|
this.set({ runningUpdate: false })
|
||||||
|
this.addStatuses(newStatuses)
|
||||||
stop('onScrollToBottom')
|
stop('onScrollToBottom')
|
||||||
},
|
},
|
||||||
addStatuses(newStatuses) {
|
addStatuses(newStatuses) {
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
index="{{item.index}}"
|
index="{{item.index}}"
|
||||||
/>
|
/>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
{{#if $showFooter}}
|
||||||
|
<VirtualListFooter component="{{footerComponent}}"/>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<style>
|
<style>
|
||||||
.virtual-list {
|
.virtual-list {
|
||||||
|
@ -17,6 +20,7 @@
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
import VirtualListItem from './VirtualListItem'
|
import VirtualListItem from './VirtualListItem'
|
||||||
|
import VirtualListFooter from './VirtualListFooter.html'
|
||||||
import { virtualListStore } from '../_utils/virtualListStore'
|
import { virtualListStore } from '../_utils/virtualListStore'
|
||||||
import throttle from 'lodash/throttle'
|
import throttle from 'lodash/throttle'
|
||||||
import { mark, stop } from '../_utils/marks'
|
import { mark, stop } from '../_utils/marks'
|
||||||
|
@ -26,6 +30,9 @@
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
oncreate () {
|
oncreate () {
|
||||||
|
this.observe('showFooter', showFooter => {
|
||||||
|
this.store.set({showFooter: showFooter})
|
||||||
|
})
|
||||||
this.observe('items', (items) => {
|
this.observe('items', (items) => {
|
||||||
mark('set items')
|
mark('set items')
|
||||||
this.store.set({
|
this.store.set({
|
||||||
|
@ -55,7 +62,8 @@
|
||||||
}),
|
}),
|
||||||
store: () => virtualListStore,
|
store: () => virtualListStore,
|
||||||
components: {
|
components: {
|
||||||
VirtualListItem
|
VirtualListItem,
|
||||||
|
VirtualListFooter
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
distanceFromBottom: ($scrollHeight, $scrollTop, $offsetHeight) => {
|
distanceFromBottom: ($scrollHeight, $scrollTop, $offsetHeight) => {
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
<div class="virtual-list-footer"
|
||||||
|
ref:node
|
||||||
|
style="transform: translateY({{$heightWithoutFooter}}px);" >
|
||||||
|
<:Component {component} />
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.virtual-list-footer {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import { virtualListStore } from '../_utils/virtualListStore'
|
||||||
|
import { AsyncLayout } from '../_utils/AsyncLayout'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
oncreate() {
|
||||||
|
const asyncLayout = new AsyncLayout(() => '__footer__')
|
||||||
|
asyncLayout.observe('__footer__', this.refs.node, (rect) => {
|
||||||
|
asyncLayout.disconnect()
|
||||||
|
this.store.set({footerHeight: rect.height})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
store: () => virtualListStore,
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -42,6 +42,8 @@ class VirtualListStore extends Store {
|
||||||
const virtualListStore = new VirtualListStore({
|
const virtualListStore = new VirtualListStore({
|
||||||
items: [],
|
items: [],
|
||||||
itemHeights: {},
|
itemHeights: {},
|
||||||
|
showFooter: false,
|
||||||
|
footerHeight: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
virtualListStore.compute('visibleItems',
|
virtualListStore.compute('visibleItems',
|
||||||
|
@ -79,7 +81,9 @@ virtualListStore.compute('visibleItems',
|
||||||
return visibleItems
|
return visibleItems
|
||||||
})
|
})
|
||||||
|
|
||||||
virtualListStore.compute('height', ['items', 'itemHeights'], (items, itemHeights) => {
|
virtualListStore.compute('heightWithoutFooter',
|
||||||
|
['items', 'itemHeights'],
|
||||||
|
(items, itemHeights) => {
|
||||||
let sum = 0
|
let sum = 0
|
||||||
let i = -1
|
let i = -1
|
||||||
let len = items.length
|
let len = items.length
|
||||||
|
@ -89,6 +93,13 @@ virtualListStore.compute('height', ['items', 'itemHeights'], (items, itemHeights
|
||||||
return sum
|
return sum
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
virtualListStore.compute('height',
|
||||||
|
['heightWithoutFooter', 'showFooter', 'footerHeight'],
|
||||||
|
(heightWithoutFooter, showFooter, footerHeight) => {
|
||||||
|
return showFooter ? (heightWithoutFooter + footerHeight) : heightWithoutFooter
|
||||||
|
})
|
||||||
|
|
||||||
virtualListStore.compute('numItems', ['items'], (items) => items.length)
|
virtualListStore.compute('numItems', ['items'], (items) => items.length)
|
||||||
|
|
||||||
if (process.browser && process.env.NODE_ENV !== 'production') {
|
if (process.browser && process.env.NODE_ENV !== 'production') {
|
||||||
|
|
Loading…
Reference in New Issue