mirror of
https://github.com/tenrok/vue-select.git
synced 2026-05-17 02:29:37 +03:00
Merge branch 'beta' into beta-vite
This commit is contained in:
@@ -1,38 +1,27 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
name: Bug Report
|
||||
about: Create a bug report to help fix an issue with the component.
|
||||
title: ''
|
||||
labels: ''
|
||||
labels: 'bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
> Please respect maintainers time by filling in these sections. Your issue will likely be closed without this information.
|
||||
|
||||
- Vue Version:
|
||||
- Vue Select Version:
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
**Reproduction Link**
|
||||
A link to a reproduction of the bug. This is a huge help.
|
||||
|
||||
**Steps To Reproduce**
|
||||
Outline the steps to reproduce the bug.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Discussions
|
||||
url: https://github.com/sagalbot/vue-select/discussions
|
||||
about: If you're not submitting a bug or feature request, please use discussions instead.
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div style="background: #282c34; padding: 1rem; border-radius: 0.3rem">
|
||||
<v-select :options="countries" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import countries from '../data/countries.js'
|
||||
export default {
|
||||
data: () => ({ countries }),
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
>>> {
|
||||
--vs-controls-color: #664cc3;
|
||||
--vs-border-color: #664cc3;
|
||||
|
||||
--vs-dropdown-bg: #282c34;
|
||||
--vs-dropdown-color: #cc99cd;
|
||||
--vs-dropdown-option-color: #cc99cd;
|
||||
|
||||
--vs-selected-bg: #664cc3;
|
||||
--vs-selected-color: #eeeeee;
|
||||
|
||||
--vs-search-input-color: #eeeeee;
|
||||
|
||||
--vs-dropdown-option--active-bg: #664cc3;
|
||||
--vs-dropdown-option--active-color: #eeeeee;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<v-select
|
||||
v-model="selected"
|
||||
:reduce="(option) => option.id"
|
||||
:options="[
|
||||
{ label: 'One', id: 1 },
|
||||
{ label: 'Two', id: 2 },
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
selected: 3,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
+48
-17
@@ -1,17 +1,47 @@
|
||||
Vue Select offers many APIs for customizing the look and feel from the component. You can use
|
||||
[scoped slots](../api/slots.md), [custom child components](components.md), or modify the built in
|
||||
SCSS variables.
|
||||
Vue Select offers many APIs for customizing the look and feel from the component. You can use
|
||||
[scoped slots](../api/slots.md), [custom child components](components.md), or modify the built in
|
||||
CSS properties.
|
||||
|
||||
::: tip
|
||||
Support for CSS variables (custom properties) is currently on the road map for those
|
||||
that are not using sass in their projects.
|
||||
:::
|
||||
## CSS Variables
|
||||
|
||||
## SCSS Variables
|
||||
Vue Select uses custom CSS properties throughout the component to handle visual opinions. This
|
||||
allows for quite a bit of flexibility in styling, without having to hook into a build system for
|
||||
generating your own styles with something like SASS. If there is a value that you think should use a
|
||||
CSS property instead of a hardcoded CSS value, please submit a PR.
|
||||
|
||||
## Dark Mode Example
|
||||
|
||||
Without writing any CSS yourself, you can completely customize the look and feel of Vue Select
|
||||
through the use of CSS variables. In this example, we adjust the colors of the component to match
|
||||
for a dark mode application.
|
||||
|
||||
In this case, the variables are scoped to only this implementation of the component, but you could
|
||||
place these variables anywhere in your applications CSS file to implement at a global level for your
|
||||
app.
|
||||
|
||||
Check the MDN docs for more info
|
||||
about [CSS Custom Properties.](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties)
|
||||
|
||||
<CssVariables style="margin-top: 1rem;" />
|
||||
|
||||
<<< @/.vuepress/components/CssVariables.vue
|
||||
|
||||
### Available CSS Variables <Badge type="primary">3.18+</Badge>
|
||||
|
||||
<<< @/../src/css/global/variables.css
|
||||
|
||||
## SCSS <Badge type="warning">Deprecated in v3.18</Badge>
|
||||
|
||||
::: warning Deprecation Notice
|
||||
The SCSS build been deprecated for the `v3.x` release, and will be
|
||||
removed in `v4.0.0`. The files will remain in the v3 codebase if you really need them, but the
|
||||
recommended approach is to leverage the included CSS variables instead.
|
||||
:::
|
||||
|
||||
Variables are leveraged in as much of the component styles as possible. If you really want to dig
|
||||
into the SCSS, the files are located in `src/scss`. The variables listed below can be found at
|
||||
[`src/scss/global/_variables`](https://github.com/sagalbot/vue-select/blob/master/src/scss/global/_variables.scss).
|
||||
[`src/scss/global/_variables`](https://github.com/sagalbot/vue-select/blob/master/src/scss/global/_variables.scss)
|
||||
.
|
||||
|
||||
All variables are implemented with `!default` in order to make them easier to override in your
|
||||
application.
|
||||
@@ -23,12 +53,12 @@ application.
|
||||
Vue Select takes the approach of using selectors with a single level of specificity, while using
|
||||
classes that are very specific to Vue Select to avoid collisions with your app.
|
||||
|
||||
All classes within Vue Select use the `vs__` prefix, and selectors are generally a single classname
|
||||
Most classes within Vue Select use the `vs__` prefix, and selectors are generally a single classname
|
||||
– unless there is a state being applied to the component.
|
||||
|
||||
In order to override a default property in your app, you should add one level of specificity.
|
||||
The easiest way to do this, is to add `.v-select` before the `vs__*` selector if you want to adjust
|
||||
all instances of Vue Select, or add your own classname if you just want to affect one.
|
||||
In order to override a default property in your app, you should add one level of specificity. The
|
||||
easiest way to do this, is to add `.v-select` before the `vs__*` selector if you want to adjust all
|
||||
instances of Vue Select, or add your own classname if you just want to affect one.
|
||||
|
||||
<CssSpecificity />
|
||||
|
||||
@@ -38,17 +68,18 @@ all instances of Vue Select, or add your own classname if you just want to affec
|
||||
|
||||
By default, the dropdown transitions with a `.15s` cubic-bezier opacity fade in/out. The component
|
||||
uses the [VueJS transition system](https://vuejs.org/v2/guide/transitions.html). By default, the
|
||||
transition name is `vs__fade`. There's a couple ways to override or change this transition.
|
||||
transition name is `vs__fade`. There's a couple ways to override or change this transition.
|
||||
|
||||
1. Use the `transition` prop. Applying this prop will change the name of the animation classes and
|
||||
negate the default CSS. If you want to remove it entirely, you can set it to an empty string.
|
||||
negate the default CSS. If you want to remove it entirely, you can set it to an empty string.
|
||||
|
||||
```html
|
||||
|
||||
<v-select transition="" />
|
||||
```
|
||||
|
||||
2. You can also override the default CSS for the `vs__fade` transition. Again, if you
|
||||
wanted to eliminate the transition entirely:
|
||||
2. You can also override the default CSS for the `vs__fade` transition. Again, if you wanted to
|
||||
eliminate the transition entirely:
|
||||
|
||||
```css
|
||||
.vs__fade-enter-active,
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
## Yarn / NPM
|
||||
Install with yarn:
|
||||
|
||||
Install with yarn or npm:
|
||||
|
||||
```bash
|
||||
# vue 2
|
||||
yarn add vue-select
|
||||
|
||||
# vue 3
|
||||
yarn add vue-select@beta
|
||||
|
||||
# or, using NPM
|
||||
npm install vue-select
|
||||
```
|
||||
@@ -22,12 +28,6 @@ The component itself does not include any CSS. You'll need to include it separat
|
||||
import 'vue-select/dist/vue-select.css';
|
||||
```
|
||||
|
||||
Alternatively, you can import the scss for complete control of the component styles:
|
||||
|
||||
```scss
|
||||
@import "vue-select/src/scss/vue-select.scss";
|
||||
```
|
||||
|
||||
## In the Browser
|
||||
|
||||
vue-select ships as an UMD module that is accessible in the browser. When loaded
|
||||
@@ -56,5 +56,5 @@ Vue.component('v-select', VueSelect.VueSelect);
|
||||
|
||||
## Vue Compatibility
|
||||
|
||||
- If you're on Vue `1.x`, use vue-select `1.x`.
|
||||
- The `1.x` branch has not received updates since the 2.0 release.
|
||||
- Vue `2.x`, use vue-select `3.x`.
|
||||
- Vue `3.x`, use vue-select `3.x@beta`.
|
||||
|
||||
+92
-59
@@ -2,11 +2,12 @@
|
||||
|
||||
### `v-model`
|
||||
|
||||
The most common use case for vue-select is to have the chosen value synced with a parent component. vue-select
|
||||
takes advantage of the `v-model` syntax to sync values with a parent. The `v-model` syntax works with
|
||||
primitives and objects.
|
||||
The most common use case for vue-select is to have the chosen value synced with a parent component.
|
||||
vue-select takes advantage of the `v-model` syntax to sync values with a parent. The `v-model`
|
||||
syntax works with primitives and objects.
|
||||
|
||||
```html
|
||||
|
||||
<v-select v-model="selected" />
|
||||
```
|
||||
|
||||
@@ -14,25 +15,26 @@ Note that when using the `multiple` prop, the `v-model` value will always be an
|
||||
|
||||
### Props and Events
|
||||
|
||||
Sometimes `v-model` might not fit your use case. For example, when working with [Vuex](https://vuex.vuejs.org),
|
||||
you'll need to trigger a mutation rather than mutating a value directly. In that case, maybe you need
|
||||
to bind a pre-selected value, and trigger a mutation when it changes.
|
||||
Sometimes `v-model` might not fit your use case. For example, when working
|
||||
with [Vuex](https://vuex.vuejs.org), you'll need to trigger a mutation rather than mutating a value
|
||||
directly. In that case, maybe you need to bind a pre-selected value, and trigger a mutation when it
|
||||
changes.
|
||||
|
||||
vue-select exposes the `value` prop and an `input` event to enable this. This combo of props and
|
||||
vue-select exposes the `value` prop and an `input` event to enable this. This combo of props and
|
||||
events is also how Vue wires up the `v-model` syntax internally.
|
||||
|
||||
#### Prop: `value`
|
||||
|
||||
The `value` prop lets vue-select know what value is currently selected. It will accept strings,
|
||||
numbers or objects. If you're using a `multiple` v-select, you'll want to pass an array.
|
||||
The `value` prop lets vue-select know what value is currently selected. It will accept strings,
|
||||
numbers or objects. If you're using a `multiple` v-select, you'll want to pass an array.
|
||||
|
||||
```html
|
||||
|
||||
<v-select :value="selected" />
|
||||
```
|
||||
|
||||
::: tip 🤓
|
||||
Anytime you bind the `value` prop directly, you're responsible for updating the bound variable
|
||||
in your code using the `@input` event.
|
||||
::: tip 🤓 Anytime you bind the `value` prop directly, you're responsible for updating the bound
|
||||
variable in your code using the `@input` event.
|
||||
:::
|
||||
|
||||
#### Event: `input`
|
||||
@@ -42,50 +44,66 @@ state as it's only parameter.
|
||||
|
||||
#### Vuex Support
|
||||
|
||||
The `value` prop and `emit` event are very useful when using a state management tool, like Vuex.
|
||||
You can bind the selected value with `:value="$store.myValue"`, and use the `input` event to
|
||||
trigger a mutation, or dispatch an action – or anything else you might need to do when the selection
|
||||
changes.
|
||||
The `value` prop and `emit` event are very useful when using a state management tool, like Vuex. You
|
||||
can bind the selected value with `:value="$store.myValue"`, and use the `input` event to trigger a
|
||||
mutation, or dispatch an action – or anything else you might need to do when the selection changes.
|
||||
|
||||
```html
|
||||
|
||||
<v-select :value="$store.myValue" @input="setSelected" />
|
||||
```
|
||||
|
||||
```js
|
||||
methods: {
|
||||
setSelected(value) {
|
||||
// trigger a mutation, or dispatch an action
|
||||
}
|
||||
setSelected(value)
|
||||
{
|
||||
// trigger a mutation, or dispatch an action
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Single/Multiple
|
||||
|
||||
By default, vue-select supports choosing a single value. If you need multiple values, use the
|
||||
`multiple` boolean prop, much the same way you would on an HTML `<select>` element. When `multiple`
|
||||
is true, `v-model` and `value` must be an array.
|
||||
|
||||
```html
|
||||
|
||||
<v-select multiple v-model="selected" :options="['Canada','United States']" />
|
||||
```
|
||||
|
||||
<v-select multiple :options="['Canada','United States']" />
|
||||
|
||||
## Transforming Selections
|
||||
|
||||
When the `options` array contains objects, vue-select returns the whole object as dropdown value
|
||||
When the `options` array contains objects, vue-select returns the whole object as dropdown value
|
||||
upon selection. This approach makes no assumptions about the data you need, and provides a lot of
|
||||
flexibility. However, there will be situations where maybe you just need to return a single key
|
||||
from an object.
|
||||
flexibility. However, there will be situations where you just need to return a single key from an
|
||||
object.
|
||||
|
||||
### Returning a single key with `reduce`
|
||||
### Returning a single key with `reduce`
|
||||
|
||||
If you need to return a single key, or transform the selection before it is synced, vue-select
|
||||
provides a `reduce` callback that allows you to transform a selected option before it is passed to
|
||||
If you need to return a single key, or transform the selection before it is synced, vue-select
|
||||
provides a `reduce` callback that allows you to transform a selected option before it is passed to
|
||||
the `@input` event. Consider this data structure:
|
||||
|
||||
|
||||
```js
|
||||
let options = [{code: 'CA', country: 'Canada'}];
|
||||
```
|
||||
|
||||
If we want to display the `country`, but return the `code` to `v-model`, we can use the `reduce`
|
||||
|
||||
If we want to display the `country`, but return the `code` to `v-model`, we can use the `reduce`
|
||||
prop to receive only the data that's required.
|
||||
|
||||
|
||||
```html
|
||||
<v-select :options="options" :reduce="country => country.code" label="country" />
|
||||
|
||||
<v-select :options="options" :reduce="country => country.code" label="country" />
|
||||
```
|
||||
|
||||
### Deep Nested Values
|
||||
|
||||
|
||||
The `reduce` property also works well when you have a deeply nested value:
|
||||
|
||||
|
||||
```
|
||||
{
|
||||
country: 'canada',
|
||||
@@ -95,30 +113,42 @@ The `reduce` property also works well when you have a deeply nested value:
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
```html
|
||||
<v-select :options="options" :reduce="country => country.meta.code" label="country" />
|
||||
|
||||
<v-select :options="options" :reduce="country => country.meta.code" label="country" />
|
||||
```
|
||||
|
||||
|
||||
<reducer-nested-value />
|
||||
|
||||
## Single/Multiple Selection
|
||||
## Caveats with `reduce`
|
||||
|
||||
By default, vue-select supports choosing a single value. If you need multiple values, use the
|
||||
`multiple` boolean prop, much the same way you would on an HTML `<select>` element. When `multiple`
|
||||
is true, `v-model` and `value` must be an array.
|
||||
|
||||
The most common issue with `reduce` is when the component displays your _reduced_ _value_ instead of
|
||||
it's _label_. This happens when you supply Vue Select a `value` or `v-model` binding with a reduced_
|
||||
value, but the complete option object is not present in the `options` array.
|
||||
|
||||
```html
|
||||
<v-select multiple v-model="selected" :options="['Canada','United States']" />
|
||||
```
|
||||
<v-select multiple :options="['Canada','United States']" />
|
||||
<ReducedWithNoMatchingOption />
|
||||
|
||||
<<< @/.vuepress/components/ReducedWithNoMatchingOption.vue
|
||||
|
||||
In the example above, the component was supplied with an ID that doesn't exist in the `options`
|
||||
array. When `value` changes, Vue Select searches the supplied options, running each one
|
||||
through `reduce` until the corresponding option is found. When that option doesn't exist, Vue Select
|
||||
will end up displaying the `value` supplied.
|
||||
|
||||
::: warning
|
||||
|
||||
When providing Vue Select with a _reduced_ `value` - the object that the value was reduced from must
|
||||
exist in the `options` array.
|
||||
|
||||
:::
|
||||
|
||||
## Tagging
|
||||
|
||||
To allow input that's not present within the options, set the `taggable` prop to true.
|
||||
|
||||
```html
|
||||
|
||||
<v-select taggable multiple />
|
||||
```
|
||||
|
||||
@@ -127,6 +157,7 @@ To allow input that's not present within the options, set the `taggable` prop to
|
||||
If you want added tags to be pushed to the options array, set `push-tags` to true.
|
||||
|
||||
```html
|
||||
|
||||
<v-select taggable multiple push-tags />
|
||||
```
|
||||
|
||||
@@ -136,10 +167,11 @@ If you want added tags to be pushed to the options array, set `push-tags` to tru
|
||||
|
||||
When combining `taggable` with `reduce`, you must define the `createOption` prop. The
|
||||
`createOption` function is responsible for defining the structure of the objects that Vue Select
|
||||
will create for you when adding a tag. It should return a value that has the same properties as the
|
||||
will create for you when adding a tag. It should return a value that has the same properties as the
|
||||
rest of your `options`.
|
||||
|
||||
If you don't define `createOption`, Vue Select will construct a simple object following this structure:
|
||||
If you don't define `createOption`, Vue Select will construct a simple object following this
|
||||
structure:
|
||||
`{[this.label]: searchText}`. If you're using `reduce`, this is probably not what your options look
|
||||
like, which is why you'll need to set the function yourself.
|
||||
|
||||
@@ -147,28 +179,29 @@ like, which is why you'll need to set the function yourself.
|
||||
|
||||
We have a taggable select for adding books to a collection. We're just concerned about getting the
|
||||
book title added, and our server side code will add the author details in a background process. The
|
||||
user has already selected a book.
|
||||
user has already selected a book.
|
||||
|
||||
```js
|
||||
const options = [
|
||||
{
|
||||
title: "HTML5",
|
||||
author: {
|
||||
firstName: "Remy",
|
||||
lastName: "Sharp"
|
||||
{
|
||||
title: "HTML5",
|
||||
author: {
|
||||
firstName: "Remy",
|
||||
lastName: "Sharp"
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
```html
|
||||
|
||||
<v-select
|
||||
taggable
|
||||
multiple
|
||||
label="title"
|
||||
:options="options"
|
||||
:create-option="book => ({ title: book, author: { firstName: '', lastName: '' } })"
|
||||
:reduce="book => `${book.author.firstName} ${book.author.lastName}`"
|
||||
taggable
|
||||
multiple
|
||||
label="title"
|
||||
:options="options"
|
||||
:create-option="book => ({ title: book, author: { firstName: '', lastName: '' } })"
|
||||
:reduce="book => `${book.author.firstName} ${book.author.lastName}`"
|
||||
/>
|
||||
```
|
||||
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
"dotenv": "^8.2.0",
|
||||
"fuse.js": "^6.4.0",
|
||||
"gh-pages": "^2.2.0",
|
||||
"node-sass": "^4.12.0",
|
||||
"octonode": "^0.9.5",
|
||||
"sass-loader": "^8.0.2",
|
||||
"vue": "^2.6.10",
|
||||
"vuepress": "^1.4.0",
|
||||
"vuex": "^3.1.0"
|
||||
|
||||
+26
-574
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vue-select",
|
||||
"version": "4.0.0-beta.2",
|
||||
"version": "4.0.0-beta.3",
|
||||
"description": "Everything you wish the HTML <select> element could do, wrapped up into a lightweight, extensible Vue component.",
|
||||
"author": "Jeff Sagal <sagalbot@gmail.com>",
|
||||
"homepage": "https://vue-select.org",
|
||||
@@ -45,7 +45,7 @@
|
||||
"autoprefixer": "^10.4.2",
|
||||
"bundlewatch": "^0.2.5",
|
||||
"commitizen": "^4.0.3",
|
||||
"coveralls": "^3.0.2",
|
||||
"coveralls": "^3.1.1",
|
||||
"cross-env": "^5.2.0",
|
||||
"cz-conventional-changelog": "3.1.0",
|
||||
"eslint": "^8.5.0",
|
||||
@@ -107,7 +107,7 @@
|
||||
{
|
||||
"path": "./dist/vue-select.css",
|
||||
"compression": "none",
|
||||
"maxSize": "6 KB"
|
||||
"maxSize": "8 KB"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+14
-15
@@ -1,5 +1,5 @@
|
||||
<style lang="scss">
|
||||
@import '../scss/vue-select.scss';
|
||||
<style>
|
||||
@import '../css/vue-select.css';
|
||||
</style>
|
||||
|
||||
<template>
|
||||
@@ -33,7 +33,6 @@
|
||||
</slot>
|
||||
<button
|
||||
v-if="multiple"
|
||||
ref="deselectButtons"
|
||||
:ref="(el) => (deselectButtons[i] = el)"
|
||||
:disabled="disabled"
|
||||
type="button"
|
||||
@@ -119,9 +118,9 @@
|
||||
</slot>
|
||||
</li>
|
||||
<li v-if="filteredOptions.length === 0" class="vs__no-options">
|
||||
<slot name="no-options" v-bind="scope.noOptions"
|
||||
>Sorry, no matching options.</slot
|
||||
>
|
||||
<slot name="no-options" v-bind="scope.noOptions">
|
||||
Sorry, no matching options.
|
||||
</slot>
|
||||
</li>
|
||||
<slot name="list-footer" v-bind="scope.listFooter" />
|
||||
</ul>
|
||||
@@ -137,13 +136,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import pointerScroll from '@/mixins/pointerScroll.js'
|
||||
import typeAheadPointer from '@/mixins/typeAheadPointer.js'
|
||||
import ajax from '@/mixins/ajax.js'
|
||||
import childComponents from '@/components/childComponents.js'
|
||||
import appendToBody from '@/directives/appendToBody.js'
|
||||
import sortAndStringify from '@/utility/sortAndStringify.js'
|
||||
import uniqueId from '@/utility/uniqueId.js'
|
||||
import pointerScroll from '../mixins/pointerScroll'
|
||||
import typeAheadPointer from '../mixins/typeAheadPointer'
|
||||
import ajax from '../mixins/ajax'
|
||||
import childComponents from './childComponents'
|
||||
import appendToBody from '../directives/appendToBody'
|
||||
import sortAndStringify from '../utility/sortAndStringify'
|
||||
import uniqueId from '../utility/uniqueId'
|
||||
|
||||
/**
|
||||
* @name VueSelect
|
||||
@@ -723,7 +722,7 @@ export default {
|
||||
value = this.$data._value
|
||||
}
|
||||
|
||||
if (value) {
|
||||
if (value !== undefined && value !== null) {
|
||||
return [].concat(value)
|
||||
}
|
||||
|
||||
@@ -746,7 +745,7 @@ export default {
|
||||
* @returns {HTMLInputElement}
|
||||
*/
|
||||
searchEl() {
|
||||
return this.$slots['search']
|
||||
return !!this.$slots['search']
|
||||
? this.$refs.selectedOptions.querySelector(
|
||||
this.searchInputQuerySelector
|
||||
)
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
:root {
|
||||
--vs-transition-timing-function: cubic-bezier(1, 0.5, 0.8, 1);
|
||||
--vs-transition-duration: 0.15s;
|
||||
}
|
||||
|
||||
/* KeyFrames */
|
||||
@-webkit-keyframes vSelectSpinner {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes vSelectSpinner {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Dropdown Default Transition */
|
||||
.vs__fade-enter-active,
|
||||
.vs__fade-leave-active {
|
||||
pointer-events: none;
|
||||
transition: opacity var(--vs-transition-duration)
|
||||
var(--vs-transition-timing-function);
|
||||
}
|
||||
.vs__fade-enter,
|
||||
.vs__fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
.v-select {
|
||||
position: relative;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.v-select,
|
||||
.v-select * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/** Component States */
|
||||
|
||||
/*
|
||||
* Disabled
|
||||
*
|
||||
* When the component is disabled, all interaction
|
||||
* should be prevented. Here we modify the bg color,
|
||||
* and change the cursor displayed on the interactive
|
||||
* components.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--vs-disabled-bg: var(--vs-state-disabled-bg);
|
||||
--vs-disabled-color: var(--vs-state-disabled-color);
|
||||
--vs-disabled-cursor: var(--vs-state-disabled-cursor);
|
||||
}
|
||||
|
||||
.vs--disabled {
|
||||
.vs__dropdown-toggle,
|
||||
.vs__clear,
|
||||
.vs__search,
|
||||
.vs__selected,
|
||||
.vs__open-indicator {
|
||||
cursor: var(--vs-disabled-cursor);
|
||||
background-color: var(--vs-disabled-bg);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* RTL - Right to Left Support
|
||||
*
|
||||
* Because we're using a flexbox layout, the `dir="rtl"`
|
||||
* HTML attribute does most of the work for us by
|
||||
* rearranging the child elements visually.
|
||||
*/
|
||||
|
||||
.v-select[dir='rtl'] {
|
||||
.vs__actions {
|
||||
padding: 0 3px 0 6px;
|
||||
}
|
||||
|
||||
.vs__clear {
|
||||
margin-left: 6px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.vs__deselect {
|
||||
margin-left: 0;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.vs__dropdown-menu {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
:root {
|
||||
--vs-colors--lightest: rgba(60, 60, 60, 0.26);
|
||||
--vs-colors--light: rgba(60, 60, 60, 0.5);
|
||||
--vs-colors--dark: #333;
|
||||
--vs-colors--darkest: rgba(0, 0, 0, 0.15);
|
||||
|
||||
/* Search Input */
|
||||
--vs-search-input-color: inherit;
|
||||
--vs-search-input-placeholder-color: inherit;
|
||||
|
||||
/* Font */
|
||||
--vs-font-size: 1rem;
|
||||
--vs-line-height: 1.4;
|
||||
|
||||
/* Disabled State */
|
||||
--vs-state-disabled-bg: rgb(248, 248, 248);
|
||||
--vs-state-disabled-color: var(--vs-colors--light);
|
||||
--vs-state-disabled-controls-color: var(--vs-colors--light);
|
||||
--vs-state-disabled-cursor: not-allowed;
|
||||
|
||||
/* Borders */
|
||||
--vs-border-color: var(--vs-colors--lightest);
|
||||
--vs-border-width: 1px;
|
||||
--vs-border-style: solid;
|
||||
--vs-border-radius: 4px;
|
||||
|
||||
/* Actions: house the component controls */
|
||||
--vs-actions-padding: 4px 6px 0 3px;
|
||||
|
||||
/* Component Controls: Clear, Open Indicator */
|
||||
--vs-controls-color: var(--vs-colors--light);
|
||||
--vs-controls-size: 1;
|
||||
--vs-controls--deselect-text-shadow: 0 1px 0 #fff;
|
||||
|
||||
/* Selected */
|
||||
--vs-selected-bg: #f0f0f0;
|
||||
--vs-selected-color: var(--vs-colors--dark);
|
||||
--vs-selected-border-color: var(--vs-border-color);
|
||||
--vs-selected-border-style: var(--vs-border-style);
|
||||
--vs-selected-border-width: var(--vs-border-width);
|
||||
|
||||
/* Dropdown */
|
||||
--vs-dropdown-bg: #fff;
|
||||
--vs-dropdown-color: inherit;
|
||||
--vs-dropdown-z-index: 1000;
|
||||
--vs-dropdown-min-width: 160px;
|
||||
--vs-dropdown-max-height: 350px;
|
||||
--vs-dropdown-box-shadow: 0px 3px 6px 0px var(--vs-colors--darkest);
|
||||
|
||||
/* Options */
|
||||
--vs-dropdown-option-bg: #000;
|
||||
--vs-dropdown-option-color: var(--vs-dropdown-color);
|
||||
--vs-dropdown-option-padding: 3px 20px;
|
||||
|
||||
/* Active State */
|
||||
--vs-dropdown-option--active-bg: #5897fb;
|
||||
--vs-dropdown-option--active-color: #fff;
|
||||
|
||||
/* Deselect State */
|
||||
--vs-dropdown-option--deselect-bg: #fb5858;
|
||||
--vs-dropdown-option--deselect-color: #fff;
|
||||
|
||||
/* Transitions */
|
||||
--vs-transition-timing-function: cubic-bezier(1, -0.115, 0.975, 0.855);
|
||||
--vs-transition-duration: 150ms;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/* Clear Button */
|
||||
|
||||
.vs__clear {
|
||||
fill: var(--vs-controls-color);
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
margin-right: 8px;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* Dropdown Menu */
|
||||
|
||||
.vs__dropdown-menu {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
/* calc to ensure the left and right borders of the dropdown appear flush with the toggle. */
|
||||
top: calc(100% - var(--vs-border-width));
|
||||
left: 0;
|
||||
z-index: var(--vs-dropdown-z-index);
|
||||
padding: 5px 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
max-height: var(--vs-dropdown-max-height);
|
||||
min-width: var(--vs-dropdown-min-width);
|
||||
overflow-y: auto;
|
||||
box-shadow: var(--vs-dropdown-box-shadow);
|
||||
border: var(--vs-border-width) var(--vs-border-style) var(--vs-border-color);
|
||||
border-top-style: none;
|
||||
border-radius: 0 0 var(--vs-border-radius) var(--vs-border-radius);
|
||||
text-align: left;
|
||||
list-style: none;
|
||||
background: var(--vs-dropdown-bg);
|
||||
color: var(--vs-dropdown-color);
|
||||
}
|
||||
|
||||
.vs__no-options {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* List Items */
|
||||
.vs__dropdown-option {
|
||||
line-height: 1.42857143; /* Normalize line height */
|
||||
display: block;
|
||||
padding: var(--vs-dropdown-option-padding);
|
||||
clear: both;
|
||||
color: var(--vs-dropdown-option-color); /* Overrides most CSS frameworks */
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vs__dropdown-option--highlight {
|
||||
background: var(--vs-dropdown-option--active-bg);
|
||||
color: var(--vs-dropdown-option--active-color);
|
||||
}
|
||||
|
||||
.vs__dropdown-option--deselect {
|
||||
background: var(--vs-dropdown-option--deselect-bg);
|
||||
color: var(--vs-dropdown-option--deselect-color);
|
||||
}
|
||||
|
||||
.vs__dropdown-option--disabled {
|
||||
background: var(--vs-state-disabled-bg);
|
||||
color: var(--vs-state-disabled-color);
|
||||
cursor: var(--vs-state-disabled-cursor);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
Dropdown Toggle
|
||||
|
||||
The dropdown toggle is the primary wrapper of the component. It
|
||||
has two direct descendants: .vs__selected-options, and .vs__actions.
|
||||
|
||||
.vs__selected-options holds the .vs__selected's as well as the
|
||||
main search input.
|
||||
|
||||
.vs__actions holds the clear button and dropdown toggle.
|
||||
*/
|
||||
|
||||
.vs__dropdown-toggle {
|
||||
appearance: none;
|
||||
display: flex;
|
||||
padding: 0 0 4px 0;
|
||||
background: none;
|
||||
border: var(--vs-border-width) var(--vs-border-style) var(--vs-border-color);
|
||||
border-radius: var(--vs-border-radius);
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.vs__selected-options {
|
||||
display: flex;
|
||||
flex-basis: 100%;
|
||||
flex-grow: 1;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 2px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.vs__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--vs-actions-padding);
|
||||
}
|
||||
|
||||
/* Dropdown Toggle States */
|
||||
.vs--searchable .vs__dropdown-toggle {
|
||||
cursor: text;
|
||||
}
|
||||
.vs--unsearchable .vs__dropdown-toggle {
|
||||
cursor: pointer;
|
||||
}
|
||||
.vs--open .vs__dropdown-toggle {
|
||||
border-bottom-color: transparent;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* Open Indicator */
|
||||
|
||||
/*
|
||||
The open indicator appears as a down facing
|
||||
caret on the right side of the select.
|
||||
*/
|
||||
|
||||
.vs__open-indicator {
|
||||
fill: var(--vs-controls-color);
|
||||
transform: scale(var(--vs-controls-size));
|
||||
transition: transform var(--vs-transition-duration)
|
||||
var(--vs-transition-timing-function);
|
||||
transition-timing-function: var(--vs-transition-timing-function);
|
||||
}
|
||||
|
||||
/* Open State */
|
||||
|
||||
.vs--open .vs__open-indicator {
|
||||
transform: rotate(180deg) scale(var(--vs-controls-size));
|
||||
}
|
||||
|
||||
/* Loading State */
|
||||
|
||||
.vs--loading .vs__open-indicator {
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/* Search Input */
|
||||
|
||||
/**
|
||||
* Super weird bug... If this declaration is grouped
|
||||
* below, the cancel button will still appear in chrome.
|
||||
* If it's up here on it's own, it'll hide it.
|
||||
*/
|
||||
.vs__search::-webkit-search-cancel-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vs__search::-webkit-search-decoration,
|
||||
.vs__search::-webkit-search-results-button,
|
||||
.vs__search::-webkit-search-results-decoration,
|
||||
.vs__search::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vs__search,
|
||||
.vs__search:focus {
|
||||
color: var(--vs-search-input-color);
|
||||
appearance: none;
|
||||
line-height: var(--vs-line-height);
|
||||
font-size: var(--vs-font-size);
|
||||
border: 1px solid transparent;
|
||||
border-left: none;
|
||||
outline: none;
|
||||
margin: 4px 0 0 0;
|
||||
padding: 0 7px;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
width: 0;
|
||||
max-width: 100%;
|
||||
flex-grow: 1;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.vs__search::placeholder {
|
||||
color: var(--vs-search-input-placeholder-color);
|
||||
}
|
||||
|
||||
/**
|
||||
States
|
||||
*/
|
||||
|
||||
/* Unsearchable */
|
||||
.vs--unsearchable {
|
||||
.vs__search {
|
||||
opacity: 1;
|
||||
}
|
||||
&:not(.vs--disabled) .vs__search {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
/* Single, when searching but not loading or open */
|
||||
.vs--single.vs--searching:not(.vs--open):not(.vs--loading) {
|
||||
.vs__search {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* Selected Tags */
|
||||
.vs__selected {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: var(--vs-selected-bg);
|
||||
border: var(--vs-selected-border-width) var(--vs-selected-border-style)
|
||||
var(--vs-selected-border-color);
|
||||
border-radius: var(--vs-border-radius);
|
||||
color: var(--vs-selected-color);
|
||||
line-height: var(--vs-line-height);
|
||||
margin: 4px 2px 0px 2px;
|
||||
padding: 0 0.25em;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.vs__deselect {
|
||||
display: inline-flex;
|
||||
appearance: none;
|
||||
margin-left: 4px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
fill: var(--vs-controls-color);
|
||||
text-shadow: var(--vs-controls--deselect-text-shadow);
|
||||
}
|
||||
|
||||
/* States */
|
||||
|
||||
.vs--single {
|
||||
.vs__selected {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
&.vs--open .vs__selected,
|
||||
&.vs--loading .vs__selected {
|
||||
position: absolute;
|
||||
opacity: 0.4;
|
||||
}
|
||||
&.vs--searching .vs__selected {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Loading Spinner */
|
||||
.vs__spinner {
|
||||
align-self: center;
|
||||
opacity: 0;
|
||||
font-size: 5px;
|
||||
text-indent: -9999em;
|
||||
overflow: hidden;
|
||||
border-top: 0.9em solid rgba(100, 100, 100, 0.1);
|
||||
border-right: 0.9em solid rgba(100, 100, 100, 0.1);
|
||||
border-bottom: 0.9em solid rgba(100, 100, 100, 0.1);
|
||||
border-left: 0.9em solid rgba(60, 60, 60, 0.45);
|
||||
transform: translateZ(0)
|
||||
scale(var(--vs-controls--spinner-size, var(--vs-controls-size)));
|
||||
animation: vSelectSpinner 1.1s infinite linear;
|
||||
transition: opacity 0.1s;
|
||||
}
|
||||
.vs__spinner,
|
||||
.vs__spinner:after {
|
||||
border-radius: 50%;
|
||||
width: 5em;
|
||||
height: 5em;
|
||||
transform: scale(var(--vs-controls--spinner-size, var(--vs-controls-size)));
|
||||
}
|
||||
|
||||
/* Loading Spinner States */
|
||||
.vs--loading .vs__spinner {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
@import 'global/variables.css';
|
||||
@import 'global/component.css';
|
||||
@import 'global/animations.css';
|
||||
@import 'global/states.css';
|
||||
|
||||
@import 'modules/dropdown-toggle.css';
|
||||
@import 'modules/open-indicator.css';
|
||||
@import 'modules/clear.css';
|
||||
@import 'modules/dropdown-menu.css';
|
||||
@import 'modules/dropdown-option.css';
|
||||
@import 'modules/selected.css';
|
||||
@import 'modules/search-input.css';
|
||||
@import 'modules/spinner.css';
|
||||
@@ -14,6 +14,16 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
open(open) {
|
||||
if (open) {
|
||||
this.typeAheadToLastSelected()
|
||||
}
|
||||
},
|
||||
selectedValue() {
|
||||
if (this.open) {
|
||||
this.typeAheadToLastSelected()
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
@@ -57,9 +67,21 @@ export default {
|
||||
typeAheadSelect() {
|
||||
const typeAheadOption = this.filteredOptions[this.typeAheadPointer]
|
||||
|
||||
if (typeAheadOption) {
|
||||
if (typeAheadOption && this.selectable(typeAheadOption)) {
|
||||
this.select(typeAheadOption)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Moves the pointer to the last selected option.
|
||||
*/
|
||||
typeAheadToLastSelected() {
|
||||
this.typeAheadPointer =
|
||||
this.selectedValue.length !== 0
|
||||
? this.filteredOptions.indexOf(
|
||||
this.selectedValue[this.selectedValue.length - 1]
|
||||
)
|
||||
: -1
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
$vs-colors: (
|
||||
lightest: rgba(60, 60, 60, 0.26),
|
||||
light: rgba(60, 60, 60, 0.5),
|
||||
dark: #333,
|
||||
darkest: rgba(0, 0, 0, .15),
|
||||
lightest: rgba(60, 60, 60, 0.26),
|
||||
light: rgba(60, 60, 60, 0.5),
|
||||
dark: #333,
|
||||
darkest: rgba(0, 0, 0, 0.15),
|
||||
) !default;
|
||||
|
||||
// Global Component Variables
|
||||
$vs-component-bg: none !default;
|
||||
$vs-component-line-height: 1.4 !default;
|
||||
$vs-component-placeholder-color: inherit !default;
|
||||
|
||||
|
||||
@@ -16,39 +16,39 @@ $border-color: $vs-border-color;
|
||||
$border-radius: $vs-border-radius;
|
||||
|
||||
.vs__dropdown-toggle {
|
||||
appearance: none;
|
||||
display: flex;
|
||||
padding: 0 0 4px 0;
|
||||
background: none;
|
||||
border: $border-width $border-style $border-color;
|
||||
border-radius: $border-radius;
|
||||
white-space: normal;
|
||||
appearance: none;
|
||||
display: flex;
|
||||
padding: 0 0 4px 0;
|
||||
background: $vs-component-bg;
|
||||
border: $border-width $border-style $border-color;
|
||||
border-radius: $border-radius;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.vs__selected-options {
|
||||
display: flex;
|
||||
flex-basis: 100%;
|
||||
flex-grow: 1;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 2px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-basis: 100%;
|
||||
flex-grow: 1;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 2px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.vs__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 6px 0 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 6px 0 3px;
|
||||
}
|
||||
|
||||
/* Dropdown Toggle States */
|
||||
.vs--searchable .vs__dropdown-toggle {
|
||||
cursor: text;
|
||||
cursor: text;
|
||||
}
|
||||
.vs--unsearchable .vs__dropdown-toggle {
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
.vs--open .vs__dropdown-toggle {
|
||||
border-bottom-color: transparent;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-color: transparent;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
+22
-12
@@ -1,13 +1,23 @@
|
||||
@import "global/variables";
|
||||
@import "global/component";
|
||||
@import "global/animations";
|
||||
@import "global/states";
|
||||
/**
|
||||
Support for SASS is deprecated as of v3.18.
|
||||
|
||||
@import "modules/dropdown-toggle";
|
||||
@import "modules/open-indicator";
|
||||
@import "modules/clear";
|
||||
@import "modules/dropdown-menu";
|
||||
@import "modules/dropdown-option";
|
||||
@import "modules/selected";
|
||||
@import "modules/search-input";
|
||||
@import "modules/spinner";
|
||||
The files remain here if your build is dependent on them
|
||||
but they will not receive updates in future releases. All
|
||||
SASS variables have been translated into CSS variables, so
|
||||
migration should be quite simple if you'd like to move over.
|
||||
|
||||
In v4, these files will be removed.
|
||||
*/
|
||||
@import 'global/variables';
|
||||
@import 'global/component';
|
||||
@import 'global/animations';
|
||||
@import 'global/states';
|
||||
|
||||
@import 'modules/dropdown-toggle';
|
||||
@import 'modules/open-indicator';
|
||||
@import 'modules/clear';
|
||||
@import 'modules/dropdown-menu';
|
||||
@import 'modules/dropdown-option';
|
||||
@import 'modules/selected';
|
||||
@import 'modules/search-input';
|
||||
@import 'modules/spinner';
|
||||
|
||||
+14
-2
@@ -9,11 +9,23 @@ import Vue from 'vue'
|
||||
* @param Wrapper {Wrapper<Vue>}
|
||||
* @param searchText
|
||||
*/
|
||||
export const searchSubmit = (Wrapper, searchText = false) => {
|
||||
export const searchSubmit = async (Wrapper, searchText = undefined) => {
|
||||
await Wrapper.get('input').trigger('focus')
|
||||
|
||||
if (searchText) {
|
||||
Wrapper.vm.search = searchText
|
||||
await Wrapper.vm.$nextTick()
|
||||
}
|
||||
Wrapper.get('input').trigger('keydown.enter')
|
||||
|
||||
await Wrapper.get('input').trigger('keydown.enter')
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus the search input
|
||||
*/
|
||||
export const searchFocus = async (Wrapper) => {
|
||||
await Wrapper.get('input').trigger('focus')
|
||||
await Wrapper.vm.$nextTick()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@ describe('Search Slot Scope', () => {
|
||||
/**
|
||||
* @see https://www.w3.org/WAI/PF/aria/states_and_properties#aria-activedescendant
|
||||
*/
|
||||
describe('aria-activedescendant', () => {
|
||||
fdescribe('aria-activedescendant', () => {
|
||||
it('adds the active descendant attribute only when the dropdown is open and there is a typeAheadPointer value', async () => {
|
||||
const Select = mountDefault()
|
||||
|
||||
@@ -21,7 +21,11 @@ describe('Search Slot Scope', () => {
|
||||
})
|
||||
|
||||
it("adds the active descendant attribute when there's a typeahead value and an open dropdown", async () => {
|
||||
const Select = mountDefault()
|
||||
const Select = mountDefault({ modelValue: 'three' }, [
|
||||
'one',
|
||||
'two',
|
||||
'three',
|
||||
])
|
||||
|
||||
Select.vm.open = true
|
||||
Select.vm.typeAheadPointer = 1
|
||||
@@ -29,7 +33,7 @@ describe('Search Slot Scope', () => {
|
||||
|
||||
expect(
|
||||
Select.vm.scope.search.attributes['aria-activedescendant']
|
||||
).toEqual(`vs${Select.vm.uid}__option-1`)
|
||||
).toEqual(`vs${Select.vm.uid}__option-2`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { searchSubmit, selectTag, selectWithProps } from '../helpers'
|
||||
import { selectTag, selectWithProps } from '../helpers'
|
||||
|
||||
describe('CreateOption When Tagging Is Enabled', () => {
|
||||
it('can select the current search text as a string', async () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { selectWithProps } from '../helpers'
|
||||
import { searchSubmit, selectWithProps } from '../helpers'
|
||||
|
||||
describe('Selectable prop', () => {
|
||||
it('should select selectable option if clicked', async () => {
|
||||
@@ -54,4 +54,19 @@ describe('Selectable prop', () => {
|
||||
|
||||
expect(Select.vm.typeAheadPointer).toEqual(0)
|
||||
})
|
||||
|
||||
it('should not let the user select an unselectable option with return', async () => {
|
||||
const Select = selectWithProps({
|
||||
options: ['one', 'two'],
|
||||
multiple: true,
|
||||
selectable: (option) => option !== 'two',
|
||||
})
|
||||
|
||||
// this sets the typeAheadPointer to 0
|
||||
await searchSubmit(Select, 'one')
|
||||
expect(Select.vm.selectedValue).toEqual(['one'])
|
||||
|
||||
await searchSubmit(Select, 'two')
|
||||
expect(Select.vm.selectedValue).toEqual(['one'])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -218,6 +218,23 @@ describe('VS - Selecting Values', () => {
|
||||
expect(Select.vm.selectedValue).toEqual(options)
|
||||
})
|
||||
|
||||
fit('can select a false boolean option', async () => {
|
||||
const Select = mountDefault({
|
||||
options: [false],
|
||||
})
|
||||
|
||||
expect(Select.vm.isOptionSelected(false)).toBeFalsy()
|
||||
expect(Select.vm.optionExists(false)).toBeTruthy()
|
||||
|
||||
Select.vm.open = true
|
||||
await Select.vm.$nextTick()
|
||||
|
||||
Select.find('.vs__dropdown-option').trigger('click')
|
||||
await Select.vm.$nextTick()
|
||||
|
||||
expect(Select.vm.selectedValue).toEqual([false])
|
||||
})
|
||||
|
||||
describe('input Event', () => {
|
||||
it('will trigger the input event when the selection changes', () => {
|
||||
const Select = shallowMount(VueSelect)
|
||||
|
||||
@@ -165,10 +165,7 @@ describe('When Tagging Is Enabled', () => {
|
||||
options: [{ label: 'one' }, two],
|
||||
})
|
||||
|
||||
Select.vm.search = 'two'
|
||||
await Select.vm.$nextTick()
|
||||
|
||||
searchSubmit(Select)
|
||||
await searchSubmit(Select, 'two')
|
||||
expect(Select.vm.selectedValue).toEqual([two])
|
||||
})
|
||||
|
||||
@@ -180,10 +177,7 @@ describe('When Tagging Is Enabled', () => {
|
||||
options: [{ label: 'one' }, two],
|
||||
})
|
||||
|
||||
Select.vm.search = 'two'
|
||||
await Select.vm.$nextTick()
|
||||
|
||||
searchSubmit(Select)
|
||||
await searchSubmit(Select, 'two')
|
||||
expect(Select.vm.selectedValue).toEqual([two])
|
||||
})
|
||||
|
||||
|
||||
@@ -44,4 +44,37 @@ describe('Moving the Typeahead Pointer', () => {
|
||||
Select.vm.typeAheadDown()
|
||||
expect(Select.vm.typeAheadPointer).toEqual(2)
|
||||
})
|
||||
|
||||
it('will set the pointer to the selected option when opening', async () => {
|
||||
const Select = shallowMount(VueSelect, {
|
||||
propsData: {
|
||||
modelValue: 'three',
|
||||
options: ['one', 'two', 'three'],
|
||||
},
|
||||
})
|
||||
|
||||
Select.get('input').trigger('focus')
|
||||
await Select.vm.$nextTick()
|
||||
|
||||
expect(Select.vm.typeAheadPointer).toEqual(2)
|
||||
})
|
||||
|
||||
it('will set the pointer to the reduced selected option when opening', async () => {
|
||||
const Select = shallowMount(VueSelect, {
|
||||
propsData: {
|
||||
modelValue: 3,
|
||||
reduce: ({ value }) => value,
|
||||
options: [
|
||||
{ label: 'one', value: 1 },
|
||||
{ label: 'two', value: 2 },
|
||||
{ label: 'three', value: 3 },
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
Select.get('input').trigger('focus')
|
||||
await Select.vm.$nextTick()
|
||||
|
||||
expect(Select.vm.typeAheadPointer).toEqual(2)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1727,16 +1727,16 @@ cosmiconfig@^6.0.0:
|
||||
path-type "^4.0.0"
|
||||
yaml "^1.7.2"
|
||||
|
||||
coveralls@^3.0.2:
|
||||
version "3.0.9"
|
||||
resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.0.9.tgz#8cfc5a5525f84884e2948a0bf0f1c0e90aac0420"
|
||||
integrity sha512-nNBg3B1+4iDox5A5zqHKzUTiwl2ey4k2o0NEcVZYvl+GOSJdKBj4AJGKLv6h3SvWch7tABHePAQOSZWM9E2hMg==
|
||||
coveralls@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.1.1.tgz#f5d4431d8b5ae69c5079c8f8ca00d64ac77cf081"
|
||||
integrity sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww==
|
||||
dependencies:
|
||||
js-yaml "^3.13.1"
|
||||
lcov-parse "^1.0.0"
|
||||
log-driver "^1.2.7"
|
||||
minimist "^1.2.0"
|
||||
request "^2.88.0"
|
||||
minimist "^1.2.5"
|
||||
request "^2.88.2"
|
||||
|
||||
create-error-class@^3.0.0:
|
||||
version "3.0.2"
|
||||
@@ -2098,7 +2098,14 @@ domexception@^4.0.0:
|
||||
dependencies:
|
||||
webidl-conversions "^7.0.0"
|
||||
|
||||
domhandler@^4.2.0, domhandler@^4.2.2, domhandler@^4.3.0:
|
||||
domhandler@^4.2.0:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f"
|
||||
integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==
|
||||
dependencies:
|
||||
domelementtype "^2.2.0"
|
||||
|
||||
domhandler@^4.2.2, domhandler@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626"
|
||||
integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==
|
||||
@@ -4427,6 +4434,11 @@ lodash.merge@^4.6.1, lodash.merge@^4.6.2:
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lodash.sortby@^4.7.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
|
||||
|
||||
lodash.toarray@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
|
||||
@@ -6168,7 +6180,7 @@ repeat-string@^1.6.1:
|
||||
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
|
||||
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
|
||||
|
||||
request@^2.88.0:
|
||||
request@^2.88.0, request@^2.88.2:
|
||||
version "2.88.2"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
|
||||
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
|
||||
@@ -7086,12 +7098,12 @@ tough-cookie@~2.5.0:
|
||||
psl "^1.1.28"
|
||||
punycode "^2.1.1"
|
||||
|
||||
tr46@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
|
||||
integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
|
||||
tr46@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
|
||||
integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=
|
||||
dependencies:
|
||||
punycode "^2.1.1"
|
||||
punycode "^2.1.0"
|
||||
|
||||
traverse@~0.6.6:
|
||||
version "0.6.6"
|
||||
@@ -7538,7 +7550,20 @@ vscode-vue-languageservice@0.31.4:
|
||||
vscode-pug-languageservice "0.31.4"
|
||||
vscode-typescript-languageservice "0.31.4"
|
||||
|
||||
vue-eslint-parser@^8.0.0, vue-eslint-parser@^8.0.1:
|
||||
vue-eslint-parser@^7.0.0:
|
||||
version "7.10.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-7.10.0.tgz#ea4e4b10fd10aa35c8a79ac783488d8abcd29be8"
|
||||
integrity sha512-7tc/ewS9Vq9Bn741pvpg8op2fWJPH3k32aL+jcIcWGCTzh/zXSdh7pZ5FV3W2aJancP9+ftPAv292zY5T5IPCg==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
eslint-scope "^5.1.1"
|
||||
eslint-visitor-keys "^1.1.0"
|
||||
espree "^6.2.1"
|
||||
esquery "^1.4.0"
|
||||
lodash "^4.17.21"
|
||||
semver "^6.3.0"
|
||||
|
||||
vue-eslint-parser@^8.0.0:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-8.2.0.tgz#8c3990deb901b0d528d99f4d052a831cd1d0284c"
|
||||
integrity sha512-hvl8OVT8imlKk/lQyhkshqwQQChzHETcBd5abiO4ePw7ib7QUZLfW+2TUrJHKUvFOCFRJrDin5KJO9OHzB5bRQ==
|
||||
@@ -7591,6 +7616,11 @@ wcwidth@^1.0.0:
|
||||
dependencies:
|
||||
defaults "^1.0.3"
|
||||
|
||||
webidl-conversions@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
||||
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
|
||||
|
||||
webidl-conversions@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
|
||||
|
||||
Reference in New Issue
Block a user