md-iview 1.0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. package/README.md +2 -0
  2. package/package.json +116 -0
  3. package/src/components/libs/util.js +117 -0
  4. package/src/components/md-error-page/403.less +92 -0
  5. package/src/components/md-error-page/403.vue +34 -0
  6. package/src/components/md-error-page/404.less +60 -0
  7. package/src/components/md-error-page/404.vue +34 -0
  8. package/src/components/md-error-page/500.less +73 -0
  9. package/src/components/md-error-page/500.vue +36 -0
  10. package/src/components/md-error-page/demo/index.less +22 -0
  11. package/src/components/md-error-page/demo/index.vue +97 -0
  12. package/src/components/md-form-item/index.js +3 -0
  13. package/src/components/md-icon/icon.vue +77 -0
  14. package/src/components/md-icon/icons.js +5 -0
  15. package/src/components/md-icon/index.js +11 -0
  16. package/src/components/md-icon/style/index.less +1 -0
  17. package/src/components/md-loading/index.js +55 -0
  18. package/src/components/md-loading/index.vue +53 -0
  19. package/src/components/md-loading/md-loading.js +38 -0
  20. package/src/components/md-print/demo/index.vue +260 -0
  21. package/src/components/md-print/index.js +123 -0
  22. package/src/components/md-rich-editor/index.vue +69 -0
  23. package/src/components/md-rich-editor/module/image-extend/index.js +216 -0
  24. package/src/components/md-scroll-bar/demo/index.vue +102 -0
  25. package/src/components/md-scroll-bar/index.js +3 -0
  26. package/src/components/md-scroll-bar/index.less +90 -0
  27. package/src/components/md-scroll-bar/index.vue +250 -0
  28. package/src/components/md-select/index.js +7 -0
  29. package/src/components/md-select/select.vue +841 -0
  30. package/src/components/md-shrinkable-menu/components/sidebarMenu.vue +167 -0
  31. package/src/components/md-shrinkable-menu/components/sidebarMenuShrink.vue +119 -0
  32. package/src/components/md-shrinkable-menu/demo/data/cachePage.js +1 -0
  33. package/src/components/md-shrinkable-menu/demo/data/currentPath.js +9 -0
  34. package/src/components/md-shrinkable-menu/demo/data/menu.js +575 -0
  35. package/src/components/md-shrinkable-menu/demo/data/menu2.js +1017 -0
  36. package/src/components/md-shrinkable-menu/demo/data/pageTagsList.js +153 -0
  37. package/src/components/md-shrinkable-menu/demo/index.less +297 -0
  38. package/src/components/md-shrinkable-menu/demo/index.vue +285 -0
  39. package/src/components/md-shrinkable-menu/index.vue +112 -0
  40. package/src/components/md-shrinkable-menu/sidebar.vue +195 -0
  41. package/src/components/md-shrinkable-menu/styles/menu.less +5 -0
  42. package/src/components/md-shrinkable-menu/styles/sidebar.less +363 -0
  43. package/src/components/md-split-pane/demo/index.vue +101 -0
  44. package/src/components/md-split-pane/index.js +3 -0
  45. package/src/components/md-split-pane/index.less +93 -0
  46. package/src/components/md-split-pane/index.vue +230 -0
  47. package/src/components/md-table/action-tooltip.vue +45 -0
  48. package/src/components/md-table/can-edit-v2.vue +823 -0
  49. package/src/components/md-table/can-edit.vue +723 -0
  50. package/src/components/md-table/custom-cell.vue +71 -0
  51. package/src/components/md-table/date-picker-cell-v2.vue +48 -0
  52. package/src/components/md-table/date-picker-cell.vue +39 -0
  53. package/src/components/md-table/demo/data/search.js +67 -0
  54. package/src/components/md-table/demo/data/table2csv.js +200 -0
  55. package/src/components/md-table/demo/data/table2excel.js +239 -0
  56. package/src/components/md-table/demo/data/table_data.js +251 -0
  57. package/src/components/md-table/demo/editable-table.vue +144 -0
  58. package/src/components/md-table/demo/exportable-table.vue +124 -0
  59. package/src/components/md-table/demo/widgets/header-search.vue +88 -0
  60. package/src/components/md-table/drop-down-cell-v2.vue +87 -0
  61. package/src/components/md-table/drop-down-cell.vue +81 -0
  62. package/src/components/md-table/editable-expand.vue +143 -0
  63. package/src/components/md-table/expand.vue +97 -0
  64. package/src/components/md-table/index.vue +53 -0
  65. package/src/components/md-table/iview-table/cell.vue +99 -0
  66. package/src/components/md-table/iview-table/expand.js +21 -0
  67. package/src/components/md-table/iview-table/export-csv.js +76 -0
  68. package/src/components/md-table/iview-table/header.js +16 -0
  69. package/src/components/md-table/iview-table/index.js +2 -0
  70. package/src/components/md-table/iview-table/mixin.js +31 -0
  71. package/src/components/md-table/iview-table/table-body.vue +101 -0
  72. package/src/components/md-table/iview-table/table-head.vue +311 -0
  73. package/src/components/md-table/iview-table/table-tr.vue +31 -0
  74. package/src/components/md-table/iview-table/table.vue +1026 -0
  75. package/src/components/md-table/iview-table/util.js +93 -0
  76. package/src/components/md-table/libs/table2excel.js +100 -0
  77. package/src/components/md-table/select-cell-v2.vue +64 -0
  78. package/src/components/md-table/select-cell.vue +46 -0
  79. package/src/components/md-table/table.less +76 -0
  80. package/src/components/md-toolbar/index.vue +171 -0
  81. package/src/components/md-tree/index.js +2 -0
  82. package/src/components/md-tree/node.vue +238 -0
  83. package/src/components/md-tree/render.js +17 -0
  84. package/src/components/md-tree/tree.vue +241 -0
  85. package/src/components/utilities/can.js +35 -0
  86. package/src/directives/index.js +34 -0
  87. package/src/directives/resize.js +27 -0
  88. package/src/directives/scroll.js +27 -0
  89. package/src/directives/style/bg-color.js +23 -0
  90. package/src/directives/style/color.js +23 -0
  91. package/src/directives/style/font-size.js +23 -0
  92. package/src/directives/style/height.js +23 -0
  93. package/src/directives/style/lineHeight.js +23 -0
  94. package/src/directives/style/margin.js +48 -0
  95. package/src/directives/style/opacity.js +23 -0
  96. package/src/directives/style/padding.js +48 -0
  97. package/src/directives/style/width.js +24 -0
  98. package/src/index.js +442 -0
  99. package/src/locale/lang.js +5 -0
  100. package/src/mixins/colorable.js +51 -0
  101. package/src/style/color/bezierEasing.less +110 -0
  102. package/src/style/color/colorPalette.less +75 -0
  103. package/src/style/color/colors.less +146 -0
  104. package/src/style/color/tinyColor.less +1184 -0
  105. package/src/style/common.less +72 -0
  106. package/src/style/components/_ripple.less +60 -0
  107. package/src/style/components/_shrinkable-menu.less +46 -0
  108. package/src/style/components/_toolbar.less +96 -0
  109. package/src/style/components/index.less +3 -0
  110. package/src/style/components/rich-editor.less +6 -0
  111. package/src/style/index.less +10 -0
  112. package/src/style/theme.less +155 -0
  113. package/src/utils/color.js +46 -0
  114. package/src/utils/console.js +105 -0
  115. package/src/utils/load.js +79 -0
  116. package/src/utils/mask.js +139 -0
  117. package/src/utils/mixins.js +5 -0
  118. package/src/utils/validate.js +271 -0
