2
0
mirror of https://github.com/tenrok/vue-select.git synced 2026-06-07 07:12:23 +03:00

Merge branch 'master' into ft/prettier

This commit is contained in:
Jeff Sagal
2021-07-29 20:17:39 -07:00
78 changed files with 5113 additions and 2869 deletions
+8 -2
View File
@@ -29,10 +29,16 @@ jobs:
node-version: 12
- name: Install dependencies
run: yarn install --frozen-lockfile
run: |
yarn install --frozen-lockfile
cd docs
yarn install --frozen-lockfile
- name: Build
- name: Build Dist
run: yarn build
- name: Bundlewatch
run: npx bundlewatch
- name: Build Docs
run: yarn build:docs
+1
View File
@@ -2,6 +2,7 @@
node_modules
# local env files
.env
.env.local
.env.*.local
+35 -16
View File
@@ -1,18 +1,22 @@
# vue-select ![Current Release](https://img.shields.io/github/release/sagalbot/vue-select.svg?style=flat-square) ![Bundle Size](https://flat.badgen.net/bundlephobia/min/vue-select) ![Monthly Downloads](https://img.shields.io/npm/dm/vue-select.svg?style=flat-square) ![Code Coverage](https://img.shields.io/coveralls/github/sagalbot/vue-select.svg?style=flat-square) ![Maintainability Score](https://img.shields.io/codeclimate/maintainability/sagalbot/vue-select.svg?style=flat-square) ![MIT License](https://img.shields.io/github/license/sagalbot/vue-select.svg?style=flat-square)
# vue-select ![Current Release](https://img.shields.io/github/release/sagalbot/vue-select.svg?style=flat-square) ![Release Date](https://img.shields.io/github/release-date/sagalbot/vue-select?style=flat-square) ![Bundle Size](https://flat.badgen.net/bundlephobia/min/vue-select) ![Monthly Downloads](https://img.shields.io/npm/dm/vue-select.svg?style=flat-square) [![Coverage Status](https://coveralls.io/repos/github/sagalbot/vue-select/badge.svg?branch=master)](https://coveralls.io/github/sagalbot/vue-select?branch=master) ![MIT License](https://img.shields.io/github/license/sagalbot/vue-select.svg?style=flat-square)
> **Everything you wish the HTML `<select>` element could do, wrapped up into a lightweight, zero
dependency, extensible Vue component.**
> **Everything you wish the HTML `<select>` element could do, wrapped up into a lightweight, zero
> dependency, extensible Vue component.**
Vue Select is a feature rich select/dropdown/typeahead component. It provides a default
template that fits most use cases for a filterable select dropdown. The component is designed to be as
lightweight as possible, while maintaining high standards for accessibility,
developer experience, and customization.
- Tagging
- Filtering / Searching
- Vuex Support
- AJAX Support
- SSR Support
- Accessible
- ~20kb Total / ~5kb CSS / ~15kb JS
- Select Single/Multiple Options
- Customizable with slots and SCSS variables
- Tested with Bootstrap 3/4, Bulma, Foundation
- +95% Test Coverage
- Zero dependencies
## Documentation
@@ -20,32 +24,47 @@ dependency, extensible Vue component.**
Complete documentation and examples available at https://vue-select.org.
- **[API Documentation](https://vue-select.org)**
- **[Sandbox Demo](https://vue-select.org/sandbox.html)**
- **[CodePen Template](http://codepen.io/sagalbot/pen/NpwrQO)**
- **[GitHub Projects](https://github.com/sagalbot/vue-select/projects)**
## Sponsors :tada:
It takes a lot of effort to maintain this project. If it has saved you development time, please consider [sponsoring the project](https://github.com/sponsors/sagalbot)
with GitHub sponsors!
Huge thanks to the [sponsors](https://github.com/sponsors/sagalbot) and [contributors](https://github.com/sagalbot/vue-select/graphs/contributors) that make Vue Select possible!
## Install
```bash
$ npm install vue-select
yarn add vue-select
# or use npm
npm install vue-select
```
Register the component
Then, import and register the component:
```js
import Vue from 'vue'
import vSelect from 'vue-select'
import Vue from "vue";
import vSelect from "vue-select";
Vue.component('v-select', vSelect)
Vue.component("v-select", vSelect);
```
You may now use the component in your markup
The component itself does not include any CSS. You'll need to include it separately:
```html
<v-select v-model="selected" :options="['Vue.js','React']"></v-select>
```js
import "vue-select/dist/vue-select.css";
```
You can also include vue-select directly in the browser. Check out the
Alternatively, you can import the scss for complete control of the component styles:
```scss
@import "vue-select/src/scss/vue-select.scss";
```
You can also include vue-select directly in the browser. Check out the
[documentation for loading from CDN.](https://vue-select.org/guide/install.html#in-the-browser).
## License
+29 -24
View File
@@ -1,39 +1,44 @@
<template>
<div id="app">
<sandbox hide-help v-slot="config">
<v-select v-bind="config"/>
</sandbox>
<v-select v-model="selected" v-bind="config" />
</div>
</template>
<script>
import vSelect from '../src/components/Select';
import Sandbox from '../docs/.vuepress/components/Sandbox';
// import countries from '../docs/.vuepress/data/countryCodes';
// import books from '../docs/.vuepress/data/books';
import vSelect from "../src/components/Select";
import countries from "../docs/.vuepress/data/countryCodes";
import books from "../docs/.vuepress/data/books";
export default {
components: {Sandbox, vSelect},
components: { vSelect },
data: () => ({
selected: null,
config: {
options: countries
}
})
};
</script>
<style>
html,
body {
margin: 0;
height: 100%;
font-family: -apple-system, sans-serif;
}
html,
body {
margin: 0;
height: 100%;
font-family: -apple-system, sans-serif;
}
#app {
height: 100%;
}
#app {
height: 100%;
max-width: 20rem;
margin: 10rem auto 0;
}
hr {
border: none;
border-bottom: 1px solid #cacaca;
margin-bottom: 1em;
padding-top: 1em;
width: 90%;
}
hr {
border: none;
border-bottom: 1px solid #cacaca;
margin-bottom: 1em;
padding-top: 1em;
width: 90%;
}
</style>
+1
View File
@@ -3,6 +3,7 @@
<head>
<meta charset="utf-8">
<title>Vue Select Dev</title>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
<!--<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.5.3/css/foundation.min.css">-->
<!--<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css">-->
<!--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">-->
+3
View File
@@ -0,0 +1,3 @@
# personal access token used to fetch
# sponsor listings when working locally
GITHUB_TOKEN=
@@ -1,9 +1,10 @@
<template>
<div>
<v-select
placeholder="choose a country"
v-model="selected"
:options="['Canada', 'United States']"
:components="{Deselect}"
:options="['Canada', 'United States']"
/>
</div>
</template>
@@ -0,0 +1,63 @@
<template>
<ul>
<li v-for="{ login, avatar_url, html_url, contributions } in contributors">
<img :src="`${avatar_url}&s=75`" :alt="`${login}'s Avatar`" />
<div>
<a :href="html_url">@{{ login }}</a>
<br /><a
class="contributions-link"
:href="
`https://github.com/sagalbot/vue-select/commits?author=${login}`
"
>{{ contributions }} contributions</a
>
</div>
</li>
</ul>
</template>
<script>
import { CONTRIBUTORS } from "@dynamic/constants";
export default {
data: () => ({
contributors: CONTRIBUTORS.filter(
({ login }) => login !== "semantic-release-bot"
)
})
};
</script>
<style scoped>
ul {
list-style: none;
padding: 0;
max-width: 100%;
margin-top: 2rem;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
img {
width: 75px;
height: 75px;
margin-right: 1rem;
border-radius: 100%;
}
li {
display: inline-flex;
align-items: center;
margin-bottom: 2rem;
margin-right: 1rem;
}
a {
display: inline-block;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.contributions-link {
color: #2c5282;
}
</style>
+31
View File
@@ -0,0 +1,31 @@
<template>
<v-select :filter="fuseSearch" :options="books" :getOptionLabel="option => option.title">
<template #option="{ author, title }">
{{ title }}
<br />
<cite>{{ author.firstName }} {{ author.lastName }}</cite>
</template>
</v-select>
</template>
<script>
import Fuse from "fuse.js";
import books from "../data/books";
export default {
computed: {
books: () => books
},
methods: {
fuseSearch(options, search) {
const fuse = new Fuse(options, {
keys: ["title", "author.firstName", "author.lastName"],
shouldSort: true
});
return search.length
? fuse.search(search).map(({ item }) => item)
: fuse.list;
}
}
};
</script>
@@ -0,0 +1,74 @@
<template>
<v-select
:options="paginated"
:filterable="false"
@open="onOpen"
@close="onClose"
@search="query => search = query"
>
<template #list-footer>
<li ref="load" class="loader" v-show="hasNextPage">
Loading more options...
</li>
</template>
</v-select>
</template>
<script>
import countries from '../data/countries';
export default {
name: "InfiniteScroll",
data: () => ({
observer: null,
limit: 10,
search: ''
}),
mounted () {
/**
* You could do this directly in data(), but since these docs
* are server side rendered, IntersectionObserver doesn't exist
* in that environment, so we need to do it in mounted() instead.
*/
this.observer = new IntersectionObserver(this.infiniteScroll);
},
computed: {
filtered () {
return countries.filter(country => country.includes(this.search));
},
paginated () {
return this.filtered.slice(0, this.limit);
},
hasNextPage () {
return this.paginated.length < this.filtered.length;
},
},
methods: {
async onOpen () {
if (this.hasNextPage) {
await this.$nextTick();
this.observer.observe(this.$refs.load)
}
},
onClose () {
this.observer.disconnect();
},
async infiniteScroll ([{isIntersecting, target}]) {
if (isIntersecting) {
const ul = target.offsetParent;
const scrollTop = target.offsetParent.scrollTop;
this.limit += 10;
await this.$nextTick();
ul.scrollTop = scrollTop;
}
}
}
}
</script>
<style scoped>
.loader {
text-align: center;
color: #bbbbbb;
}
</style>
+49
View File
@@ -0,0 +1,49 @@
<template>
<v-select :options="paginated" @search="query => search = query" :filterable="false">
<li slot="list-footer" class="pagination">
<button @click="offset -= 10" :disabled="!hasPrevPage">Prev</button>
<button @click="offset += 10" :disabled="!hasNextPage">Next</button>
</li>
</v-select>
</template>
<script>
import countries from '../data/countries';
export default {
data: () => ({
countries,
search: '',
offset: 0,
limit: 10,
}),
computed: {
filtered () {
return this.countries.filter(country => country.includes(this.search));
},
paginated () {
return this.filtered.slice(this.offset, this.limit + this.offset);
},
hasNextPage () {
const nextOffset = this.offset + 10;
return Boolean(this.filtered.slice(nextOffset, this.limit + nextOffset).length);
},
hasPrevPage () {
const prevOffset = this.offset - 10;
return Boolean(this.filtered.slice(prevOffset, this.limit + prevOffset).length);
}
},
};
</script>
<style scoped>
.pagination {
display: flex;
margin: .25rem .25rem 0;
}
.pagination button {
flex-grow: 1;
}
.pagination button:hover {
cursor: pointer;
}
</style>
@@ -22,7 +22,7 @@ import { createPopper } from '@popperjs/core';
export default {
data: () => ({countries, placement: 'top'}),
methods: {
withPopper (dropdownList, component, {width},) {
withPopper (dropdownList, component, {width}) {
/**
* We need to explicitly define the dropdown width since
* it is usually inherited from the parent with CSS.
@@ -39,7 +39,7 @@ export default {
* wrapper so that we can set some styles for when the dropdown is placed
* above.
*/
createPopper(component.$refs.toggle, dropdownList, {
const popper = createPopper(component.$refs.toggle, dropdownList, {
placement: this.placement,
modifiers: [
{
@@ -56,6 +56,12 @@ export default {
},
}]
});
/**
* To prevent memory leaks Popper needs to be destroyed.
* If you return function, it will be called just before dropdown is removed from DOM.
*/
return () => popper.destroy();
}
}
};
-5
View File
@@ -226,11 +226,6 @@ export default {
loading(false);
});
}, 250),
fuseSearch (options, search) {
return new Fuse(options, {
keys: ['title', 'author.firstName', 'author.lastName'],
}).search(search);
},
},
};
</script>
+7
View File
@@ -0,0 +1,7 @@
<template>
<v-select append-to-body>
<template #footer>
<div style="opacity: .8">Bottom of the component, in the footer slot!</div>
</template>
</v-select>
</template>
+7
View File
@@ -0,0 +1,7 @@
<template>
<v-select>
<template #header>
<div style="opacity: .8">Top of the component, in the header slot!</div>
</template>
</v-select>
</template>
@@ -0,0 +1,7 @@
<template>
<v-select>
<template #list-footer>
<li style="text-align: center">Bottom of the list!</li>
</template>
</v-select>
</template>
@@ -0,0 +1,7 @@
<template>
<v-select>
<template #list-header>
<li style="text-align: center">Top of the list!</li>
</template>
</v-select>
</template>
@@ -0,0 +1,7 @@
<template>
<v-select>
<template #no-options="{ search, searching, loading }">
This is the no options slot.
</template>
</v-select>
</template>
@@ -0,0 +1,7 @@
<template>
<v-select>
<template #open-indicator="{ attributes }">
<span v-bind="attributes">🔽</span>
</template>
</v-select>
</template>
+24
View File
@@ -0,0 +1,24 @@
<template>
<v-select :options="books" label="title">
<template #option="{ title, author }">
<h3 style="margin: 0">{{ title }}</h3>
<em>{{ author.firstName }} {{ author.lastName }}</em>
</template>
</v-select>
</template>
<script>
export default {
data: () => ({
books: [
{
title: "Old Man's War",
author: {
firstName: "John",
lastName: "Scalzi"
}
}
]
})
}
</script>
+12
View File
@@ -0,0 +1,12 @@
<template>
<v-select>
<template #search="{ attributes, events }">
<input
maxlength="1"
class="vs__search"
v-bind="attributes"
v-on="events"
>
</template>
</v-select>
</template>
@@ -0,0 +1,26 @@
<template>
<v-select v-model="selected" :options="books" label="title">
<template #selected-option="{ title, author }">
<div style="display: flex; align-items: baseline;">
<strong>{{ title }}</strong>
<em style="margin-left: .5rem;">by {{ author.firstName }} {{ author.lastName }}</em>
</div>
</template>
</v-select>
</template>
<script>
const book = {
title: "Old Man's War",
author: {
firstName: "John",
lastName: "Scalzi"
}
};
export default {
data: () => ({
books: [book],
selected: book
})
}
</script>
@@ -0,0 +1,23 @@
<template>
<v-select :options="books" label="title">
<template #selected-option-container="{ option, deselect, multiple, disabled }">
<div class="vs__selected">{{ option.title }}</div>
</template>
</v-select>
</template>
<script>
export default {
data: () => ({
books: [
{
title: "Old Man's War",
author: {
firstName: "John",
lastName: "Scalzi"
}
}
]
})
}
</script>
@@ -0,0 +1,9 @@
<template>
<v-select :loading="true">
<template #spinner="{ loading }">
<div v-if="loading" style="border-left-color: rgba(88,151,251,0.71)" class="vs__spinner">
The .vs__spinner class will hide the text for me.
</div>
</template>
</v-select>
</template>
@@ -0,0 +1,25 @@
<template>
<p class="sponsor">
Are you using Vue Select on a lot of projects? Please consider
<a href="https://github.com/sponsors/sagalbot" target="_blank">
sponsoring @sagalbot!
</a>
</p>
</template>
<style scoped>
.sponsor {
display: block;
border: 2px solid #e2e8f0;
background: #f7fafc;
border-radius: 10px;
padding: 0.25rem;
font-weight: 600;
text-align: center;
margin: 0 auto;
color: #8492a4;
}
p a {
color: #48BB78;
}
</style>
+146
View File
@@ -0,0 +1,146 @@
<template>
<div class="sponsor-me">
<div class="avatar">
<a href="https://github.com/sponsors/sagalbot">
<img
src="https://avatars2.githubusercontent.com/u/692538?s=400&u=a5ab0d164266bd2d59ce1a514835627b4cc4f24f&v=4"
alt="Jeff Sagal's Avatar"
/>
</a>
</div>
<div class="cta">
<p style="font-size: 1.2rem; font-weight: 600;">
Hi! I'm Jeff Sagal, the author of Vue Select.
</p>
<p>
I've spent hundreds of hours working alongside contributors to make Vue
Select the best it can be. I've researched UX and accessibility
patterns, squashed bugs, reviewed code, tested-cross browser, and spent
many evenings and weekends working on these docs.
</p>
<p>
If it's saved you time on your projects, please consider supporting me
on GitHub sponsors.
</p>
<div class="links">
<a
href="https://github.com/sponsors/sagalbot"
class="button"
target="_blank"
>
<svg viewBox="0 0 20 20">
<path
d="M10 0a10 10 0 00-3.16 19.49c.5.1.68-.22.68-.48l-.01-1.7c-2.78.6-3.37-1.34-3.37-1.34-.46-1.16-1.11-1.47-1.11-1.47-.9-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.9 1.52 2.34 1.08 2.91.83.1-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.94 0-1.1.39-1.99 1.03-2.69a3.6 3.6 0 01.1-2.64s.84-.27 2.75 1.02a9.58 9.58 0 015 0c1.91-1.3 2.75-1.02 2.75-1.02.55 1.37.2 2.4.1 2.64.64.7 1.03 1.6 1.03 2.69 0 3.84-2.34 4.68-4.57 4.93.36.31.68.92.68 1.85l-.01 2.75c0 .26.18.58.69.48A10 10 0 0010 0"
/>
</svg>
Sponsor Vue Select!
</a>
<a href="https://twitter.com/sagalbot" target="_blank" class="social-link">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path
d="M23.954 4.569a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.691 8.094 4.066 6.13 1.64 3.161a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.061a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.937 4.937 0 004.604 3.417 9.868 9.868 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.054 0 13.999-7.496 13.999-13.986 0-.209 0-.42-.015-.63a9.936 9.936 0 002.46-2.548l-.047-.02z"
/>
</svg>
Follow @sagalbot
</a>
</div>
</div>
</div>
</template>
<style scoped>
.sponsor-me {
display: flex;
border: 2px solid #c3dafe;
background: #ebf4ff;
border-radius: 10px;
/*align-items: top;*/
padding: 2rem 1rem;
margin: 3rem 0;
flex-direction: column;
}
.avatar {
display: flex;
align-items: center;
justify-content: space-evenly;
padding: 1rem;
}
.cta {
/*text-align: center;*/
}
@media (min-width: 1150px) {
.sponsor-me {
flex-direction: row;
}
.avatar {
display: flex;
justify-content: flex-start;
flex-direction: column;
margin: 0 2rem;
}
.cta {
text-align: left;
}
}
p {
margin-top: 0;
}
.avatar img {
max-width: 150px;
border-radius: 100%;
}
.button {
display: inline-flex;
text-align: center;
align-items: center;
padding: 0.75rem 1rem;
background: #63b3ed;
color: #fff;
font-weight: 600;
border-radius: 5px;
margin-top: 0.5rem;
transition: background-color 0.25s, box-shadow 0.25s;
}
.button svg {
max-width: 25px;
fill: currentColor;
margin-right: 0.5rem;
}
a.button:hover {
box-shadow: inset 0px 0px 3px #3182ce;
background: #90cdf4;
text-decoration: none;
text-shadow: 0 1px 1px #63b3ed;
}
.links {
display: flex;
flex-direction: column;
align-items: center;
}
.links a {
margin: 1rem;
}
@media (min-width: 1150px) {
.links {
flex-direction: row;
justify-content: flex-start;
}
.links a {
margin: 0 2rem 0 0;
}
}
.social-link {
display: flex;
align-items: center;
color: #3182ce;
}
.social-link svg {
fill: currentColor;
width: 20px;
margin-right: 0.5rem;
}
.social-link:hover {
color: #2c5282;
text-decoration: none;
}
</style>
+56
View File
@@ -0,0 +1,56 @@
<template>
<ul>
<li v-for="{ createdAt, login, avatarUrl } in sponsors">
<img :src="avatarUrl + '&s=150'" :alt="`@${login}'s avatar`" />
<p>
<a :href="`https://github.com/${login}`">@{{ login }}</a> <br />
Sponsor since {{ createdAt }}
</p>
</li>
</ul>
</template>
<script>
import { SPONSORS } from "@dynamic/constants";
import { format } from "date-fns";
export default {
data: () => ({
sponsors: SPONSORS.map(({ createdAt, sponsorEntity }) => ({
createdAt: format(new Date(createdAt), "LLL yyyy"),
...sponsorEntity
}))
}),
};
</script>
<style scoped>
ul {
list-style: none;
padding: 0;
max-width: 100%;
margin-top: 2rem;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
img {
width: 75px;
height: 75px;
margin-right: 1rem;
border-radius: 100%;
}
li {
display: inline-flex;
align-items: center;
margin-bottom: 2rem;
/*max-width: 220px;*/
}
a {
display: inline-block;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
+8 -142
View File
@@ -1,147 +1,13 @@
const isDeployPreview = process.env.hasOwnProperty('DEPLOY_PREVIEW');
const meta = {
title: 'Vue Select | VueJS Select2/Chosen Component',
description: 'Everything you wish the HTML select element could do, wrapped up into a lightweight, extensible Vue component.',
url: 'https://vue-select.org',
};
let head = [
[
'link',
{
href: '//fonts.googleapis.com/css?family=Source+Sans+Pro:400,600|Roboto Mono',
rel: 'stylesheet',
type: 'text/css',
}],
[
'link',
{
href: '//fonts.googleapis.com/css?family=Dosis:300&amp;text=Vue Select',
rel: 'stylesheet',
type: 'text/css',
}],
['link', {rel: 'icon', href: `/vue-logo.png`}],
['meta', {name: 'theme-color', content: '#3eaf7c'}],
['meta', {name: 'apple-mobile-web-app-capable', content: 'yes'}],
['meta', {name: 'apple-mobile-web-app-status-bar-style', content: 'black'}],
[
'link',
{rel: 'apple-touch-icon', href: `/icons/apple-touch-icon-152x152.png`}],
[
'link',
{rel: 'mask-icon', href: '/icons/safari-pinned-tab.svg', color: '#3eaf7c'}],
[
'meta',
{
name: 'msapplication-TileImage',
content: '/icons/msapplication-icon-144x144.png',
}],
['meta', {name: 'msapplication-TileColor', content: '#000000'}],
['meta', {name: 'title', content: meta.title}],
['meta', {name: 'description', content: meta.description}],
['link', {rel: 'icon', href: meta.icon, type: 'image/png'}],
['meta', {property: 'og:image', content: meta.icon}],
['meta', {property: 'twitter:image', content: meta.icon}],
['meta', {name: 'description', content: meta.description}],
['meta', {property: 'og:description', content: ''}],
['meta', {property: 'twitter:description', content: meta.description}],
['meta', {property: 'twitter:title', content: meta.title}],
['meta', {property: 'og:title', content: meta.title}],
['meta', {property: 'og:site_name', content: meta.title}],
['meta', {property: 'og:url', content: meta.url}],
];
if (isDeployPreview) {
head.push(
['meta', {name: 'robots', content: 'noindex'}],
['meta', {name: 'googlebot', content: 'noindex'}],
);
}
const {description} = require('./config/meta');
const head = require('./config/head');
const plugins = require('./config/plugins');
const themeConfig = require('./config/themeConfig');
module.exports = {
title: 'Vue Select',
description: meta.description,
description,
head,
plugins: {
'@vuepress/google-analytics': {
ga: isDeployPreview ? '' : 'UA-12818324-8',
},
'@vuepress/pwa': {
serviceWorker: false,
updatePopup: true,
},
'@vuepress/plugin-register-components': {},
'@vuepress/plugin-active-header-links': {},
'@vuepress/plugin-search': {},
'@vuepress/plugin-nprogress': {},
},
themeConfig: {
repo: 'sagalbot/vue-select',
editLinks: true,
docsDir: 'docs',
nav: [
{text: 'Home', link: '/'},
{text: 'Sandbox', link: '/sandbox'},
],
sidebar: {
'/': [
{
title: 'Getting Started',
collapsable: false,
children: [
['guide/install', 'Installation'],
['guide/options', 'Dropdown Options'],
['guide/values', 'Selecting Values'],
['guide/upgrading', 'Upgrading 2.x to 3.x'],
],
},
{
title: 'Templating & Styling',
collapsable: false,
children: [
['guide/components', 'Child Components'],
['guide/css', 'CSS & Selectors'],
['guide/slots', 'Slots'],
],
},
{
title: 'Accessibility',
collapsable: false,
children: [
['guide/accessibility', 'WAI-ARIA Spec'],
['guide/localization', 'Localization'],
],
},
{
title: 'Use Cases',
collapsable: false,
children: [
['guide/validation', 'Validation'],
['guide/selectable', 'Limiting Selections'],
['guide/vuex', 'Vuex'],
['guide/ajax', 'AJAX'],
['guide/loops', 'Using in Loops'],
],
},
{
title: 'Customizing',
collapsable: false,
children: [
['guide/keydown', 'Keydown Events'],
['guide/positioning', 'Dropdown Position']
],
},
{
title: 'API',
collapsable: false,
children: [
['api/props', 'Props'],
['api/slots', 'Slots'],
['api/events', 'Events'],
],
},
],
},
},
plugins,
themeConfig,
};
+57
View File
@@ -0,0 +1,57 @@
const isDeployPreview = require('./isDeployPreview');
const meta = require('./meta');
const head = [
[
'link',
{
href: '//fonts.googleapis.com/css?family=Source+Sans+Pro:400,600|Roboto Mono',
rel: 'stylesheet',
type: 'text/css',
}],
[
'link',
{
href: '//fonts.googleapis.com/css?family=Dosis:300&amp;text=Vue Select',
rel: 'stylesheet',
type: 'text/css',
}],
['link', {rel: 'icon', href: `/vue-logo.png`}],
['meta', {name: 'theme-color', content: '#3eaf7c'}],
['meta', {name: 'apple-mobile-web-app-capable', content: 'yes'}],
['meta', {name: 'apple-mobile-web-app-status-bar-style', content: 'black'}],
[
'link',
{rel: 'apple-touch-icon', href: `/icons/apple-touch-icon-152x152.png`}],
[
'link',
{rel: 'mask-icon', href: '/icons/safari-pinned-tab.svg', color: '#3eaf7c'}],
[
'meta',
{
name: 'msapplication-TileImage',
content: '/icons/msapplication-icon-144x144.png',
}],
['meta', {name: 'msapplication-TileColor', content: '#000000'}],
['meta', {name: 'title', content: meta.title}],
['meta', {name: 'description', content: meta.description}],
['link', {rel: 'icon', href: meta.icon, type: 'image/png'}],
['meta', {property: 'og:image', content: meta.icon}],
['meta', {property: 'twitter:image', content: meta.icon}],
['meta', {name: 'description', content: meta.description}],
['meta', {property: 'og:description', content: ''}],
['meta', {property: 'twitter:description', content: meta.description}],
['meta', {property: 'twitter:title', content: meta.title}],
['meta', {property: 'og:title', content: meta.title}],
['meta', {property: 'og:site_name', content: meta.title}],
['meta', {property: 'og:url', content: meta.url}],
];
if (isDeployPreview) {
head.push(
['meta', {name: 'robots', content: 'noindex'}],
['meta', {name: 'googlebot', content: 'noindex'}],
);
}
module.exports = head;
+1
View File
@@ -0,0 +1 @@
module.exports = process.env.hasOwnProperty('DEPLOY_PREVIEW');
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
title: 'Vue Select | VueJS Select2/Chosen Component',
description: 'Everything you wish the HTML select element could do, wrapped up into a lightweight, extensible Vue component.',
url: 'https://vue-select.org',
icon: '/vue-logo.png'
};
+22
View File
@@ -0,0 +1,22 @@
const isDeployPreview = require("./isDeployPreview");
module.exports = [
[
"@vuepress/google-analytics",
{
ga: isDeployPreview ? "" : "UA-12818324-8"
}
],
[
"@vuepress/pwa",
{
serviceWorker: false,
updatePopup: true
}
],
"@vuepress/plugin-register-components",
"@vuepress/plugin-active-header-links",
"@vuepress/plugin-search",
"@vuepress/plugin-nprogress",
require('../github/index')
];
+78
View File
@@ -0,0 +1,78 @@
module.exports = {
repo: 'sagalbot/vue-select',
editLinks: true,
docsDir: 'docs',
nav: [
{text: 'Sandbox', link: '/sandbox'},
],
sidebar: {
'/': [
{
title: 'Community',
collapsable: false,
children: [
['sponsors', 'Sponsors 🎉'],
['contributors', 'Contributors'],
],
},
{
title: 'Getting Started',
collapsable: false,
children: [
['guide/install', 'Installation'],
['guide/options', 'Dropdown Options'],
['guide/values', 'Selecting Values'],
['guide/upgrading', 'Upgrading 2.x to 3.x'],
],
},
{
title: 'Templating & Styling',
collapsable: false,
children: [
['guide/components', 'Child Components'],
['guide/css', 'CSS & Selectors'],
['guide/slots', 'Slots'],
],
},
{
title: 'Accessibility',
collapsable: false,
children: [
['guide/accessibility', 'WAI-ARIA Spec'],
['guide/localization', 'Localization'],
],
},
{
title: 'Use Cases',
collapsable: false,
children: [
['guide/validation', 'Validation'],
['guide/selectable', 'Limiting Selections'],
['guide/pagination', 'Pagination'],
['guide/infinite-scroll', 'Infinite Scroll'],
['guide/vuex', 'Vuex'],
['guide/ajax', 'AJAX'],
['guide/loops', 'Using in Loops'],
],
},
{
title: 'Customizing',
collapsable: false,
children: [
['guide/keydown', 'Keydown Events'],
['guide/positioning', 'Dropdown Position'],
['guide/filtering', 'Option Filtering'],
],
},
{
title: 'API',
collapsable: false,
children: [
['api/props', 'Props'],
['api/slots', 'Slots'],
['api/events', 'Events'],
],
},
],
},
};
@@ -0,0 +1,65 @@
require("dotenv").config();
const axios = require("axios");
const { graphql } = require("@octokit/graphql");
module.exports = async () => ({
name: "constants.js",
content: `
export const SPONSORS = ${JSON.stringify(await getSponsors())};
export const CONTRIBUTORS = ${JSON.stringify(await getContributors())};
`
});
/**
* Get a list of vue select contributors.
* @return {Promise<T>}
*/
async function getContributors() {
const { data } = await axios.get(
"https://api.github.com/repos/sagalbot/vue-select/contributors?per_page=100"
);
return data;
}
/**
* Get a list of the current sponsors. Requires GITHUB_TOKEN to be set.
* @return {Promise<*[]|ProfileNode[]|postcss.ChildNode[]|Array<parser.Node>|[]>}
*/
async function getSponsors() {
const query = `
{
user(login: "sagalbot") {
sponsorshipsAsMaintainer(first: 100) {
nodes {
sponsorEntity {
... on User {
id
avatarUrl
login
}
... on Organization {
id
avatarUrl
login
}
}
createdAt
}
}
}
}
`;
try {
const { user } = await graphql(query, {
headers: {
authorization: `token ${process.env.GITHUB_TOKEN || ""}`
}
});
return user.sponsorshipsAsMaintainer.nodes;
} catch (e) {
console.log(`${e.status} ${e.name} - Couldn't fetch sponsor data.`);
return [];
}
}
+5
View File
@@ -0,0 +1,5 @@
const clientDynamicModules = require('./clientDynamicModules');
module.exports = {
clientDynamicModules: async () => await clientDynamicModules(),
};
@@ -1,43 +0,0 @@
<script>
export default {
render (h) {
return h('div', {attrs: {id: 'codefund'}});
},
mounted () {
this.load();
},
watch: {
'$route' (to, from) {
if (
to.path !== from.path
// Only reload if the ad has been loaded
// otherwise it's possible that the script is appended but
// the ads are not loaded yet. This would result in duplicated ads.
&& this.$el.querySelector('#cf')
) {
this.$el.innerHTML = '';
this.load();
}
},
},
methods: {
load () {
// const template = 'default';
// const template = 'vertical';
// const template = 'float-with-close';
// const template = 'image-only';
// const template = 'image-centered';
// const template = 'square';
const template = 'centered';
// const template = 'bottom-bar';
const s = document.createElement('script');
s.id = '_codefund_js';
s.src = `//codefund.app/properties/193/funder.js?template=${template}`;
s.async = 'async';
this.$el.appendChild(s);
},
},
};
</script>
@@ -0,0 +1,54 @@
<script>
/**
* @see https://ethical-ad-client.readthedocs.io/en/latest/
*/
export default {
render(h) {
return h("div", {
attrs: {
id: "ads",
"data-ea-publisher": "vue-select",
"data-ea-type": "image"
},
class: "flat horizontal"
});
},
mounted() {
this.load();
},
watch: {
$route(to, from) {
if (
to.path !== from.path &&
// Only reload if the ad has been loaded
// otherwise it's possible that the script is appended but
// the ads are not loaded yet. This would result in duplicated ads.
[...this.$el.classList].includes("loaded")
) {
this.$el.innerHTML = "";
this.$el.classList.remove("loaded");
this.load();
}
}
},
methods: {
load() {
const s = document.createElement("script");
s.id = "_ads_js";
s.src = `https://media.ethicalads.io/media/client/ethicalads.min.js`;
s.async = "async";
this.$el.appendChild(s);
}
}
};
</script>
<style scoped>
#ads {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
min-height: 264px;
}
</style>
+9 -14
View File
@@ -1,27 +1,22 @@
<template>
<ParentLayout>
<CodeFund slot="sidebar-top"/>
</ParentLayout>
<ParentLayout></ParentLayout>
</template>
<script>
import ParentLayout from '@parent-theme/layouts/Layout.vue'
import CodeFund from '../components/CodeFund.vue'
import ParentLayout from "@parent-theme/layouts/Layout.vue";
export default {
components: {
ParentLayout,
CodeFund
}
}
};
</script>
<style>
#codefund {
border-bottom: 1px solid #eaecef;
min-height: 115px;
}
#codefund + .sidebar-links {
padding-top: 1rem;
}
#ads {
border-bottom: 1px solid #eaecef;
}
#ads + .sidebar-links {
padding-top: 1rem;
}
</style>
+24 -18
View File
@@ -1,38 +1,44 @@
<SponsorBanner />
# Vue Select
![Current Release](https://img.shields.io/github/release/sagalbot/vue-select.svg?style=flat-square)
![Bundle Size](https://flat.badgen.net/bundlephobia/min/vue-select)
![Monthly Downloads](https://img.shields.io/npm/dm/vue-select.svg?style=flat-square)
![Code Coverage](https://img.shields.io/coveralls/github/sagalbot/vue-select.svg?style=flat-square)
![Maintainability Score](https://img.shields.io/codeclimate/maintainability/sagalbot/vue-select.svg?style=flat-square)
![MIT License](https://img.shields.io/github/license/sagalbot/vue-select.svg?style=flat-square)
![Release Date](https://img.shields.io/github/release-date/sagalbot/vue-select?style=flat-square)
![Bundle Size](https://flat.badgen.net/bundlephobia/min/vue-select)
![Monthly Downloads](https://img.shields.io/npm/dm/vue-select.svg?style=flat-square)
![Coverage Status](https://coveralls.io/repos/github/sagalbot/vue-select/badge.svg?branch=master)
![MIT License](https://img.shields.io/github/license/sagalbot/vue-select.svg?style=flat-square)
> Everything you wish the HTML `<select>` element could do, wrapped
up into a lightweight, extensible Vue component.
> Everything you wish the HTML `<select>` element could do, wrapped
> up into a lightweight, extensible Vue component.
Vue Select is a feature rich select/dropdown/typeahead component. It provides a default
template that fits the 80% use case for a select dropdown. Here it is by default:
template that fits most use cases for a filterable select dropdown. The component is designed to be as
lightweight as possible, while maintaining high standards for accessibility,
developer experience, and customization.
<div style="max-width:25rem; margin: 0 auto; padding: 1rem 0;">
<country-select />
</div>
If you want to get a quick sense of what vue-select can do, check out
[the sandbox](sandbox.md).
Vue Select aims to be as lightweight as possible, while maintaining high standards for accessibility,
developer experience, and customization. Huge thanks to the [sponsors](sponsors.md) and
[contributors](contributors.md) that make Vue Select possible!
## Features
#### Features
- Tagging
- Filtering/Searching
- Filtering / Searching
- Vuex Support
- AJAX Support
- SSR Support
- Select Single/Multiple Options
- Tested with Bootstrap 3/4, Bulma, Foundation
- +95% Test Coverage
- Accessible
- ~20kb Total / ~5kb CSS / ~15kb JS
- Select Single/Multiple Options
- Customizable with slots and SCSS variables
- Zero dependencies
#### Resources
- **[CodePen Template](http://codepen.io/sagalbot/pen/NpwrQO)**
## Resources
- **[GitHub](https://github.com/sagalbot/vue-select)**
- **[Projects](https://github.com/sagalbot/vue-select/projects)**
- **[CodePen Template](http://codepen.io/sagalbot/pen/NpwrQO)**
+97 -2
View File
@@ -4,22 +4,99 @@ Triggered when the selected value changes. Used internally for `v-model`.
```js
/**
* @param val {Object|String}` - selected option.
* @param {Object|String} val - selected option.
*/
this.$emit("input", val);
```
## `open`
Triggered when the dropdown is open.
```js
this.$emit("open");
```
## `close`
Triggered when the dropdown is closed.
```js
this.$emit("close");
```
## `option:selecting` <Badge text="v3.11.0+" />
Triggered after an option has been selected, <strong>before</strong> updating internal state.
```js
this.$emit("option:selecting", selectedOption);
```
## `option:selected` <Badge text="v3.11.0+" />
Triggered when an option has been selected, <strong>after</strong> updating internal state.
```js
this.$emit("option:selected", selectedOption);
```
## `option:deselecting` <Badge text="v3.11.0+" />
Triggered when an option has been deselected, <strong>before</strong> updating internal state.
```js
this.$emit("option:deselecting", selectedOption);
```
## `option:deselected` <Badge text="v3.11.0+" />
Triggered when an option has been deselected, <strong>after</strong> updating internal state.
```js
this.$emit("option:deselected", deselectedOption);
```
## `option:created`
Triggered when `taggable` is `true` and a new option has been created.
```js
/**
* @param newOption {Object} - created option
* @param {Object} newOption - created option
*/
this.$emit("option:created", newOption);
```
## `search`
Anytime the search string changes, emit the
'search' event. The event is passed with two
parameters: the search string, and a function
that accepts a boolean parameter to toggle the
loading state.
See the [AJAX Guide](/guide/ajax.html#loading-options-with-ajax)
for a complete example.
```js
/**
* @param {String} searchString - the search string
* @param {Function} toggleLoading - function to toggle loading state, accepts true or false boolean
*/
this.$emit('search', this.search, this.toggleLoading);
```
```vue
<!-- example usage -->
<v-select
@search="(search, loading) => {
loading(true)
fetchOptions(search).then(() => loading(false))
}"
/>
```
## `search:blur`
Triggered when the text input loses focus. The dropdown will close immediately before this
@@ -37,3 +114,21 @@ event is triggered.
```js
this.$emit("search:focus");
```
## `search`
Triggered when the search text changes.
```js
/**
* Anytime the search string changes, emit the
* 'search' event. The event is passed with two
* parameters: the search string, and a function
* that accepts a boolean parameter to toggle the
* loading state.
*
* @emits search
*/
this.$emit('search', newSearchString, toggleLoading);
```
+356 -238
View File
@@ -13,121 +13,42 @@ appendToBody: {
},
```
## value
Contains the currently selected value. Very similar to a
`value` attribute on an `<input>`. You can listen for changes
using 'change' event using v-on.
## autocomplete
The value provided here will be bound to the [autocomplete
HTML attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete)
on the search input. Defaults to `off`.
```js
value: {
default: null
autocomplete: {
type: String,
default: 'off'
},
```
## options
An array of strings or objects to be used as dropdown choices.
If you are using an array of objects, vue-select will look for
a `label` key (ex. `[{label: 'Canada', value: 'CA'}]`). A
custom label key can be set with the `label` prop.
## autoscroll <Badge text="v3.10.0+" />
When true, the dropdown will automatically scroll to ensure
that the option highlighted is fully within the dropdown viewport
when navigating with keyboard arrows.
```js
options: {
type: Array,
default() {
return [];
}
},
autoscroll: {
type: Boolean,
default: true
}
```
## disabled
Disable the entire component.
```js
disabled: {
type: Boolean,
default: false
},
```
## clearable
Can the user clear the selected property?
```js
clearable: {
type: Boolean,
default: true
},
```
## maxHeight
::: warning Deprecated in `v2.x` & Removed in `v3.0`
This prop was removed in `v3.0`. You can use the `$vs-dropdown-max-height`
SCSS variable to adjust this setting in `v3.x`.
:::
Sets the max-height property on the dropdown list.
```js
maxHeight: {
type: String,
default: "400px"
},
```
## searchable
Enable/disable filtering the options.
```js
searchable: {
type: Boolean,
default: true
},
```
## multiple
Equivalent to the `multiple` attribute on a `<select>` input.
```js
multiple: {
type: Boolean,
default: false
},
```
## placeholder
Equivalent to the `placeholder` attribute on an `<input>`.
```js
placeholder: {
type: String,
default: ""
},
```
## transition
Sets a Vue transition property on the `.dropdown-menu`. vue-select
does not include CSS for transitions, you'll need to add them yourself.
```js
transition: {
type: String,
default: "fade"
},
```
## calculatePosition <Badge text="v3.7.0+" />
When `appendToBody` is true, this function is responsible for positioning the drop down list.
If a function is returned from `calculatePosition`, it will be called when the drop down list
is removed from the DOM. This allows for any garbage collection you may need to do.
See [Dropdown Position](../guide/positioning.md) for more details.
```js
@@ -139,6 +60,7 @@ calculatePosition: {
* @param width {string} calculated width in pixels of the dropdown menu
* @param top {string} absolute position top value in pixels relative to the document
* @param left {string} absolute position left value in pixels relative to the document
* @return {function|void}
*/
default(dropdownList, component, {width, top, left}) {
dropdownList.style.top = top;
@@ -148,17 +70,19 @@ calculatePosition: {
}
```
## clearSearchOnSelect
Enables/disables clearing the search text when an option is selected.
## clearable
Can the user clear the selected property?
```js
clearSearchOnSelect: {
clearable: {
type: Boolean,
default: true
},
```
## clearSearchOnBlur
Enables/disables clearing the search text when the search input is blurred.
@@ -172,6 +96,19 @@ clearSearchOnBlur: {
},
```
## clearSearchOnSelect
Enables/disables clearing the search text when an option is selected.
```js
clearSearchOnSelect: {
type: Boolean,
default: true
},
```
## closeOnSelect
Close a dropdown when an option is chosen. Set to false to keep the dropdown
@@ -184,62 +121,141 @@ closeOnSelect: {
},
```
## label
## components <Badge text="v3.1.0+" />
Tells vue-select what key to use when generating option
labels when each `option` is an object.
API to overwrite default vue-select components with your own. This can be used to change the clear button or select chevron with your own markup.
The object provided to the components prop will be merged with Vue Select's default components.
See [Components guide](../guide/components.md) for more details.
```js
label: {
type: String,
default: "label"
},
```
import Deselect from './Deselect';
import OpenIndicator from './OpenIndicator';
## reduce
// ...
When working with objects, the reduce
prop allows you to transform a given
object to only the information you
want passed to a v-model binding
or @input event.
```js
reduce: {
type: Function,
default: option => option,
},
```
## getOptionLabel
Callback to generate the label text. If `{option}`
is an object, returns `option[this.label]` by default.
Label text is used for filtering comparison and
displaying. If you only need to adjust the
display, you should use the `option` and
`selected-option` slots.
```js
getOptionLabel: {
type: Function,
default(option) {
if (typeof option === 'object') {
if (!option.hasOwnProperty(this.label)) {
return console.warn(
`[vue-select warn]: Label key "option.${this.label}" does not` +
` exist in options object ${JSON.stringify(option)}.\n` +
'https://vue-select.org/api/props.html#getoptionlabel'
)
}
return option[this.label]
}
return option;
components: {
type: Object,
default: function () {
Deselect,
OpenIndicator
}
},
```
## createOption
User defined function for adding Options
```js
createOption: {
type: Function,
default(newOption) {
if (typeof this.optionList[0] === 'object') {
newOption = {[this.label]: newOption}
}
this.$emit('option:created', newOption)
return newOption
}
},
```
## dir
Sets RTL support. Accepts `ltr`, `rtl`, `auto`.
```js
dir: {
type: String,
default: "auto"
},
```
## disabled
Disable the entire component.
```js
disabled: {
type: Boolean,
default: false
},
```
## dropdownShouldOpen <Badge text="v3.12.0+" />
Determines whether the dropdown should open. Used
for overriding the default dropdown behaviour. Receives
the vue-select instance as the single argument to the function.
```js
dropdownShouldOpen: {
type: Function,
default({noDrop, open, mutableLoading}) {
return noDrop ? false : open && !mutableLoading;
}
}
```
## filter
Callback to filter results when search text
is provided. Default implementation loops
each option, and returns the result of
this.filterBy.
```js
filter: {
type: Function,
default(options, search) {
return options.filter(option => {
let label = this.getOptionLabel(option);
if (typeof label === "number") {
label = label.toString();
}
return this.filterBy(option, label, search);
});
}
},
```
## filterable
When true, existing options will be filtered
by the search text. Should not be used in conjunction
with taggable.
```js
filterable: {
type: Boolean,
default: true
},
```
## filterBy
Callback to determine if the provided option should
match the current search text. Used to determine
if the option should be displayed.
```js
filterBy: {
type: Function,
default(option, label, search) {
return (label || '').toLowerCase().indexOf(search.toLowerCase()) > -1
}
},
```
## getOptionKey
Callback to get an option key. If `option`
@@ -272,6 +288,102 @@ getOptionKey: {
},
```
## getOptionLabel
Callback to generate the label text. If `{option}`
is an object, returns `option[this.label]` by default.
Label text is used for filtering comparison and
displaying. If you only need to adjust the
display, you should use the `option` and
`selected-option` slots.
```js
getOptionLabel: {
type: Function,
default(option) {
if (typeof option === 'object') {
if (!option.hasOwnProperty(this.label)) {
return console.warn(
`[vue-select warn]: Label key "option.${this.label}" does not` +
` exist in options object ${JSON.stringify(option)}.\n` +
'https://vue-select.org/api/props.html#getoptionlabel'
)
}
return option[this.label]
}
return option;
}
},
```
## inputId
Sets the id of the input element.
```js
inputId: {
type: String
},
```
## label
Tells vue-select what key to use when generating option
labels when each `option` is an object.
```js
label: {
type: String,
default: "label"
},
```
## maxHeight
::: warning Deprecated in `v2.x` & Removed in `v3.0`
This prop was removed in `v3.0`. You can use the `$vs-dropdown-max-height`
SCSS variable to adjust this setting in `v3.x`.
:::
Sets the max-height property on the dropdown list.
```js
maxHeight: {
type: String,
default: "400px"
},
```
## multiple
Equivalent to the `multiple` attribute on a `<select>` input.
```js
multiple: {
type: Boolean,
default: false
},
```
## noDrop
Disable the dropdown entirely.
```js
noDrop: {
type: Boolean,
default: false
},
```
## onTab
Select the current value if `selectOnTab` is enabled
@@ -287,28 +399,36 @@ onTab: {
},
```
## taggable
Enable/disable creating options from searchInput.
## options
An array of strings or objects to be used as dropdown choices.
If you are using an array of objects, vue-select will look for
a `label` key (ex. `[{label: 'Canada', value: 'CA'}]`). A
custom label key can be set with the `label` prop.
```js
taggable: {
type: Boolean,
default: false
options: {
type: Array,
default() {
return [];
}
},
```
## tabindex
Set the tabindex for the input field.
## placeholder
Equivalent to the `placeholder` attribute on an `<input>`.
```js
tabindex: {
type: Number,
default: null
placeholder: {
type: String,
default: ""
},
```
## pushTags
When true, newly created tags will be added to
@@ -321,74 +441,23 @@ pushTags: {
},
```
## filterable
When true, existing options will be filtered
by the search text. Should not be used in conjunction
with taggable.
## reduce
When working with objects, the reduce
prop allows you to transform a given
object to only the information you
want passed to a v-model binding
or @input event.
```js
filterable: {
type: Boolean,
default: true
},
```
## filterBy
Callback to determine if the provided option should
match the current search text. Used to determine
if the option should be displayed.
```js
filterBy: {
type: Function,
default(option, label, search) {
return (label | "").toLowerCase().indexOf(search.toLowerCase()) > -1;
}
},
```
## filter
Callback to filter results when search text
is provided. Default implementation loops
each option, and returns the result of
this.filterBy.
```js
filter: {
type: Function,
default(options, search) {
return options.filter(option => {
let label = this.getOptionLabel(option);
if (typeof label === "number") {
label = label.toString();
}
return this.filterBy(option, label, search);
});
}
},
```
## createOption
User defined function for adding Options
```js
createOption: {
reduce: {
type: Function,
default(newOption) {
if (typeof this.optionList[0] === 'object') {
newOption = {[this.label]: newOption}
}
this.$emit('option:created', newOption)
return newOption
}
default: option => option,
},
```
## resetOnOptionsChange
When false, updating the options will not reset the selected value.
@@ -410,37 +479,36 @@ resetOnOptionsChange: {
},
```
## noDrop
Disable the dropdown entirely.
## searchable
Enable/disable filtering the options.
```js
noDrop: {
searchable: {
type: Boolean,
default: false
default: true
},
```
## inputId
Sets the id of the input element.
## selectable <Badge text="v3.3.0+" />
The `selectable` prop determines if an option is selectable or not. If `selectable` returns false
for a given option, it will be displayed with a `vs__dropdown-option--disabled` class. The option
will be disabled and unable to be selected.
```js
inputId: {
type: String
selectable: {
type: Function,
/**
* @param {Object|String} option
* @return {boolean}
*/
default: option => true,
},
```
## dir
Sets RTL support. Accepts `ltr`, `rtl`, `auto`.
```js
dir: {
type: String,
default: "auto"
},
```
## selectOnTab
@@ -452,3 +520,53 @@ selectOnTab: {
default: false
}
```
## tabindex
Set the tabindex for the input field.
```js
tabindex: {
type: Number,
default: null
},
```
## taggable
Enable/disable creating options from searchInput.
```js
taggable: {
type: Boolean,
default: false
},
```
## transition
Sets a Vue transition property on the `.dropdown-menu`. vue-select
does not include CSS for transitions, you'll need to add them yourself.
```js
transition: {
type: String,
default: "fade"
},
```
## value
Contains the currently selected value. Very similar to a
`value` attribute on an `<input>`. You can listen for changes
using 'change' event using v-on.
```js
value: {
default: null
},
```
+151 -81
View File
@@ -3,116 +3,186 @@ Vue Select leverages scoped slots to allow for total customization of the presen
Slots can be used to change the look and feel of the UI, or to simply swap out text.
:::
## Selected Option(s)
<style>
.slot-docs h2 {
border-top: 1px solid #f0f0f0;
border-bottom: none;
margin-top: 2rem;
padding-top: 2rem;
}
.slot-docs h2:first-child {
border-top: none;
margin-top: 0;
}
</style>
### `selected-option`
<div class="slot-docs">
#### Scope:
## `footer` <Badge text="3.8.0+" />
- `option {Object}` - A selected option
Displayed at the bottom of the component, below `.vs__dropdown-toggle`.
```html
<slot
name="selected-option"
v-bind="(typeof option === 'object')?option:{[label]: option}"
>
{{ getOptionLabel(option) }}
</slot>
```
When implementing this slot, you'll likely need to use `appendToBody` to position the dropdown.
Otherwise content in this slot will affect it's positioning.
### `selected-option-container`
- `search {string}` - the current search query
- `loading {boolean}` - is the component loading
- `searching {boolean}` - is the component searching
- `filteredOptions {array}` - options filtered by the search text
- `deselect {function}` - function to deselect an option
#### Scope:
<SlotFooter />
<<< @/.vuepress/components/SlotFooter.vue
- `option {Object}` - A selected option
- `deselect {Function}` - Method used to deselect a given option when `multiple` is true
- `disabled {Boolean}` - Determine if the component is disabled
- `multiple {Boolean}` - If the component supports the selection of multiple values
## `header` <Badge text="3.8.0+" />
```html
<slot
v-for="option in valueAsArray"
name="selected-option-container"
:option="(typeof option === 'object')?option:{[label]: option}"
:deselect="deselect"
:multiple="multiple"
:disabled="disabled"
>
<span class="selected-tag" v-bind:key="option.index">
<slot
name="selected-option"
v-bind="(typeof option === 'object')?option:{[label]: option}"
>
{{ getOptionLabel(option) }}
</slot>
<button
v-if="multiple"
:disabled="disabled"
@click="deselect(option)"
type="button"
class="close"
aria-label="Remove option"
>
<span aria-hidden="true">&times;</span>
</button>
</span>
</slot>
```
Displayed at the top of the component, above `.vs__dropdown-toggle`.
## Component Actions
- `search {string}` - the current search query
- `loading {boolean}` - is the component loading
- `searching {boolean}` - is the component searching
- `filteredOptions {array}` - options filtered by the search text
- `deselect {function}` - function to deselect an option
### `spinner`
<SlotHeader />
<<< @/.vuepress/components/SlotHeader.vue
#### Scope:
## `list-footer` <Badge text="3.8.0+" />
- `loading {Boolean}` - if the component is in a loading state
Displayed as the last item in the dropdown. No content by default. Parent element is the `<ul>`,
so this slot should contain a root `<li>`.
```html
<slot name="spinner" v-bind="scope.spinner">
<div class="vs__spinner" v-show="mutableLoading">Loading...</div>
</slot>
```
- `search {string}` - the current search query
- `loading {boolean}` - is the component loading
- `searching {boolean}` - is the component searching
- `filteredOptions {array}` - options filtered by the search text
### `open-indicator`
<SlotListFooter />
<<< @/.vuepress/components/SlotListFooter.vue
## `list-header` <Badge text="3.8.0+" />
Displayed as the first item in the dropdown. No content by default. Parent element is the `<ul>`,
so this slot should contain a root `<li>`.
- `search {string}` - the current search query
- `loading {boolean}` - is the component loading
- `searching {boolean}` - is the component searching
- `filteredOptions {array}` - options filtered by the search text
<SlotListHeader />
<<< @/.vuepress/components/SlotListHeader.vue
## `no-options`
The no options slot is displayed above `list-footer` in the dropdown when
`filteredOptions.length === 0`.
- `search {string}` - the current search query
- `loading {boolean}` - is the component loading
- `searching {boolean}` - is the component searching
<SlotNoOptions />
<<< @/.vuepress/components/SlotNoOptions.vue
## `open-indicator`
The open indicator is the caret icon on the component used to indicate dropdown status.
```js
attributes : {
attributes: {
'ref': 'openIndicator',
'role': 'presentation',
'class': 'vs__open-indicator',
}
```
```vue
<slot name="open-indicator" v-bind="scope.openIndicator">
<component :is="childComponents.OpenIndicator" v-if="!noDrop" v-bind="scope.openIndicator.attributes"/>
</slot>
```
<SlotOpenIndicator />
<<< @/.vuepress/components/SlotOpenIndicator.vue
## Dropdown
## `option`
### `option`
The current option within the dropdown, contained within `<li>`.
- `option {Object}` - The currently iterated option from `filteredOptions`
```html
<slot
name="option"
v-bind="(typeof option === 'object')?option:{[label]: option}"
>
{{ getOptionLabel(option) }}
</slot>
<SlotOption />
<<< @/.vuepress/components/SlotOption.vue
## `search`
The search input has a lot of bindings, but they're grouped into `attributes` and `events`. Most
of the time, you will just be binding those two with `v-on="events"` and `v-bind="attributes"`.
If you want the default styling, you'll need to add `.vs__search` to the input you provide.
```js
/**
* Attributes to be bound to a search input.
*/
attributes: {
'disabled': this.disabled,
'placeholder': this.searchPlaceholder,
'tabindex': this.tabindex,
'readonly': !this.searchable,
'id': this.inputId,
'aria-autocomplete': 'list',
'aria-labelledby': `vs${this.uid}__combobox`,
'aria-controls': `vs${this.uid}__listbox`,
'aria-activedescendant': this.typeAheadPointer > -1
? `vs${this.uid}__option-${this.typeAheadPointer}`
: '',
'ref': 'search',
'type': 'search',
'autocomplete': this.autocomplete,
'value': this.search,
},
/**
* Events that this element should handle.
*/
events: {
'compositionstart': () => this.isComposing = true,
'compositionend': () => this.isComposing = false,
'keydown': this.onSearchKeyDown,
'blur': this.onSearchBlur,
'focus': this.onSearchFocus,
'input': (e) => this.search = e.target.value,
}
```
### `no-options`
<SlotSearch />
<<< @/.vuepress/components/SlotSearch.vue{5-6}
The no options slot is displayed in the dropdown when `filteredOptions.length === 0`.
## `selected-option`
- `search {String}` - the current search text
- `searching {Boolean}` - if the component has search text
The text displayed within `selected-option-container`.
```vue
<slot name="no-options" v-bind="scope.noOptions">
Sorry, no matching options.
</slot>
```
This slot doesn't exist if `selected-option-container` is implemented.
- `option {Object}` - A selected option
<SlotSelectedOption />
<<< @/.vuepress/components/SlotSelectedOption.vue
## `selected-option-container`
This is the root element where `v-for="option in selectedValue"`. Most of the time you'll want to
use `selected-option`, but this container is useful if you want to disable the deselect button,
or have fine grain control over the markup.
- `option {Object}` - Currently iterated selected option
- `deselect {Function}` - Method used to deselect a given option when `multiple` is true
- `disabled {Boolean}` - Determine if the component is disabled
- `multiple {Boolean}` - If the component supports the selection of multiple values
<SlotSelectedOptionContainer />
<<< @/.vuepress/components/SlotSelectedOptionContainer.vue
## `spinner`
- `loading {Boolean}` - if the component is in a loading state
<SlotSpinner />
<<< @/.vuepress/components/SlotSpinner.vue
</div>
+10
View File
@@ -0,0 +1,10 @@
---
sidebarDepth: 0
---
# Contributors
Vue Select is supported by a community of awesome contributors! Without their contributions,
the package would not be what it is today.
<Contributors />
+1 -1
View File
@@ -58,7 +58,7 @@ export default {
## Setting Globally at Registration
If you want to all instances of Vue Select to use your custom components throughout your app, while
If you want all instances of Vue Select to use your custom components throughout your app, while
only having to set the implementation once, you can do so when registering Vue Select as a component.
```js
+19
View File
@@ -0,0 +1,19 @@
Vue Select provides two props accepting `functions` that can be used to implement custom filtering
algorithms.
- `filter` <Badge text="v2.5.0+" />
- `filterBy` <Badge text="v2.5.0+" />
By default, the component will perform a very basic check to see if an options label includes
the current search text. If you're using scoped slots, you might have information within the
option templates that should be searchable. Or maybe you just want a better search algorithm that
can do fuzzy search matching.
## Filtering with Fuse.js
You can use the `filter` and `filterBy` props to hook right into something like
[Fuse.js](https://fusejs.io/) that can handle searching multiple object keys with fuzzy matchings.
<FuseFilter />
<<< @/.vuepress/components/FuseFilter.vue
+23
View File
@@ -0,0 +1,23 @@
Vue Select doesn't ship with first party support for infinite scroll, but it's possible to implement
by hooking into the `open`, `close`, and `search` events, along with the `filterable` prop, and the
`list-footer` slot.
Let's break down the example below, starting with the `data`.
- `observer` - a new `IntersectionObserver` with `infiniteScroll` set as the callback
- `limit` - the number of options to display
- `search` - since we've disabled Vue Selects filtering, we'll need to filter options ourselves
When Vue Select opens, the `open` event is emitted and `onOpen` will be called. We wait for
`$nextTick()` so that the `$ref` we need will exist, then begin observing it for intersection.
The observer is set to call `infiniteScroll` when the `<li>` is completely visible within the list.
Some fancy destructuring is done here to get the first `ObservedEntry`, and specifically the
`isIntersecting` & `target` properties. If the `<li>` is intersecting, we increase the `limit`, and
ensure that the scroll position remains where it was before the list size changed. Again, it's
important to wait for `$nextTick` here so that the DOM elements have been inserted before setting
the scroll position.
<InfiniteScroll />
<<< @/.vuepress/components/InfiniteScroll.vue
+18
View File
@@ -0,0 +1,18 @@
::: tip <Badge text="3.8.0+" />
Pagination is supported using slots available with Vue Select 3.8 and above.
:::
Pagination can be a super helpful tool when working with large sets of data. If you have 1,000
options, the component is going to render 1,000 DOM nodes. That's a lot of nodes to insert/remove,
and chances are your user is only interested in a few of them anyways.
To implement pagination with Vue Select, you can take advantage of the `list-footer` slot. It
appears below all other options in the drop down list.
To make pagination work properly with filtering, you'll have to handle it yourself in the parent.
You can use the `filterable` boolean to turn off Vue Select's filtering, and then hook into the
`search` event to use the current search query in the parent component.
<Paginated />
<<< @/.vuepress/components/Paginated.vue
+17 -12
View File
@@ -1,6 +1,6 @@
{
"name": "@vue-select/docs",
"version": "1.0",
"version": "1.0.0",
"license": "MIT",
"scripts": {
"serve": "vuepress dev",
@@ -8,20 +8,25 @@
"build:preview": "cross-env DEPLOY_PREVIEW=true vuepress build"
},
"devDependencies": {
"@octokit/graphql": "^4.3.1",
"@popperjs/core": "^2.1.0",
"@vuepress/plugin-active-header-links": "^1.0.0-alpha.47",
"@vuepress/plugin-google-analytics": "^1.0.0-alpha.47",
"@vuepress/plugin-nprogress": "^1.0.0-alpha.47",
"@vuepress/plugin-pwa": "^1.0.0-alpha.47",
"@vuepress/plugin-register-components": "^1.0.0-alpha.47",
"@vuepress/plugin-search": "^1.0.0-alpha.47",
"cross-env": "^5.2.0",
"fuse.js": "^3.4.4",
"gh-pages": "^0.11.0",
"@vuepress/plugin-active-header-links": "^1.4.0",
"@vuepress/plugin-google-analytics": "^1.4.0",
"@vuepress/plugin-nprogress": "^1.4.0",
"@vuepress/plugin-pwa": "^1.4.0",
"@vuepress/plugin-register-components": "^1.4.0",
"@vuepress/plugin-search": "^1.4.0",
"axios": "^0.19.2",
"cross-env": "^7.0.2",
"date-fns": "^2.11.0",
"dotenv": "^8.2.0",
"fuse.js": "^6.4.0",
"gh-pages": "^2.2.0",
"node-sass": "^4.12.0",
"sass-loader": "^7.1.0",
"octonode": "^0.9.5",
"sass-loader": "^8.0.2",
"vue": "^2.6.10",
"vuepress": "^1.0.0-alpha.47",
"vuepress": "^1.4.0",
"vuex": "^3.1.0"
}
}
+9
View File
@@ -0,0 +1,9 @@
---
sidebarDepth: 0
---
<SponsorMe />
## Sponsors
<Sponsors />
+2397 -1730
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vue-select",
"version": "3.7.0",
"version": "3.12.2",
"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",
+6 -3
View File
@@ -6,9 +6,12 @@ module.exports = {
"@semantic-release/npm",
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
["@semantic-release/github", {
"assets": ["dist/**"]
}],
[
"@semantic-release/github",
{
assets: ["dist/**"]
}
],
[
"@semantic-release/git",
{
+128 -87
View File
@@ -4,7 +4,8 @@
<template>
<div :dir="dir" class="v-select" :class="stateClasses">
<div :id="`vs${uid}__combobox`" ref="toggle" @mousedown.prevent="toggleDropdown" class="vs__dropdown-toggle" role="combobox" :aria-expanded="dropdownOpen.toString()" :aria-owns="`vs${uid}__listbox`" aria-label="Search for option">
<slot name="header" v-bind="scope.header" />
<div :id="`vs${uid}__combobox`" ref="toggle" @mousedown="toggleDropdown($event)" class="vs__dropdown-toggle" role="combobox" :aria-expanded="dropdownOpen.toString()" :aria-owns="`vs${uid}__listbox`" aria-label="Search for option">
<div class="vs__selected-options" ref="selectedOptions">
<slot v-for="option in selectedValue"
@@ -51,9 +52,9 @@
</slot>
</div>
</div>
<transition :name="transition">
<ul ref="dropdownMenu" v-if="dropdownOpen" :id="`vs${uid}__listbox`" class="vs__dropdown-menu" role="listbox" @mousedown.prevent="onMousedown" @mouseup="onMouseUp" v-append-to-body>
<ul ref="dropdownMenu" v-if="dropdownOpen" :id="`vs${uid}__listbox`" :key="`vs${uid}__listbox`" class="vs__dropdown-menu" role="listbox" @mousedown.prevent="onMousedown" @mouseup="onMouseUp" tabindex="-1" v-append-to-body>
<slot name="list-header" v-bind="scope.listHeader" />
<li
role="option"
v-for="(option, index) in filteredOptions"
@@ -69,12 +70,14 @@
{{ getOptionLabel(option) }}
</slot>
</li>
<li v-if="filteredOptions.length === 0" class="vs__no-options" @mousedown.stop="">
<li v-if="filteredOptions.length === 0" class="vs__no-options">
<slot name="no-options" v-bind="scope.noOptions">Sorry, no matching options.</slot>
</li>
<slot name="list-footer" v-bind="scope.listFooter" />
</ul>
<ul v-else :id="`vs${uid}__listbox`" role="listbox" style="display: none; visibility: hidden;"></ul>
</transition>
<slot name="footer" v-bind="scope.footer" />
</div>
</template>
@@ -84,6 +87,7 @@
import ajax from '../mixins/ajax'
import childComponents from './childComponents';
import appendToBody from '../directives/appendToBody';
import sortAndStringify from '../utility/sortAndStringify'
import uniqueId from '../utility/uniqueId';
/**
@@ -281,12 +285,16 @@
},
/**
* Callback to get an option key. If {option}
* is an object and has an {id}, returns {option.id}
* by default, otherwise tries to serialize {option}
* to JSON.
* Generate a unique identifier for each option. If `option`
* is an object and `option.hasOwnProperty('id')` exists,
* `option.id` is used by default, otherwise the option
* will be serialized to JSON.
*
* The key must be unique for an option.
* If you are supplying a lot of options, you should
* provide your own keys, as JSON.stringify can be
* slow with lots of objects.
*
* The result of this function *must* be unique.
*
* @type {Function}
* @param {Object || String} option
@@ -294,22 +302,21 @@
*/
getOptionKey: {
type: Function,
default(option) {
if (typeof option === 'object' && option.id) {
return option.id
} else {
try {
return JSON.stringify(option)
} catch(e) {
return console.warn(
`[vue-select warn]: Could not stringify option ` +
`to generate unique key. Please provide'getOptionKey' prop ` +
`to return a unique key for each option.\n` +
'https://vue-select.org/api/props.html#getoptionkey'
);
}
default (option) {
if (typeof option !== 'object') {
return option;
}
}
try {
return option.hasOwnProperty('id') ? option.id : sortAndStringify(option);
} catch (e) {
const warning = `[vue-select warn]: Could not stringify this option ` +
`to generate unique key. Please provide'getOptionKey' prop ` +
`to return a unique key for each option.\n` +
'https://vue-select.org/api/props.html#getoptionkey';
return console.warn(warning, option, e);
}
},
},
/**
@@ -393,7 +400,7 @@
* @return {Boolean}
*/
filter: {
"type": Function,
type: Function,
default(options, search) {
return options.filter((option) => {
let label = this.getOptionLabel(option)
@@ -533,9 +540,13 @@
},
/**
* When `appendToBody` is true, this function
* is responsible for positioning the drop
* down list.
* When `appendToBody` is true, this function is responsible for
* positioning the drop down list.
*
* If a function is returned from `calculatePosition`, it will
* be called when the drop down list is removed from the DOM.
* This allows for any garbage collection you may need to do.
*
* @since v3.7.0
* @see http://vue-select.org/guide/positioning.html
*/
@@ -547,12 +558,27 @@
* @param width {string} calculated width in pixels of the dropdown menu
* @param top {string} absolute position top value in pixels relative to the document
* @param left {string} absolute position left value in pixels relative to the document
* @return {function|void}
*/
default(dropdownList, component, {width, top, left}) {
dropdownList.style.top = top;
dropdownList.style.left = left;
dropdownList.style.width = width;
}
},
/**
* Determines whether the dropdown should be open.
* Receives the component instance as the only argument.
*
* @since v3.12.0
* @return boolean
*/
dropdownShouldOpen: {
type: Function,
default({noDrop, open, mutableLoading}) {
return noDrop ? false : open && !mutableLoading;
}
}
},
@@ -607,6 +633,10 @@
*/
multiple() {
this.clearSelection()
},
open(isOpen) {
this.$emit(isOpen ? 'open' : 'close');
}
},
@@ -617,7 +647,7 @@
this.setInternalValueFromOptions(this.value)
}
this.$on('option:created', this.maybePushTag)
this.$on('option:created', this.pushTag)
},
methods: {
@@ -641,17 +671,17 @@
* @return {void}
*/
select(option) {
this.$emit('option:selecting', option);
if (!this.isOptionSelected(option)) {
if (this.taggable && !this.optionExists(option)) {
option = this.createOption(option);
this.$emit('option:created', option);
}
if (this.multiple) {
option = this.selectedValue.concat(option)
}
this.updateValue(option);
this.$emit('option:selected', option);
}
this.onAfterSelect(option)
},
@@ -661,9 +691,11 @@
* @return {void}
*/
deselect (option) {
this.$emit('option:deselecting', option);
this.updateValue(this.selectedValue.filter(val => {
return !this.optionComparator(val, option);
}));
this.$emit('option:deselected', option);
},
/**
@@ -681,7 +713,7 @@
*/
onAfterSelect(option) {
if (this.closeOnSelect) {
this.open = !this.open
this.open = !this.open;
this.searchEl.blur()
}
@@ -699,7 +731,7 @@
* @param value
*/
updateValue (value) {
if (this.isTrackingValues) {
if (typeof this.value === 'undefined') {
// Vue select has to manage value
this.$data._value = value;
}
@@ -717,22 +749,28 @@
/**
* Toggle the visibility of the dropdown menu.
* @param {Event} e
* @param {Event} event
* @return {void}
*/
toggleDropdown ({target}) {
toggleDropdown (event) {
const targetIsNotSearch = event.target !== this.searchEl;
if (targetIsNotSearch) {
event.preventDefault();
}
// don't react to click on deselect/clear buttons,
// they dropdown state will be set in their click handlers
const ignoredButtons = [
...(this.$refs['deselectButtons'] || []),
...([this.$refs['clearButton']] || [])
...([this.$refs['clearButton']] || []),
];
if (ignoredButtons.some(ref => ref.contains(target) || ref === target)) {
if (this.searchEl === undefined || ignoredButtons.filter(Boolean).some(ref => ref.contains(event.target) || ref === event.target)) {
event.preventDefault();
return;
}
if (this.open) {
if (this.open && targetIsNotSearch) {
this.searchEl.blur();
} else if (!this.disabled) {
this.open = true;
@@ -746,42 +784,22 @@
* @return {Boolean} True when selected | False otherwise
*/
isOptionSelected(option) {
return this.selectedValue.some(value => {
return this.optionComparator(value, option)
})
return this.selectedValue.some(value => this.optionComparator(value, option))
},
/**
* Determine if two option objects are matching.
*
* @param value {Object}
* @param option {Object}
* @param a {Object}
* @param b {Object}
* @returns {boolean}
*/
optionComparator(value, option) {
if (typeof value !== 'object' && typeof option !== 'object') {
// Comparing primitives
if (value === option) {
return true
}
} else {
// Comparing objects
if (value === this.reduce(option)) {
return true
}
if ((this.getOptionLabel(value) === this.getOptionLabel(option)) || (this.getOptionLabel(value) === option)) {
return true
}
if (this.reduce(value) === this.reduce(option)) {
return true
}
}
return false;
optionComparator(a, b) {
return this.getOptionKey(a) === this.getOptionKey(b);
},
/**
* Finds an option from this.options
* Finds an option from the options
* where a reduced value matches
* the passed in value.
*
@@ -789,7 +807,24 @@
* @returns {*}
*/
findOptionFromReducedValue (value) {
return this.options.find(option => JSON.stringify(this.reduce(option)) === JSON.stringify(value)) || value;
const predicate = option => JSON.stringify(this.reduce(option)) === JSON.stringify(value);
const matches = [
...this.options,
...this.pushedTags,
].filter(predicate);
if (matches.length === 1) {
return matches[0];
}
/**
* This second loop is needed to cover an edge case where `taggable` + `reduce`
* were used in conjunction with a `create-option` that doesn't create a
* unique reduced value.
* @see https://github.com/sagalbot/vue-select/issues/1089#issuecomment-597238735
*/
return matches.find(match => this.optionComparator(match, this.$data._value)) || value;
},
/**
@@ -808,7 +843,7 @@
* @return {this.value}
*/
maybeDeleteValue() {
if (!this.searchEl.value.length && this.selectedValue && this.clearable) {
if (!this.searchEl.value.length && this.selectedValue && this.selectedValue.length && this.clearable) {
let value = null;
if (this.multiple) {
value = [...this.selectedValue.slice(0, this.selectedValue.length - 1)]
@@ -825,14 +860,7 @@
* @return {boolean}
*/
optionExists(option) {
return this.optionList.some(opt => {
if (typeof opt === 'object' && this.getOptionLabel(opt) === option) {
return true
} else if (opt === option) {
return true
}
return false
})
return this.optionList.some(_option => this.optionComparator(_option, option))
},
/**
@@ -852,10 +880,8 @@
* @param {Object || String} option
* @return {void}
*/
maybePushTag(option) {
if (this.pushTags) {
this.pushedTags.push(option)
}
pushTag (option) {
this.pushedTags.push(option);
},
/**
@@ -937,7 +963,7 @@
};
const defaults = {
// delete
// backspace
8: e => this.maybeDeleteValue(),
// tab
9: e => this.onTab(),
@@ -981,7 +1007,6 @@
*/
selectedValue () {
let value = this.value;
if (this.isTrackingValues) {
// Vue select has to manage value internally
value = this.$data._value;
@@ -1002,7 +1027,7 @@
* @return {Array}
*/
optionList () {
return this.options.concat(this.pushedTags);
return this.options.concat(this.pushTags ? this.pushedTags : []);
},
/**
@@ -1020,6 +1045,12 @@
* @returns {Object}
*/
scope () {
const listSlot = {
search: this.search,
loading: this.loading,
searching: this.searching,
filteredOptions: this.filteredOptions
};
return {
search: {
attributes: {
@@ -1031,11 +1062,13 @@
'aria-autocomplete': 'list',
'aria-labelledby': `vs${this.uid}__combobox`,
'aria-controls': `vs${this.uid}__listbox`,
'aria-activedescendant': this.typeAheadPointer > -1 ? `vs${this.uid}__option-${this.typeAheadPointer}` : '',
'ref': 'search',
'type': 'search',
'autocomplete': this.autocomplete,
'value': this.search,
...(this.dropdownOpen && this.filteredOptions[this.typeAheadPointer] ? {
'aria-activedescendant': `vs${this.uid}__option-${this.typeAheadPointer}`
} : {}),
},
events: {
'compositionstart': () => this.isComposing = true,
@@ -1051,6 +1084,7 @@
},
noOptions: {
search: this.search,
loading: this.mutableLoading,
searching: this.searching,
},
openIndicator: {
@@ -1060,6 +1094,10 @@
'class': 'vs__open-indicator',
},
},
listHeader: listSlot,
listFooter: listSlot,
header: { ...listSlot, deselect: this.deselect },
footer: { ...listSlot, deselect: this.deselect }
};
},
@@ -1108,7 +1146,7 @@
* @return {Boolean} True if open
*/
dropdownOpen() {
return this.noDrop ? false : this.open && !this.mutableLoading
return this.dropdownShouldOpen(this);
},
/**
@@ -1138,10 +1176,13 @@
}
let options = this.search.length ? this.filter(optionList, this.search, this) : optionList;
if (this.taggable && this.search.length && !this.optionExists(this.search)) {
options.unshift(this.search)
if (this.taggable && this.search.length) {
const createdOption = this.createOption(this.search);
if (!this.optionExists(createdOption)) {
options.unshift(createdOption);
}
}
return options
return options;
},
/**
@@ -1158,7 +1199,7 @@
*/
showClearButton() {
return !this.multiple && this.clearable && !this.open && !this.isValueEmpty
}
},
},
}
+16 -10
View File
@@ -1,21 +1,27 @@
export default {
inserted (el, bindings, {context}) {
if (context.appendToBody) {
const {height, top, left} = context.$refs.toggle.getBoundingClientRect();
context.calculatePosition(el, context, {
width: context.$refs.toggle.clientWidth + 'px',
top: (window.scrollY + top + height) + 'px',
left: (window.scrollX + left) + 'px',
const {height, top, left, width} = context.$refs.toggle.getBoundingClientRect();
let scrollX = window.scrollX || window.pageXOffset;
let scrollY = window.scrollY || window.pageYOffset;
el.unbindPosition = context.calculatePosition(el, context, {
width: width + 'px',
left: (scrollX + left) + 'px',
top: (scrollY + top + height) + 'px',
});
document.body.appendChild(el);
}
},
unbind (el, bindings, vnode) {
if (vnode.context.appendToBody && el.parentNode) {
el.parentNode.removeChild(el);
unbind (el, bindings, {context}) {
if (context.appendToBody) {
if (el.unbindPosition && typeof el.unbindPosition === 'function') {
el.unbindPosition();
}
if (el.parentNode) {
el.parentNode.removeChild(el);
}
}
},
}
+28 -60
View File
@@ -1,8 +1,17 @@
export default {
props: {
autoscroll: {
type: Boolean,
default: true
}
},
watch: {
typeAheadPointer() {
this.maybeAdjustScroll();
}
if (this.autoscroll) {
this.maybeAdjustScroll();
}
},
},
methods: {
@@ -13,75 +22,34 @@ export default {
* @returns {*}
*/
maybeAdjustScroll() {
let pixelsToPointerTop = this.pixelsToPointerTop();
let pixelsToPointerBottom = this.pixelsToPointerBottom();
const optionEl =
this.$refs.dropdownMenu?.children[this.typeAheadPointer] || false;
if (pixelsToPointerTop <= this.viewport().top) {
return this.scrollTo(pixelsToPointerTop);
} else if (pixelsToPointerBottom >= this.viewport().bottom) {
return this.scrollTo(this.viewport().top + this.pointerHeight());
}
},
if (optionEl) {
const bounds = this.getDropdownViewport();
const { top, bottom, height } = optionEl.getBoundingClientRect();
/**
* The distance in pixels from the top of the dropdown
* list to the top of the current pointer element.
* @returns {number}
*/
pixelsToPointerTop() {
let pixelsToPointerTop = 0;
if (this.$refs.dropdownMenu && this.dropdownOpen) {
for (let i = 0; i < this.typeAheadPointer; i++) {
pixelsToPointerTop += this.$refs.dropdownMenu.children[i]
.offsetHeight;
if (top < bounds.top) {
return (this.$refs.dropdownMenu.scrollTop = optionEl.offsetTop);
} else if (bottom > bounds.bottom) {
return (this.$refs.dropdownMenu.scrollTop =
optionEl.offsetTop - (bounds.height - height));
}
}
return pixelsToPointerTop;
},
/**
* The distance in pixels from the top of the dropdown
* list to the bottom of the current pointer element.
* @returns {*}
*/
pixelsToPointerBottom() {
return this.pixelsToPointerTop() + this.pointerHeight();
},
/**
* The offsetHeight of the current pointer element.
* @returns {number}
*/
pointerHeight() {
let element = this.$refs.dropdownMenu
? this.$refs.dropdownMenu.children[this.typeAheadPointer]
: false;
return element ? element.offsetHeight : 0;
},
/**
* The currently viewable portion of the dropdownMenu.
* @returns {{top: (string|*|number), bottom: *}}
*/
viewport() {
return {
top: this.$refs.dropdownMenu ? this.$refs.dropdownMenu.scrollTop : 0,
bottom: this.$refs.dropdownMenu
? this.$refs.dropdownMenu.offsetHeight +
this.$refs.dropdownMenu.scrollTop
: 0
};
},
/**
* Scroll the dropdownMenu to a given position.
* @param position
* @returns {*}
*/
scrollTo(position) {
getDropdownViewport() {
return this.$refs.dropdownMenu
? (this.$refs.dropdownMenu.scrollTop = position)
: null;
? this.$refs.dropdownMenu.getBoundingClientRect()
: {
height: 0,
top: 0,
bottom: 0
};
}
}
};
+4 -14
View File
@@ -26,9 +26,6 @@ export default {
for (let i = this.typeAheadPointer - 1; i >= 0; i--) {
if (this.selectable(this.filteredOptions[i])) {
this.typeAheadPointer = i;
if( this.maybeAdjustScroll ) {
this.maybeAdjustScroll()
}
break;
}
}
@@ -43,9 +40,6 @@ export default {
for (let i = this.typeAheadPointer + 1; i < this.filteredOptions.length; i++) {
if (this.selectable(this.filteredOptions[i])) {
this.typeAheadPointer = i;
if( this.maybeAdjustScroll ) {
this.maybeAdjustScroll()
}
break;
}
}
@@ -57,15 +51,11 @@ export default {
* @return {void}
*/
typeAheadSelect() {
if( this.filteredOptions[ this.typeAheadPointer ] ) {
this.select( this.filteredOptions[ this.typeAheadPointer ] );
} else if (this.taggable && this.search.length){
this.select(this.search)
}
const typeAheadOption = this.filteredOptions[this.typeAheadPointer];
if( this.clearSearchOnSelect ) {
this.search = "";
if (typeAheadOption) {
this.select(typeAheadOption);
}
},
}
}
}
+1 -1
View File
@@ -28,7 +28,7 @@ $vs-border-color: map_get($vs-colors, 'lightest') !default;
// Component Controls: Clear, Open Indicator
$vs-controls-color: map_get($vs-colors, 'light') !default;
$vs-controls-size: 1 !default;
$vs-controls-deselect-text-shadow: 0 1px 0 #fff;
$vs-controls-deselect-text-shadow: 0 1px 0 #fff !default;
// Selected
$vs-selected-bg: #f0f0f0 !default;
+1
View File
@@ -13,6 +13,7 @@ $max-height: $vs-dropdown-max-height;
.vs__dropdown-menu {
display: block;
box-sizing: border-box;
position: absolute;
top: calc(100% - #{$border-width}); // -{#$border-width} here ensures the left and right borders of the dropdown appear flush with the toggle.
left: 0;
+4 -4
View File
@@ -34,6 +34,7 @@ $font-size: 1em;
width: 0;
max-width: 100%;
flex-grow: 1;
z-index: 1;
}
.vs__search::placeholder {
@@ -48,10 +49,9 @@ $font-size: 1em;
.vs--unsearchable {
.vs__search {
opacity: 1;
&:hover {
cursor: pointer;
}
}
&:not(.vs--disabled) .vs__search:hover {
cursor: pointer;
}
}
// Single, when searching but not loading or open
+1
View File
@@ -9,6 +9,7 @@
line-height: $vs-component-line-height;
margin: 4px 2px 0px 2px;
padding: 0 0.25em;
z-index: 0;
}
.vs__deselect {
+15
View File
@@ -0,0 +1,15 @@
/**
* @param sortable {object}
* @return {string}
*/
function sortAndStringify(sortable) {
const ordered = {};
Object.keys(sortable).sort().forEach(key => {
ordered[key] = sortable[key];
});
return JSON.stringify(ordered);
}
export default sortAndStringify;
+17
View File
@@ -16,6 +16,23 @@ export const searchSubmit = (Wrapper, searchText = false) => {
Wrapper.find({ ref: "search" }).trigger("keydown.enter")
};
/**
* Focus the input, enter some search text, hit return.
* @param Wrapper {Wrapper<Vue>}
* @param searchText
* @return {Promise<void>}
*/
export const selectTag = async (Wrapper, searchText) => {
Wrapper.vm.$refs.search.focus();
await Wrapper.vm.$nextTick();
Wrapper.vm.search = searchText;
await Wrapper.vm.$nextTick();
Wrapper.find({ ref: "search" }).trigger("keydown.enter");
await Wrapper.vm.$nextTick();
};
/**
* Create a new VueSelect instance with
* a provided set of props.
+35
View File
@@ -0,0 +1,35 @@
import { mountDefault } from "../helpers";
describe("Search Slot Scope", () => {
/**
* @see https://www.w3.org/WAI/PF/aria/states_and_properties#aria-activedescendant
*/
describe("aria-activedescendant", () => {
it("adds the active descendant attribute only when the dropdown is open and there is a typeAheadPointer value", async () => {
const Select = mountDefault();
expect(
Select.vm.scope.search.attributes["aria-activedescendant"]
).toEqual(undefined);
Select.vm.open = true;
await Select.vm.$nextTick();
expect(
Select.vm.scope.search.attributes["aria-activedescendant"]
).toEqual(undefined);
});
it("adds the active descendant attribute when there's a typeahead value and an open dropdown", async () => {
const Select = mountDefault();
Select.vm.open = true;
Select.vm.typeAheadPointer = 1;
await Select.vm.$nextTick();
expect(
Select.vm.scope.search.attributes["aria-activedescendant"]
).toEqual(`vs${Select.vm.uid}__option-1`);
});
});
});
+61
View File
@@ -0,0 +1,61 @@
import { mountDefault } from "../helpers";
describe("Automatic Scrolling", () => {
it("should check if the scroll position needs to be adjusted on up arrow keyUp", async () => {
// Given
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
Select.vm.typeAheadPointer = 1;
// When
Select.find({ ref: "search" }).trigger("keydown.up");
await Select.vm.$nextTick();
// Then
expect(spy).toHaveBeenCalled();
});
it("should check if the scroll position needs to be adjusted on down arrow keyUp", async () => {
// Given
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
Select.vm.typeAheadPointer = 1;
// When
Select.find({ ref: "search" }).trigger("keydown.down");
await Select.vm.$nextTick();
// Then
expect(spy).toHaveBeenCalled();
});
it("should check if the scroll position needs to be adjusted when filtered options changes", async () => {
// Given
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
Select.vm.typeAheadPointer = 1;
// When
Select.vm.search = "two";
await Select.vm.$nextTick();
// Then
expect(spy).toHaveBeenCalled();
});
it("should not adjust scroll position when autoscroll is false", async () => {
// Given
const Select = mountDefault({
autoscroll: false
});
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
Select.vm.typeAheadPointer = 1;
// When
Select.vm.search = "two";
await Select.vm.$nextTick();
// Then
expect(spy).toHaveBeenCalledTimes(0);
});
});
+30
View File
@@ -0,0 +1,30 @@
import { searchSubmit, selectTag, selectWithProps } from "../helpers";
describe("CreateOption When Tagging Is Enabled", () => {
it("can select the current search text as a string", async () => {
const Select = selectWithProps({
taggable: true,
multiple: true,
options: ["one", "two"],
createOption: option => "four"
});
await selectTag(Select, "three");
expect(Select.vm.selectedValue).toEqual(["four"]);
});
it("can select the current search text as an object", async () => {
const Select = selectWithProps({
taggable: true,
multiple: false,
value: null,
options: [],
label: "name",
createOption: title => ({ name: title })
});
await selectTag(Select, "two");
expect(Select.emitted("input")[0]).toEqual([{ name: "two" }]);
});
});
+12 -1
View File
@@ -1,4 +1,4 @@
import { selectWithProps } from "../helpers";
import { mountDefault, selectWithProps } from '../helpers';
describe("Removing values", () => {
it("can remove the given tag when its close icon is clicked", async () => {
@@ -48,6 +48,17 @@ describe("Removing values", () => {
expect(Select.vm.selectedValue).toEqual([]);
});
it('will not emit input event if value has not changed with backspace', () => {
const Select = mountDefault();
Select.vm.$data._value = 'one';
Select.find({ ref: 'search' }).trigger('keydown.backspace');
expect(Select.emitted().input.length).toBe(1);
Select.find({ ref: 'search' }).trigger('keydown.backspace');
Select.find({ ref: 'search' }).trigger('keydown.backspace');
expect(Select.emitted().input.length).toBe(1);
});
describe("Clear button", () => {
it("should be displayed on single select when value is selected", () => {
const Select = selectWithProps({
+36 -5
View File
@@ -1,10 +1,16 @@
import { selectWithProps } from "../helpers";
import OpenIndicator from "../../src/components/OpenIndicator";
const preventDefault = jest.fn()
function clickEvent (currentTarget) {
return { currentTarget, preventDefault }
}
describe("Toggling Dropdown", () => {
it("should not open the dropdown when the el is clicked but the component is disabled", () => {
const Select = selectWithProps({ disabled: true });
Select.vm.toggleDropdown({ target: Select.vm.$refs.search });
Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search));
expect(Select.vm.open).toEqual(false);
});
@@ -14,10 +20,23 @@ describe("Toggling Dropdown", () => {
options: [{ label: "one" }]
});
Select.vm.toggleDropdown({ target: Select.vm.$refs.search });
Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search));
expect(Select.vm.open).toEqual(true);
});
it("should not close the dropdown when the el is clicked and enableMouseInputSearch is set to true", () => {
const Select = selectWithProps({
value: [{ label: "one" }],
options: [{ label: "one" }],
enableMouseSearchInput: true
});
Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search));
expect(Select.vm.open).toEqual(true);
Select.vm.toggleDropdown(clickEvent(Select.vm.$el));
expect(Select.vm.open).toEqual(false)
});
it("should open the dropdown when the selected tag is clicked", () => {
const Select = selectWithProps({
value: [{ label: "one" }],
@@ -26,7 +45,7 @@ describe("Toggling Dropdown", () => {
const selectedTag = Select.find(".vs__selected").element;
Select.vm.toggleDropdown({ target: selectedTag });
Select.vm.toggleDropdown(clickEvent(selectedTag));
expect(Select.vm.open).toEqual(true);
});
@@ -35,7 +54,7 @@ describe("Toggling Dropdown", () => {
const spy = jest.spyOn(Select.vm.$refs.search, "blur");
Select.vm.open = true;
Select.vm.toggleDropdown({ target: Select.vm.$el });
Select.vm.toggleDropdown(clickEvent(Select.vm.$el));
expect(spy).toHaveBeenCalled();
});
@@ -133,7 +152,9 @@ describe("Toggling Dropdown", () => {
const Select = selectWithProps({
noDrop: true,
});
Select.vm.toggleDropdown({ target: Select.vm.$refs.search });
Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search));
expect(Select.vm.open).toEqual(true);
await Select.vm.$nextTick();
@@ -167,4 +188,14 @@ describe("Toggling Dropdown", () => {
expect(Select.classes('vs--searching')).toBeFalsy();
});
it("can be opened with dropdownShouldOpen", () => {
const Select = selectWithProps({
noDrop: true,
dropdownShouldOpen: () => true,
options: ['one']
});
expect(Select.classes('vs--open')).toBeTruthy();
expect(Select.find('.vs__dropdown-menu li')).toBeTruthy();
})
});
+15 -15
View File
@@ -5,10 +5,10 @@ describe('Custom Keydown Handlers', () => {
it('can use the map-keydown prop to trigger custom behaviour', () => {
const onKeyDown = jest.fn();
const Select = mountDefault({
mapKeydown: (defaults, vm) => ({...defaults, 32: onKeyDown}),
mapKeydown: (defaults, vm) => ({ ...defaults, 32: onKeyDown }),
});
Select.find({ref: 'search'}).trigger('keydown.space');
Select.find({ ref: 'search' }).trigger('keydown.space');
expect(onKeyDown.mock.calls.length).toBe(1);
});
@@ -20,7 +20,7 @@ describe('Custom Keydown Handlers', () => {
const spy = jest.spyOn(Select.vm, 'typeAheadSelect');
Select.find({ref: 'search'}).trigger('keydown.space');
Select.find({ ref: 'search' }).trigger('keydown.space');
expect(spy).toHaveBeenCalledTimes(1);
});
@@ -28,16 +28,16 @@ describe('Custom Keydown Handlers', () => {
it('even works when combining selectOnKeyCodes with map-keydown', () => {
const onKeyDown = jest.fn();
const Select = mountDefault({
mapKeydown: (defaults, vm) => ({...defaults, 32: onKeyDown}),
mapKeydown: (defaults, vm) => ({ ...defaults, 32: onKeyDown }),
selectOnKeyCodes: [9],
});
const spy = jest.spyOn(Select.vm, 'typeAheadSelect');
Select.find({ref: 'search'}).trigger('keydown.space');
Select.find({ ref: 'search' }).trigger('keydown.space');
expect(onKeyDown.mock.calls.length).toBe(1);
Select.find({ref: 'search'}).trigger('keydown.tab');
Select.find({ ref: 'search' }).trigger('keydown.tab');
expect(spy).toHaveBeenCalledTimes(1);
});
@@ -47,25 +47,25 @@ describe('Custom Keydown Handlers', () => {
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, 'typeAheadSelect');
Select.find({ref: 'search'}).trigger('compositionstart');
Select.find({ref: 'search'}).trigger('keydown.enter');
Select.find({ ref: 'search' }).trigger('compositionstart');
Select.find({ ref: 'search' }).trigger('keydown.enter');
expect(spy).toHaveBeenCalledTimes(0);
Select.find({ref: 'search'}).trigger('compositionend');
Select.find({ref: 'search'}).trigger('keydown.enter');
Select.find({ ref: 'search' }).trigger('compositionend');
Select.find({ ref: 'search' }).trigger('keydown.enter');
expect(spy).toHaveBeenCalledTimes(1);
});
it('will not select a value with tab if the user is composing', () => {
const Select = mountDefault({selectOnTab: true});
const Select = mountDefault({ selectOnTab: true });
const spy = jest.spyOn(Select.vm, 'typeAheadSelect');
Select.find({ref: 'search'}).trigger('compositionstart');
Select.find({ref: 'search'}).trigger('keydown.tab');
Select.find({ ref: 'search' }).trigger('compositionstart');
Select.find({ ref: 'search' }).trigger('keydown.tab');
expect(spy).toHaveBeenCalledTimes(0);
Select.find({ref: 'search'}).trigger('compositionend');
Select.find({ref: 'search'}).trigger('keydown.tab');
Select.find({ ref: 'search' }).trigger('compositionend');
Select.find({ ref: 'search' }).trigger('keydown.tab');
expect(spy).toHaveBeenCalledTimes(1);
});
+37
View File
@@ -41,4 +41,41 @@ describe("Labels", () => {
Select.vm.$data._value = "one";
expect(Select.vm.searchPlaceholder).not.toBeDefined();
});
describe('getOptionLabel', () => {
it('will return undefined if the option lacks the label key', () => {
const getOptionLabel = VueSelect.props.getOptionLabel.default.bind({ label: 'label' });
expect(getOptionLabel({name: 'vue'})).toEqual(undefined);
});
it('will return a string value for a valid key', () => {
const getOptionLabel = VueSelect.props.getOptionLabel.default.bind({ label: 'label' });
expect(getOptionLabel({label: 'vue'})).toEqual('vue');
});
/**
* this test fails because of a bug where Vue executes the default contents
* of a slot, even if it is implemented by the consumer.
* @see https://github.com/vuejs/vue/issues/10224
* @see https://github.com/vuejs/vue/pull/10229
*/
xit('will not call getOptionLabel if both scoped option slots are used and a filter is provided', () => {
const spy = spyOn(VueSelect.props.getOptionLabel, 'default');
const Select = shallowMount(VueSelect, {
propsData: {
options: [{name: 'one'}],
filter: () => {},
},
scopedSlots: {
'option': '<span class="option">{{ props.name }}</span>',
'selected-option': '<span class="selected">{{ props.name }}</span>',
},
});
Select.vm.select({name: 'one'});
expect(spy).toHaveBeenCalledTimes(0);
expect(Select.find('.selected').exists()).toBeTruthy();
});
});
});
+31
View File
@@ -0,0 +1,31 @@
import Select from '../../src/components/Select';
describe('Comparing Options', () => {
const comparator = Select.methods.optionComparator.bind({
getOptionKey: Select.props.getOptionKey.default,
});
it('can compare numbers', () => {
expect(comparator(1, 2)).toBeFalsy();
expect(comparator(1, 1)).toBeTruthy();
});
it('can compare strings', () => {
expect(comparator('one', 'one')).toBeTruthy();
expect(comparator('one', 'two')).toBeFalsy();
});
it('can compare objects', () => {
// compare ID keys
expect(comparator({label: 'halo', id: 1}, {label: 'halo', id: 2}))
.toBeFalsy();
// compare objects
expect(comparator({label: 'halo', value: 1}, {label: 'halo', value: 1}))
.toBeTruthy();
// compare objects with different orders
expect(comparator({value: 1, label: 'halo'}, {label: 'halo', value: 1}))
.toBeTruthy();
});
});
+2 -2
View File
@@ -5,11 +5,11 @@ describe('Serializing Option Keys', () => {
const getOptionKey = Select.props.getOptionKey.default;
it('can serialize strings to a key', () => {
expect(getOptionKey('vue')).toBe('"vue"');
expect(getOptionKey('vue')).toBe('vue');
});
it('can serialize integers to a key', () => {
expect(getOptionKey(1)).toBe('1');
expect(getOptionKey(1)).toBe(1);
});
it('can serialize objects to a key', () => {
+63 -4
View File
@@ -100,19 +100,36 @@ describe("When reduce prop is defined", () => {
expect(Select.vm.selectedValue).toEqual([]);
});
it("can use v-model syntax for a two way binding to a parent component", () => {
it("can use v-model syntax for a two way binding to a parent component", async () => {
const Parent = mount({
data: () => ({
reduce: option => option.value,
value: "foo",
current: "foo",
options: [
{ label: "This is Foo", value: "foo" },
{ label: "This is Bar", value: "bar" },
{ label: "This is Baz", value: "baz" }
]
}),
template: `<div><v-select :reduce="option => option.value" :options="options" v-model="value"></v-select></div>`,
components: { "v-select": VueSelect }
components: { "v-select": VueSelect },
computed: {
value: {
get() {
return this.current;
},
set(value) {
if (value == 'baz') return;
this.current = value;
}
}
},
template: `
<v-select
v-model="value"
:reduce="option => option.value"
:options="options"
/>
`
});
const Select = Parent.vm.$children[0];
@@ -120,7 +137,15 @@ describe("When reduce prop is defined", () => {
expect(Select.selectedValue).toEqual([{ label: "This is Foo", value: "foo" }]);
Select.select({ label: "This is Bar", value: "bar" });
await Select.$nextTick();
expect(Parent.vm.value).toEqual("bar");
expect(Select.selectedValue).toEqual([{ label: "This is Bar", value: "bar" }]);
// Parent denies to set baz
Select.select({ label: "This is Baz", value: "baz" });
await Select.$nextTick();
expect(Select.selectedValue).toEqual([{ label: "This is Bar", value: "bar" }]);
expect(Parent.vm.value).toEqual('bar');
});
it("can generate labels using a custom label key", () => {
@@ -226,4 +251,38 @@ describe("When reduce prop is defined", () => {
expect(Select.vm.selectedValue).toEqual([optionToChangeTo]);
});
describe('Reducing Tags', () => {
it('tracks values that have been created by the user', async () => {
const Parent = mount({
data: () => ({selected: null, options: []}),
template: `
<v-select
v-model="selected"
:options="options"
taggable
:reduce="name => name.value"
:create-option="label => ({ label, value: -1 })"
/>
`,
components: {'v-select': VueSelect},
});
const Select = Parent.vm.$children[0];
// When
Select.$refs.search.focus();
await Select.$nextTick();
Select.search = 'hello';
await Select.$nextTick();
Select.typeAheadSelect();
await Select.$nextTick();
// Then
expect(Select.selectedValue).toEqual([{label: 'hello', value: -1}]);
expect(Select.$refs.selectedOptions.textContent.trim()).toEqual('hello');
expect(Parent.vm.selected).toEqual(-1);
});
});
});
+92 -2
View File
@@ -1,5 +1,6 @@
import { mount, shallowMount } from "@vue/test-utils";
import VueSelect from "../../src/components/Select.vue";
import { mountDefault } from '../helpers';
describe("VS - Selecting Values", () => {
let defaultProps;
@@ -192,10 +193,20 @@ describe("VS - Selecting Values", () => {
value: [{ label: "foo", value: "bar" }]
}
});
expect(Select.vm.isOptionSelected("foo")).toEqual(true);
expect(Select.vm.isOptionSelected({ label: "foo", value: "bar" })).toEqual(true);
});
describe("change Event", () => {
it('can select two options with the same label', () => {
const options = [{label: 'one', id: 1}, {label: 'one', id: 2}];
const Select = mountDefault({options, multiple: true});
Select.vm.select({label: 'one', id: 1});
Select.vm.select({label: 'one', id: 2});
expect(Select.vm.selectedValue).toEqual(options);
});
describe("input Event", () => {
it("will trigger the input event when the selection changes", () => {
const Select = shallowMount(VueSelect);
Select.vm.select("bar");
@@ -209,5 +220,84 @@ describe("VS - Selecting Values", () => {
Select.vm.select("bar");
expect(Select.emitted("input")[0]).toEqual([["foo", "bar"]]);
});
it("will not trigger the input event when multiple is true and selection is repeated", () => {
const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo ", "bar"], options: ["foo", "bar", "baz"] }
});
Select.vm.select("bar");
expect(Select.emitted("input")).toBeFalsy();
});
});
describe("option:selecting Event", () => {
it("will trigger the option:selecting event when an option is selected", () => {
const Select = shallowMount(VueSelect);
Select.vm.select("bar");
expect(Select.emitted("option:selecting")[0]).toEqual(["bar"]);
});
it("will trigger the option:selecting event regardless of current value", () => {
const Select = shallowMount(VueSelect, {
propsData: { value: ["foo"], options: ["foo", "bar"] }
});
Select.vm.select("foo");
Select.vm.select("bar");
expect(Select.emitted("option:selecting")).toEqual([["foo"], ["bar"]]);
});
it("will trigger the option:selecting event with current selected item when multiple is true", () => {
const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo"], options: ["foo", "bar"] }
});
Select.vm.select("bar");
expect(Select.emitted("option:selecting")[0]).toEqual(["bar"]);
});
it("will trigger the option:selecting event regardless of current value when multiple is true", () => {
const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo", "bar"], options: ["foo", "bar"] }
});
Select.vm.select("bar");
Select.vm.select("bar");
expect(Select.emitted("option:selecting")).toEqual([["bar"], ["bar"]]);
});
});
describe("option:deselected Event", () => {
it("will trigger the option:deselected event when an option is deselected", () => {
const Select = shallowMount(VueSelect, {
propsData: { value: ["foo"], options: ["foo", "bar"] }
});
Select.vm.deselect("foo");
expect(Select.emitted("option:deselected")[0]).toEqual(["foo"]);
});
it("will trigger the option:deselected event regardless of current value", () => {
const Select = shallowMount(VueSelect, {
propsData: { value: ["foo"], options: ["foo", "bar"] }
});
Select.vm.deselect("foo");
Select.vm.deselect("bar");
expect(Select.emitted("option:deselected")).toEqual([["foo"], ["bar"]]);
});
it("will trigger the option:selected event with current selected item when multiple is true", () => {
const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo"], options: ["foo", "bar"] }
});
Select.vm.deselect("bar");
expect(Select.emitted("option:deselected")[0]).toEqual(["bar"]);
});
it("will trigger the option:selected event regardless of current value when multiple is true", () => {
const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo", "bar"], options: ["foo", "bar"] }
});
Select.vm.deselect("bar");
Select.vm.deselect("bar");
expect(Select.emitted("option:deselected")).toEqual([["bar"], ["bar"]]);
});
});
});
+47
View File
@@ -68,8 +68,55 @@ describe('Scoped Slots', () => {
await Select.vm.$nextTick();
expect(noOptions).toHaveBeenCalledWith({
loading: false,
search: 'something not there',
searching: true,
})
});
test('header slot props', async () => {
const header = jest.fn();
const Select = mountDefault({}, {
scopedSlots: {header: header},
});
await Select.vm.$nextTick();
expect(Object.keys(header.mock.calls[0][0])).toEqual([
'search', 'loading', 'searching', 'filteredOptions', 'deselect',
]);
});
test('footer slot props', async () => {
const footer = jest.fn();
const Select = mountDefault({}, {
scopedSlots: {footer: footer},
});
await Select.vm.$nextTick();
expect(Object.keys(footer.mock.calls[0][0])).toEqual([
'search', 'loading', 'searching', 'filteredOptions', 'deselect',
]);
});
test('list-header slot props', async () => {
const header = jest.fn();
const Select = mountDefault({}, {
scopedSlots: {'list-header': header},
});
Select.vm.open = true;
await Select.vm.$nextTick();
expect(Object.keys(header.mock.calls[0][0])).toEqual([
'search', 'loading', 'searching', 'filteredOptions',
]);
});
test('list-footer slot props', async () => {
const footer = jest.fn();
const Select = mountDefault({}, {
scopedSlots: {'list-footer': footer},
});
Select.vm.open = true;
await Select.vm.$nextTick();
expect(Object.keys(footer.mock.calls[0][0])).toEqual([
'search', 'loading', 'searching', 'filteredOptions',
]);
});
});
+52 -29
View File
@@ -1,6 +1,13 @@
import { searchSubmit, selectWithProps } from "../helpers";
import {
mountDefault,
searchSubmit,
selectTag,
selectWithProps,
} from '../helpers';
import Select from '../../src/components/Select';
describe("When Tagging Is Enabled", () => {
it("can determine if a given option string already exists", () => {
const Select = selectWithProps({ taggable: true, options: ["one", "two"] });
expect(Select.vm.optionExists("one")).toEqual(true);
@@ -13,8 +20,8 @@ describe("When Tagging Is Enabled", () => {
options: [{ label: "one" }, { label: "two" }]
});
expect(Select.vm.optionExists("one")).toEqual(true);
expect(Select.vm.optionExists("three")).toEqual(false);
expect(Select.vm.optionExists({label: "one"})).toEqual(true);
expect(Select.vm.optionExists({label: "three"})).toEqual(false);
});
it("can determine if a given option object already exists when using custom labels", () => {
@@ -24,11 +31,13 @@ describe("When Tagging Is Enabled", () => {
label: "foo"
});
expect(Select.vm.optionExists("one")).toEqual(true);
expect(Select.vm.optionExists("three")).toEqual(false);
const createOption = (text) => Select.vm.createOption(text);
expect(Select.vm.optionExists(createOption("one"))).toEqual(true);
expect(Select.vm.optionExists(createOption("three"))).toEqual(false);
});
it("can add the current search text as the first item in the options list", () => {
it("can add the current search text as the first item in the options list", async () => {
const Select = selectWithProps({
taggable: true,
multiple: true,
@@ -37,36 +46,37 @@ describe("When Tagging Is Enabled", () => {
});
Select.vm.search = "three";
await Select.vm.$nextTick();
expect(Select.vm.filteredOptions).toEqual(["three"]);
});
it("can select the current search text as a string", () => {
it("can select the current search text as a string", async () => {
const Select = selectWithProps({
taggable: true,
multiple: true,
options: ["one", "two"]
});
searchSubmit(Select, "three");
await selectTag(Select, "three");
expect(Select.vm.selectedValue).toEqual(["three"]);
});
it("can select the current search text as an object", () => {
it("can select the current search text as an object", async () => {
const Select = selectWithProps({
taggable: true,
multiple: true,
options: [{ label: "one" }]
});
searchSubmit(Select, "two");
await selectTag(Select, "two");
expect(Select.vm.selectedValue).toEqual([
{ label: "two" }
]);
});
it("should add a freshly created option/tag to the options list when pushTags is true", () => {
it("should add a freshly created option/tag to the options list when pushTags is true", async () => {
const Select = selectWithProps({
pushTags: true,
taggable: true,
@@ -75,12 +85,12 @@ describe("When Tagging Is Enabled", () => {
options: ["one", "two"]
});
searchSubmit(Select, "three");
await selectTag(Select, "three");
expect(Select.vm.pushedTags).toEqual(["three"]);
expect(Select.vm.optionList).toEqual(["one", "two", "three"]);
});
it("should pushTags even if the consumer has defined a createOption callback", () => {
it("should pushTags even if the consumer has defined a createOption callback", async () => {
const Select = selectWithProps({
pushTags: true,
taggable: true,
@@ -88,13 +98,13 @@ describe("When Tagging Is Enabled", () => {
options: ["one", "two"]
});
searchSubmit(Select, "three");
await selectTag(Select, "three");
expect(Select.vm.pushedTags).toEqual(["three"]);
expect(Select.vm.optionList).toEqual(["one", "two", "three"]);
});
it("should add a freshly created option/tag to the options list when pushTags is true and filterable is false", () => {
it("should add a freshly created option/tag to the options list when pushTags is true and filterable is false", async () => {
const Select = selectWithProps({
filterable: false,
pushTags: true,
@@ -104,13 +114,13 @@ describe("When Tagging Is Enabled", () => {
options: ["one", "two"]
});
searchSubmit(Select, "three");
await selectTag(Select, "three");
expect(Select.vm.pushedTags).toEqual(["three"]);
expect(Select.vm.optionList).toEqual(["one", "two", "three"]);
expect(Select.vm.filteredOptions).toEqual(["one", "two", "three"]);
});
it("wont add a freshly created option/tag to the options list when pushTags is false", () => {
it("wont add a freshly created option/tag to the options list when pushTags is false", async () => {
const Select = selectWithProps({
pushTags: false,
taggable: true,
@@ -119,11 +129,11 @@ describe("When Tagging Is Enabled", () => {
options: ["one", "two"]
});
searchSubmit(Select, "three");
await selectTag(Select, "three");
expect(Select.vm.optionList).toEqual(["one", "two"]);
});
it("wont add a freshly created option/tag to the options list when pushTags is false and filterable is false", () => {
it("wont add a freshly created option/tag to the options list when pushTags is false and filterable is false", async () => {
const Select = selectWithProps({
filterable: false,
pushTags: false,
@@ -133,7 +143,7 @@ describe("When Tagging Is Enabled", () => {
options: ["one", "two"]
});
searchSubmit(Select, "three");
await selectTag(Select, "three");
expect(Select.vm.optionList).toEqual(["one", "two"]);
expect(Select.vm.filteredOptions).toEqual(["one", "two"]);
});
@@ -146,9 +156,7 @@ describe("When Tagging Is Enabled", () => {
options: ["one", two]
});
Select.vm.search = "two";
searchSubmit(Select);
await selectTag(Select, "two");
expect(Select.vm.selectedValue).toEqual([two]);
});
@@ -207,34 +215,49 @@ describe("When Tagging Is Enabled", () => {
expect(Select.vm.selectedValue).toEqual([{ label: "one" }]);
});
it("should not allow duplicate tags when using string options", () => {
it("should not allow duplicate tags when using string options", async () => {
const Select = selectWithProps({
taggable: true,
multiple: true
});
searchSubmit(Select, "one");
await selectTag(Select, "one");
expect(Select.vm.selectedValue).toEqual(["one"]);
expect(Select.vm.search).toEqual("");
searchSubmit(Select, "one");
await selectTag(Select, "one");
expect(Select.vm.selectedValue).toEqual(["one"]);
expect(Select.vm.search).toEqual("");
});
it("should not allow duplicate tags when using object options", () => {
it("should not allow duplicate tags when using object options", async () => {
const Select = selectWithProps({
taggable: true,
multiple: true,
options: [{ label: "two" }]
});
const spy = jest.spyOn(Select.vm, 'select');
searchSubmit(Select, "one");
await selectTag(Select, "one");
expect(Select.vm.selectedValue).toEqual([{ label: "one" }]);
expect(spy).lastCalledWith({label: 'one'});
expect(Select.vm.search).toEqual("");
searchSubmit(Select, "one");
await selectTag(Select, "one");
expect(Select.vm.selectedValue).toEqual([{ label: "one" }]);
expect(Select.vm.search).toEqual("");
});
it("will select an existing option on tab", async () => {
const Select = mountDefault({
taggable: true,
selectOnTab: true
});
Select.vm.typeAheadPointer = 0;
Select.find({ ref: "search" }).trigger("keydown.tab");
await Select.vm.$nextTick();
expect(Select.vm.selectedValue).toEqual(['one']);
})
});
+7 -118
View File
@@ -1,18 +1,17 @@
import { shallowMount } from '@vue/test-utils';
import { shallowMount } from "@vue/test-utils";
import VueSelect from "../../src/components/Select";
import { mountDefault, mountWithoutTestUtils } from '../helpers';
import typeAheadMixin from '../../src/mixins/typeAheadPointer';
import Vue from 'vue';
import { mountDefault, mountWithoutTestUtils } from "../helpers";
import typeAheadMixin from "../../src/mixins/typeAheadPointer";
import Vue from "vue";
describe("Moving the Typeahead Pointer", () => {
it('should set the pointer to zero when the filteredOptions watcher is called', async () => {
it("should set the pointer to zero when the filteredOptions watcher is called", async () => {
const Select = shallowMount(VueSelect, {
propsData: { options: ['one', 'two', 'three'] },
propsData: { options: ["one", "two", "three"] },
sync: false
});
Select.vm.search = 'one';
Select.vm.search = "one";
await Select.vm.$nextTick();
expect(Select.vm.typeAheadPointer).toEqual(0);
@@ -45,114 +44,4 @@ describe("Moving the Typeahead Pointer", () => {
Select.vm.typeAheadDown();
expect(Select.vm.typeAheadPointer).toEqual(2);
});
describe("Automatic Scrolling", () => {
it("should check if the scroll position needs to be adjusted on up arrow keyUp", () => {
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
Select.vm.typeAheadPointer = 1;
Select.find({ ref: "search" }).trigger("keydown.up");
expect(spy).toHaveBeenCalled();
});
it("should check if the scroll position needs to be adjusted on down arrow keyUp", () => {
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
Select.vm.typeAheadPointer = 1;
Select.find({ ref: "search" }).trigger("keydown.down");
expect(spy).toHaveBeenCalled();
});
/**
* This test fails despite working in the browser.
* After many attempts to get it to pass, it's been
* rewritten below.
*/
it.skip("should check if the scroll position needs to be adjusted when filtered options changes", () => {
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
Select.vm.search = "two";
expect(spy).toHaveBeenCalled();
});
it("should scroll up if the pointer is above the current viewport bounds", () => {
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "scrollTo");
Select.setMethods({
pixelsToPointerTop() {
return 1;
},
viewport() {
return { top: 2, bottom: 0 };
}
});
Select.vm.maybeAdjustScroll();
expect(spy).toHaveBeenCalledWith(1);
});
it("should scroll down if the pointer is below the current viewport bounds", () => {
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "scrollTo");
Select.setMethods({
pixelsToPointerBottom() {
return 2;
},
viewport() {
return { top: 0, bottom: 1 };
}
});
Select.vm.maybeAdjustScroll();
expect(spy).toHaveBeenCalledWith(
Select.vm.viewport().top + Select.vm.pointerHeight()
);
});
});
describe("Measuring pixel distances", () => {
it("should calculate pointerHeight as the offsetHeight of the pointer element if it exists", async () => {
const Select = mountDefault();
// Drop down must be open for $refs to exist
Select.vm.open = true;
await Select.vm.$nextTick();
/**
* Since JSDom doesn't render layouts, set the offsetHeight explicitly
* to 25px for each list item.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
*/
let i = 0;
for (let option of Select.vm.$refs.dropdownMenu.children) {
Object.defineProperty(option, "offsetHeight", {
value: 1 + i
});
i++;
}
// Fresh instances start with the pointer at -1
Select.vm.typeAheadPointer = -1;
expect(Select.vm.pointerHeight()).toEqual(0);
Select.vm.typeAheadPointer = 0;
expect(Select.vm.pointerHeight()).toEqual(1);
Select.vm.typeAheadPointer = 1;
expect(Select.vm.pointerHeight()).toEqual(2);
Select.vm.typeAheadPointer = 2;
expect(Select.vm.pointerHeight()).toEqual(3);
});
});
});
@@ -0,0 +1,14 @@
import sortAndStringify from '../../../src/utility/sortAndStringify';
test('it will stringify an object', () => {
expect(sortAndStringify({hello: 'world'})).toEqual('{"hello":"world"}');
});
test('it will sort attributes alphabetically', () => {
expect(sortAndStringify({b: 'b', a: 'a'})).toEqual('{"a":"a","b":"b"}');
});
test('comparing two objects with unsorted keys', () => {
expect(sortAndStringify({b: 'b', a: 'a'}))
.toEqual(sortAndStringify({a: 'a', b: 'b'}))
});
+263 -182
View File
@@ -1489,9 +1489,9 @@ acorn-walk@^6.0.1:
integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==
acorn@^5.5.3:
version "5.7.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==
version "5.7.4"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==
acorn@^6.0.1, acorn@^6.2.1:
version "6.4.0"
@@ -1879,9 +1879,9 @@ aws-sign2@~0.7.0:
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
aws4@^1.8.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
version "1.10.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2"
integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==
axios@^0.19.0:
version "0.19.2"
@@ -2096,9 +2096,9 @@ big.js@^5.2.2:
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
bin-links@^1.1.2, bin-links@^1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-1.1.7.tgz#34b79ea9d0e575d7308afeff0c6b2fc24c793359"
integrity sha512-/eaLaTu7G7/o7PV04QPy1HRT65zf+1tFkPGv0sPTV0tRwufooYBQO3zrcyGgm+ja+ZtBf2GEuKjDRJ2pPG+yqA==
version "1.1.8"
resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-1.1.8.tgz#bd39aadab5dc4bdac222a07df5baf1af745b2228"
integrity sha512-KgmVfx+QqggqP9dA3iIc5pA4T1qEEEL+hOhOhNPaUm77OTrJoOXE/C05SJLNJe6m/2wUK7F1tDSou7n5TfCDzQ==
dependencies:
bluebird "^3.5.3"
cmd-shim "^3.0.0"
@@ -2380,9 +2380,9 @@ bytes@3.1.0, bytes@^3.0.0:
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
cacache@^12.0.0, cacache@^12.0.2, cacache@^12.0.3:
version "12.0.3"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390"
integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==
version "12.0.4"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c"
integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==
dependencies:
bluebird "^3.5.5"
chownr "^1.1.1"
@@ -2623,12 +2623,7 @@ chokidar@^2.0.2, chokidar@^2.1.5, chokidar@^2.1.8:
optionalDependencies:
fsevents "^1.2.7"
chownr@^1.1.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142"
integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==
chownr@^1.1.2, chownr@^1.1.4:
chownr@^1.1.1, chownr@^1.1.2, chownr@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
@@ -3608,14 +3603,14 @@ debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
dependencies:
ms "^2.1.1"
debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5:
debug@^3.1.0, debug@^3.1.1, debug@^3.2.5:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
dependencies:
ms "^2.1.1"
debuglog@^1.0.1:
debuglog@*, debuglog@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
@@ -4094,7 +4089,24 @@ error-ex@^1.2.0, error-ex@^1.3.1:
dependencies:
is-arrayish "^0.2.1"
es-abstract@^1.17.0-next.1, es-abstract@^1.17.2:
es-abstract@^1.17.0-next.1, es-abstract@^1.17.5:
version "1.17.6"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a"
integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==
dependencies:
es-to-primitive "^1.2.1"
function-bind "^1.1.1"
has "^1.0.3"
has-symbols "^1.0.1"
is-callable "^1.2.0"
is-regex "^1.1.0"
object-inspect "^1.7.0"
object-keys "^1.1.1"
object.assign "^4.1.0"
string.prototype.trimend "^1.0.1"
string.prototype.trimstart "^1.0.1"
es-abstract@^1.17.2:
version "1.17.4"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184"
integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==
@@ -4316,9 +4328,9 @@ etag@~1.8.1:
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
eventemitter3@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb"
integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
events@^3.0.0:
version "3.1.0"
@@ -4585,9 +4597,9 @@ fb-watchman@^2.0.0:
bser "2.1.1"
figgy-pudding@^3.4.1, figgy-pudding@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==
version "3.5.2"
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==
figures@^2.0.0:
version "2.0.0"
@@ -4766,11 +4778,9 @@ follow-redirects@1.5.10:
debug "=3.1.0"
follow-redirects@^1.0.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb"
integrity sha512-4eyLK6s6lH32nOvLLwlIOnr9zrL8Sm+OvW4pVTJNoXeGzYIkHVf+pADQi+OJ0E67hiuSLezPVPyBcIZO50TmmQ==
dependencies:
debug "^3.0.0"
version "1.13.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
for-in@^1.0.2:
version "1.0.2"
@@ -4924,9 +4934,9 @@ gensync@^1.0.0-beta.1:
integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==
gentle-fs@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/gentle-fs/-/gentle-fs-2.3.0.tgz#13538db5029400f98684be4894e8a7d8f0d1ea7f"
integrity sha512-3k2CgAmPxuz7S6nKK+AqFE2AdM1QuwqKLPKzIET3VRwK++3q96MsNFobScDjlCrq97ZJ8y5R725MOlm6ffUCjg==
version "2.3.1"
resolved "https://registry.yarnpkg.com/gentle-fs/-/gentle-fs-2.3.1.tgz#11201bf66c18f930ddca72cf69460bdfa05727b1"
integrity sha512-OlwBBwqCFPcjm33rF2BjW+Pr6/ll2741l+xooiwTCeaX2CA1ZuclavyMBe0/KlR21/XGsgY6hzEQZ15BdNa13Q==
dependencies:
aproba "^1.1.2"
chownr "^1.1.2"
@@ -5030,7 +5040,7 @@ glob@7.1.4:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@~7.1.1:
glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@@ -5151,10 +5161,10 @@ got@^6.7.1:
unzip-response "^2.0.1"
url-parse-lax "^1.0.0"
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
"graceful-readlink@>= 1.0.0":
version "1.0.1"
@@ -5195,7 +5205,7 @@ har-schema@^2.0.0:
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
har-validator@~5.1.0:
har-validator@~5.1.3:
version "5.1.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
@@ -5333,12 +5343,7 @@ hook-std@^2.0.0:
resolved "https://registry.yarnpkg.com/hook-std/-/hook-std-2.0.0.tgz#ff9aafdebb6a989a354f729bb6445cf4a3a7077c"
integrity sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==
hosted-git-info@^2.1.4:
version "2.8.5"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c"
integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==
hosted-git-info@^2.7.1, hosted-git-info@^2.8.7:
hosted-git-info@^2.1.4, hosted-git-info@^2.7.1, hosted-git-info@^2.8.8:
version "2.8.8"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
@@ -5529,9 +5534,9 @@ http-proxy-middleware@0.19.1:
micromatch "^3.1.10"
http-proxy@^1.17.0:
version "1.18.0"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a"
integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==
version "1.18.1"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
dependencies:
eventemitter3 "^4.0.0"
follow-redirects "^1.0.0"
@@ -5680,7 +5685,7 @@ import-local@2.0.0, import-local@^2.0.0:
pkg-dir "^3.0.0"
resolve-cwd "^2.0.0"
imurmurhash@^0.1.4:
imurmurhash@*, imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
@@ -5741,9 +5746,9 @@ inherits@2.0.3:
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
version "1.3.7"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==
init-package-json@^1.10.3:
version "1.10.3"
@@ -5906,7 +5911,12 @@ is-buffer@^1.1.5:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
is-callable@^1.1.4, is-callable@^1.1.5:
is-callable@^1.1.4, is-callable@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb"
integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==
is-callable@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab"
integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==
@@ -5926,9 +5936,9 @@ is-ci@^2.0.0:
ci-info "^2.0.0"
is-cidr@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/is-cidr/-/is-cidr-3.1.0.tgz#72e233d8e1c4cd1d3f11713fcce3eba7b0e3476f"
integrity sha512-3kxTForpuj8O4iHn0ocsn1jxRm5VYm60GDghK6HXmpn4IyZOoRy9/GmdjFA2yEMqw91TB1/K3bFTuI7FlFNR1g==
version "3.1.1"
resolved "https://registry.yarnpkg.com/is-cidr/-/is-cidr-3.1.1.tgz#e92ef121bdec2782271a77ce487a8b8df3718ab7"
integrity sha512-Gx+oErgq1j2jAKCR2Kbq0b3wbH0vQKqZ0wOlHxm0o56nq51Cs/DZA8oz9dMDhbHyHEGgJ86eTeVudtgMMOx3Mw==
dependencies:
cidr-regex "^2.0.10"
@@ -6143,6 +6153,13 @@ is-regex@^1.0.4, is-regex@^1.0.5:
dependencies:
has "^1.0.3"
is-regex@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff"
integrity sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==
dependencies:
has-symbols "^1.0.1"
is-resolvable@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
@@ -6933,9 +6950,9 @@ levn@^0.3.0, levn@~0.3.0:
type-check "~0.3.2"
libcipm@^4.0.7:
version "4.0.7"
resolved "https://registry.yarnpkg.com/libcipm/-/libcipm-4.0.7.tgz#76cd675c98bdaae64db88b782b01b804b6d02c8a"
integrity sha512-fTq33otU3PNXxxCTCYCYe7V96o59v/o7bvtspmbORXpgFk+wcWrGf5x6tBgui5gCed/45/wtPomBsZBYm5KbIw==
version "4.0.8"
resolved "https://registry.yarnpkg.com/libcipm/-/libcipm-4.0.8.tgz#dcea4919e10dfbce420327e63901613b9141bc89"
integrity sha512-IN3hh2yDJQtZZ5paSV4fbvJg4aHxCCg5tcZID/dSVlTuUiWktsgaldVljJv6Z5OUlYspx6xQkbR0efNodnIrOA==
dependencies:
bin-links "^1.1.2"
bluebird "^3.5.1"
@@ -6943,7 +6960,7 @@ libcipm@^4.0.7:
find-npm-prefix "^1.0.2"
graceful-fs "^4.1.11"
ini "^1.3.5"
lock-verify "^2.0.2"
lock-verify "^2.1.0"
mkdirp "^0.5.1"
npm-lifecycle "^3.0.0"
npm-logical-tree "^1.2.1"
@@ -7053,9 +7070,9 @@ libnpmteam@^1.0.2:
npm-registry-fetch "^4.0.0"
libnpx@^10.2.2:
version "10.2.2"
resolved "https://registry.yarnpkg.com/libnpx/-/libnpx-10.2.2.tgz#5a4171b9b92dd031463ef66a4af9f5cbd6b09572"
integrity sha512-ujaYToga1SAX5r7FU5ShMFi88CWpY75meNZtr6RtEyv4l2ZK3+Wgvxq2IqlwWBiDZOqhumdeiocPS1aKrCMe3A==
version "10.2.3"
resolved "https://registry.yarnpkg.com/libnpx/-/libnpx-10.2.3.tgz#d5e01f12d383ffca9a947807ca6a8f587d38fe2c"
integrity sha512-bCvdARu55fLQBhMfcYGF0GznF1kB2sqxq/9zKZ3652M8DDFWpVpCnpgzjzn0yWMDMez5ZGMBiX24yR11uEYZVQ==
dependencies:
dotenv "^5.0.1"
npm-package-arg "^6.0.0"
@@ -7173,6 +7190,11 @@ lockfile@^1.0.4:
dependencies:
signal-exit "^3.0.2"
lodash._baseindexof@*:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"
integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=
lodash._baseuniq@~4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
@@ -7181,11 +7203,33 @@ lodash._baseuniq@~4.6.0:
lodash._createset "~4.0.0"
lodash._root "~3.0.0"
lodash._bindcallback@*:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4=
lodash._cacheindexof@*:
version "3.0.2"
resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"
integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=
lodash._createcache@*:
version "3.1.2"
resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=
dependencies:
lodash._getnative "^3.0.0"
lodash._createset@~4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=
lodash._getnative@*, lodash._getnative@^3.0.0:
version "3.9.1"
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
lodash._reinterpolate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
@@ -7241,6 +7285,11 @@ lodash.merge@^4.6.1:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash.restparam@*:
version "3.6.1"
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=
lodash.sortby@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
@@ -7448,10 +7497,10 @@ marked-terminal@^4.0.0:
node-emoji "^1.10.0"
supports-hyperlinks "^2.0.0"
marked@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/marked/-/marked-0.8.0.tgz#ec5c0c9b93878dc52dd54be8d0e524097bd81a99"
integrity sha512-MyUe+T/Pw4TZufHkzAfDj6HarCBWia2y27/bhuYkTaiUnfDYFnCP3KUN+9oM7Wi6JA2rymtVYbQu3spE0GCmxQ==
marked@^1.0.0:
version "1.2.4"
resolved "https://registry.yarnpkg.com/marked/-/marked-1.2.4.tgz#94e99230b03496c9383b1322ac51bc17dd388a1d"
integrity sha512-6x5TFGCTKSQBLTZtOburGxCxFEBJEGYVLwCMTBCxzvyuisGcC20UNzDSJhCr/cJ/Kmh6ulfJm10g6WWEAJ3kvg==
md5.js@^1.3.4:
version "1.3.5"
@@ -7612,7 +7661,19 @@ mime-db@1.43.0, "mime-db@>= 1.43.0 < 2":
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58"
integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==
mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24:
mime-db@1.44.0:
version "1.44.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
mime-types@^2.1.12, mime-types@~2.1.19:
version "2.1.27"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
dependencies:
mime-db "1.44.0"
mime-types@~2.1.17, mime-types@~2.1.24:
version "2.1.26"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06"
integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==
@@ -7673,16 +7734,16 @@ minimist-options@^3.0.1:
arrify "^1.0.1"
is-plain-obj "^1.1.0"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
minimist@1.2.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0:
minimist@1.2.0, minimist@^1.1.1, minimist@^1.1.3:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
minimist@^1.2.0, minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
minimist@~0.0.1:
version "0.0.10"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
@@ -7727,13 +7788,20 @@ mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
mkdirp@0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
dependencies:
minimist "0.0.8"
"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.0, mkdirp@~0.5.1:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
minimist "^1.2.5"
modify-values@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
@@ -7866,18 +7934,18 @@ node-emoji@^1.10.0:
lodash.toarray "^4.4.0"
node-fetch-npm@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7"
integrity sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==
version "2.0.4"
resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz#6507d0e17a9ec0be3bec516958a497cec54bf5a4"
integrity sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg==
dependencies:
encoding "^0.1.11"
json-parse-better-errors "^1.0.0"
safe-buffer "^5.1.1"
node-fetch@^2.3.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
node-forge@0.9.0:
version "0.9.0"
@@ -7902,10 +7970,10 @@ node-gyp@^3.8.0:
tar "^2.0.0"
which "1"
node-gyp@^5.0.2, node-gyp@^5.0.7:
version "5.1.0"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.0.tgz#8e31260a7af4a2e2f994b0673d4e0b3866156332"
integrity sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw==
node-gyp@^5.0.2, node-gyp@^5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e"
integrity sha512-WH0WKGi+a4i4DUt2mHnvocex/xPLp9pYt5R6M2JdFB7pJ7Z34hveZ4nDTGTiLXCkitA9T8HFZjhinBCiVHYcWw==
dependencies:
env-paths "^2.2.0"
glob "^7.1.4"
@@ -8006,10 +8074,10 @@ node-sass@^4.12.0:
dependencies:
abbrev "1"
nopt@^4.0.1, nopt@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=
nopt@^4.0.1, nopt@^4.0.3, nopt@~4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==
dependencies:
abbrev "1"
osenv "^0.1.4"
@@ -8052,9 +8120,9 @@ normalize-url@^5.0.0:
integrity sha512-bAEm2fx8Dq/a35Z6PIRkkBBJvR56BbEJvhpNtvCZ4W9FyORSna77fn+xtYFjqk5JpBS+fMnAOG/wFgkQBmB7hw==
npm-audit-report@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/npm-audit-report/-/npm-audit-report-1.3.2.tgz#303bc78cd9e4c226415076a4f7e528c89fc77018"
integrity sha512-abeqS5ONyXNaZJPGAf6TOUMNdSe1Y6cpc9MLBRn+CuUoYbfdca6AxOyXVlfIv9OgKX+cacblbG5w7A6ccwoTPw==
version "1.3.3"
resolved "https://registry.yarnpkg.com/npm-audit-report/-/npm-audit-report-1.3.3.tgz#8226deeb253b55176ed147592a3995442f2179ed"
integrity sha512-8nH/JjsFfAWMvn474HB9mpmMjrnKb1Hx/oTAdjv4PT9iZBvBxiZ+wtDUapHCJwLqYGQVPaAfs+vL5+5k9QndXw==
dependencies:
cli-table3 "^0.5.0"
console-control-strings "^1.1.0"
@@ -8079,9 +8147,9 @@ npm-install-checks@^3.0.2:
semver "^2.3.0 || 3.x || 4 || 5"
npm-lifecycle@^3.0.0, npm-lifecycle@^3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.4.tgz#de6975c7d8df65f5150db110b57cce498b0b604c"
integrity sha512-tgs1PaucZwkxECGKhC/stbEgFyc3TGh2TJcg2CDr6jbvQRdteHNhmMeljRzpe4wgFAXQADoy1cSqqi7mtiAa5A==
version "3.1.5"
resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz#9882d3642b8c82c815782a12e6a1bfeed0026309"
integrity sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g==
dependencies:
byline "^5.0.0"
graceful-fs "^4.1.15"
@@ -8130,7 +8198,7 @@ npm-pick-manifest@^3.0.0, npm-pick-manifest@^3.0.2:
npm-package-arg "^6.0.0"
semver "^5.4.1"
npm-profile@^4.0.2:
npm-profile@^4.0.2, npm-profile@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/npm-profile/-/npm-profile-4.0.4.tgz#28ee94390e936df6d084263ee2061336a6a1581b"
integrity sha512-Ta8xq8TLMpqssF0H60BXS1A90iMoM6GeKwsmravJ6wYjWwSzcYBTdyWa3DZCYqPutacBMEm7cxiOkiIeCUAHDQ==
@@ -8139,10 +8207,10 @@ npm-profile@^4.0.2:
figgy-pudding "^3.4.1"
npm-registry-fetch "^4.0.0"
npm-registry-fetch@^4.0.0, npm-registry-fetch@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-4.0.3.tgz#3c2179e39e04f9348b1c2979545951d36bee8766"
integrity sha512-WGvUx0lkKFhu9MbiGFuT9nG2NpfQ+4dCJwRwwtK2HK5izJEvwDxMeUyqbuMS7N/OkpVCqDorV6rO5E4V9F8lJw==
npm-registry-fetch@^4.0.0, npm-registry-fetch@^4.0.5:
version "4.0.5"
resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-4.0.5.tgz#cb87cf7f25bfb048d6c3ee19d115bebf93ea5bfa"
integrity sha512-yQ0/U4fYpCCqmueB2g8sc+89ckQ3eXpmU4+Yi2j5o/r0WkKvE2+Y0tK3DEILAtn2UaQTkjTHxIXe2/CSdit+/Q==
dependencies:
JSONStream "^1.3.4"
bluebird "^3.5.1"
@@ -8167,14 +8235,14 @@ npm-run-path@^4.0.0:
path-key "^3.0.0"
npm-user-validate@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/npm-user-validate/-/npm-user-validate-1.0.0.tgz#8ceca0f5cea04d4e93519ef72d0557a75122e951"
integrity sha1-jOyg9c6gTU6TUZ73LQVXp1Ei6VE=
version "1.0.1"
resolved "https://registry.yarnpkg.com/npm-user-validate/-/npm-user-validate-1.0.1.tgz#31428fc5475fe8416023f178c0ab47935ad8c561"
integrity sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw==
npm@^6.10.3:
version "6.14.1"
resolved "https://registry.yarnpkg.com/npm/-/npm-6.14.1.tgz#3b80f6f1aa11a9868860dcf897665f80ab38a204"
integrity sha512-2hi3UF7g5VL8VKm46Bx5GAW28DPb8BJZbj2uONMBMhY8XkJ56lSmHJNFcjTQr7KHZqWqiBT5BugaQEy+Y/4T2g==
version "6.14.6"
resolved "https://registry.yarnpkg.com/npm/-/npm-6.14.6.tgz#1a81ce1fac2bf5457dbf6342ceed503627ff228f"
integrity sha512-axnz6iHFK6WPE0js/+mRp+4IOwpHn5tJEw5KB6FiCU764zmffrhsYHbSHi2kKqNkRBt53XasXjngZfBD3FQzrQ==
dependencies:
JSONStream "^1.3.5"
abbrev "~1.1.1"
@@ -8203,10 +8271,10 @@ npm@^6.10.3:
fs-vacuum "~1.2.10"
fs-write-stream-atomic "~1.0.10"
gentle-fs "^2.3.0"
glob "^7.1.4"
graceful-fs "^4.2.3"
glob "^7.1.6"
graceful-fs "^4.2.4"
has-unicode "~2.0.1"
hosted-git-info "^2.8.7"
hosted-git-info "^2.8.8"
iferr "^1.0.2"
infer-owner "^1.0.4"
inflight "~1.0.6"
@@ -8234,10 +8302,10 @@ npm@^6.10.3:
lru-cache "^5.1.1"
meant "~1.0.1"
mississippi "^3.0.0"
mkdirp "~0.5.1"
mkdirp "^0.5.5"
move-concurrently "^1.0.1"
node-gyp "^5.0.7"
nopt "~4.0.1"
node-gyp "^5.1.0"
nopt "^4.0.3"
normalize-package-data "^2.5.0"
npm-audit-report "^1.3.2"
npm-cache-filename "~1.0.2"
@@ -8246,8 +8314,8 @@ npm@^6.10.3:
npm-package-arg "^6.1.1"
npm-packlist "^1.4.8"
npm-pick-manifest "^3.0.2"
npm-profile "^4.0.2"
npm-registry-fetch "^4.0.3"
npm-profile "^4.0.4"
npm-registry-fetch "^4.0.5"
npm-user-validate "~1.0.0"
npmlog "~4.1.2"
once "~1.4.0"
@@ -8268,7 +8336,7 @@ npm@^6.10.3:
readdir-scoped-modules "^1.1.0"
request "^2.88.0"
retry "^0.12.0"
rimraf "^2.6.3"
rimraf "^2.7.1"
safe-buffer "^5.1.2"
semver "^5.7.1"
sha "^3.0.0"
@@ -8349,9 +8417,9 @@ object-hash@^2.0.1:
integrity sha512-b+2AKjAf6uQlxxv8ChHdM+VT4eeX+ZSwv+pk2xIXZWbo+yxn4/En1iC+GHe/OFYa9on0AhFF2PvuAcFHoiiHaA==
object-inspect@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"
integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==
version "1.8.0"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0"
integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==
object-is@^1.0.1:
version "1.0.2"
@@ -8584,7 +8652,14 @@ p-limit@^1.1.0:
dependencies:
p-try "^1.0.0"
p-limit@^2.0.0, p-limit@^2.2.0:
p-limit@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
dependencies:
p-try "^2.0.0"
p-limit@^2.2.0:
version "2.2.2"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e"
integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==
@@ -9457,10 +9532,10 @@ pseudomap@^1.0.2:
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
psl@^1.1.24, psl@^1.1.28:
version "1.7.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c"
integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==
psl@^1.1.28:
version "1.8.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
public-encrypt@^4.0.0:
version "4.0.3"
@@ -9504,7 +9579,7 @@ punycode@1.3.2:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
punycode@^1.2.4, punycode@^1.4.1:
punycode@^1.2.4:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
@@ -9535,9 +9610,9 @@ qs@~6.5.2:
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
query-string@^6.8.2:
version "6.11.0"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.11.0.tgz#dc27a05733d1be66f16d0f83dfa957270f45f66d"
integrity sha512-jS+me8X3OEGFTsF6kF+vUUMFG/d3WUCvD7bHhfZP5784nOq1pjj8yau/u86nfOncmcN6ZkSWKWkKAvv/MGxzLA==
version "6.13.1"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.13.1.tgz#d913ccfce3b4b3a713989fe6d39466d92e71ccad"
integrity sha512-RfoButmcK+yCta1+FuU8REvisx1oEzhMKwhLUNcepQTPGcNMp1sIqjnfCtfnvGSQZQEhaBHvccujtWoUV3TTbA==
dependencies:
decode-uri-component "^0.2.0"
split-on-first "^1.0.0"
@@ -9753,7 +9828,7 @@ read@1, read@~1.0.1, read@~1.0.7:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
"readable-stream@2 || 3", readable-stream@^3.6.0:
"readable-stream@2 || 3", readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@@ -9762,15 +9837,6 @@ read@1, read@~1.0.1, read@~1.0.7:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readable-stream@^3.0.6, readable-stream@^3.1.1:
version "3.5.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.5.0.tgz#465d70e6d1087f6162d079cd0b5db7fbebfd1606"
integrity sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readable-stream@~1.1.10:
version "1.1.14"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
@@ -9998,9 +10064,9 @@ request-promise-native@^1.0.5:
tough-cookie "^2.3.3"
request@^2.87.0, request@^2.88.0:
version "2.88.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.8.0"
@@ -10009,7 +10075,7 @@ request@^2.87.0, request@^2.88.0:
extend "~3.0.2"
forever-agent "~0.6.1"
form-data "~2.3.2"
har-validator "~5.1.0"
har-validator "~5.1.3"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
@@ -10019,7 +10085,7 @@ request@^2.87.0, request@^2.88.0:
performance-now "^2.1.0"
qs "~6.5.2"
safe-buffer "^5.1.2"
tough-cookie "~2.4.3"
tough-cookie "~2.5.0"
tunnel-agent "^0.6.0"
uuid "^3.3.2"
@@ -10090,13 +10156,20 @@ resolve@1.1.7:
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.2.0, resolve@^1.3.2, resolve@^1.8.1:
resolve@^1.1.6, resolve@^1.12.0, resolve@^1.2.0, resolve@^1.3.2, resolve@^1.8.1:
version "1.15.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"
integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==
dependencies:
path-parse "^1.0.6"
resolve@^1.10.0:
version "1.17.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
dependencies:
path-parse "^1.0.6"
restore-cursor@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
@@ -10155,7 +10228,7 @@ right-pad@^1.0.1:
resolved "https://registry.yarnpkg.com/right-pad/-/right-pad-1.0.1.tgz#8ca08c2cbb5b55e74dafa96bf7fd1a27d568c8d0"
integrity sha1-jKCMLLtbVedNr6lr9/0aJ9VoyNA=
rimraf@2, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3:
rimraf@2, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
@@ -10214,9 +10287,9 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-regex@^1.1.0:
version "1.1.0"
@@ -10309,9 +10382,9 @@ selfsigned@^1.10.7:
node-forge "0.9.0"
semantic-release@^17.0.4:
version "17.0.4"
resolved "https://registry.yarnpkg.com/semantic-release/-/semantic-release-17.0.4.tgz#4ca739b2bf80f8ce5e49b05f12c15f49ca233d6d"
integrity sha512-5y9QRSrZtdvACmlpX5DvEVsvFuKRDUVn7JVJFxPVLGrGofDf1d0M/+hA1wFmCjiJZ+VCY8bYaSqVqF14KCF9rw==
version "17.2.3"
resolved "https://registry.yarnpkg.com/semantic-release/-/semantic-release-17.2.3.tgz#11f10b851d4e75b1015b17515c433049b3df994c"
integrity sha512-MY1MlowGQrkOR7+leOD8ICkVOC6i1szbwDODdbJ0UdshtMx8Ms0bhpRQmEEliqYKEb5PLv/dqs6zKKuHT7UxTg==
dependencies:
"@semantic-release/commit-analyzer" "^8.0.0"
"@semantic-release/error" "^2.2.0"
@@ -10330,14 +10403,14 @@ semantic-release@^17.0.4:
hook-std "^2.0.0"
hosted-git-info "^3.0.0"
lodash "^4.17.15"
marked "^0.8.0"
marked "^1.0.0"
marked-terminal "^4.0.0"
micromatch "^4.0.2"
p-each-series "^2.1.0"
p-reduce "^2.0.0"
read-pkg-up "^7.0.0"
resolve-from "^5.0.0"
semver "^7.1.1"
semver "^7.3.2"
semver-diff "^3.1.1"
signale "^1.2.1"
yargs "^15.0.1"
@@ -10376,10 +10449,10 @@ semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.1.1, semver@^7.1.2:
version "7.1.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6"
integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==
semver@^7.1.2, semver@^7.3.2:
version "7.3.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
semver@~5.3.0:
version "5.3.0"
@@ -10537,9 +10610,9 @@ sigmund@^1.0.1:
integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=
signal-exit@^3.0.0, signal-exit@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
version "3.0.3"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
signale@^1.2.1:
version "1.4.0"
@@ -10734,22 +10807,22 @@ spawn-error-forwarder@~1.0.0:
integrity sha1-Gv2Uc46ZmwNG17n8NzvlXgdXcCk=
spdx-correct@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
version "3.1.1"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==
dependencies:
spdx-expression-parse "^3.0.0"
spdx-license-ids "^3.0.0"
spdx-exceptions@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977"
integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==
version "2.3.0"
resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d"
integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==
spdx-expression-parse@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0"
integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==
version "3.0.1"
resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==
dependencies:
spdx-exceptions "^2.1.0"
spdx-license-ids "^3.0.0"
@@ -10973,6 +11046,14 @@ string-width@^4.1.0, string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
string.prototype.trimend@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913"
integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.5"
string.prototype.trimleft@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74"
@@ -10989,6 +11070,14 @@ string.prototype.trimright@^2.1.1:
define-properties "^1.1.3"
function-bind "^1.1.1"
string.prototype.trimstart@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54"
integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.5"
string_decoder@^1.0.0, string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
@@ -11396,7 +11485,7 @@ toposort@^1.0.0:
resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029"
integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk=
tough-cookie@^2.3.3, tough-cookie@^2.3.4:
tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
@@ -11404,14 +11493,6 @@ tough-cookie@^2.3.3, tough-cookie@^2.3.4:
psl "^1.1.28"
punycode "^2.1.1"
tough-cookie@~2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
dependencies:
psl "^1.1.24"
punycode "^1.4.1"
tr46@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
@@ -12138,9 +12219,9 @@ websocket-driver@>=0.5.1:
websocket-extensions ">=0.1.1"
websocket-extensions@>=0.1.1:
version "0.1.3"
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==
version "0.1.4"
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
version "1.0.5"