md-iview 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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>