@@ -0,0 +1,841 @@
1
+ <template>
2
+ <div
3
+ :class="classes"
4
+ v-click-outside.capture="onClickOutside"
5
+ v-click-outside:mousedown.capture="onClickOutside"
6
+ v-click-outside:touchstart.capture="onClickOutside"
7
+ >
8
+ <div
9
+ ref="reference"
10
+
11
+ :class="selectionCls"
12
+ :tabindex="selectTabindex"
13
+
14
+ @blur="toggleHeaderFocus"
15
+ @focus="toggleHeaderFocus"
16
+
17
+ @click="toggleMenu"
18
+ @keydown.esc="handleKeydown"
19
+ @keydown.enter="handleKeydown"
20
+ @keydown.up.prevent="handleKeydown"
21
+ @keydown.down.prevent="handleKeydown"
22
+ @keydown.tab="handleKeydown"
23
+ @keydown.delete="handleKeydown"
24
+
25
+
26
+ @mouseenter="hasMouseHoverHead = true"
27
+ @mouseleave="hasMouseHoverHead = false"
28
+
29
+ >
30
+ <slot name="input">
31
+ <input type="hidden" :name="name" :value="publicValue">
32
+ <select-head
33
+ :filterable="filterable"
34
+ :multiple="multiple"
35
+ :values="values"
36
+ :clearable="canBeCleared"
37
+ :prefix="prefix"
38
+ :disabled="disabled"
39
+ :remote="remote"
40
+ :input-element-id="elementId"
41
+ :initial-label="initialLabel"
42
+ :placeholder="localePlaceholder"
43
+ :query-prop="query"
44
+ :max-tag-count="maxTagCount"
45
+ :max-tag-placeholder="maxTagPlaceholder"
46
+
47
+ @on-query-change="onQueryChange"
48
+ @on-input-focus="isFocused = true"
49
+ @on-input-blur="isFocused = false"
50
+ @on-clear="clearSingleSelect"
51
+ >
52
+ <slot name="prefix" slot="prefix"></slot>
53
+ </select-head>
54
+ </slot>
55
+ </div>
56
+ <transition name="transition-drop">
57
+ <Drop
58
+ :class="dropdownCls"
59
+ v-show="dropVisible"
60
+ :placement="placement"
61
+ ref="dropdown"
62
+ :data-transfer="transfer"
63
+ :transfer="transfer"
64
+ v-transfer-dom
65
+ >
66
+ <ul v-show="showNotFoundLabel" :class="[prefixCls + '-not-found']"><li>{{ localeNotFoundText }}</li></ul>
67
+ <ul :class="prefixCls + '-dropdown-list'">
68
+ <functional-options
69
+ v-if="(!remote) || (remote && !loading)"
70
+ :options="selectOptions"
71
+ :slot-update-hook="updateSlotOptions"
72
+ :slot-options="slotOptions"
73
+ ></functional-options>
74
+ </ul>
75
+ <ul v-show="loading" :class="[prefixCls + '-loading']">{{ localeLoadingText }}</ul>
76
+ </Drop>
77
+ </transition>
78
+ </div>
79
+ </template>
80
+ <script>
81
+ import Drop from 'iview/src/components/select/dropdown';
82
+ import {directive as clickOutside} from 'v-click-outside-x';
83
+ import TransferDom from 'iview/src/directives/transfer-dom';
84
+ import { oneOf } from 'iview/src/utils/assist';
85
+ import Emitter from 'iview/src/mixins/emitter';
86
+ import Locale from 'iview/src/mixins/locale';
87
+ import SelectHead from 'iview/src/components/select/select-head.vue';
88
+ import FunctionalOptions from 'iview/src/components/select/functional-options.vue';
89
+
90
+ const prefixCls = 'ivu-select';
91
+ const optionRegexp = /^i-option$|^Option$/i;
92
+ const optionGroupRegexp = /option-?group/i;
93
+
94
+ const findChild = (instance, checkFn) => {
95
+ let match = checkFn(instance);
96
+ if (match) return instance;
97
+ for (let i = 0, l = instance.$children.length; i < l; i++){
98
+ const child = instance.$children[i];
99
+ match = findChild(child, checkFn);
100
+ if (match) return match;
101
+ }
102
+ };
103
+
104
+ const findOptionsInVNode = (node) => {
105
+ const opts = node.componentOptions;
106
+ if (opts && opts.tag.match(optionRegexp)) return [node];
107
+ if (!node.children && (!opts || !opts.children)) return [];
108
+ const children = [...(node.children || []), ...(opts && opts.children || [])];
109
+ const options = children.reduce(
110
+ (arr, el) => [...arr, ...findOptionsInVNode(el)], []
111
+ ).filter(Boolean);
112
+ return options.length > 0 ? options : [];
113
+ };
114
+
115
+ const extractOptions = (options) => options.reduce((options, slotEntry) => {
116
+ return options.concat(findOptionsInVNode(slotEntry));
117
+ }, []);
118
+
119
+ const applyProp = (node, propName, value) => {
120
+ return {
121
+ ...node,
122
+ componentOptions: {
123
+ ...node.componentOptions,
124
+ propsData: {
125
+ ...node.componentOptions.propsData,
126
+ [propName]: value,
127
+ }
128
+ }
129
+ };
130
+ };
131
+
132
+ const getNestedProperty = (obj, path) => {
133
+ const keys = path.split('.');
134
+ return keys.reduce((o, key) => o && o[key] || null, obj);
135
+ };
136
+
137
+ const getOptionLabel = option => {
138
+ if (option.componentOptions.propsData.label) return option.componentOptions.propsData.label;
139
+ const textContent = (option.componentOptions.children || []).reduce((str, child) => str + (child.text || ''), '');
140
+ const innerHTML = getNestedProperty(option, 'data.domProps.innerHTML');
141
+ return textContent || (typeof innerHTML === 'string' ? innerHTML : '');
142
+ };
143
+
144
+ const checkValuesNotEqual = (value,publicValue,values) => {
145
+
146
+ const strValue = JSON.stringify(value);
147
+ const strPublic = JSON.stringify(publicValue);
148
+ const strValues = JSON.stringify(values.map( item => {
149
+ return item.value;
150
+ }));
151
+
152
+ // console.log(strValue, strPublic, strValues);
153
+
154
+ return strValue !== strPublic || strValue !== strValues || strValues !== strPublic;
155
+ };
156
+
157
+
158
+ const ANIMATION_TIMEOUT = 300;
159
+
160
+ export default {
161
+ name: 'iSelect',
162
+ mixins: [ Emitter, Locale ],
163
+ components: { FunctionalOptions, Drop, SelectHead },
164
+ directives: { clickOutside, TransferDom },
165
+ props: {
166
+ value: {
167
+ type: [String, Number, Array],
168
+ default: ''
169
+ },
170
+ // 使用时,也得设置 value 才行
171
+ label: {
172
+ type: [String, Number, Array],
173
+ default: ''
174
+ },
175
+ multiple: {
176
+ type: Boolean,
177
+ default: false
178
+ },
179
+ disabled: {
180
+ type: Boolean,
181
+ default: false
182
+ },
183
+ clearable: {
184
+ type: Boolean,
185
+ default: false
186
+ },
187
+ placeholder: {
188
+ type: String
189
+ },
190
+ filterable: {
191
+ type: Boolean,
192
+ default: false
193
+ },
194
+ filterMethod: {
195
+ type: Function
196
+ },
197
+ remoteMethod: {
198
+ type: Function
199
+ },
200
+ loading: {
201
+ type: Boolean,
202
+ default: false
203
+ },
204
+ loadingText: {
205
+ type: String
206
+ },
207
+ size: {
208
+ validator (value) {
209
+ return oneOf(value, ['small', 'large', 'default']);
210
+ },
211
+ default () {
212
+ return !this.$IVIEW || this.$IVIEW.size === '' ? 'default' : this.$IVIEW.size;
213
+ }
214
+ },
215
+ labelInValue: {
216
+ type: Boolean,
217
+ default: false
218
+ },
219
+ notFoundText: {
220
+ type: String
221
+ },
222
+ placement: {
223
+ validator (value) {
224
+ return oneOf(value, ['top', 'bottom', 'top-start', 'bottom-start', 'top-end', 'bottom-end']);
225
+ },
226
+ default: 'bottom-start'
227
+ },
228
+ transfer: {
229
+ type: Boolean,
230
+ default () {
231
+ return !this.$IVIEW || this.$IVIEW.transfer === '' ? false : this.$IVIEW.transfer;
232
+ }
233
+ },
234
+ // Use for AutoComplete
235
+ autoComplete: {
236
+ type: Boolean,
237
+ default: false
238
+ },
239
+ name: {
240
+ type: String
241
+ },
242
+ elementId: {
243
+ type: String
244
+ },
245
+ transferClassName: {
246
+ type: String
247
+ },
248
+ // 3.4.0
249
+ prefix: {
250
+ type: String
251
+ },
252
+ // 3.4.0
253
+ maxTagCount: {
254
+ type: Number
255
+ },
256
+ // 3.4.0
257
+ maxTagPlaceholder: {
258
+ type: Function
259
+ }
260
+ },
261
+ mounted(){
262
+ this.$on('on-select-selected', this.onOptionClick);
263
+
264
+ // set the initial values if there are any
265
+ if (!this.remote && this.selectOptions.length > 0){
266
+ this.values = this.getInitialValue().map(value => {
267
+ if (typeof value !== 'number' && !value) return null;
268
+ return this.getOptionData(value);
269
+ }).filter(Boolean);
270
+ }
271
+
272
+ this.checkUpdateStatus();
273
+ },
274
+ data () {
275
+
276
+ return {
277
+ prefixCls: prefixCls,
278
+ values: [],
279
+ dropDownWidth: 0,
280
+ visible: false,
281
+ focusIndex: -1,
282
+ isFocused: false,
283
+ query: '',
284
+ initialLabel: this.label,
285
+ hasMouseHoverHead: false,
286
+ slotOptions: this.$slots.default,
287
+ caretPosition: -1,
288
+ lastRemoteQuery: '',
289
+ unchangedQuery: true,
290
+ hasExpectedValue: false,
291
+ preventRemoteCall: false,
292
+ filterQueryChange: false, // #4273
293
+ };
294
+ },
295
+ computed: {
296
+ classes () {
297
+ return [
298
+ `${prefixCls}`,
299
+ {
300
+ [`${prefixCls}-visible`]: this.visible,
301
+ [`${prefixCls}-disabled`]: this.disabled,
302
+ [`${prefixCls}-multiple`]: this.multiple,
303
+ [`${prefixCls}-single`]: !this.multiple,
304
+ [`${prefixCls}-show-clear`]: this.showCloseIcon,
305
+ [`${prefixCls}-${this.size}`]: !!this.size
306
+ }
307
+ ];
308
+ },
309
+ dropdownCls () {
310
+ return {
311
+ [prefixCls + '-dropdown-transfer']: this.transfer,
312
+ [prefixCls + '-multiple']: this.multiple && this.transfer,
313
+ ['ivu-auto-complete']: this.autoComplete,
314
+ [this.transferClassName]: this.transferClassName
315
+ };
316
+ },
317
+ selectionCls () {
318
+ return {
319
+ [`${prefixCls}-selection`]: !this.autoComplete,
320
+ [`${prefixCls}-selection-focused`]: this.isFocused
321
+ };
322
+ },
323
+ localePlaceholder() {
324
+ if (typeof this.placeholder === 'undefined') {
325
+ return this.t('i.select.placeholder');
326
+ } else {
327
+ return this.placeholder;
328
+ }
329
+ },
330
+ localeNotFoundText () {
331
+ if (typeof this.notFoundText === 'undefined') {
332
+ return this.t('i.select.noMatch');
333
+ } else {
334
+ return this.notFoundText;
335
+ }
336
+ },
337
+ localeLoadingText () {
338
+ if (typeof this.loadingText === 'undefined') {
339
+ return this.t('i.select.loading');
340
+ } else {
341
+ return this.loadingText;
342
+ }
343
+ },
344
+ transitionName () {
345
+ return this.placement === 'bottom' ? 'slide-up' : 'slide-down';
346
+ },
347
+ dropVisible () {
348
+ let status = true;
349
+ const noOptions = !this.selectOptions || this.selectOptions.length === 0;
350
+ if (!this.loading && this.remote && this.query === '' && noOptions) status = false;
351
+
352
+ if (this.autoComplete && noOptions) status = false;
353
+
354
+ return this.visible && status;
355
+ },
356
+ showNotFoundLabel () {
357
+ const {loading, remote, selectOptions} = this;
358
+ return selectOptions && selectOptions.length === 0 && (!remote || (remote && !loading));
359
+ },
360
+ publicValue(){
361
+ if (this.labelInValue){
362
+ return this.multiple ? this.values : this.values[0];
363
+ } else {
364
+ return this.multiple ? this.values.map(option => option.value) : (this.values[0] || {}).value;
365
+ }
366
+ },
367
+ canBeCleared(){
368
+ const uiStateMatch = this.hasMouseHoverHead || this.active;
369
+ const qualifiesForClear = !this.multiple && !this.disabled && this.clearable;
370
+ return uiStateMatch && qualifiesForClear && this.reset; // we return a function
371
+ },
372
+ selectOptions() {
373
+ const selectOptions = [];
374
+ const slotOptions = (this.slotOptions || []);
375
+ let optionCounter = -1;
376
+ const currentIndex = this.focusIndex;
377
+ const selectedValues = this.values.filter(Boolean).map(({value}) => value);
378
+ if (this.autoComplete) {
379
+ const copyChildren = (node, fn) => {
380
+ return {
381
+ ...node,
382
+ children: (node.children || []).map(fn).map(child => copyChildren(child, fn))
383
+ };
384
+ };
385
+ const autoCompleteOptions = extractOptions(slotOptions);
386
+ const selectedSlotOption = autoCompleteOptions[currentIndex];
387
+
388
+ return slotOptions.map(node => {
389
+ if (node === selectedSlotOption || getNestedProperty(node, 'componentOptions.propsData.value') === this.value) return applyProp(node, 'isFocused', true);
390
+ return copyChildren(node, (child) => {
391
+ if (child !== selectedSlotOption) return child;
392
+ return applyProp(child, 'isFocused', true);
393
+ });
394
+ });
395
+ }
396
+ for (let option of slotOptions) {
397
+
398
+ const cOptions = option.componentOptions;
399
+ if (!cOptions) continue;
400
+ if (cOptions.tag.match(optionGroupRegexp)){
401
+ let children = cOptions.children;
402
+
403
+ // remove filtered children
404
+ if (this.filterable){
405
+ children = children.filter(
406
+ ({componentOptions}) => this.validateOption(componentOptions)
407
+ );
408
+ }
409
+
410
+ // fix #4371
411
+ children = children.map(opt => {
412
+ optionCounter = optionCounter + 1;
413
+ return this.processOption(opt, selectedValues, optionCounter === currentIndex);
414
+ });
415
+
416
+ // keep the group if it still has children // fix #4371
417
+ if (children.length > 0) selectOptions.push({...option,componentOptions:{...cOptions,children:children}});
418
+ } else {
419
+ // ignore option if not passing filter
420
+ if (this.filterQueryChange) {
421
+ const optionPassesFilter = this.filterable ? this.validateOption(cOptions) : option;
422
+ if (!optionPassesFilter) continue;
423
+ }
424
+
425
+ optionCounter = optionCounter + 1;
426
+ selectOptions.push(this.processOption(option, selectedValues, optionCounter === currentIndex));
427
+ }
428
+ }
429
+
430
+ return selectOptions;
431
+ },
432
+ flatOptions(){
433
+ return extractOptions(this.selectOptions);
434
+ },
435
+ selectTabindex(){
436
+ return this.disabled || this.filterable ? -1 : 0;
437
+ },
438
+ remote(){
439
+ return typeof this.remoteMethod === 'function';
440
+ }
441
+ },
442
+ methods: {
443
+ setQuery(query){ // PUBLIC API
444
+ if (query) {
445
+ this.onQueryChange(query);
446
+ return;
447
+ }
448
+ if (query === null) {
449
+ this.onQueryChange('');
450
+ this.values = [];
451
+ // #5620,修复清空搜索关键词后,重新搜索相同的关键词没有触发远程搜索
452
+ this.lastRemoteQuery = '';
453
+ }
454
+ },
455
+ clearSingleSelect(){ // PUBLIC API
456
+ this.$emit('on-clear');
457
+ this.hideMenu();
458
+ if (this.clearable) this.reset();
459
+ },
460
+ getOptionData(value){
461
+ const option = this.flatOptions.find(({componentOptions}) => componentOptions.propsData.value === value);
462
+ if (!option) return null;
463
+ const label = getOptionLabel(option);
464
+ return {
465
+ value: value,
466
+ label: label,
467
+ };
468
+ },
469
+ getInitialValue(){
470
+ const {multiple, remote, value} = this;
471
+ let initialValue = Array.isArray(value) ? value : [value];
472
+ if (!multiple && (typeof initialValue[0] === 'undefined' || (String(initialValue[0]).trim() === '' && !Number.isFinite(initialValue[0])))) initialValue = [];
473
+ if (remote && !multiple && value) {
474
+ const data = this.getOptionData(value);
475
+ this.query = data ? data.label : String(value);
476
+ }
477
+ return initialValue.filter((item) => {
478
+ return Boolean(item) || item === 0;
479
+ });
480
+ },
481
+ processOption(option, values, isFocused){
482
+ if (!option.componentOptions) return option;
483
+ const optionValue = option.componentOptions.propsData.value;
484
+ const disabled = option.componentOptions.propsData.disabled;
485
+ const isSelected = values.includes(optionValue);
486
+
487
+ const propsData = {
488
+ ...option.componentOptions.propsData,
489
+ selected: isSelected,
490
+ isFocused: isFocused,
491
+ disabled: typeof disabled === 'undefined' ? false : disabled !== false,
492
+ };
493
+
494
+ return {
495
+ ...option,
496
+ componentOptions: {
497
+ ...option.componentOptions,
498
+ propsData: propsData
499
+ }
500
+ };
501
+ },
502
+
503
+ validateOption({children, elm, propsData}){
504
+ const value = propsData.value;
505
+ const label = propsData.label || '';
506
+ const textContent = (elm && elm.textContent) || (children || []).reduce((str, node) => {
507
+ const nodeText = node.elm ? node.elm.textContent : node.text;
508
+ return `${str} ${nodeText}`;
509
+ }, '') || '';
510
+ const stringValues = JSON.stringify([value, label, textContent]);
511
+ const query = this.query.toLowerCase().trim();
512
+ return stringValues.toLowerCase().includes(query);
513
+ },
514
+
515
+ toggleMenu (e, force) {
516
+ if (this.disabled) {
517
+ return false;
518
+ }
519
+
520
+ this.visible = typeof force !== 'undefined' ? force : !this.visible;
521
+ if (this.visible){
522
+ this.dropDownWidth = this.$el.getBoundingClientRect().width;
523
+ this.broadcast('Drop', 'on-update-popper');
524
+ }
525
+ },
526
+ hideMenu () {
527
+ this.toggleMenu(null, false);
528
+ setTimeout(() => this.unchangedQuery = true, ANIMATION_TIMEOUT);
529
+ },
530
+ onClickOutside(event){
531
+ if (this.visible) {
532
+ if (event.type === 'mousedown') {
533
+ event.preventDefault();
534
+ return;
535
+ }
536
+
537
+ if (this.transfer) {
538
+ const {$el} = this.$refs.dropdown;
539
+ if ($el === event.target || $el.contains(event.target)) {
540
+ return;
541
+ }
542
+ }
543
+
544
+
545
+ if (this.filterable) {
546
+ const input = this.$el.querySelector('input[type="text"]');
547
+ this.caretPosition = input.selectionStart;
548
+ this.$nextTick(() => {
549
+ const caretPosition = this.caretPosition === -1 ? input.value.length : this.caretPosition;
550
+ input.setSelectionRange(caretPosition, caretPosition);
551
+ });
552
+ }
553
+
554
+ if (!this.autoComplete) event.stopPropagation();
555
+ event.preventDefault();
556
+ this.hideMenu();
557
+ this.isFocused = true;
558
+ } else {
559
+ this.caretPosition = -1;
560
+ this.isFocused = false;
561
+ }
562
+ },
563
+ reset(){
564
+ this.query = '';
565
+ this.focusIndex = -1;
566
+ this.unchangedQuery = true;
567
+ this.values = [];
568
+ this.filterQueryChange = false;
569
+ },
570
+ handleKeydown (e) {
571
+ if (e.key === 'Backspace'){
572
+ return; // so we don't call preventDefault
573
+ }
574
+
575
+ if (this.visible) {
576
+ e.preventDefault();
577
+ if (e.key === 'Tab'){
578
+ e.stopPropagation();
579
+ }
580
+
581
+ // Esc slide-up
582
+ if (e.key === 'Escape') {
583
+ e.stopPropagation();
584
+ this.hideMenu();
585
+ }
586
+ // next
587
+ if (e.key === 'ArrowUp') {
588
+ this.navigateOptions(-1);
589
+ }
590
+ // prev
591
+ if (e.key === 'ArrowDown') {
592
+ this.navigateOptions(1);
593
+ }
594
+ // enter
595
+ if (e.key === 'Enter') {
596
+ if (this.focusIndex === -1) return this.hideMenu();
597
+ const optionComponent = this.flatOptions[this.focusIndex];
598
+
599
+ // fix a script error when searching
600
+ if (optionComponent) {
601
+ const option = this.getOptionData(optionComponent.componentOptions.propsData.value);
602
+ this.onOptionClick(option);
603
+ } else {
604
+ this.hideMenu();
605
+ }
606
+ }
607
+ } else {
608
+ const keysThatCanOpenSelect = ['ArrowUp', 'ArrowDown'];
609
+ if (keysThatCanOpenSelect.includes(e.key)) this.toggleMenu(null, true);
610
+ }
611
+
612
+
613
+ },
614
+ navigateOptions(direction){
615
+ const optionsLength = this.flatOptions.length - 1;
616
+
617
+ let index = this.focusIndex + direction;
618
+ if (index < 0) index = optionsLength;
619
+ if (index > optionsLength) index = 0;
620
+
621
+ // find nearest option in case of disabled options in between
622
+ if (direction > 0){
623
+ let nearestActiveOption = -1;
624
+ for (let i = 0; i < this.flatOptions.length; i++){
625
+ const optionIsActive = !this.flatOptions[i].componentOptions.propsData.disabled;
626
+ if (optionIsActive) nearestActiveOption = i;
627
+ if (nearestActiveOption >= index) break;
628
+ }
629
+ index = nearestActiveOption;
630
+ } else {
631
+ let nearestActiveOption = this.flatOptions.length;
632
+ for (let i = optionsLength; i >= 0; i--){
633
+ const optionIsActive = !this.flatOptions[i].componentOptions.propsData.disabled;
634
+ if (optionIsActive) nearestActiveOption = i;
635
+ if (nearestActiveOption <= index) break;
636
+ }
637
+ index = nearestActiveOption;
638
+ }
639
+
640
+ this.focusIndex = index;
641
+ },
642
+ onOptionClick(option) {
643
+ if (this.multiple){
644
+
645
+ // keep the query for remote select
646
+ if (this.remote) this.lastRemoteQuery = this.lastRemoteQuery || this.query;
647
+ else this.lastRemoteQuery = '';
648
+
649
+ const valueIsSelected = this.values.find(({value}) => value === option.value);
650
+ if (valueIsSelected){
651
+ this.values = this.values.filter(({value}) => value !== option.value);
652
+ } else {
653
+ this.values = this.values.concat(option);
654
+ }
655
+
656
+ this.isFocused = true; // so we put back focus after clicking with mouse on option elements
657
+ } else {
658
+ this.query = String(option.label).trim();
659
+ this.values = [option];
660
+ this.lastRemoteQuery = '';
661
+ this.hideMenu();
662
+ }
663
+
664
+ this.focusIndex = this.flatOptions.findIndex((opt) => {
665
+ if (!opt || !opt.componentOptions) return false;
666
+ return opt.componentOptions.propsData.value === option.value;
667
+ });
668
+
669
+ if (this.filterable){
670
+ const inputField = this.$el.querySelector('input[type="text"]');
671
+ if (!this.autoComplete) this.$nextTick(() => inputField.focus());
672
+ }
673
+ this.broadcast('Drop', 'on-update-popper');
674
+ setTimeout(() => {
675
+ this.filterQueryChange = false;
676
+ }, ANIMATION_TIMEOUT);
677
+ },
678
+ onQueryChange(query) {
679
+ if (query.length > 0 && query !== this.query) {
680
+ // in 'AutoComplete', when set an initial value asynchronously,
681
+ // the 'dropdown list' should be stay hidden.
682
+ // [issue #5150]
683
+ if (this.autoComplete) {
684
+ let isInputFocused =
685
+ document.hasFocus &&
686
+ document.hasFocus() &&
687
+ document.activeElement === this.$el.querySelector('input');
688
+ this.visible = isInputFocused;
689
+ } else {
690
+ this.visible = true;
691
+ }
692
+ }
693
+
694
+ this.query = query;
695
+ this.unchangedQuery = this.visible;
696
+ this.filterQueryChange = true;
697
+ },
698
+ toggleHeaderFocus({type}){
699
+ if (this.disabled) {
700
+ return;
701
+ }
702
+ this.isFocused = type === 'focus';
703
+ },
704
+ updateSlotOptions(){
705
+ this.slotOptions = this.$slots.default;
706
+ },
707
+ checkUpdateStatus() {
708
+ if (this.getInitialValue().length > 0 && this.selectOptions.length === 0) {
709
+ this.hasExpectedValue = true;
710
+ }
711
+ },
712
+ },
713
+ watch: {
714
+ value(value){
715
+ const {getInitialValue, getOptionData, publicValue, values} = this;
716
+ this.checkUpdateStatus();
717
+
718
+ if (value === '') this.values = [];
719
+ else if (checkValuesNotEqual(value,publicValue,values)) {
720
+ this.$nextTick(() => {
721
+ let filterValue = getInitialValue().map(getOptionData).filter(Boolean);
722
+ if (filterValue.length>0) {
723
+ this.values = filterValue
724
+ }
725
+ // this.values = getInitialValue().map(getOptionData).filter(Boolean)
726
+ });
727
+ this.dispatch('FormItem', 'on-form-change', this.publicValue);
728
+ }
729
+ },
730
+ values(now, before){
731
+
732
+ const newValue = JSON.stringify(now);
733
+ const oldValue = JSON.stringify(before);
734
+ // v-model is always just the value, event with labelInValue === true
735
+ const vModelValue = (this.publicValue && this.labelInValue) ?
736
+ (this.multiple ? this.publicValue.map(({value}) => value) : this.publicValue.value) :
737
+ this.publicValue;
738
+ let shouldEmitInput = newValue !== oldValue && vModelValue !== this.value;
739
+ if (shouldEmitInput) {
740
+ this.$emit('input', vModelValue); // to update v-model
741
+ this.$emit('on-change', this.publicValue);
742
+ this.dispatch('FormItem', 'on-form-change', this.publicValue);
743
+ }
744
+ },
745
+ query (query) {
746
+ this.$emit('on-query-change', query);
747
+ const {remoteMethod, lastRemoteQuery} = this;
748
+ const hasValidQuery = query !== '' && (query !== lastRemoteQuery || !lastRemoteQuery);
749
+ const shouldCallRemoteMethod = remoteMethod && hasValidQuery && !this.preventRemoteCall;
750
+ this.preventRemoteCall = false; // remove the flag
751
+
752
+ if (shouldCallRemoteMethod){
753
+ this.focusIndex = -1;
754
+ const promise = this.remoteMethod(query);
755
+ this.initialLabel = '';
756
+ if (promise && promise.then){
757
+ promise.then(options => {
758
+ if (options) this.options = options;
759
+ });
760
+ }
761
+ }
762
+ if (query !== '' && this.remote) this.lastRemoteQuery = query;
763
+ },
764
+ loading(state){
765
+ if (state === false){
766
+ this.updateSlotOptions();
767
+ }
768
+ },
769
+ isFocused(focused){
770
+ const el = this.filterable ? this.$el.querySelector('input[type="text"]') : this.$el;
771
+ el[this.isFocused ? 'focus' : 'blur']();
772
+
773
+ // restore query value in filterable single selects
774
+ const [selectedOption] = this.values;
775
+ if (selectedOption && this.filterable && !this.multiple && !focused){
776
+ const selectedLabel = String(selectedOption.label || selectedOption.value).trim();
777
+ if (selectedLabel && this.query !== selectedLabel) {
778
+ this.preventRemoteCall = true;
779
+ this.query = selectedLabel;
780
+ }
781
+ }
782
+ },
783
+ focusIndex(index){
784
+ if (index < 0 || this.autoComplete) return;
785
+ // update scroll
786
+ const optionValue = this.flatOptions[index].componentOptions.propsData.value;
787
+ const optionInstance = findChild(this, ({$options}) => {
788
+ return $options.componentName === 'select-item' && $options.propsData.value === optionValue;
789
+ });
790
+
791
+ let bottomOverflowDistance = optionInstance.$el.getBoundingClientRect().bottom - this.$refs.dropdown.$el.getBoundingClientRect().bottom;
792
+ let topOverflowDistance = optionInstance.$el.getBoundingClientRect().top - this.$refs.dropdown.$el.getBoundingClientRect().top;
793
+ if (bottomOverflowDistance > 0) {
794
+ this.$refs.dropdown.$el.scrollTop += bottomOverflowDistance;
795
+ }
796
+ if (topOverflowDistance < 0) {
797
+ this.$refs.dropdown.$el.scrollTop += topOverflowDistance;
798
+ }
799
+ },
800
+ dropVisible(open){
801
+ this.broadcast('Drop', open ? 'on-update-popper' : 'on-destroy-popper');
802
+ },
803
+ selectOptions(){
804
+ if (this.hasExpectedValue && this.selectOptions.length > 0){
805
+ if (this.values.length === 0) {
806
+ this.values = this.getInitialValue();
807
+ }
808
+ this.values = this.values.map(this.getOptionData).filter(Boolean);
809
+ this.hasExpectedValue = false;
810
+ }
811
+
812
+ if (this.slotOptions && this.slotOptions.length === 0){
813
+ this.query = '';
814
+ }
815
+
816
+ // 当 dropdown 一开始在控件下部显示,而滚动页面后变成在上部显示,如果选项列表的长度由内部动态变更了(搜索情况)
817
+ // dropdown 的位置不会重新计算,需要重新计算
818
+ this.broadcast('Drop', 'on-update-popper');
819
+ },
820
+ visible(state){
821
+ this.$emit('on-open-change', state);
822
+ },
823
+ slotOptions(options, old){
824
+ // #4626,当 Options 的 label 更新时,v-model 的值未更新
825
+ // remote 下,调用 getInitialValue 有 bug
826
+ if (!this.remote) {
827
+ const values = this.getInitialValue();
828
+ if (this.flatOptions && this.flatOptions.length && values.length && !this.multiple) {
829
+ this.values = values.map(this.getOptionData).filter(Boolean);
830
+ }
831
+ }
832
+
833
+ // 当 dropdown 在控件上部显示时,如果选项列表的长度由外部动态变更了,
834
+ // dropdown 的位置会有点问题,需要重新计算
835
+ if (options && old && options.length !== old.length) {
836
+ this.broadcast('Drop', 'on-update-popper');
837
+ }
838
+ },
839
+ }
840
+ };
841
+ </script>