All files / features/centers-dropdown-to-screen-on-mobile dropdown-mobile-centering.js

88.29% Statements 83/94
70% Branches 7/10
75% Functions 6/8
88.29% Lines 83/94

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 951x 1x 1x 1x 1x 1x 1x 1x 1x 1x 4x 4x 1x 1x 5x 5x 5x 5x 5x 5x     5x 5x 5x 4x 4x 4x 5x 5x       5x 5x 5x 5x 5x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 5x 5x 4x       4x 4x       4x 4x 4x 5x 5x 5x 5x 5x 4x 4x 4x 4x 4x 4x 4x 4x 4x 5x 5x 5x 5x 1x 1x 1x 1x 1x 1x 1x 4x 4x 4x 4x 4x 4x  
import { dropdownEl } from '../../utils/dynamic-select-dom.js'
import { isMobile } from '../../utils/mobile-detection.js'
import { IterableWeakSet } from '../../utils/iterable-weak-struct.js'
/** @import {DynamicSelect} from '../../utils/dynamic-select-dom.js' */
 
/**
 * @returns {boolean} true if dropdown should be centered on screen instead
 * of being anchored to the select box
 */
export function shouldCenterDropdown () {
  return isMobile()
}
 
const positionFixer = (() => {
  /** @type {IterableWeakSet<DynamicSelect>} */
  const selectBoxes = new IterableWeakSet()
 
  const viewport = window.visualViewport
  if (!viewport) {
    return {
      applyOnSelect () {},
    }
  }
 
  const startListening = () => {
    viewport.addEventListener('scroll', handler)
    viewport.addEventListener('resize', handler)
  }
 
  const stopListening = () => {
    viewport.removeEventListener('scroll', handler)
    viewport.removeEventListener('resize', handler)
  }
 
  /**
   * @param {HTMLDialogElement} element - dialog element
   */
  const updateDropdownPosition = (element) => {
    const offsetLeft = viewport.offsetLeft
    const offsetTop = viewport.offsetTop
 
    const left = offsetLeft + viewport.width / 2
    const top = offsetTop + viewport.height / 2
 
    // You could also do this by setting style.left and style.top if you
    // use width: 100% instead.
    element.style.transform = `translate(calc(${left}px - 50%), calc(${top}px - 50%)) scale(${1 / viewport.scale})`
    element.style.transformOrigin = 'center'
  }
 
  const handler = () => {
    if (selectBoxes.size <= 0) {
      stopListening()
      return
    }
    for (const select of selectBoxes) {
      if (!select.open) {
        selectBoxes.delete(select)
        continue
      }
      updateDropdownPosition(dropdownEl(select))
    }
  }
 
  /**
   * @param {DynamicSelect} dynamicSelect - target dynamic select
   */
  const applyOnSelect = (dynamicSelect) => {
    selectBoxes.add(dynamicSelect)
    if (dynamicSelect.open) {
      const dropdown = dropdownEl(dynamicSelect)
      updateDropdownPosition(dropdown)
      // apply twice because first `getBoundingClientRect` is not applying in updated transformation
      updateDropdownPosition(dropdown)
    }
    startListening()
  }
 
  return {
    applyOnSelect,
  }
})()
 
/**
 * Updated dropdown content based on the content in dynamic select in light DOM
 * @param {DynamicSelect} dynamicSelect - web component element reference
 */
export function centerDropdownPosition (dynamicSelect) {
  const dropdown = dropdownEl(dynamicSelect)
  positionFixer.applyOnSelect(dynamicSelect)
  dropdown.classList.remove('top-direction', 'left-direction')
  dropdown.style.marginTop = ''
  dropdown.style.marginLeft = ''
}