• element autocomplete源码


    autocomplete/src/autocomplete.vue

    <template>
      <div
        class="el-autocomplete"
        v-clickoutside="close"
        aria-haspopup="listbox"
        role="combobox"
        :aria-expanded="suggestionVisible"
        :aria-owns="id"
      >
        <el-input
          ref="input"
          v-bind="[$props, $attrs]"
          @input="handleChange"
          @focus="handleFocus"
          @blur="handleBlur"
          @clear="handleClear"
          @keydown.up.native.prevent="highlight(highlightedIndex - 1)"
          @keydown.down.native.prevent="highlight(highlightedIndex + 1)"
          @keydown.enter.native="handleKeyEnter"
          @keydown.native.tab="close"
        >
          <template slot="prepend" v-if="$slots.prepend">
            <slot name="prepend"></slot>
          </template>
          <template slot="append" v-if="$slots.append">
            <slot name="append"></slot>
          </template>
          <template slot="prefix" v-if="$slots.prefix">
            <slot name="prefix"></slot>
          </template>
          <template slot="suffix" v-if="$slots.suffix">
            <slot name="suffix"></slot>
          </template>
        </el-input>
        <el-autocomplete-suggestions
          visible-arrow
          :class="[popperClass ? popperClass : '']"
          :popper-options="popperOptions"
          :append-to-body="popperAppendToBody"
          ref="suggestions"
          :placement="placement"
          :id="id">
          <li
            v-for="(item, index) in suggestions"
            :key="index"
            :class="{'highlighted': highlightedIndex === index}"
            @click="select(item)"
            :id="`${id}-item-${index}`"
            role="option"
            :aria-selected="highlightedIndex === index"
          >
            <slot :item="item">
              {{ item[valueKey] }}
            </slot>
          </li>
        </el-autocomplete-suggestions>
      </div>
    </template>
    <script>
      import debounce from 'throttle-debounce/debounce';
      import ElInput from 'element-ui/packages/input';
      import Clickoutside from 'element-ui/src/utils/clickoutside';
      import ElAutocompleteSuggestions from './autocomplete-suggestions.vue';
      import Emitter from 'element-ui/src/mixins/emitter';
      import Migrating from 'element-ui/src/mixins/migrating';
      import { generateId } from 'element-ui/src/utils/util';
      import Focus from 'element-ui/src/mixins/focus';
    
      export default {
        name: 'ElAutocomplete',
    
        mixins: [Emitter, Focus('input'), Migrating],
    
        inheritAttrs: false,
    
        componentName: 'ElAutocomplete',
    
        components: {
          ElInput,
          ElAutocompleteSuggestions
        },
    
        directives: { Clickoutside },
    
        props: {
          valueKey: {
            type: String,
            default: 'value'
          },
          popperClass: String,
          popperOptions: Object,
          placeholder: String,
          clearable: {
            type: Boolean,
            default: false
          },
          disabled: Boolean,
          name: String,
          size: String,
          value: String,
          maxlength: Number,
          minlength: Number,
          autofocus: Boolean,
          fetchSuggestions: Function,
          triggerOnFocus: {
            type: Boolean,
            default: true
          },
          customItem: String,
          selectWhenUnmatched: {
            type: Boolean,
            default: false
          },
          prefixIcon: String,
          suffixIcon: String,
          label: String,
          debounce: {
            type: Number,
            default: 300
          },
          placement: {
            type: String,
            default: 'bottom-start'
          },
          hideLoading: Boolean,
          popperAppendToBody: {
            type: Boolean,
            default: true
          },
          highlightFirstItem: {
            type: Boolean,
            default: false
          }
        },
        data() {
          return {
            activated: false,
            suggestions: [],
            loading: false,
            highlightedIndex: -1,
            suggestionDisabled: false
          };
        },
        computed: {
          suggestionVisible() {
            const suggestions = this.suggestions;
            let isValidData = Array.isArray(suggestions) && suggestions.length > 0;
            return (isValidData || this.loading) && this.activated;
          },
          id() {
            return `el-autocomplete-${generateId()}`;
          }
        },
        watch: {
          suggestionVisible(val) {
            let $input = this.getInput();
            if ($input) {
              this.broadcast('ElAutocompleteSuggestions', 'visible', [val, $input.offsetWidth]);
            }
          }
        },
        methods: {
          getMigratingConfig() {
            return {
              props: {
                'custom-item': 'custom-item is removed, use scoped slot instead.',
                'props': 'props is removed, use value-key instead.'
              }
            };
          },
          getData(queryString) {
            if (this.suggestionDisabled) {
              return;
            }
            this.loading = true;
            this.fetchSuggestions(queryString, (suggestions) => {
              this.loading = false;
              if (this.suggestionDisabled) {
                return;
              }
              if (Array.isArray(suggestions)) {
                this.suggestions = suggestions;
                this.highlightedIndex = this.highlightFirstItem ? 0 : -1;
              } else {
                console.error('[Element Error][Autocomplete]autocomplete suggestions must be an array');
              }
            });
          },
          handleChange(value) {
            this.$emit('input', value);
            this.suggestionDisabled = false;
            if (!this.triggerOnFocus && !value) {
              this.suggestionDisabled = true;
              this.suggestions = [];
              return;
            }
            this.debouncedGetData(value);
          },
          handleFocus(event) {
            this.activated = true;
            this.$emit('focus', event);
            if (this.triggerOnFocus) {
              this.debouncedGetData(this.value);
            }
          },
          handleBlur(event) {
            this.$emit('blur', event);
          },
          handleClear() {
            this.activated = false;
            this.$emit('clear');
          },
          close(e) {
            this.activated = false;
          },
          handleKeyEnter(e) {
            if (this.suggestionVisible && this.highlightedIndex >= 0 && this.highlightedIndex < this.suggestions.length) {
              e.preventDefault();
              this.select(this.suggestions[this.highlightedIndex]);
            } else if (this.selectWhenUnmatched) {
              this.$emit('select', {value: this.value});
              this.$nextTick(_ => {
                this.suggestions = [];
                this.highlightedIndex = -1;
              });
            }
          },
          select(item) {
            this.$emit('input', item[this.valueKey]);
            this.$emit('select', item);
            this.$nextTick(_ => {
              this.suggestions = [];
              this.highlightedIndex = -1;
            });
          },
          highlight(index) {
            if (!this.suggestionVisible || this.loading) { return; }
            if (index < 0) {
              this.highlightedIndex = -1;
              return;
            }
            if (index >= this.suggestions.length) {
              index = this.suggestions.length - 1;
            }
            const suggestion = this.$refs.suggestions.$el.querySelector('.el-autocomplete-suggestion__wrap');
            const suggestionList = suggestion.querySelectorAll('.el-autocomplete-suggestion__list li');
    
            let highlightItem = suggestionList[index];
            let scrollTop = suggestion.scrollTop;
            let offsetTop = highlightItem.offsetTop;
    
            if (offsetTop + highlightItem.scrollHeight > (scrollTop + suggestion.clientHeight)) {
              suggestion.scrollTop += highlightItem.scrollHeight;
            }
            if (offsetTop < scrollTop) {
              suggestion.scrollTop -= highlightItem.scrollHeight;
            }
            this.highlightedIndex = index;
            let $input = this.getInput();
            $input.setAttribute('aria-activedescendant', `${this.id}-item-${this.highlightedIndex}`);
          },
          getInput() {
            return this.$refs.input.getInput();
          }
        },
        mounted() {
          this.debouncedGetData = debounce(this.debounce, this.getData);
          this.$on('item-click', item => {
            this.select(item);
          });
          let $input = this.getInput();
          $input.setAttribute('role', 'textbox');
          $input.setAttribute('aria-autocomplete', 'list');
          $input.setAttribute('aria-controls', 'id');
          $input.setAttribute('aria-activedescendant', `${this.id}-item-${this.highlightedIndex}`);
        },
        beforeDestroy() {
          this.$refs.suggestions.$destroy();
        }
      };
    </script>

    autocomplete/src/autocomplete-suggestion.vue

    <template>
      <transition name="el-zoom-in-top" @after-leave="doDestroy">
        <div
          v-show="showPopper"
          class="el-autocomplete-suggestion el-popper"
          :class="{ 'is-loading': !parent.hideLoading && parent.loading }"
          :style="{  dropdownWidth }"
          role="region">
          <el-scrollbar
            tag="ul"
            wrap-class="el-autocomplete-suggestion__wrap"
            view-class="el-autocomplete-suggestion__list">
            <li v-if="!parent.hideLoading && parent.loading"><i class="el-icon-loading"></i></li>
            <slot v-else>
            </slot>
          </el-scrollbar>
        </div>
      </transition>
    </template>
    <script>
      import Popper from 'element-ui/src/utils/vue-popper';
      import Emitter from 'element-ui/src/mixins/emitter';
      import ElScrollbar from 'element-ui/packages/scrollbar';
    
      export default {
        components: { ElScrollbar },
        mixins: [Popper, Emitter],
    
        componentName: 'ElAutocompleteSuggestions',
    
        data() {
          return {
            parent: this.$parent,
            dropdownWidth: ''
          };
        },
    
        props: {
          options: {
            default() {
              return {
                gpuAcceleration: false
              };
            }
          },
          id: String
        },
    
        methods: {
          select(item) {
            this.dispatch('ElAutocomplete', 'item-click', item);
          }
        },
    
        updated() {
          this.$nextTick(_ => {
            this.popperJS && this.updatePopper();
          });
        },
    
        mounted() {
          this.$parent.popperElm = this.popperElm = this.$el;
          this.referenceElm = this.$parent.$refs.input.$refs.input;
          this.referenceList = this.$el.querySelector('.el-autocomplete-suggestion__list');
          this.referenceList.setAttribute('role', 'listbox');
          this.referenceList.setAttribute('id', this.id);
        },
    
        created() {
          this.$on('visible', (val, inputWidth) => {
            this.dropdownWidth = inputWidth + 'px';
            this.showPopper = val;
          });
        }
      };
    </script>
  • 相关阅读:
    error和exception有什么区别?
    金额转换,阿拉伯数字的金额转换成中国传统的形式如:(¥1011)->(一千零一拾一元整)输出?
    HTTP请求的GET与POST方式的区别
    解释一下什么是servlet?
    参数Parameters、变量Variables
    数据库事务的四大隔离级别以及处理的问题
    redis安装
    CVB生命周期(APIView源码解析)
    前端页面渲染机制
    Django基础之request
  • 原文地址:https://www.cnblogs.com/wsk1576025821/p/10912937.html
Copyright © 2020-2023  润新知