175 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<div class="compose-box-button-sentinel {hideAndFadeIn}" ref:sentinel></div>
 | 
						|
<div class="compose-box-button-wrapper {showSticky ? 'compose-box-button-sticky' : ''} {hideAndFadeIn}"
 | 
						|
     ref:wrapper >
 | 
						|
  <ComposeButton {overLimit} {sticky} on:click="onClickButton()" />
 | 
						|
</div>
 | 
						|
<style>
 | 
						|
  .compose-box-button-wrapper {
 | 
						|
    /*
 | 
						|
     * We want pointer-events only for the sticky button, so use fit-content so that
 | 
						|
     * the element doesn't take up the full width, and then set its left margin to
 | 
						|
     * auto so that it sticks to the right. fit-content doesn't work in Edge, but
 | 
						|
     * that just means that content that is level with the button is not clickable.
 | 
						|
     */
 | 
						|
    width: -moz-fit-content;
 | 
						|
    width: fit-content;
 | 
						|
    margin-left: auto;
 | 
						|
    display: flex;
 | 
						|
    justify-content: flex-end;
 | 
						|
  }
 | 
						|
 | 
						|
  .compose-box-button-sticky {
 | 
						|
    position: -webkit-sticky;
 | 
						|
    position: sticky;
 | 
						|
  }
 | 
						|
 | 
						|
  :global(.compose-box-button-sticky, .compose-box-button-fixed) {
 | 
						|
    z-index: 5000;
 | 
						|
    top: 52px; /* padding-top for .main-content plus 10px */
 | 
						|
  }
 | 
						|
 | 
						|
  @media (max-width: 767px) {
 | 
						|
    :global(.compose-box-button-sticky, .compose-box-button-fixed) {
 | 
						|
      top: 57px; /* padding-top for .main-content plus 5px */
 | 
						|
    }
 | 
						|
  }
 | 
						|
  @media (max-width: 991px) {
 | 
						|
    :global(.compose-box-button-sticky, .compose-box-button-fixed) {
 | 
						|
      top: 62px; /* padding-top for .main-content plus 10px */
 | 
						|
    }
 | 
						|
  }
 | 
						|
  @supports (-webkit-overflow-scrolling: touch) {
 | 
						|
    .compose-box-button-sticky {
 | 
						|
      /* disable sticky positioning on iOS due to
 | 
						|
         https://github.com/nolanlawson/pinafore/issues/667 */
 | 
						|
      position: relative;
 | 
						|
      z-index: 0;
 | 
						|
      top: 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
</style>
 | 
						|
<script>
 | 
						|
  import ComposeButton from './ComposeButton.html'
 | 
						|
  import { store } from '../../_store/store'
 | 
						|
  import { importShowComposeDialog } from '../dialog/asyncDialogs'
 | 
						|
  import { observe } from 'svelte-extras'
 | 
						|
 | 
						|
  const USE_IOS_WORKAROUND = process.browser && CSS.supports('-webkit-overflow-scrolling', 'touch')
 | 
						|
 | 
						|
  export default {
 | 
						|
    oncreate () {
 | 
						|
      this.setupStickyObserver()
 | 
						|
      this.setupIOSWorkaround()
 | 
						|
    },
 | 
						|
    ondestroy () {
 | 
						|
      this.teardownStickyObserver()
 | 
						|
    },
 | 
						|
    store: () => store,
 | 
						|
    data: () => ({
 | 
						|
      sticky: false
 | 
						|
    }),
 | 
						|
    computed: {
 | 
						|
      timelineInitialized: ({ $timelineInitialized }) => $timelineInitialized
 | 
						|
    },
 | 
						|
    methods: {
 | 
						|
      observe,
 | 
						|
      onClickButton () {
 | 
						|
        let { sticky } = this.get()
 | 
						|
        if (sticky) {
 | 
						|
          // when the button is sticky, we're scrolled down the home timeline,
 | 
						|
          // so we should launch a new compose dialog
 | 
						|
          this.showDialog()
 | 
						|
        } else {
 | 
						|
          // else we're actually posting a new toot, let our parent know
 | 
						|
          this.fire('postAction')
 | 
						|
        }
 | 
						|
      },
 | 
						|
      async showDialog () {
 | 
						|
        (await importShowComposeDialog())()
 | 
						|
      },
 | 
						|
      setupIOSWorkaround () {
 | 
						|
        // This is an elaborate fix for https://github.com/nolanlawson/pinafore/issues/667
 | 
						|
        // We detect iOS using support for -webkit-overflow-scrolling: touch
 | 
						|
        // (both here and in global.scss). Then, we set the main content element
 | 
						|
        // to be overflow-x: hidden, which normally would break the sticky button
 | 
						|
        // because its parent is now the scrolling context. So for iOS only, we
 | 
						|
        // create a fake sticky button by listening to intersecting events
 | 
						|
        // and inserting a permanently fixed-position element into the DOM.
 | 
						|
        let { showSticky } = this.get()
 | 
						|
        if (!USE_IOS_WORKAROUND || !showSticky) {
 | 
						|
          return
 | 
						|
        }
 | 
						|
 | 
						|
        let cleanup = () => {
 | 
						|
          let existingElement = document.getElementById('the-sticky-button')
 | 
						|
          if (existingElement) {
 | 
						|
            document.body.removeChild(existingElement)
 | 
						|
          }
 | 
						|
          if (this.__fixedStickyButton) {
 | 
						|
            this.__fixedStickyButton.destroy()
 | 
						|
            this.__fixedStickyButton = null
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        let createFixedStickyButton = () => {
 | 
						|
          let element = document.createElement('div')
 | 
						|
          element.setAttribute('id', 'the-sticky-button')
 | 
						|
          element.classList.add('compose-box-button-wrapper')
 | 
						|
          element.classList.add('compose-box-button-fixed')
 | 
						|
          document.body.appendChild(element)
 | 
						|
          let rect = this.refs.wrapper.getBoundingClientRect()
 | 
						|
          Object.assign(element.style, {
 | 
						|
            left: `${rect.left}px`,
 | 
						|
            position: 'fixed'
 | 
						|
          })
 | 
						|
          this.__fixedStickyButton = new ComposeButton({
 | 
						|
            target: element,
 | 
						|
            data: {
 | 
						|
              sticky: true,
 | 
						|
              overLimit: false
 | 
						|
            }
 | 
						|
          })
 | 
						|
          this.__fixedStickyButton.on('click', () => this.showDialog())
 | 
						|
        }
 | 
						|
 | 
						|
        this.observe('sticky', sticky => {
 | 
						|
          cleanup()
 | 
						|
          if (sticky) {
 | 
						|
            createFixedStickyButton()
 | 
						|
          }
 | 
						|
        })
 | 
						|
        this.on('destroy', () => cleanup())
 | 
						|
      },
 | 
						|
      setupStickyObserver () {
 | 
						|
        let sentinel = this.refs.sentinel
 | 
						|
 | 
						|
        this.__stickyObserver = new IntersectionObserver(entries => this.onObserve(entries))
 | 
						|
        this.__stickyObserver.observe(sentinel)
 | 
						|
 | 
						|
        // also create a one-shot observer for the $timelineInitialized event,
 | 
						|
        // due to a bug in Firefox where when the scrollTop is set
 | 
						|
        // manually, the other observer doesn't necessarily fire
 | 
						|
        this.observe('timelineInitialized', timelineInitialized => {
 | 
						|
          if (timelineInitialized) {
 | 
						|
            let observer = new IntersectionObserver(entries => {
 | 
						|
              this.onObserve(entries)
 | 
						|
              observer.disconnect()
 | 
						|
            })
 | 
						|
            observer.observe(sentinel)
 | 
						|
          }
 | 
						|
        }, { init: false })
 | 
						|
      },
 | 
						|
      onObserve (entries) {
 | 
						|
        this.set({ sticky: !entries[0].isIntersecting })
 | 
						|
      },
 | 
						|
      teardownStickyObserver () {
 | 
						|
        if (this.__stickyObserver) {
 | 
						|
          this.__stickyObserver.disconnect()
 | 
						|
        }
 | 
						|
      }
 | 
						|
    },
 | 
						|
    components: {
 | 
						|
      ComposeButton
 | 
						|
    }
 | 
						|
  }
 | 
						|
</script>
 |