element popover源碼


index.js

import Popover from './src/main';
import directive from './src/directive';
import Vue from 'vue';
// 自定義指令
Vue.directive('popover', directive);
/**
 * eg: 
 * <el-popover
    ref="popover"
    placement="right"
    title="標題"
    width="200"
    trigger="focus"
    content="這是一段內容,這是一段內容,這是一段內容,這是一段內容。">
  </el-popover>
  <el-button v-popover:popover>focus 激活</el-button>
 */

/* istanbul ignore next */
Popover.install = function (Vue) {
  Vue.directive('popover', directive);
  Vue.component(Popover.name, Popover);
};
Popover.directive = directive;

export default Popover;

directive.js

const getReference = (el, binding, vnode) => {
  const _ref = binding.expression ? binding.value : binding.arg;
  const popper = vnode.context.$refs[_ref];
  if (popper) {
    if (Array.isArray(popper)) {
      popper[0].$refs.reference = el;
    } else {
      popper.$refs.reference = el;
    }
  }
};

export default {
  // 綁定,只執行一次
  bind (el, binding, vnode) {
    getReference(el, binding, vnode);
  },
  // 插入父級
  inserted (el, binding, vnode) {
    getReference(el, binding, vnode);
  }
};

main.vue

<template>
  <span>
  <!-- after-enter    顯示動畫播放完畢后觸發 -->
  <!-- after-leave    隱藏動畫播放完畢后觸發 -->
    <transition
      :name="transition"
      @after-enter="handleAfterEnter"
      @after-leave="handleAfterLeave">
      <div
        class="el-popover el-popper"
        :class="[popperClass, content && 'el-popover--plain']"
        ref="popper"
        v-show="!disabled && showPopper"
        :style="{ width: width + 'px' }"
        role="tooltip"
        :id="tooltipId"
        :aria-hidden="(disabled || !showPopper) ? 'true' : 'false'"
      >
        <div class="el-popover__title" v-if="title" v-text="title"></div>
        <slot>{{ content }}</slot>
      </div>
    </transition>
    <slot name="reference"></slot>
  </span>
</template>
<script>
import Popper from 'element-ui/src/utils/vue-popper';
import { on, off } from 'element-ui/src/utils/dom';
import { addClass, removeClass } from 'element-ui/src/utils/dom';
import { generateId } from 'element-ui/src/utils/util';

