<template>
  <div
    v-show="maskLayerShow"
    class="mask-layer"
    @mousedown.stop
    @mouseup.stop
    @pointerdown.stop
    @pointerup.stop
    @touchstart.stop
    @touchend.stop
    @touchmove.prevent
    @wheel.prevent
  >
    <div class="mask-layer__content" @touchmove.stop v-touch-tap.self.stop="emitClose">
      <slot></slot>
    </div>
  </div>
</template>

<script setup>
import { onUnmounted, watch } from 'vue'

const props = defineProps({
  maskLayerShow: {
    type: Boolean,
    required: true,
  },
  lockScroll: {
    type: Boolean,
    default: true,
  },
})
const emit = defineEmits(['close'])

const emitClose = () => {
  emit('close')
}

let lockCount = 0
let lockedScrollY = 0
let previousBodyStyle = {}
let previousHtmlStyle = {}

const getStyleSnapshot = (el) => ({
  overflow: el.style.overflow,
  position: el.style.position,
  top: el.style.top,
  left: el.style.left,
  right: el.style.right,
  width: el.style.width,
  touchAction: el.style.touchAction,
  overscrollBehavior: el.style.overscrollBehavior,
})

const restoreStyleSnapshot = (el, style) => {
  Object.keys(style).forEach((key) => {
    el.style[key] = style[key]
  })
}

// 弹窗展示时锁住背景页滚动，iOS 需要固定 body 并在关闭后恢复滚动位置。
const lockPageScroll = () => {
  if (typeof window === 'undefined' || typeof document === 'undefined') return

  if (lockCount === 0) {
    const body = document.body
    const html = document.documentElement

    lockedScrollY = window.scrollY || html.scrollTop || body.scrollTop || 0
    previousBodyStyle = getStyleSnapshot(body)
    previousHtmlStyle = getStyleSnapshot(html)

    html.style.overflow = 'hidden'
    html.style.touchAction = 'none'
    html.style.overscrollBehavior = 'none'

    body.style.overflow = 'hidden'
    body.style.position = 'fixed'
    body.style.top = `-${lockedScrollY}px`
    body.style.left = '0'
    body.style.right = '0'
    body.style.width = '100%'
    body.style.touchAction = 'none'
    body.style.overscrollBehavior = 'none'
  }

  lockCount += 1
}

const unlockPageScroll = () => {
  if (typeof window === 'undefined' || typeof document === 'undefined') return
  if (lockCount <= 0) return

  lockCount -= 1

  if (lockCount === 0) {
    restoreStyleSnapshot(document.body, previousBodyStyle)
    restoreStyleSnapshot(document.documentElement, previousHtmlStyle)
    window.scrollTo(0, lockedScrollY)
  }
}

watch(
  () => props.maskLayerShow,
  (newVal) => {
    if (!props.lockScroll) return

    if (newVal) {
      lockPageScroll()
    } else {
      unlockPageScroll()
    }
  },
  { immediate: true }
)

onUnmounted(() => {
  if (props.lockScroll && props.maskLayerShow) {
    unlockPageScroll()
  }
})
</script>

<style lang="scss" scoped>
* {
  color: black;
}

.mask-layer {
  width: 100vw;
  height: 100vh;
  background-color: #00000080;
  position: fixed;
  inset: 0;
  z-index: 10000 !important;
  overflow: hidden;
  touch-action: none;
  overscroll-behavior: none;
  -webkit-tap-highlight-color: transparent;
}

.mask-layer__content {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
</style>
