zoukankan      html  css  js  c++  java
  • 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 + '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
         {},
        // 是否显示 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>
  • 相关阅读:
    异步、+回调机制、线程queue、线程Event、协程、单线程实现遇到IO切换
    GIL、进/线程池、同/异步、阻/非阻塞
    锁——死锁——单个锁锁死
    网络编程之多线程
    后台Response和异常和日志封装、跨域问题及解决、es6的箭头函数、xadmin后台管理
    pip换源、虚拟环境搭建、
    非对称加密和对称加密的区别
    JWT、多方式登录、django缓存
    自定制频率、自动生成接口文档、JWT、自定制auth认证类
    books系列表接口、表断关联、分页器、根据IP限制频率
  • 原文地址:https://www.cnblogs.com/wsk1576025821/p/10975695.html
Copyright © 2011-2022 走看看