<!--
* @FileDescription: 文本自动省略号 (未系统测试，稳定性待验证)
* @Author: 黄琪勇
* @Date:2023-08-04 14:35:58
-->
<template>
   <el-tooltip :placement="props.placement" :disabled="!ellipsisData.oversize || !props.tooltip">
      <template #content>
         <div :style="{ maxWidth: `${props.maxWidth}px` }">{{ props.text }}</div>
      </template>
      <div ref="ellipsisDom" class="ellipsis" :key="ellipsisData.key" :class="{ vis: !ellipsisData.computedReady }">
         <span ref="textDom">{{ props.text }}</span>
         <span v-show="ellipsisData.oversize">...</span>
      </div>
   </el-tooltip>
</template>

<script setup lang="ts">
import { shallowReactive, watch, onMounted, nextTick, ref } from "vue"
import { getStyle } from "@/libs/tools"

watch([() => props.disabled, () => props.text, () => props.height, () => props.length, () => props.lines], () => {
   updateText()
})
onMounted(() => {
   init()
})
const props = withDefaults(
   defineProps<{
      text: string
      height?: number
      lines?: number
      length?: number
      disabled?: boolean
      tooltip?: boolean
      placement?:
         | "top"
         | "top-start"
         | "top-end"
         | "bottom"
         | "bottom-start"
         | "bottom-end"
         | "left"
         | "left-start"
         | "left-end"
         | "right"
         | "right-start"
         | "right-end"
      maxWidth?: number
   }>(),
   {
      disabled: false,
      tooltip: true,
      placement: "top",
      maxWidth: 250
   }
)
const textDom = ref<HTMLElement>()
const ellipsisDom = ref<HTMLDivElement>()
const ellipsisData = shallowReactive({
   oversize: false,
   computedReady: false, // 先隐形计算，计算好后，再根据配置显示
   key: 0
})

function init() {
   if (!props.disabled) {
      computeText()
   }
}
function updateText() {
   // 强制刷新组件
   ellipsisData.key++
   init()
}
function computeText() {
   ellipsisData.oversize = false
   ellipsisData.computedReady = false
   nextTick(() => {
      let text = props.text
      const length = props.length
      if (length) {
         const textLength = text.length
         if (textLength > length) {
            ellipsisData.oversize = true
            text = text.slice(0, length)
            textDom.value!.innerText = text
         }
      } else if (props.lines || props.height) {
         let height = props.height || 0
         const lines = props.lines
         if (!height && lines) {
            const lineHeight = parseInt(getStyle(ellipsisDom.value!, "lineHeight"))
            height = lineHeight * lines
         }
         if (ellipsisDom.value!.offsetHeight > height) {
            ellipsisData.oversize = true
            nextTick(() => {
               while (ellipsisDom.value!.offsetHeight > height) {
                  if (ellipsisDom.value!.offsetHeight > height * 3) {
                     textDom.value!.innerText = text = text.substring(0, Math.floor(text.length / 2))
                  } else {
                     textDom.value!.innerText = text = text.substring(0, text.length - 1)
                  }
               }
            })
         }
      }
      ellipsisData.computedReady = true
   })
}
</script>

<style lang="scss" scoped>
.ellipsis {
   &.vis {
      visibility: hidden;
   }
}
</style>
