<template>
  <view :class="`wd-resize ${customClass}`" :style="rootStyle">
    <!--插槽需要脱离父容器文档流，防止父容器固宽固高，进而导致插槽大小被被父容器限制-->
    <view :class="`wd-resize__container ${customContainerClass}`">
      <!--被监听的插槽-->
      <slot />
      <!--监听插槽变大-->
      <scroll-view
        class="wd-resize__wrapper"
        :scroll-y="true"
        :scroll-top="expandScrollTop"
        :scroll-x="true"
        :scroll-left="expandScrollLeft"
        @scroll="onScrollHandler"
      >
        <view class="wd-resize__wrapper--placeholder" style="height: 100000px; width: 100000px"></view>
      </scroll-view>
      <!--监听插槽变小-->
      <scroll-view
        class="wd-resize__wrapper"
        :scroll-y="true"
        :scroll-top="shrinkScrollTop"
        :scroll-x="true"
        :scroll-left="shrinkScrollLeft"
        @scroll="onScrollHandler"
      >
        <view class="wd-resize__wrapper--placeholder" style="height: 250%; width: 250%"></view>
      </scroll-view>
    </view>
  </view>
</template>

<script lang="ts">
export default {
  name: 'wd-resize',
  options: {
    virtualHost: true,
    addGlobalClass: true,
    styleIsolation: 'shared'
  }
}
</script>

<script lang="ts" setup>
import { computed, getCurrentInstance, onMounted, ref } from 'vue'
import { addUnit, objToStyle } from '../common/util'

interface Props {
  customClass?: string
  customContainerClass?: string
  customStyle?: string
}

const props = withDefaults(defineProps<Props>(), {
  customStyle: '',
  customClass: '',
  customContainerClass: ''
})

const expandScrollTop = ref<number>(0)
const shrinkScrollTop = ref<number>(0)
const expandScrollLeft = ref<number>(0)
const shrinkScrollLeft = ref<number>(0)
const height = ref<number>(0)
const width = ref<number>(0)
const scrollEventCount = ref<number>(0)

const rootStyle = computed(() => {
  const style: Record<string, string | number> = {
    width: addUnit(width.value),
    height: addUnit(height.value)
  }
  return `${objToStyle(style)};${props.customStyle}`
})
let onScrollHandler = () => {}
const { proxy } = getCurrentInstance() as any

const emit = defineEmits(['resize'])

onMounted(() => {
  const query = uni
    .createSelectorQuery()
    // #ifndef MP-ALIPAY
    .in(proxy)
    // #endif
    .select('.wd-resize__container')
    .boundingClientRect()
  query.exec(([res]) => {
    // 闭包记录容器高度
    let lastHeight = res.height
    let lastWidth = res.width
    // 立即填充父容器高宽
    height.value = lastHeight
    width.value = lastWidth
    // 监听滚动事件
    onScrollHandler = () => {
      const query = uni
        .createSelectorQuery()
        // #ifndef MP-ALIPAY
        .in(proxy)
        // #endif
        .select('.wd-resize__container')
        .boundingClientRect()
      query.exec(([res]) => {
        // 前两次滚动事件被触发，说明 created 的修改已渲染，通知用户代码当前容器大小
        if (scrollEventCount.value++ === 0) {
          const result: Record<string, string | number> = {}
          ;['bottom', 'top', 'left', 'right', 'height', 'width'].forEach((propName) => {
            result[propName] = res[propName]
          })
          emit('resize', result)
        }
        // 滚动条拉到底部会触发两次多余的事件，屏蔽掉。
        if (scrollEventCount.value < 3) return
        // 手动设置父容器高宽，防止父容器坍塌
        // 滚动完，重新获取容器新的高度
        const newHeight = res.height
        const newWidth = res.width
        // 立即填充父容器高宽
        height.value = newHeight
        width.value = newWidth
        // 宽高都改变时，只需要触发一次 size 事件
        const emitStack: number[] = []
        if (newHeight !== lastHeight) {
          lastHeight = newHeight
          emitStack.push(1)
        }
        if (newWidth !== lastWidth) {
          lastWidth = newWidth
          emitStack.push(1)
        }
        if (emitStack.length !== 0) {
          const result = {}
          ;['bottom', 'top', 'left', 'right', 'height', 'width'].forEach((propName) => {
            result[propName] = res[propName]
          })
          emit('resize', result)
        }
        // 滚动条拉到底部（如果使用 nextTick 效果更佳）
        scrollToBottom({
          lastWidth: lastWidth,
          lastHeight: lastHeight
        })
      })
    }
    // 滚动条拉到底部（如果使用 nextTick 效果更佳）
    scrollToBottom({
      lastWidth: lastWidth,
      lastHeight: lastHeight
    })
  })
})

function scrollToBottom({ lastWidth, lastHeight }) {
  expandScrollTop.value = 100000 + lastHeight
  shrinkScrollTop.value = 3 * height.value + lastHeight
  expandScrollLeft.value = 100000 + lastWidth
  shrinkScrollLeft.value = 3 * width.value + lastWidth
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>