export default {
  name: 'ElPopover',

  mixins: [Popper],

  props: {
    // 觸發方式    String    click/focus/hover/manual    click
    trigger: {
      type: String,
      default: 'click',
      validator: value => ['click', 'focus', 'hover', 'manual'].indexOf(value) > -1
    },
    // 觸發方式為 hover 時的顯示延遲,單位為毫秒    Number
    openDelay: {
      type: Number,
      default: 0
    },
    // title    標題    String
    title: String,
    // Popover 是否可用    Boolean
    disabled: Boolean,
    // 顯示的內容,也可以通過 slot 傳入 DOM
    content: String,
    // 觸發 Popover 顯示的 HTML 元素
    reference: {},
    // popper-class    為 popper 添加類名    String
    popperClass: String,
    // width    寬度    String, Number    —    最小寬度 150px
    width: {},
    // 是否顯示 Tooltip 箭頭,更多參數可見Vue-popper    Boolean    —    true
    visibleArrow: {
      default: true
    },
    // 出現位置的偏移量    Number    —    0
    arrowOffset: {
      type: Number,
      default: 0
    },
    // 定義漸變動畫    String    —    fade-in-linear
    transition: {
      type: String,
      default: 'fade-in-linear'
    },
    // Popover 組件的 tabindex    number    —    0
    tabindex: {
      type: Number,
      default: 0
    }
  },

  computed: {
    tooltipId() {
      return `el-popover-${generateId()}`;
    }
  },
  watch: {
    // 是否顯示popper
    showPopper(val) {
      if (this.disabled) {
        return;
      }
      // show    顯示時觸發
      // hide    隱藏時觸發
      val ? this.$emit('show') : this.$emit('hide');
    }
  },

  mounted() {
    let reference = this.referenceElm = this.reference || this.$refs.reference;
    const popper = this.popper || this.$refs.popper;

    if (!reference && this.$slots.reference && this.$slots.reference[0]) {
      reference = this.referenceElm = this.$slots.reference[0].elm;
    }
    // 可訪問性
    if (reference) {
      addClass(reference, 'el-popover__reference');
      reference.setAttribute('aria-describedby', this.tooltipId);
      reference.setAttribute('tabindex', this.tabindex); // tab序列
      popper.setAttribute('tabindex', 0);

      if (this.trigger !== 'click') {
        // 添加事件
        on(reference, 'focusin', () => {
          this.handleFocus();
          const instance = reference.__vue__;
          if (instance && typeof instance.focus === 'function') {
            instance.focus();
          }
        });
        on(popper, 'focusin', this.handleFocus);
        on(reference, 'focusout', this.handleBlur);
        on(popper, 'focusout', this.handleBlur);
      }
      on(reference, 'keydown', this.handleKeydown);
      on(reference, 'click', this.handleClick);
    }
    // 如果是點擊觸發
    if (this.trigger === 'click') {
      // 綁定切換
      on(reference, 'click', this.doToggle);
      // 點擊文檔,隱藏popper
      on(document, 'click', this.handleDocumentClick);
    } else if (this.trigger === 'hover') {
      // 增加鼠標移入移出事件
      on(reference, 'mouseenter', this.handleMouseEnter);
      on(popper, 'mouseenter', this.handleMouseEnter);
      on(reference, 'mouseleave', this.handleMouseLeave);
      on(popper, 'mouseleave', this.handleMouseLeave);
    } else if (this.trigger === 'focus') {
      if (this.tabindex < 0) {
        console.warn('[Element Warn][Popover]a negative taindex means that the element cannot be focused by tab key');
      }
      if (reference.querySelector('input, textarea')) {
        on(reference, 'focusin', this.doShow);
        on(reference, 'focusout', this.doClose);
      } else {
        on(reference, 'mousedown', this.doShow);
        on(reference, 'mouseup', this.doClose);
      }
    }
  },
  // 銷毀前
  beforeDestroy() {
    this.cleanup();
  },
  // 銷毀后
  deactivated() {
    this.cleanup();
  },

  methods: {
    // 切換顯示/隱藏
    doToggle() {
      this.showPopper = !this.showPopper;
    },
    // 顯示
    doShow() {
      this.showPopper = true;
    },
    // 關閉
    doClose() {
      this.showPopper = false;
    },
    // 聚焦
    handleFocus() {
      addClass(this.referenceElm, 'focusing');
      if (this.trigger === 'click' || this.trigger === 'focus') this.showPopper = true;
    },
    // 點擊事件
    handleClick() {
      removeClass(this.referenceElm, 'focusing');
    },
    // 失焦
    handleBlur() {
      removeClass(this.referenceElm, 'focusing');
      if (this.trigger === 'click' || this.trigger === 'focus') this.showPopper = false;
    },
    // 鼠標移入
    handleMouseEnter() {
      clearTimeout(this._timer);
      if (this.openDelay) {
        this._timer = setTimeout(() => {
          this.showPopper = true;
        }, this.openDelay);
      } else {
        this.showPopper = true;
      }
    },
    // 鍵盤按下事件
    handleKeydown(ev) {
      if (ev.keyCode === 27 && this.trigger !== 'manual') { // esc
        this.doClose();
      }
    },
    // 鼠標移出
    handleMouseLeave() {
      clearTimeout(this._timer);
      this._timer = setTimeout(() => {
        this.showPopper = false;
      }, 200);
    },
    // 點擊文檔事件
    handleDocumentClick(e) {
      let reference = this.reference || this.$refs.reference;
      const popper = this.popper || this.$refs.popper;

      if (!reference && this.$slots.reference && this.$slots.reference[0]) {
        reference = this.referenceElm = this.$slots.reference[0].elm;
      }
      if (!this.$el ||
        !reference ||
        this.$el.contains(e.target) ||
        reference.contains(e.target) ||
        !popper ||
        popper.contains(e.target)) return;
      this.showPopper = false;
    },
    // 動畫進入完成
    handleAfterEnter() {
      // 向外部暴露after-enter事件
      this.$emit('after-enter');
    },
    // 動畫離開完成
    handleAfterLeave() {
      // 向外部暴露after-leave事件
      this.$emit('after-leave');
      // 銷毀
      this.doDestroy();
    },
    // 清除
    cleanup() {
      // 有延時,清除定時器
      if (this.openDelay) {
        clearTimeout(this._timer);
      }
    }
  },

  destroyed() {
    const reference = this.reference;
    // 卸載事件
    off(reference, 'click', this.doToggle);
    off(reference, 'mouseup', this.doClose);
    off(reference, 'mousedown', this.doShow);
    off(reference, 'focusin', this.doShow);
    off(reference, 'focusout', this.doClose);
    off(reference, 'mousedown', this.doShow);
    off(reference, 'mouseup', this.doClose);
    off(reference, 'mouseleave', this.handleMouseLeave);
    off(reference, 'mouseenter', this.handleMouseEnter);
    off(document, 'click', this.handleDocumentClick);
  }
};
</script>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM