{"version":3,"file":"index2.mjs","sources":["../../../../../src/components/ivue-tabs/index.vue"],"sourcesContent":["<template>\r\n  <div :class=\"prefixCls\">\r\n    <!-- 头部 -->\r\n    <div class=\"ivue-tabs-bar\" ref=\"bar\">\r\n      <!-- 左边按钮 -->\r\n      <transition name=\"fade-transition\">\r\n        <ivue-icon\r\n          :class=\"`ivue-tabs-icon ivue-tabs-icon--prev`\"\r\n          v-show=\"hasArrows && data.prevIconVisible\"\r\n          @click=\"handleScrollTo('prev')\"\r\n          >{{ prevIcon }}</ivue-icon\r\n        >\r\n      </transition>\r\n      <!-- 内容 -->\r\n      <div\r\n        :class=\"tabWrapperClasses\"\r\n        :style=\"tabWrapperStyles\"\r\n        v-touch=\"{\r\n          start: (e) => overflowCheck(e, onTouchStart),\r\n          move: (e) => overflowCheck(e, onTouchMove),\r\n          end: (e) => overflowCheck(e, onTouchEnd),\r\n        }\"\r\n        ref=\"wrapper\"\r\n      >\r\n        <div\r\n          :class=\"tabContainerClasses\"\r\n          :style=\"containerStyles\"\r\n          ref=\"container\"\r\n        >\r\n          <slider\r\n            :sliderLeft=\"data.sliderLeft\"\r\n            :sliderWidth=\"data.sliderWidth\"\r\n            :color=\"sliderColor\"\r\n          ></slider>\r\n          <slot name=\"header\"></slot>\r\n        </div>\r\n      </div>\r\n      <!-- 右边按钮 -->\r\n      <transition name=\"fade-transition\">\r\n        <ivue-icon\r\n          :class=\"`ivue-tabs-icon ivue-tabs-icon--next`\"\r\n          v-show=\"hasArrows && data.nextIconVisible\"\r\n          @click=\"handleScrollTo('next')\"\r\n          >{{ nextIcon }}</ivue-icon\r\n        >\r\n      </transition>\r\n    </div>\r\n    <!-- 内容 -->\r\n    <div\r\n      class=\"ivue-tabs-items\"\r\n      v-touch=\"{\r\n        left: () => handleSwipeItem('next'),\r\n        right: () => handleSwipeItem('prev'),\r\n      }\"\r\n    >\r\n      <slot name=\"content\"></slot>\r\n    </div>\r\n  </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport {\r\n  defineComponent,\r\n  reactive,\r\n  provide,\r\n  computed,\r\n  ref,\r\n  watch,\r\n  nextTick,\r\n  onMounted,\r\n  watchEffect,\r\n} from 'vue';\r\n\r\nimport tabsComputed from './tabs-computed';\r\nimport tabsTouch from './tabs-touch';\r\nimport IvueIcon from '../ivue-icon/index.vue';\r\nimport Slider from './slider.vue';\r\nimport Touch from '../../utils/directives/touch';\r\n\r\n// type\r\nimport { Props, Data, TabsContextKey } from './types/tabs';\r\n\r\nconst prefixCls = 'ivue-tabs';\r\n\r\nexport default defineComponent({\r\n  name: prefixCls,\r\n  directives: { Touch },\r\n  // 声明事件\r\n  emits: ['update:modelValue'],\r\n  props: {\r\n    /**\r\n     * 当前激活 tab 面板的 name，可以使用 v-model 双向绑定数据\r\n     *\r\n     * @type {String,Number}\r\n     */\r\n    modelValue: {\r\n      type: [String, Number],\r\n      default: '',\r\n    },\r\n    /**\r\n     * 导航内容居中\r\n     *\r\n     * @type {Boolean}\r\n     */\r\n    centered: {\r\n      type: Boolean,\r\n      default: false,\r\n    },\r\n    /**\r\n     * 导航内容向右\r\n     *\r\n     * @type {Boolean}\r\n     */\r\n    right: {\r\n      type: Boolean,\r\n      default: false,\r\n    },\r\n    /**\r\n     * 是否显示导航栏箭头\r\n     *\r\n     * @type {Boolean}\r\n     */\r\n    showArrows: {\r\n      type: Boolean,\r\n      default: true,\r\n    },\r\n    /**\r\n     * 左边按钮\r\n     *\r\n     * @type {String}\r\n     */\r\n    prevIcon: {\r\n      type: String,\r\n      default: 'chevron_left',\r\n    },\r\n    /**\r\n     * 右边按钮\r\n     *\r\n     * @type {String}\r\n     */\r\n    nextIcon: {\r\n      type: String,\r\n      default: 'chevron_right',\r\n    },\r\n    /**\r\n     * 固定宽度标签\r\n     *\r\n     * @type {Boolean}\r\n     */\r\n    fixedWidth: {\r\n      type: Boolean,\r\n      default: false,\r\n    },\r\n    /**\r\n     * 自动适应宽度标签\r\n     *\r\n     * @type {Boolean}\r\n     */\r\n    autoWidth: {\r\n      type: Boolean,\r\n      default: false,\r\n    },\r\n    /**\r\n     * 导航高度\r\n     *\r\n     * @type {String,Number}\r\n     */\r\n    height: {\r\n      type: [String, Number],\r\n    },\r\n    /**\r\n     * 滑动条颜色\r\n     *\r\n     * @type {String}\r\n     */\r\n    sliderColor: {\r\n      type: String,\r\n      default: 'primary',\r\n    },\r\n    /**\r\n     * 滑动条隐藏\r\n     *\r\n     * @type {Boolean}\r\n     */\r\n    hideSlider: {\r\n      type: Boolean,\r\n      default: false,\r\n    },\r\n    /**\r\n     * 调整显示箭头时设置的前后滚动的间距\r\n     *\r\n     * @type {Number | String}\r\n     */\r\n    arrowsMargin: {\r\n      type: [Number, String],\r\n      default: 40,\r\n    },\r\n  },\r\n  setup(props: Props, { emit }) {\r\n    // dom\r\n\r\n    // tabs-container\r\n    const container = ref<HTMLElement>(null);\r\n\r\n    // tabs-wrapper\r\n    const wrapper = ref<HTMLElement>(null);\r\n\r\n    // tabs-bar\r\n    const bar = ref<HTMLElement>(null);\r\n\r\n    // data\r\n    const data = reactive<Data>({\r\n      // tab导航数组\r\n      tabs: [],\r\n      // tab内容数组\r\n      tabsItem: [],\r\n      // 导航栏是否需要滚动\r\n      isOverflowing: false,\r\n      // 下一个按钮显示\r\n      nextIconVisible: false,\r\n      // 上一个按钮显示\r\n      prevIconVisible: false,\r\n      // 滚动位置\r\n      scrollOffset: 0,\r\n      // 开始滑动x轴\r\n      startX: 0,\r\n      // 导航栏宽度\r\n      widths: {\r\n        bar: 0,\r\n        container: 0,\r\n        wrapper: 0,\r\n      },\r\n      // 重置时间函数\r\n      resizeTimeout: null,\r\n      // 滑动条位置\r\n      sliderLeft: 0,\r\n      // 滑动条宽度\r\n      sliderWidth: 0,\r\n      // 控制内容初始化动画效果\r\n      isBooted: false,\r\n      // 是否初始化完成\r\n      isInit: false,\r\n    });\r\n\r\n    // computed\r\n    const { hasArrows, containerStyles, activeTab, activeIndex } = tabsComputed(\r\n      props,\r\n      data\r\n    );\r\n\r\n    // tab外层样式\r\n    const tabWrapperClasses = computed(() => {\r\n      return {\r\n        'ivue-tabs-wrapper': true,\r\n      };\r\n    });\r\n\r\n    // tab外层样式\r\n    const tabWrapperStyles = computed(() => {\r\n      const regexp = new RegExp(/[a-zA-Z]/g);\r\n\r\n      // 是否有单位\r\n      const isUnit = regexp.test(`${props.arrowsMargin}`);\r\n\r\n      return {\r\n        'margin-left': hasArrows.value\r\n          ? !isUnit\r\n            ? `${props.arrowsMargin}px`\r\n            : props.arrowsMargin\r\n          : '',\r\n        'margin-right': hasArrows.value\r\n          ? !isUnit\r\n            ? `${props.arrowsMargin}px`\r\n            : props.arrowsMargin\r\n          : '',\r\n        transition: data.isInit ? '' : 'none 0s',\r\n      };\r\n    });\r\n\r\n    // tab样式\r\n    const tabContainerClasses = computed(() => {\r\n      return {\r\n        [`${prefixCls}-container`]: true,\r\n        [`${prefixCls}-container--centered`]: props.centered,\r\n        [`${prefixCls}-container--right`]: props.right,\r\n        [`${prefixCls}-container--overflow`]: data.isOverflowing,\r\n        [`${prefixCls}-container--fixed-width`]: props.fixedWidth,\r\n        [`${prefixCls}-container--auto-width`]: props.autoWidth,\r\n      };\r\n    });\r\n\r\n    // methods\r\n\r\n    const {\r\n      onTouchStart,\r\n      onTouchMove,\r\n      onTouchEnd,\r\n      newOffset,\r\n      handleSwipeItem,\r\n    } = tabsTouch(data, container, wrapper, activeIndex);\r\n\r\n    // 初始化\r\n    const initDate = () => {\r\n      nextTick(() => {\r\n        // 滚动导航栏\r\n        scrollIntoView();\r\n      });\r\n\r\n      callSlider();\r\n    };\r\n\r\n    // 是否可以滚动\r\n    const overflowCheck = (e, fn) => {\r\n      data.isOverflowing && fn(e);\r\n    };\r\n\r\n    // 清除tab导航\r\n    const unregister = (name: string) => {\r\n      data.tabs = data.tabs.filter((o) => {\r\n        return o.tabName !== name;\r\n      });\r\n    };\r\n\r\n    // 清除tab item\r\n    const unregisterItems = (name: string) => {\r\n      data.tabsItem = data.tabsItem.filter((o) => {\r\n        return o.tabName !== name;\r\n      });\r\n    };\r\n\r\n    // 获取导航宽度\r\n    const setWidths = () => {\r\n      const _bar = bar.value ? bar.value?.clientWidth : 0;\r\n      const _container = container.value ? container.value.clientWidth : 0;\r\n      const _wrapper = wrapper.value ? wrapper.value.clientWidth : 0;\r\n\r\n      data.widths = {\r\n        bar: _bar,\r\n        container: _container,\r\n        wrapper: _wrapper,\r\n      };\r\n\r\n      setOverflow();\r\n    };\r\n\r\n    // 设置导航栏是否滚动\r\n    const setOverflow = () => {\r\n      data.isOverflowing = data.widths.bar < data.widths.container;\r\n    };\r\n\r\n    // 设置滑动条\r\n    const callSlider = () => {\r\n      const _activeTab = activeTab.value;\r\n\r\n      if (props.hideSlider || !_activeTab) {\r\n        return false;\r\n      }\r\n\r\n      nextTick(() => {\r\n        if (!_activeTab || !_activeTab.$el || _activeTab.disabled) {\r\n          return;\r\n        }\r\n\r\n        data.sliderLeft = _activeTab.$el.offsetLeft;\r\n        data.sliderWidth = _activeTab.$el.clientWidth;\r\n      });\r\n    };\r\n\r\n    // 滚动导航栏\r\n    const scrollIntoView = () => {\r\n      const _activeTab = activeTab.value;\r\n\r\n      if (!_activeTab) {\r\n        return;\r\n      }\r\n      if (!data.isOverflowing) {\r\n        return (data.scrollOffset = 0);\r\n      }\r\n\r\n      const activeTabBounding = _activeTab.$el.getBoundingClientRect();\r\n      const navScrollBounding = wrapper.value.getBoundingClientRect();\r\n      const navBounding = container.value.getBoundingClientRect();\r\n\r\n      const currentOffset = data.scrollOffset;\r\n      let newOffset = currentOffset;\r\n\r\n      if (\r\n        parseInt(`${navBounding.right}`) <\r\n        parseInt(`${navScrollBounding.right}`)\r\n      ) {\r\n        newOffset =\r\n          container.value.offsetWidth - parseInt(`${navScrollBounding.width}`);\r\n      }\r\n\r\n      if (\r\n        parseInt(activeTabBounding.left) < parseInt(`${navScrollBounding.left}`)\r\n      ) {\r\n        newOffset =\r\n          currentOffset -\r\n          (parseInt(`${navScrollBounding.left}`) -\r\n            parseInt(activeTabBounding.left));\r\n      } else if (\r\n        parseInt(activeTabBounding.right) >\r\n        parseInt(`${navScrollBounding.right}`)\r\n      ) {\r\n        newOffset =\r\n          currentOffset +\r\n          parseInt(activeTabBounding.right) -\r\n          parseInt(`${navScrollBounding.right}`);\r\n      }\r\n\r\n      if (currentOffset !== newOffset) {\r\n        const maxOffset = data.widths.container - data.widths.wrapper;\r\n        const isOffset = newOffset + data.widths.wrapper;\r\n        const widthsContainer = data.widths.container;\r\n\r\n        if (isOffset > widthsContainer) {\r\n          newOffset = maxOffset;\r\n        }\r\n\r\n        data.scrollOffset = newOffset;\r\n      }\r\n    };\r\n\r\n    // 检查是否有icon\r\n    const checkIcons = () => {\r\n      data.prevIconVisible = checkPrevIcon();\r\n      data.nextIconVisible = checkNextIcon();\r\n    };\r\n\r\n    // 检查右边按钮\r\n    const checkNextIcon = () => {\r\n      const offset = data.scrollOffset + data.widths.wrapper;\r\n\r\n      if (offset === data.widths.container) {\r\n        return false;\r\n      }\r\n\r\n      return (\r\n        data.widths.container >\r\n        (hasArrows.value ? offset - Number(props.arrowsMargin) : offset)\r\n      );\r\n    };\r\n\r\n    // 检查左边按钮\r\n    const checkPrevIcon = () => {\r\n      return data.scrollOffset > 0;\r\n    };\r\n\r\n    // 点击箭头滚动\r\n    const handleScrollTo = (direction) => {\r\n      data.scrollOffset = newOffset(direction);\r\n    };\r\n\r\n    // 监听resize\r\n    const onResize = () => {\r\n      setWidths();\r\n\r\n      clearTimeout(data.resizeTimeout);\r\n\r\n      data.resizeTimeout = setTimeout(() => {\r\n        // 设置滑动条\r\n        callSlider();\r\n        // 滚动导航栏\r\n        scrollIntoView();\r\n        // 检查是否有icon\r\n        checkIcons();\r\n      }, 300);\r\n    };\r\n\r\n    // 导航点击\r\n    const tabNavClick = (tab) => {\r\n      // 判断禁用\r\n      if (tab.disabled) {\r\n        return;\r\n      }\r\n\r\n      updateValue(tab.tabName);\r\n    };\r\n\r\n    // 更新当前激活的导航\r\n    const updateValue = (value: string | number) => {\r\n      // updated v-model\r\n      emit('update:modelValue', value);\r\n    };\r\n\r\n    // 更新激活的item\r\n    const updateItems = (reverse: boolean) => {\r\n      for (let index = data.tabsItem.length; --index >= 0; ) {\r\n        data.tabsItem[index].handleToggle(\r\n          activeIndex.value === index,\r\n          reverse,\r\n          data.isBooted\r\n        );\r\n      }\r\n\r\n      data.isBooted = true;\r\n    };\r\n\r\n    // watch\r\n\r\n    // 监听导航滑动\r\n    watch(\r\n      () => data.scrollOffset,\r\n      () => {\r\n        // 是否开启按钮\r\n        if (hasArrows.value) {\r\n          data.nextIconVisible = checkNextIcon();\r\n          data.prevIconVisible = checkPrevIcon();\r\n        }\r\n      }\r\n    );\r\n\r\n    // 监听激活项\r\n    watch(\r\n      () => props.modelValue,\r\n      () => {\r\n        initDate();\r\n      }\r\n    );\r\n\r\n    // data\r\n    watch(\r\n      () => data.tabsItem,\r\n      () => {\r\n        nextTick(() => {\r\n          onResize();\r\n        });\r\n      },\r\n      {\r\n        deep: true,\r\n      }\r\n    );\r\n\r\n    // 监听固定标签\r\n    watch(\r\n      () => props.fixedWidth,\r\n      () => {\r\n        callSlider();\r\n      }\r\n    );\r\n\r\n    // 监听激活的index\r\n    watch(\r\n      () => activeIndex.value,\r\n      (current, previous) => {\r\n        const reverse = current < previous;\r\n\r\n        updateItems(reverse);\r\n      }\r\n    );\r\n\r\n    // provide\r\n    provide(\r\n      TabsContextKey,\r\n      reactive({\r\n        props,\r\n        data,\r\n        unregister: unregister,\r\n        tabNavClick: tabNavClick,\r\n        unregisterItems: unregisterItems,\r\n      })\r\n    );\r\n\r\n    // watch\r\n\r\n    // 同步监听 tab导航数组\r\n    watchEffect(() => {\r\n      if (data.tabs) {\r\n        onResize();\r\n      }\r\n    });\r\n\r\n    // onMounted\r\n    onMounted(() => {\r\n      nextTick(() => {\r\n        data.isInit = true;\r\n      });\r\n\r\n      initDate();\r\n    });\r\n\r\n    return {\r\n      prefixCls,\r\n\r\n      // dom\r\n      container,\r\n      bar,\r\n      wrapper,\r\n\r\n      // data\r\n      data,\r\n\r\n      // computed\r\n      tabWrapperClasses,\r\n      hasArrows,\r\n      tabContainerClasses,\r\n      containerStyles,\r\n      tabWrapperStyles,\r\n\r\n      // methods\r\n      overflowCheck,\r\n      onTouchStart,\r\n      onTouchMove,\r\n      onTouchEnd,\r\n      handleScrollTo,\r\n      unregister,\r\n      handleSwipeItem,\r\n      tabNavClick,\r\n    };\r\n  },\r\n  components: {\r\n    IvueIcon,\r\n    Slider,\r\n  },\r\n});\r\n</script>\r\n"],"names":["_resolveDirective","_normalizeClass","_createCommentVNode","_createElementVNode","_createVNode","_Transition","_withCtx","_withDirectives","_openBlock","_createElementBlock","_normalizeStyle"],"mappings":";;;;MAGkC,UAAM,GAAA;AAAA,EAAA,KAAA,EAAA,eAAA;AAAA;;;;;;2BAFtCA,iBAwDM,OAAA,CAAA,CAAA;;IAvDJ,KAAA,EAAWC,cAAA,CAAA,IAAA,CAAA,SAAA,CAAA;AAAA,GACX,EAAA;AAAA,IACEC,mBAAA,gBAAA,CAAA;AAAA,IAQaC,kBAAA,CAAA,OAAA,UAAA,EAAA;AAAA,MAAAD,mBAPI,4BAAiB,CAAA;AAAA,MAAAE,YAAlCC,UAOa,EAAA;AAAA,QAAA,IAAA,EAAA,iBAAA;AAAA;;QALH,OAAA,EAAAC,QAAA,MAAA;AAAA,UAAAC,cAAA,CAEAH,YAAE,oBAAA,EAAA;AAAA,YAAA,KAAA,EAAAH,eAAA,CAAA,mCAAA,CAAA,CAAA;AAAA,YACO,OAAA,EAAA,OAAA,CAAA,CAAA,KAAA,MAAA,CAAA,KAAA,CAAA,MAAA,KAAA,IAAA,CAAA,cAAA,CAAA,MAAA,CAAA,CAAA;AAAA,WAAA,EAAA;AAAA;;;;;;;;QAGnB,CAAA,EAAA,CAAA;AAAA,OAAA,CAAA;AAAA,yBAEQ,gBAAE,CAAA;AAAA,MAAAM,cACF,EAAAC,SAAA,EAAkB,EAAAC,kBAAA,CAAA,KAAA,EAAA;AAAA,QAMxB,KAAA,EAAIR,cAAS,CAAA,IAAA,CAAA,iBAAA,CAAA;AAAA,QAAA,KAAA,EAAAS,cAAA,CAAA,IAAA,CAAA,gBAAA,CAAA;AAAA,QAEb,GAAA,EAAA,SAAA;AAAA,OAAA,EAAA;AAAA,2BAEQ,KAAE,EAAA;AAAA,UACR,KAAA,EAAIT,cAAW,CAAA,IAAA,CAAA,mBAAA,CAAA;AAAA,UAAA,KAAA,EAAAS,cAAA,CAAA,IAAA,CAAA,eAAA,CAAA;AAAA,UAEf,GAAA,EAAA,WAAA;AAAA,SAAA,EAAA;AAAA,sBAEc,iBAAO,EAAA;AAAA,YAClB,UAAA,EAAO,KAAA,IAAA,CAAA,UAAA;AAAA,YAAA,WAAA,EAAA,KAAA,IAAA,CAAA,WAAA;AAAA,YAEV,OAA2B,IAAA,CAAA,WAAA;AAAA,aAAA,IAAA,EAAA,CAAA,EAAA,CAAA,YAAA,EAAA,aAAA,EAAA,OAAA,CAAA,CAAA;AAAA;;OAjBO,EAAA,CAAA,CAAA,GAAA;AAAA,QAAwD,CAAA,gBAAA,EAAA;AAAA,UAAiD,OAAA,CAAA,CAAA,KAAK,KAAe,aAAA,CAAA,CAAA,EAAE,KAAU,YAAA,CAAA;AAAA,UAAA,MAAA,CAAA,CAAA,KAAA,KAAA,aAAA,CAAA,CAAA,EAAA,KAAA,WAAA,CAAA;AAAA;SAoB/K,CAAA;AAAA,OACA,CAAA;AAAA,MAAAR,mBAAiB,4BAAiB,CAAA;AAAA,MAAAE,YAAlCC,UAOa,EAAA;AAAA,QAAA,IAAA,EAAA,iBAAA;AAAA;;QALH,OAAA,EAAAC,QAAA,MAAA;AAAA,UAAAC,cAAA,CAEAH,YAAE,oBAAA,EAAA;AAAA,YAAA,KAAA,EAAAH,eAAA,CAAA,mCAAA,CAAA,CAAA;AAAA,YACO,OAAA,EAAA,OAAA,CAAA,CAAA,KAAA,MAAA,CAAA,KAAA,CAAA,MAAA,KAAA,IAAA,CAAA,cAAA,CAAA,MAAA,CAAA,CAAA;AAAA,WAAA,EAAA;AAAA;;;;;;;;;OAIrB,CAAA;AAAA,OAAA,GAAA,CAAA;AAAA,IAQ8BC,mBAAA,gBAAA,CAAA;AAAA,IAAAK,cAAA,EAAAC,SAAA,EAAA,EAAAC,kBAAA,CAAA,OAAA,UAAA,EAAA;AAAA;;MALmD,CAAA,gBAAA,EAAA;AAAA,QAAA,IAAA,EAAA,MAAA,IAAA,CAAA,eAAA,CAAA,MAAA,CAAA;AAAA;;;;;;;;;"}