项目原始demo,不改动
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

VSelect.js 22 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778
  1. // Styles
  2. import "../../../src/components/VTextField/VTextField.sass";
  3. import "../../../src/components/VSelect/VSelect.sass"; // Components
  4. import VChip from '../VChip';
  5. import VMenu from '../VMenu';
  6. import VSelectList from './VSelectList'; // Extensions
  7. import VInput from '../VInput';
  8. import VTextField from '../VTextField/VTextField'; // Mixins
  9. import Comparable from '../../mixins/comparable';
  10. import Filterable from '../../mixins/filterable'; // Directives
  11. import ClickOutside from '../../directives/click-outside'; // Utilities
  12. import mergeData from '../../util/mergeData';
  13. import { getPropertyFromItem, getObjectValueByPath, keyCodes } from '../../util/helpers';
  14. import { consoleError } from '../../util/console'; // Types
  15. import mixins from '../../util/mixins';
  16. export const defaultMenuProps = {
  17. closeOnClick: false,
  18. closeOnContentClick: false,
  19. disableKeys: true,
  20. openOnClick: false,
  21. maxHeight: 304
  22. }; // Types
  23. const baseMixins = mixins(VTextField, Comparable, Filterable);
  24. /* @vue/component */
  25. export default baseMixins.extend().extend({
  26. name: 'v-select',
  27. directives: {
  28. ClickOutside
  29. },
  30. props: {
  31. appendIcon: {
  32. type: String,
  33. default: '$dropdown'
  34. },
  35. attach: {
  36. type: null,
  37. default: false
  38. },
  39. cacheItems: Boolean,
  40. chips: Boolean,
  41. clearable: Boolean,
  42. deletableChips: Boolean,
  43. disableLookup: Boolean,
  44. eager: Boolean,
  45. hideSelected: Boolean,
  46. items: {
  47. type: Array,
  48. default: () => []
  49. },
  50. itemColor: {
  51. type: String,
  52. default: 'primary'
  53. },
  54. itemDisabled: {
  55. type: [String, Array, Function],
  56. default: 'disabled'
  57. },
  58. itemText: {
  59. type: [String, Array, Function],
  60. default: 'text'
  61. },
  62. itemValue: {
  63. type: [String, Array, Function],
  64. default: 'value'
  65. },
  66. menuProps: {
  67. type: [String, Array, Object],
  68. default: () => defaultMenuProps
  69. },
  70. multiple: Boolean,
  71. openOnClear: Boolean,
  72. returnObject: Boolean,
  73. smallChips: Boolean
  74. },
  75. data() {
  76. return {
  77. cachedItems: this.cacheItems ? this.items : [],
  78. menuIsBooted: false,
  79. isMenuActive: false,
  80. lastItem: 20,
  81. // As long as a value is defined, show it
  82. // Otherwise, check if multiple
  83. // to determine which default to provide
  84. lazyValue: this.value !== undefined ? this.value : this.multiple ? [] : undefined,
  85. selectedIndex: -1,
  86. selectedItems: [],
  87. keyboardLookupPrefix: '',
  88. keyboardLookupLastTime: 0
  89. };
  90. },
  91. computed: {
  92. /* All items that the select has */
  93. allItems() {
  94. return this.filterDuplicates(this.cachedItems.concat(this.items));
  95. },
  96. classes() {
  97. return { ...VTextField.options.computed.classes.call(this),
  98. 'v-select': true,
  99. 'v-select--chips': this.hasChips,
  100. 'v-select--chips--small': this.smallChips,
  101. 'v-select--is-menu-active': this.isMenuActive,
  102. 'v-select--is-multi': this.multiple
  103. };
  104. },
  105. /* Used by other components to overwrite */
  106. computedItems() {
  107. return this.allItems;
  108. },
  109. computedOwns() {
  110. return `list-${this._uid}`;
  111. },
  112. computedCounterValue() {
  113. return this.multiple ? this.selectedItems.length : (this.getText(this.selectedItems[0]) || '').toString().length;
  114. },
  115. directives() {
  116. return this.isFocused ? [{
  117. name: 'click-outside',
  118. value: this.blur,
  119. args: {
  120. closeConditional: this.closeConditional
  121. }
  122. }] : undefined;
  123. },
  124. dynamicHeight() {
  125. return 'auto';
  126. },
  127. hasChips() {
  128. return this.chips || this.smallChips;
  129. },
  130. hasSlot() {
  131. return Boolean(this.hasChips || this.$scopedSlots.selection);
  132. },
  133. isDirty() {
  134. return this.selectedItems.length > 0;
  135. },
  136. listData() {
  137. const scopeId = this.$vnode && this.$vnode.context.$options._scopeId;
  138. const attrs = scopeId ? {
  139. [scopeId]: true
  140. } : {};
  141. return {
  142. attrs: { ...attrs,
  143. id: this.computedOwns
  144. },
  145. props: {
  146. action: this.multiple,
  147. color: this.itemColor,
  148. dense: this.dense,
  149. hideSelected: this.hideSelected,
  150. items: this.virtualizedItems,
  151. itemDisabled: this.itemDisabled,
  152. itemText: this.itemText,
  153. itemValue: this.itemValue,
  154. noDataText: this.$vuetify.lang.t(this.noDataText),
  155. selectedItems: this.selectedItems
  156. },
  157. on: {
  158. select: this.selectItem
  159. },
  160. scopedSlots: {
  161. item: this.$scopedSlots.item
  162. }
  163. };
  164. },
  165. staticList() {
  166. if (this.$slots['no-data'] || this.$slots['prepend-item'] || this.$slots['append-item']) {
  167. consoleError('assert: staticList should not be called if slots are used');
  168. }
  169. return this.$createElement(VSelectList, this.listData);
  170. },
  171. virtualizedItems() {
  172. return this.$_menuProps.auto ? this.computedItems : this.computedItems.slice(0, this.lastItem);
  173. },
  174. menuCanShow: () => true,
  175. $_menuProps() {
  176. let normalisedProps = typeof this.menuProps === 'string' ? this.menuProps.split(',') : this.menuProps;
  177. if (Array.isArray(normalisedProps)) {
  178. normalisedProps = normalisedProps.reduce((acc, p) => {
  179. acc[p.trim()] = true;
  180. return acc;
  181. }, {});
  182. }
  183. return { ...defaultMenuProps,
  184. eager: this.eager,
  185. value: this.menuCanShow && this.isMenuActive,
  186. nudgeBottom: normalisedProps.offsetY ? 1 : 0,
  187. ...normalisedProps
  188. };
  189. }
  190. },
  191. watch: {
  192. internalValue(val) {
  193. this.initialValue = val;
  194. this.setSelectedItems();
  195. },
  196. menuIsBooted() {
  197. window.setTimeout(() => {
  198. if (this.getContent() && this.getContent().addEventListener) {
  199. this.getContent().addEventListener('scroll', this.onScroll, false);
  200. }
  201. });
  202. },
  203. isMenuActive(val) {
  204. window.setTimeout(() => this.onMenuActiveChange(val));
  205. if (!val) return;
  206. this.menuIsBooted = true;
  207. },
  208. items: {
  209. immediate: true,
  210. handler(val) {
  211. if (this.cacheItems) {
  212. // Breaks vue-test-utils if
  213. // this isn't calculated
  214. // on the next tick
  215. this.$nextTick(() => {
  216. this.cachedItems = this.filterDuplicates(this.cachedItems.concat(val));
  217. });
  218. }
  219. this.setSelectedItems();
  220. }
  221. }
  222. },
  223. methods: {
  224. /** @public */
  225. blur(e) {
  226. VTextField.options.methods.blur.call(this, e);
  227. this.isMenuActive = false;
  228. this.isFocused = false;
  229. this.selectedIndex = -1;
  230. },
  231. /** @public */
  232. activateMenu() {
  233. if (this.disabled || this.readonly || this.isMenuActive) return;
  234. this.isMenuActive = true;
  235. },
  236. clearableCallback() {
  237. this.setValue(this.multiple ? [] : undefined);
  238. this.setMenuIndex(-1);
  239. this.$nextTick(() => this.$refs.input && this.$refs.input.focus());
  240. if (this.openOnClear) this.isMenuActive = true;
  241. },
  242. closeConditional(e) {
  243. if (!this.isMenuActive) return true;
  244. return !this._isDestroyed && ( // Click originates from outside the menu content
  245. // Multiple selects don't close when an item is clicked
  246. !this.getContent() || !this.getContent().contains(e.target)) && // Click originates from outside the element
  247. this.$el && !this.$el.contains(e.target) && e.target !== this.$el;
  248. },
  249. filterDuplicates(arr) {
  250. const uniqueValues = new Map();
  251. for (let index = 0; index < arr.length; ++index) {
  252. const item = arr[index];
  253. const val = this.getValue(item); // TODO: comparator
  254. !uniqueValues.has(val) && uniqueValues.set(val, item);
  255. }
  256. return Array.from(uniqueValues.values());
  257. },
  258. findExistingIndex(item) {
  259. const itemValue = this.getValue(item);
  260. return (this.internalValue || []).findIndex(i => this.valueComparator(this.getValue(i), itemValue));
  261. },
  262. getContent() {
  263. return this.$refs.menu && this.$refs.menu.$refs.content;
  264. },
  265. genChipSelection(item, index) {
  266. const isDisabled = this.disabled || this.readonly || this.getDisabled(item);
  267. return this.$createElement(VChip, {
  268. staticClass: 'v-chip--select',
  269. attrs: {
  270. tabindex: -1
  271. },
  272. props: {
  273. close: this.deletableChips && !isDisabled,
  274. disabled: isDisabled,
  275. inputValue: index === this.selectedIndex,
  276. small: this.smallChips
  277. },
  278. on: {
  279. click: e => {
  280. if (isDisabled) return;
  281. e.stopPropagation();
  282. this.selectedIndex = index;
  283. },
  284. 'click:close': () => this.onChipInput(item)
  285. },
  286. key: JSON.stringify(this.getValue(item))
  287. }, this.getText(item));
  288. },
  289. genCommaSelection(item, index, last) {
  290. const color = index === this.selectedIndex && this.computedColor;
  291. const isDisabled = this.disabled || this.getDisabled(item);
  292. return this.$createElement('div', this.setTextColor(color, {
  293. staticClass: 'v-select__selection v-select__selection--comma',
  294. class: {
  295. 'v-select__selection--disabled': isDisabled
  296. },
  297. key: JSON.stringify(this.getValue(item))
  298. }), `${this.getText(item)}${last ? '' : ', '}`);
  299. },
  300. genDefaultSlot() {
  301. const selections = this.genSelections();
  302. const input = this.genInput(); // If the return is an empty array
  303. // push the input
  304. if (Array.isArray(selections)) {
  305. selections.push(input); // Otherwise push it into children
  306. } else {
  307. selections.children = selections.children || [];
  308. selections.children.push(input);
  309. }
  310. return [this.genFieldset(), this.$createElement('div', {
  311. staticClass: 'v-select__slot',
  312. directives: this.directives
  313. }, [this.genLabel(), this.prefix ? this.genAffix('prefix') : null, selections, this.suffix ? this.genAffix('suffix') : null, this.genClearIcon(), this.genIconSlot(), this.genHiddenInput()]), this.genMenu(), this.genProgress()];
  314. },
  315. genIcon(type, cb, extraData) {
  316. const icon = VInput.options.methods.genIcon.call(this, type, cb, extraData);
  317. if (type === 'append') {
  318. // Don't allow the dropdown icon to be focused
  319. icon.children[0].data = mergeData(icon.children[0].data, {
  320. attrs: {
  321. tabindex: icon.children[0].componentOptions.listeners && '-1',
  322. 'aria-hidden': 'true',
  323. 'aria-label': undefined
  324. }
  325. });
  326. }
  327. return icon;
  328. },
  329. genInput() {
  330. const input = VTextField.options.methods.genInput.call(this);
  331. delete input.data.attrs.name;
  332. input.data = mergeData(input.data, {
  333. domProps: {
  334. value: null
  335. },
  336. attrs: {
  337. readonly: true,
  338. type: 'text',
  339. 'aria-readonly': String(this.readonly),
  340. 'aria-activedescendant': getObjectValueByPath(this.$refs.menu, 'activeTile.id'),
  341. autocomplete: getObjectValueByPath(input.data, 'attrs.autocomplete', 'off')
  342. },
  343. on: {
  344. keypress: this.onKeyPress
  345. }
  346. });
  347. return input;
  348. },
  349. genHiddenInput() {
  350. return this.$createElement('input', {
  351. domProps: {
  352. value: this.lazyValue
  353. },
  354. attrs: {
  355. type: 'hidden',
  356. name: this.attrs$.name
  357. }
  358. });
  359. },
  360. genInputSlot() {
  361. const render = VTextField.options.methods.genInputSlot.call(this);
  362. render.data.attrs = { ...render.data.attrs,
  363. role: 'button',
  364. 'aria-haspopup': 'listbox',
  365. 'aria-expanded': String(this.isMenuActive),
  366. 'aria-owns': this.computedOwns
  367. };
  368. return render;
  369. },
  370. genList() {
  371. // If there's no slots, we can use a cached VNode to improve performance
  372. if (this.$slots['no-data'] || this.$slots['prepend-item'] || this.$slots['append-item']) {
  373. return this.genListWithSlot();
  374. } else {
  375. return this.staticList;
  376. }
  377. },
  378. genListWithSlot() {
  379. const slots = ['prepend-item', 'no-data', 'append-item'].filter(slotName => this.$slots[slotName]).map(slotName => this.$createElement('template', {
  380. slot: slotName
  381. }, this.$slots[slotName])); // Requires destructuring due to Vue
  382. // modifying the `on` property when passed
  383. // as a referenced object
  384. return this.$createElement(VSelectList, { ...this.listData
  385. }, slots);
  386. },
  387. genMenu() {
  388. const props = this.$_menuProps;
  389. props.activator = this.$refs['input-slot']; // Attach to root el so that
  390. // menu covers prepend/append icons
  391. if ( // TODO: make this a computed property or helper or something
  392. this.attach === '' || // If used as a boolean prop (<v-menu attach>)
  393. this.attach === true || // If bound to a boolean (<v-menu :attach="true">)
  394. this.attach === 'attach' // If bound as boolean prop in pug (v-menu(attach))
  395. ) {
  396. props.attach = this.$el;
  397. } else {
  398. props.attach = this.attach;
  399. }
  400. return this.$createElement(VMenu, {
  401. attrs: {
  402. role: undefined,
  403. offsetY: true
  404. },
  405. props,
  406. on: {
  407. input: val => {
  408. this.isMenuActive = val;
  409. this.isFocused = val;
  410. }
  411. },
  412. ref: 'menu'
  413. }, [this.genList()]);
  414. },
  415. genSelections() {
  416. let length = this.selectedItems.length;
  417. const children = new Array(length);
  418. let genSelection;
  419. if (this.$scopedSlots.selection) {
  420. genSelection = this.genSlotSelection;
  421. } else if (this.hasChips) {
  422. genSelection = this.genChipSelection;
  423. } else {
  424. genSelection = this.genCommaSelection;
  425. }
  426. while (length--) {
  427. children[length] = genSelection(this.selectedItems[length], length, length === children.length - 1);
  428. }
  429. return this.$createElement('div', {
  430. staticClass: 'v-select__selections'
  431. }, children);
  432. },
  433. genSlotSelection(item, index) {
  434. return this.$scopedSlots.selection({
  435. attrs: {
  436. class: 'v-chip--select'
  437. },
  438. parent: this,
  439. item,
  440. index,
  441. select: e => {
  442. e.stopPropagation();
  443. this.selectedIndex = index;
  444. },
  445. selected: index === this.selectedIndex,
  446. disabled: this.disabled || this.readonly
  447. });
  448. },
  449. getMenuIndex() {
  450. return this.$refs.menu ? this.$refs.menu.listIndex : -1;
  451. },
  452. getDisabled(item) {
  453. return getPropertyFromItem(item, this.itemDisabled, false);
  454. },
  455. getText(item) {
  456. return getPropertyFromItem(item, this.itemText, item);
  457. },
  458. getValue(item) {
  459. return getPropertyFromItem(item, this.itemValue, this.getText(item));
  460. },
  461. onBlur(e) {
  462. e && this.$emit('blur', e);
  463. },
  464. onChipInput(item) {
  465. if (this.multiple) this.selectItem(item);else this.setValue(null); // If all items have been deleted,
  466. // open `v-menu`
  467. if (this.selectedItems.length === 0) {
  468. this.isMenuActive = true;
  469. } else {
  470. this.isMenuActive = false;
  471. }
  472. this.selectedIndex = -1;
  473. },
  474. onClick(e) {
  475. if (this.isDisabled) return;
  476. if (!this.isAppendInner(e.target)) {
  477. this.isMenuActive = true;
  478. }
  479. if (!this.isFocused) {
  480. this.isFocused = true;
  481. this.$emit('focus');
  482. }
  483. this.$emit('click', e);
  484. },
  485. onEscDown(e) {
  486. e.preventDefault();
  487. if (this.isMenuActive) {
  488. e.stopPropagation();
  489. this.isMenuActive = false;
  490. }
  491. },
  492. onKeyPress(e) {
  493. if (this.multiple || this.readonly || this.disableLookup) return;
  494. const KEYBOARD_LOOKUP_THRESHOLD = 1000; // milliseconds
  495. const now = performance.now();
  496. if (now - this.keyboardLookupLastTime > KEYBOARD_LOOKUP_THRESHOLD) {
  497. this.keyboardLookupPrefix = '';
  498. }
  499. this.keyboardLookupPrefix += e.key.toLowerCase();
  500. this.keyboardLookupLastTime = now;
  501. const index = this.allItems.findIndex(item => {
  502. const text = (this.getText(item) || '').toString();
  503. return text.toLowerCase().startsWith(this.keyboardLookupPrefix);
  504. });
  505. const item = this.allItems[index];
  506. if (index !== -1) {
  507. this.lastItem = Math.max(this.lastItem, index + 5);
  508. this.setValue(this.returnObject ? item : this.getValue(item));
  509. this.$nextTick(() => this.$refs.menu.getTiles());
  510. setTimeout(() => this.setMenuIndex(index));
  511. }
  512. },
  513. onKeyDown(e) {
  514. if (this.readonly && e.keyCode !== keyCodes.tab) return;
  515. const keyCode = e.keyCode;
  516. const menu = this.$refs.menu; // If enter, space, open menu
  517. if ([keyCodes.enter, keyCodes.space].includes(keyCode)) this.activateMenu();
  518. this.$emit('keydown', e);
  519. if (!menu) return; // If menu is active, allow default
  520. // listIndex change from menu
  521. if (this.isMenuActive && keyCode !== keyCodes.tab) {
  522. this.$nextTick(() => {
  523. menu.changeListIndex(e);
  524. this.$emit('update:list-index', menu.listIndex);
  525. });
  526. } // If menu is not active, up and down can do
  527. // one of 2 things. If multiple, opens the
  528. // menu, if not, will cycle through all
  529. // available options
  530. if (!this.isMenuActive && [keyCodes.up, keyCodes.down].includes(keyCode)) return this.onUpDown(e); // If escape deactivate the menu
  531. if (keyCode === keyCodes.esc) return this.onEscDown(e); // If tab - select item or close menu
  532. if (keyCode === keyCodes.tab) return this.onTabDown(e); // If space preventDefault
  533. if (keyCode === keyCodes.space) return this.onSpaceDown(e);
  534. },
  535. onMenuActiveChange(val) {
  536. // If menu is closing and mulitple
  537. // or menuIndex is already set
  538. // skip menu index recalculation
  539. if (this.multiple && !val || this.getMenuIndex() > -1) return;
  540. const menu = this.$refs.menu;
  541. if (!menu || !this.isDirty) return; // When menu opens, set index of first active item
  542. for (let i = 0; i < menu.tiles.length; i++) {
  543. if (menu.tiles[i].getAttribute('aria-selected') === 'true') {
  544. this.setMenuIndex(i);
  545. break;
  546. }
  547. }
  548. },
  549. onMouseUp(e) {
  550. if (this.hasMouseDown && e.which !== 3 && !this.isDisabled) {
  551. // If append inner is present
  552. // and the target is itself
  553. // or inside, toggle menu
  554. if (this.isAppendInner(e.target)) {
  555. this.$nextTick(() => this.isMenuActive = !this.isMenuActive); // If user is clicking in the container
  556. // and field is enclosed, activate it
  557. } else if (this.isEnclosed) {
  558. this.isMenuActive = true;
  559. }
  560. }
  561. VTextField.options.methods.onMouseUp.call(this, e);
  562. },
  563. onScroll() {
  564. if (!this.isMenuActive) {
  565. requestAnimationFrame(() => this.getContent().scrollTop = 0);
  566. } else {
  567. if (this.lastItem >= this.computedItems.length) return;
  568. const showMoreItems = this.getContent().scrollHeight - (this.getContent().scrollTop + this.getContent().clientHeight) < 200;
  569. if (showMoreItems) {
  570. this.lastItem += 20;
  571. }
  572. }
  573. },
  574. onSpaceDown(e) {
  575. e.preventDefault();
  576. },
  577. onTabDown(e) {
  578. const menu = this.$refs.menu;
  579. if (!menu) return;
  580. const activeTile = menu.activeTile; // An item that is selected by
  581. // menu-index should toggled
  582. if (!this.multiple && activeTile && this.isMenuActive) {
  583. e.preventDefault();
  584. e.stopPropagation();
  585. activeTile.click();
  586. } else {
  587. // If we make it here,
  588. // the user has no selected indexes
  589. // and is probably tabbing out
  590. this.blur(e);
  591. }
  592. },
  593. onUpDown(e) {
  594. const menu = this.$refs.menu;
  595. if (!menu) return;
  596. e.preventDefault(); // Multiple selects do not cycle their value
  597. // when pressing up or down, instead activate
  598. // the menu
  599. if (this.multiple) return this.activateMenu();
  600. const keyCode = e.keyCode; // Cycle through available values to achieve
  601. // select native behavior
  602. menu.isBooted = true;
  603. window.requestAnimationFrame(() => {
  604. menu.getTiles();
  605. keyCodes.up === keyCode ? menu.prevTile() : menu.nextTile();
  606. menu.activeTile && menu.activeTile.click();
  607. });
  608. },
  609. selectItem(item) {
  610. if (!this.multiple) {
  611. this.setValue(this.returnObject ? item : this.getValue(item));
  612. this.isMenuActive = false;
  613. } else {
  614. const internalValue = (this.internalValue || []).slice();
  615. const i = this.findExistingIndex(item);
  616. i !== -1 ? internalValue.splice(i, 1) : internalValue.push(item);
  617. this.setValue(internalValue.map(i => {
  618. return this.returnObject ? i : this.getValue(i);
  619. })); // When selecting multiple
  620. // adjust menu after each
  621. // selection
  622. this.$nextTick(() => {
  623. this.$refs.menu && this.$refs.menu.updateDimensions();
  624. }); // We only need to reset list index for multiple
  625. // to keep highlight when an item is toggled
  626. // on and off
  627. if (!this.multiple) return;
  628. const listIndex = this.getMenuIndex();
  629. this.setMenuIndex(-1); // There is no item to re-highlight
  630. // when selections are hidden
  631. if (this.hideSelected) return;
  632. this.$nextTick(() => this.setMenuIndex(listIndex));
  633. }
  634. },
  635. setMenuIndex(index) {
  636. this.$refs.menu && (this.$refs.menu.listIndex = index);
  637. },
  638. setSelectedItems() {
  639. const selectedItems = [];
  640. const values = !this.multiple || !Array.isArray(this.internalValue) ? [this.internalValue] : this.internalValue;
  641. for (const value of values) {
  642. const index = this.allItems.findIndex(v => this.valueComparator(this.getValue(v), this.getValue(value)));
  643. if (index > -1) {
  644. selectedItems.push(this.allItems[index]);
  645. }
  646. }
  647. this.selectedItems = selectedItems;
  648. },
  649. setValue(value) {
  650. const oldValue = this.internalValue;
  651. this.internalValue = value;
  652. value !== oldValue && this.$emit('change', value);
  653. },
  654. isAppendInner(target) {
  655. // return true if append inner is present
  656. // and the target is itself or inside
  657. const appendInner = this.$refs['append-inner'];
  658. return appendInner && (appendInner === target || appendInner.contains(target));
  659. }
  660. }
  661. });
  662. //# sourceMappingURL=VSelect.js.map