diff --git a/changelog.d/better-scroll-button.add b/changelog.d/better-scroll-button.add new file mode 100644 index 000000000..b206869d1 --- /dev/null +++ b/changelog.d/better-scroll-button.add @@ -0,0 +1 @@ +Add support for detachable scrollTop button diff --git a/src/App.js b/src/App.js index 1c1b3a8c2..6bb9ba04b 100644 --- a/src/App.js +++ b/src/App.js @@ -20,6 +20,8 @@ import { defineAsyncComponent } from 'vue' import { useShoutStore } from './stores/shout' import { useInterfaceStore } from './stores/interface' +import { throttle } from 'lodash' + export default { name: 'app', components: { @@ -58,16 +60,23 @@ export default { // Load the locale from the storage const val = this.$store.getters.mergedConfig.interfaceLanguage this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) - window.addEventListener('resize', this.updateMobileState) document.getElementById('modal').classList = ['-' + this.layoutType] + + // Create bound handlers + this.updateScrollState = throttle(this.scrollHandler, 200) + this.updateMobileState = throttle(this.resizeHandler, 200) }, mounted () { + window.addEventListener('resize', this.updateMobileState) + this.scrollParent.addEventListener('scroll', this.updateScrollState) + if (useInterfaceStore().themeApplied) { this.removeSplash() } }, unmounted () { window.removeEventListener('resize', this.updateMobileState) + this.scrollParent.removeEventListener('scroll', this.updateScrollState) }, computed: { themeApplied () { @@ -146,13 +155,23 @@ export default { }, noSticky () { return this.$store.getters.mergedConfig.disableStickyHeaders }, showScrollbars () { return this.$store.getters.mergedConfig.showScrollbars }, + scrollParent () { return window; /* this.$refs.appContentRef */ }, ...mapGetters(['mergedConfig']) }, methods: { - updateMobileState () { + resizeHandler () { useInterfaceStore().setLayoutWidth(windowWidth()) useInterfaceStore().setLayoutHeight(windowHeight()) }, + scrollHandler () { + const scrollPosition = this.scrollParent === window ? window.scrollY : this.scrollParent.scrollTop + + if (scrollPosition != 0) { + this.$refs.appContentRef.classList.add(['-scrolled']) + } else { + this.$refs.appContentRef.classList.remove(['-scrolled']) + } + }, removeSplash () { document.querySelector('#status').textContent = this.$t('splash.fun_' + Math.ceil(Math.random() * 4)) const splashscreenRoot = document.querySelector('#splash') diff --git a/src/App.vue b/src/App.vue index 34cc6b804..e5e088bc3 100644 --- a/src/App.vue +++ b/src/App.vue @@ -16,6 +16,7 @@
diff --git a/src/components/scroll_top_button/scroll_top_button.js b/src/components/scroll_top_button/scroll_top_button.js new file mode 100644 index 000000000..bdc45b9b4 --- /dev/null +++ b/src/components/scroll_top_button/scroll_top_button.js @@ -0,0 +1,18 @@ +const ScrollTopButton = { + props: { + fast: { + type: Boolean, + required: false, + default: false + } + }, + methods: { + scrollToTop() { + const speed = this.fast ? 'instant' : 'smooth'; + + window.scrollTo({ top: 0, behavior: speed }) + } + } +} + +export default ScrollTopButton diff --git a/src/components/scroll_top_button/scroll_top_button.vue b/src/components/scroll_top_button/scroll_top_button.vue new file mode 100644 index 000000000..ed73059c4 --- /dev/null +++ b/src/components/scroll_top_button/scroll_top_button.vue @@ -0,0 +1,29 @@ + + + + diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index 2bc811b58..61cc8affd 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -5,6 +5,7 @@ import Conversation from '../conversation/conversation.vue' import TimelineMenu from '../timeline_menu/timeline_menu.vue' import QuickFilterSettings from '../quick_filter_settings/quick_filter_settings.vue' import QuickViewSettings from '../quick_view_settings/quick_view_settings.vue' +import ScrollTopButton from '../scroll_top_button/scroll_top_button.vue' import { debounce, throttle, keyBy } from 'lodash' import { library } from '@fortawesome/fontawesome-svg-core' import { faCircleNotch, faCirclePlus, faCog, faMinus, faArrowUp, faCheck } from '@fortawesome/free-solid-svg-icons' @@ -47,6 +48,7 @@ const Timeline = { }, components: { Status, + ScrollTopButton, Conversation, TimelineMenu, QuickFilterSettings, @@ -144,9 +146,6 @@ const Timeline = { this.$store.commit('setLoading', { timeline: this.timelineName, value: false }) }, methods: { - scrollToTop () { - window.scrollTo({ top: this.$el.offsetTop }) - }, stopBlockingClicks: debounce(function () { this.blockingClicks = false }, 1000), @@ -251,7 +250,6 @@ const Timeline = { } }, handleScroll: throttle(function (e) { - this.showScrollTop = this.$el.offsetTop < window.scrollY this.determineVisibleStatuses() this.scrollLoad(e) }, 200), diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index e34c42d9f..81dad3bd3 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -8,25 +8,7 @@ v-if="!embedded" :timeline-name="timelineName" /> -
- -
+