mirror of
https://github.com/tenrok/vue-select.git
synced 2026-05-17 02:29:37 +03:00
Sass & Class Renames (#759)
* - add autoprefixer - add cssnano - add postcss-loader - remove unused packages * create RTL scss module * add vs__ prefix to open-indicator, extract to module * module for dropdown-toggle * vs__clear module * vs__dropdown-menu module * rename `selected-tag` to `vs__selected` * remove rtl class * remove dropdown class * search-input scss module * move animations to global module * refactor dropdown list items * - spinner slot is now scoped with `loading` variable - move spinner to scss module * apply vs__search class directly to search input: if you're using the slot, you might not want default styles * finish global modules * make RTL a component state * - update component states to use vs-- prefix - rename dropdownClasses to stateClasses * remove unused property * Closes #760 * fix states * more state fixes * rename .close to vs__deselect * - simplify dev.html - start on 'sandbox' development * update build * - update webpack config - move Sandbox to VuePress folder * update external framework version links * assign grid areas, ensure 100% height outside of docs * limit specificity * first pass at assigning variables * assign 'darkest' * remove max-height prop * rename 'component' variables to 'state' * update badges * add deprecation notice to docs * bump travis config * add coveralls coverage reporter * bump netlify config * additional pass pulling up to variables * start converting to SVG icons * middle align action icons * update netlify config * netlify bump * fix travis * fix travis * try lcov * netlify attempt * prune old packages * bump travis config
This commit is contained in:
+5
-4
@@ -1,8 +1,9 @@
|
||||
language: node_js
|
||||
cache: npm
|
||||
|
||||
node_js:
|
||||
- node
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
script:
|
||||
- yarn test --coverage --coverageReporters=text-lcov | coveralls
|
||||
- yarn test --coverage --coverageReporters=text-lcov
|
||||
- codecov
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const chokidar = require('chokidar');
|
||||
const VueLoaderPlugin = require('vue-loader').VueLoaderPlugin;
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
|
||||
@@ -8,7 +7,9 @@ const env = process.env.NODE_ENV === 'production'
|
||||
? 'production'
|
||||
: 'development';
|
||||
|
||||
const extractOrInjectStyles = process.env.NODE_ENV !== 'production'
|
||||
const devtool = env === 'production' ? 'source-map' : 'eval-source-map';
|
||||
|
||||
const extractOrInjectStyles = env !== 'production'
|
||||
? 'vue-style-loader'
|
||||
: MiniCssExtractPlugin.loader;
|
||||
|
||||
@@ -19,7 +20,7 @@ module.exports = {
|
||||
publicPath: '/',
|
||||
filename: '[name].js',
|
||||
},
|
||||
// devtool: env === 'production' ? 'source-map' : 'eval-source-map',
|
||||
devtool,
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue'],
|
||||
alias: {
|
||||
@@ -47,13 +48,10 @@ module.exports = {
|
||||
use: [
|
||||
extractOrInjectStyles,
|
||||
'css-loader',
|
||||
'postcss-loader',
|
||||
'sass-loader',
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
loader: 'vue-html-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
@@ -65,17 +63,8 @@ module.exports = {
|
||||
}),
|
||||
new VueLoaderPlugin(),
|
||||
],
|
||||
devServer: {
|
||||
hot: true,
|
||||
hotOnly: true,
|
||||
inline: true,
|
||||
port: 8080,
|
||||
before (app, server) {
|
||||
chokidar.watch([
|
||||
'./**/*.html',
|
||||
]).on('all', function () {
|
||||
server.sockWrite(server.sockets, 'content-changed');
|
||||
});
|
||||
},
|
||||
stats: {
|
||||
children: false,
|
||||
modules: false,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
const merge = require('webpack-merge');
|
||||
const chokidar = require('chokidar');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const baseWebpackConfig = require('./webpack.base.conf');
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
module.exports = merge(baseWebpackConfig, {
|
||||
entry: './dev/dev.js',
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: './dev/dev.html',
|
||||
inject: true,
|
||||
}),
|
||||
@@ -14,4 +17,23 @@ module.exports = merge(baseWebpackConfig, {
|
||||
optimization: {
|
||||
noEmitOnErrors: true,
|
||||
},
|
||||
devServer: {
|
||||
hot: true,
|
||||
hotOnly: true,
|
||||
open: true,
|
||||
inline: true,
|
||||
stats: {
|
||||
children: false,
|
||||
modules: false,
|
||||
chunks: false,
|
||||
},
|
||||
port: 8080,
|
||||
before (app, server) {
|
||||
chokidar.watch([
|
||||
'./**/*.html',
|
||||
]).on('all', function () {
|
||||
server.sockWrite(server.sockets, 'content-changed');
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
+20
-115
@@ -1,130 +1,35 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<v-select placeholder="default" :options="options"></v-select>
|
||||
<v-select placeholder="default, RTL" :options="options" dir="rtl"></v-select>
|
||||
<v-select placeholder="default, options=[1,5,10]" :options="[1,5,10]"></v-select>
|
||||
<v-select placeholder="multiple" multiple :options="options"></v-select>
|
||||
<v-select placeholder="multiple, taggable" multiple taggable :options="options" no-drop></v-select>
|
||||
<v-select placeholder="multiple, taggable, push-tags" multiple push-tags taggable :options="[{label: 'Foo', value: 'foo'}]"></v-select>
|
||||
<v-select placeholder="multiple, closeOnSelect=true" multiple :options="['cat', 'dog', 'bear']"></v-select>
|
||||
<v-select placeholder="multiple, closeOnSelect=false" multiple :close-on-select="false" :options="['cat', 'dog', 'bear']"></v-select>
|
||||
<v-select placeholder="searchable=false" :options="options" :searchable="false"></v-select>
|
||||
<v-select placeholder="search github.." label="full_name" @search="search" :options="ajaxRes"></v-select>
|
||||
<v-select placeholder="custom option template" :options="options" multiple>
|
||||
<template slot="selected-option" slot-scope="option">
|
||||
{{option.label}}
|
||||
<sandbox hide-help>
|
||||
<template slot-scope="config">
|
||||
|
||||
<v-select v-bind="config" />
|
||||
|
||||
</template>
|
||||
<template slot="option" slot-scope="option">
|
||||
{{option.label}} ({{option.value}})
|
||||
</template>
|
||||
</v-select>
|
||||
<v-select placeholder="custom option template for string array" taggable :options="['cat', 'dog', 'bear']" multiple>
|
||||
<template slot="selected-option" slot-scope="option">
|
||||
{{option.label}}
|
||||
</template>
|
||||
<template slot="option" slot-scope="option">
|
||||
{{option.label}}
|
||||
</template>
|
||||
</v-select>
|
||||
<v-select multiple placeholder="custom label template" :options="options">
|
||||
<span
|
||||
slot="selected-option-container"
|
||||
slot-scope="props"
|
||||
class="selected-tag"
|
||||
>
|
||||
{{ props.option.label }} ({{ props.option.value }})
|
||||
<button v-if="props.multiple" @click="props.deselect(props.option)" type="button" class="close" aria-label="Remove option">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</span>
|
||||
</v-select>
|
||||
<v-select placeholder="select on tab" :select-on-tab="true" :options="options"></v-select>
|
||||
<v-select placeholder="disabled" disabled value="disabled"></v-select>
|
||||
<v-select placeholder="disabled multiple" disabled multiple :value="['disabled', 'multiple']"></v-select>
|
||||
<v-select placeholder="filterable=false, @search=searchPeople" label="first_name" :filterable="false" @search="searchPeople" :options="people"></v-select>
|
||||
<v-select placeholder="filtering with fuse.js" label="title" :options="fuseSearchOptions" :filter="fuseSearch">
|
||||
<template slot="option" slot-scope="option">
|
||||
<strong>{{ option.title }}</strong><br>
|
||||
<em>{{ `${option.author.firstName} ${option.author.lastName}` }}</em>
|
||||
</template>
|
||||
</v-select>
|
||||
</sandbox>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Fuse from "fuse.js";
|
||||
import debounce from "lodash/debounce";
|
||||
import vSelect from "../src/components/Select.vue";
|
||||
import countries from "./data/countryCodes";
|
||||
import fuseSearchOptions from "./data/books";
|
||||
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';
|
||||
|
||||
export default {
|
||||
components: { vSelect },
|
||||
data() {
|
||||
return {
|
||||
placeholder: "placeholder",
|
||||
value: null,
|
||||
options: countries,
|
||||
ajaxRes: [],
|
||||
people: [],
|
||||
fuseSearchOptions
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
search(search, loading) {
|
||||
loading(true);
|
||||
this.getRepositories(search, loading, this);
|
||||
},
|
||||
searchPeople(search, loading) {
|
||||
loading(true);
|
||||
this.getPeople(loading, this);
|
||||
},
|
||||
getPeople: debounce((loading, vm) => {
|
||||
vm.$http.get(`https://reqres.in/api/users?per_page=10`).then(res => {
|
||||
vm.people = res.data.data;
|
||||
loading(false);
|
||||
});
|
||||
}, 250),
|
||||
getRepositories: debounce((search, loading, vm) => {
|
||||
vm.$http
|
||||
.get(`https://api.github.com/search/repositories?q=${search}`)
|
||||
.then(res => {
|
||||
vm.ajaxRes = res.data.items;
|
||||
loading(false);
|
||||
});
|
||||
}, 250),
|
||||
fuseSearch(options, search) {
|
||||
return new Fuse(options, {
|
||||
keys: ["title", "author.firstName", "author.lastName"]
|
||||
}).search(search);
|
||||
}
|
||||
}
|
||||
components: {Sandbox, vSelect},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/*@import "https://cdnjs.cloudflare.com/ajax/libs/foundation/6.3.1/css/foundation.min.css";*/
|
||||
/*@import "https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.2/css/bulma.min.css";*/
|
||||
/*@import "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css";*/
|
||||
/*@import "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css";*/
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
font-family: -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
font-family: -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 95vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.v-select {
|
||||
width: 25em;
|
||||
margin: 1em;
|
||||
}
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
+7
-80
@@ -1,87 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Vue Select Dev</title>
|
||||
<!--<link href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.3.1/css/foundation.min.css" rel="stylesheet">-->
|
||||
<!--<link href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.2/css/bulma.min.css" rel="stylesheet">-->
|
||||
<!--<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">-->
|
||||
<!--<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css">-->
|
||||
<style>
|
||||
|
||||
#app {
|
||||
height: 95vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
align-content: center;
|
||||
font-family: -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
.v-select {
|
||||
width: 25em;
|
||||
margin: 1em;
|
||||
}
|
||||
</style>
|
||||
<meta charset="utf-8">
|
||||
<title>Vue Select Dev</title>
|
||||
<!--<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">-->
|
||||
<!--<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css">-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<v-select placeholder="default" :options="options"></v-select>
|
||||
<v-select placeholder="default, RTL" :options="options" dir="rtl"></v-select>
|
||||
<v-select placeholder="default, options=[1,5,10]" :options="[1,5,10]"></v-select>
|
||||
<v-select placeholder="multiple" multiple :options="options"></v-select>
|
||||
<v-select placeholder="multiple, taggable" multiple taggable :options="options" no-drop></v-select>
|
||||
<v-select placeholder="multiple, taggable, push-tags" multiple push-tags taggable :options="[{label: 'Foo', value: 'foo'}]"></v-select>
|
||||
<v-select placeholder="multiple, closeOnSelect=true" multiple :options="['cat', 'dog', 'bear']"></v-select>
|
||||
<v-select placeholder="multiple, closeOnSelect=false" multiple :close-on-select="false" :options="['cat', 'dog', 'bear']"></v-select>
|
||||
<v-select placeholder="searchable=false" :options="options" :searchable="false"></v-select>
|
||||
<v-select placeholder="search github.." label="full_name" @search="search" :options="ajaxRes"></v-select>
|
||||
<v-select placeholder="custom option template" :options="options" multiple>
|
||||
<template slot="selected-option" slot-scope="option">
|
||||
{{option.label}}
|
||||
</template>
|
||||
<template slot="option" slot-scope="option">
|
||||
{{option.label}} ({{option.value}})
|
||||
</template>
|
||||
</v-select>
|
||||
<v-select placeholder="custom option template for string array" taggable :options="['cat', 'dog', 'bear']" multiple>
|
||||
<template slot="selected-option" slot-scope="option">
|
||||
{{option.label}}
|
||||
</template>
|
||||
<template slot="option" slot-scope="option">
|
||||
{{option.label}}
|
||||
</template>
|
||||
</v-select>
|
||||
<v-select multiple placeholder="custom label template" :options="options">
|
||||
<span
|
||||
slot="selected-option-container"
|
||||
slot-scope="props"
|
||||
class="selected-tag"
|
||||
>
|
||||
{{ props.option.label }} ({{ props.option.value }})
|
||||
<button v-if="props.multiple" @click="props.deselect(props.option)" type="button" class="close" aria-label="Remove option">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</span>
|
||||
</v-select>
|
||||
<v-select placeholder="select on tab" :select-on-tab="true" :options="options"></v-select>
|
||||
<v-select placeholder="disabled" disabled value="disabled"></v-select>
|
||||
<v-select placeholder="disabled multiple" disabled multiple :value="['disabled', 'multiple']"></v-select>
|
||||
<v-select placeholder="filterable=false, @search=searchPeople" label="first_name" :filterable="false" @search="searchPeople" :options="people"></v-select>
|
||||
<v-select placeholder="filtering with fuse.js" label="title" :options="fuseSearchOptions" :filter="fuseSearch">
|
||||
<template slot="option" scope="option">
|
||||
<strong>{{ option.title }}</strong><br>
|
||||
<em>{{ option.author.firstName + ' ' + option.author.lastName }}</em>
|
||||
</template>
|
||||
</v-select>
|
||||
<v-select placeholder="Vue select with no options and a custom no-option span" >
|
||||
<span slot="no-options">Custom no options message</span>
|
||||
</v-select>
|
||||
</div>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
<template>
|
||||
<div id="home">
|
||||
<div class="container">
|
||||
<h1>Vue Select</h1>
|
||||
|
||||
<p class="accolades lead">
|
||||
<a href="https://github.com/sagalbot/vue-select">
|
||||
<img src="https://img.shields.io/github/stars/sagalbot/vue-select.svg?label=Stars&style=flat-square"
|
||||
alt="GitHub Stars">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/vue-select">
|
||||
<img src="https://img.shields.io/npm/dm/vue-select.svg?style=flat-square" alt="Downloads per Month">
|
||||
</a>
|
||||
<a href="https://codeclimate.com/github/sagalbot/vue-select/maintainability">
|
||||
<img src="https://img.shields.io/codeclimate/maintainability/sagalbot/vue-select.svg?style=flat-square"
|
||||
alt="Maintainability"/>
|
||||
</a>
|
||||
|
||||
<img src="https://img.shields.io/github/license/sagalbot/vue-select.svg?style=flat-square" alt="MIT License">
|
||||
<img src="https://img.shields.io/github/release/sagalbot/vue-select.svg?style=flat-square"
|
||||
alt="Current Release">
|
||||
</p>
|
||||
|
||||
<p class="lead">
|
||||
A Vue.js select component that provides similar functionality
|
||||
to Select2/Chosen without the overhead of jQuery.
|
||||
</p>
|
||||
|
||||
<ClientOnly>
|
||||
<v-select id="v-select" :options="options" @input="redirect" label="title">
|
||||
<template slot="option" slot-scope="option">
|
||||
<span class="octicon" :class="option.icon"></span>
|
||||
{{ option.title }}
|
||||
</template>
|
||||
</v-select>
|
||||
</ClientOnly>
|
||||
|
||||
<section class="content">
|
||||
<div class="feature-list">
|
||||
<ul class="list-vue">
|
||||
<li>Supports Vuex</li>
|
||||
<li>Tagging Support</li>
|
||||
<li>Custom Templating</li>
|
||||
<li>Zero JS/CSS dependencies</li>
|
||||
<li>Custom Filtering Algorithms</li>
|
||||
</ul>
|
||||
|
||||
<ul class="list-vue">
|
||||
<li>+95% Test Coverage</li>
|
||||
<li>Select Single/Multiple</li>
|
||||
<li>Typeahead Suggestions</li>
|
||||
<li>Supports RTL & Translations</li>
|
||||
<li>Plays nice with CSS Frameworks</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
And so much more! Get started with: <br>
|
||||
<code>yarn add vue-select</code>
|
||||
</p>
|
||||
|
||||
<div class="cta">
|
||||
<a class="btn btn-primary btn-outline btn-lg" href="https://github.com/sagalbot/vue-select">
|
||||
<span class="octicon octicon-mark-github"></span> View on GitHub
|
||||
</a>
|
||||
|
||||
<router-link class="btn btn-primary btn-outline btn-lg" to="/docs/">
|
||||
<span class="octicon octicon-book"></span> Read the Docs
|
||||
</router-link>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../scss/home";
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
selected: null,
|
||||
options: [
|
||||
{
|
||||
title: 'Read the Docs',
|
||||
icon: 'octicon-book',
|
||||
url: 'docs/',
|
||||
},
|
||||
{
|
||||
title: 'View on GitHub',
|
||||
icon: 'octicon-mark-github',
|
||||
url: 'https://github.com/sagalbot/vue-select',
|
||||
},
|
||||
{
|
||||
title: 'View on NPM',
|
||||
icon: 'octicon-database',
|
||||
url: 'https://www.npmjs.com/package/vue-select',
|
||||
},
|
||||
{
|
||||
title: 'View Code Climate Analysis',
|
||||
icon: 'octicon-graph',
|
||||
url: 'https://codeclimate.com/github/sagalbot/vue-select',
|
||||
},
|
||||
{
|
||||
title: 'View Codepen Examples',
|
||||
icon: 'octicon-pencil',
|
||||
url: 'https://codepen.io/collection/nrkgxV/',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
redirect (option) {
|
||||
// if (window) {
|
||||
// window.location = option.url;
|
||||
// }
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<div id="sandbox-wrap">
|
||||
<div id="config">
|
||||
|
||||
<div class="list-item" v-if="!hideHelp">
|
||||
<p>Use the controls below to adjust the props used
|
||||
by the vue-select components.</p>
|
||||
<p>The API provides
|
||||
more props than are shown here, these are some
|
||||
commonly adjusted settings.</p>
|
||||
</div>
|
||||
|
||||
<h5 class="list-item">Basic Features</h5>
|
||||
|
||||
<div class="list-item">
|
||||
<label for="multiple">
|
||||
<input id="multiple" type="checkbox" v-model="configuration.multiple">
|
||||
<code>:multiple="{{ configuration.multiple ? 'true' : 'false' }}"</code>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="list-item">
|
||||
<label for="disabled">
|
||||
<input id="disabled" type="checkbox" v-model="configuration.disabled">
|
||||
<code>:disabled="{{ configuration.disabled ? 'true' : 'false' }}"</code>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="list-item">
|
||||
<label for="clearable">
|
||||
<input id="clearable" type="checkbox" v-model="configuration.clearable">
|
||||
<code>:clearable="{{ configuration.clearable ? 'true' : 'false' }}"</code>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="list-item">
|
||||
<label for="searchable">
|
||||
<input id="searchable" type="checkbox" v-model="configuration.searchable">
|
||||
<code>:searchable="{{ configuration.searchable ? 'true' : 'false' }}"</code>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="list-item">
|
||||
<label for="filterable">
|
||||
<input id="filterable" type="checkbox" v-model="configuration.filterable">
|
||||
<code>:filterable="{{ configuration.searchable ? 'true' : 'false' }}"</code>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<h5 class="list-item">Tagging</h5>
|
||||
|
||||
<div class="list-item">
|
||||
<label for="taggable">
|
||||
<input id="taggable" type="checkbox" v-model="configuration.taggable">
|
||||
<code>:taggable="{{ configuration.taggable ? 'true' : 'false' }}"</code>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="list-item">
|
||||
<label for="noDrop">
|
||||
<input id="noDrop" type="checkbox" v-model="configuration.noDrop">
|
||||
<code>:no-drop="{{ configuration.noDrop ? 'true' : 'false' }}"</code>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="list-item">
|
||||
<label for="pushTags">
|
||||
<input id="pushTags" type="checkbox" v-model="configuration.pushTags">
|
||||
<code>:push-tags="{{ configuration.pushTags ? 'true' : 'false' }}"</code>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<h5 class="list-item">UX</h5>
|
||||
|
||||
<div class="list-item">
|
||||
<label for="selectOnTab">
|
||||
<input id="selectOnTab" type="checkbox" v-model="configuration.selectOnTab">
|
||||
<code>:select-on-tab="{{ configuration.selectOnTab ? 'true' : 'false' }}"</code>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="list-item">
|
||||
<label for="closeOnSelect">
|
||||
<input id="closeOnSelect" type="checkbox" v-model="configuration.closeOnSelect">
|
||||
<code>:close-on-select="{{ configuration.closeOnSelect ? 'true' : 'false' }}"</code>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<h5 class="list-item">Localization / i18n</h5>
|
||||
|
||||
<div class="list-item">
|
||||
<label for="rtl">
|
||||
<input id="rtl" type="radio" v-model="configuration.dir" value="rtl">
|
||||
<code>dir="rtl"</code>
|
||||
</label>
|
||||
<label for="ltr">
|
||||
<input id="ltr" type="radio" v-model="configuration.dir" value="ltr">
|
||||
<code>dir="ltr"</code>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="sandbox">
|
||||
<slot v-bind="configuration">
|
||||
<div class="example">
|
||||
<v-select v-bind="configuration" placeholder="country objects"></v-select>
|
||||
</div>
|
||||
|
||||
<div class="example">
|
||||
<v-select v-bind="configuration" placeholder="country objects, using option scoped slots">
|
||||
<template slot="selected-option" slot-scope="{ label, value }">
|
||||
{{ label }} -- {{ value }}
|
||||
</template>
|
||||
<template slot="option" slot-scope="{ label, value }">
|
||||
{{ label }} ({{ value }})
|
||||
</template>
|
||||
</v-select>
|
||||
</div>
|
||||
|
||||
<div class="example">
|
||||
<v-select v-bind="configuration" :options="['cat', 'dog', 'bear']" placeholder="string options, option slots">
|
||||
<template slot="selected-option" slot-scope="{ label }">
|
||||
{{ label }}
|
||||
</template>
|
||||
<template slot="option" slot-scope="{ label }">
|
||||
{{ label }}
|
||||
</template>
|
||||
</v-select>
|
||||
</div>
|
||||
|
||||
<div class="example">
|
||||
<v-select v-bind="configuration" :options="[1,5,10]" placeholder="options=[1,5,10]"></v-select>
|
||||
</div>
|
||||
|
||||
<div class="example">
|
||||
<v-select v-bind="configuration" label="title" :options="optionDataSets.books" :filter="fuseSearch"
|
||||
placeholder="advanced filtering w/ fuse.js + scoped slots">
|
||||
<template slot="option" slot-scope="option">
|
||||
<strong>{{ option.title }}</strong><br>
|
||||
<em>{{ `${option.author.firstName} ${option.author.lastName}` }}</em>
|
||||
</template>
|
||||
</v-select>
|
||||
</div>
|
||||
|
||||
<div class="example">
|
||||
<v-select
|
||||
v-bind="configuration"
|
||||
placeholder="search github repositories.."
|
||||
label="full_name"
|
||||
@search="search"
|
||||
:options="ajaxRes"
|
||||
>
|
||||
</v-select>
|
||||
</div>
|
||||
|
||||
<div class="example">
|
||||
<v-select v-bind="configuration" :options="[]" placeholder="options=[]"></v-select>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Fuse from 'fuse.js';
|
||||
import debounce from 'lodash/debounce';
|
||||
import vSelect from '../../../src/components/Select.vue';
|
||||
import countries from '../data/countryCodes';
|
||||
import books from '../data/books';
|
||||
|
||||
const defaultConfig = () => ({
|
||||
options: countries,
|
||||
multiple: false,
|
||||
dir: 'ltr',
|
||||
clearable: true,
|
||||
searchable: true,
|
||||
filterable: true,
|
||||
noDrop: false,
|
||||
closeOnSelect: true,
|
||||
disabled: false,
|
||||
selectOntab: false,
|
||||
placeholder: 'placeholder',
|
||||
});
|
||||
|
||||
export default {
|
||||
props: {
|
||||
hideHelp: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
components: {vSelect},
|
||||
data () {
|
||||
return {
|
||||
configuration: defaultConfig(),
|
||||
value: null,
|
||||
ajaxRes: [],
|
||||
people: [],
|
||||
optionDataSet: 'countries',
|
||||
optionDataSets: {
|
||||
countries,
|
||||
books,
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
search (search, loading) {
|
||||
loading(true);
|
||||
this.getRepositories(search, loading, this);
|
||||
},
|
||||
searchPeople (search, loading) {
|
||||
loading(true);
|
||||
this.getPeople(loading, this);
|
||||
},
|
||||
getPeople: debounce((loading, vm) => {
|
||||
vm.$http.get(`https://reqres.in/api/users?per_page=10`).then(res => {
|
||||
vm.people = res.data.data;
|
||||
loading(false);
|
||||
});
|
||||
}, 250),
|
||||
getRepositories: debounce((search, loading, vm) => {
|
||||
vm.$http
|
||||
.get(`https://api.github.com/search/repositories?q=${search}`)
|
||||
.then(res => {
|
||||
vm.ajaxRes = res.data.items;
|
||||
loading(false);
|
||||
});
|
||||
}, 250),
|
||||
fuseSearch (options, search) {
|
||||
return new Fuse(options, {
|
||||
keys: ['title', 'author.firstName', 'author.lastName'],
|
||||
}).search(search);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#sandbox-wrap {
|
||||
min-height: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: auto 75%;
|
||||
grid-template-rows: auto;
|
||||
grid-template-areas:
|
||||
"sidebar component"
|
||||
}
|
||||
|
||||
#config {
|
||||
grid-area: sidebar;
|
||||
border-right: 1px solid #eaecef;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#sandbox {
|
||||
grid-area: component;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
padding: 1rem 1rem 0;
|
||||
}
|
||||
|
||||
.list-item:not(:first-child) {
|
||||
border-top: 1px solid #eaecef;
|
||||
}
|
||||
|
||||
.example {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.v-select {
|
||||
width: 25em;
|
||||
}
|
||||
</style>
|
||||
+52
-35
@@ -1,34 +1,44 @@
|
||||
const isDeployPreview = process.env.hasOwnProperty('DEPLOY_PREVIEW');
|
||||
|
||||
const meta = {
|
||||
title: 'Vue Select | VueJS Select2/Chosen Component',
|
||||
description: 'A well-tested, native Vue.js component that provides similar functionality to Select2/Chosen without the overhead of jQuery.',
|
||||
icon: 'static/vue-logo.png',
|
||||
description: 'Everything you wish the native <select> element could do, wrapped up into a zero dependency, highly extensible Vue component.',
|
||||
url: 'http://sagalbot.github.io/vue-select/',
|
||||
icon: '/vue-logo.png',
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
title: 'Vue Select',
|
||||
description: meta.description,
|
||||
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&text=Vue Select',
|
||||
rel: 'stylesheet',
|
||||
type: 'text/css',
|
||||
}],
|
||||
[
|
||||
'link',
|
||||
{
|
||||
href: 'https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/font/octicons.min.css',
|
||||
rel: 'stylesheet',
|
||||
type: 'text/css',
|
||||
}],
|
||||
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&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' }]
|
||||
];
|
||||
|
||||
if (isDeployPreview) {
|
||||
head.push(
|
||||
['meta', {name: 'robots', content: 'noindex'}],
|
||||
['meta', {name: 'googlebot', content: 'noindex'}],
|
||||
);
|
||||
} else {
|
||||
head.push(
|
||||
['meta', {name: 'title', content: meta.title}],
|
||||
['meta', {name: 'description', content: meta.description}],
|
||||
['link', {rel: 'icon', href: meta.icon, type: 'image/png'}],
|
||||
@@ -40,16 +50,24 @@ module.exports = {
|
||||
['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: 'http://sagalbot.github.io/vue-select/'}],
|
||||
],
|
||||
serviceWorker: true,
|
||||
ga: 'UA-12818324-8',
|
||||
['meta', {property: 'og:url', content: meta.url}],
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
title: 'Vue Select',
|
||||
description: meta.description,
|
||||
head,
|
||||
serviceWorker: !isDeployPreview,
|
||||
ga: isDeployPreview ? '' : 'UA-12818324-8',
|
||||
themeConfig: {
|
||||
repo: 'sagalbot/vue-select',
|
||||
editLinks: true,
|
||||
docsDir: 'docs',
|
||||
nav: [
|
||||
{text: 'Home', link: '/'},
|
||||
{text: 'Sandbox', link: '/sandbox'},
|
||||
],
|
||||
sidebar: {
|
||||
'/': [
|
||||
{
|
||||
@@ -78,11 +96,10 @@ module.exports = {
|
||||
children: [
|
||||
['api/props', 'Props'],
|
||||
['api/slots', 'Slots'],
|
||||
['api/events', 'Events']
|
||||
['api/events', 'Events'],
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
@import 'variables';
|
||||
|
||||
#v-select {
|
||||
font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
|
||||
.dropdown-toggle {
|
||||
background: #fff;
|
||||
border-color: rgba(82, 166, 183, 0.39);
|
||||
}
|
||||
|
||||
&.dropdown.open .dropdown-toggle,
|
||||
&.dropdown.open .dropdown-menu,
|
||||
&.dropdown.open .open-indicator:before {
|
||||
border-color: #4CC3D9;
|
||||
}
|
||||
.active a {
|
||||
background: rgba(50, 50, 50, .1);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
&.dropdown li {
|
||||
border-bottom: 1px solid rgba($code-grey, .1);
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.dropdown li a {
|
||||
padding: 10px 20px;
|
||||
display: inline-flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
font-size: 1.5em;
|
||||
.octicon {
|
||||
font-size: 1.5em;
|
||||
width: 1.5em;
|
||||
}
|
||||
}
|
||||
&.dropdown .highlight a,
|
||||
&.dropdown li:hover a {
|
||||
background: #4CC3D9;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,17 +0,0 @@
|
||||
$orange: #e96900;
|
||||
$yellow: #FFC65D;
|
||||
$green: #42b983;
|
||||
$blue: #4CC3D9;
|
||||
$purple: #93648D;
|
||||
$black: #34495e;
|
||||
$red: #ff6666;
|
||||
|
||||
$gradient: linear-gradient(45deg, rgba(76,195,217,0) 0%,rgba(152,227,234,1) 100%);
|
||||
|
||||
// Code
|
||||
$code-blue: #66d9ef;
|
||||
$code-purple: #ae81ff;
|
||||
$code-black: #272822;
|
||||
$code-white: #f8f8f8;
|
||||
$code-grey: #708090;
|
||||
$code-green: #a6e22e;
|
||||
@@ -1,3 +0,0 @@
|
||||
@import '~normalize.css';
|
||||
@import 'demo';
|
||||
@import 'cyan_theme';
|
||||
+12
-7
@@ -1,10 +1,11 @@
|
||||
# Vue Select
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
> Everything you wish the native `<select>` element could do, wrapped
|
||||
up into a zero dependency, highly extensible Vue component.
|
||||
@@ -14,17 +15,21 @@ template that fits the 80% use case for a select dropdown. Here it is by default
|
||||
|
||||
<div style="max-width:50%; margin: 0 auto; padding: 1rem 0;">
|
||||
<v-select :options="['Option One','Option Two','Option Three']" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
If you want to get a quick sense of what vue-select can do, check out
|
||||
[the sandbox](sandbox.md).
|
||||
|
||||
#### Features
|
||||
- AJAX Support
|
||||
- Tagging
|
||||
- List Filtering/Searching
|
||||
- Supports Vuex
|
||||
- Filtering/Searching
|
||||
- Vuex Support
|
||||
- AJAX Support
|
||||
- SSR Support
|
||||
- Select Single/Multiple Options
|
||||
- Tested with Bootstrap 3/4, Bulma, Foundation
|
||||
- +95% Test Coverage
|
||||
- ~33kb minified with CSS
|
||||
- ~20kb Total / ~5kb CSS / ~15kb JS
|
||||
- Zero dependencies
|
||||
|
||||
#### Resources
|
||||
|
||||
@@ -50,6 +50,11 @@ clearable: {
|
||||
|
||||
## 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
|
||||
|
||||
@@ -6,7 +6,7 @@ The most common use case for `vue-select` is to have the chosen value synced wit
|
||||
<v-select v-model="selected"></v-select>
|
||||
```
|
||||
|
||||
<CodePen url="Kqxbjw" height="25"/>
|
||||
<CodePen url="Kqxbjw" height="250"/>
|
||||
|
||||
If you don't require the `value` to be synced, you can also pass the prop directly:
|
||||
|
||||
@@ -31,14 +31,10 @@ By default, `vue-select` supports choosing a single value. If you need multiple
|
||||
To allow input that's not present within the options, set the `taggable` prop to true.
|
||||
If you want new tags to be pushed to the options list, set `push-tags` to true.
|
||||
|
||||
```html
|
||||
<v-select taggable></v-select>
|
||||
```
|
||||
<CodePen url="XVoWxm" height="350"/>
|
||||
|
||||
## Return a Single Key from an Object
|
||||
|
||||
<CodePen url="XVoWxm" height="350"/>
|
||||
|
||||
When the `options` array contains objects, `vue-select` returns the whole object as dropdown value upon selection. You can specify your own `index` prop to return only the value contained in the specific property.
|
||||
|
||||
For example, consider an object with `value` and `label` properties:
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
sidebar: false
|
||||
editLink: false
|
||||
layout: Sandbox
|
||||
---
|
||||
|
||||
+8
-1
@@ -2,7 +2,14 @@
|
||||
#
|
||||
# “publish” is the directory to publish (relative to root of your repo),
|
||||
# “command” is your build command,
|
||||
|
||||
[build]
|
||||
publish = "docs/.vuepress/dist"
|
||||
command = "yarn build:docs"
|
||||
|
||||
# Deploy Preview context
|
||||
#
|
||||
# All deploys resulting from a pull/merge request will
|
||||
# inherit these settings.
|
||||
[context.deploy-preview]
|
||||
publish = "docs/.vuepress/dist"
|
||||
command = "yarn add --dev webpack@~4.28 && yarn && yarn build:preview"
|
||||
|
||||
+9
-10
@@ -8,10 +8,11 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"start": "npm run dev",
|
||||
"dev": "webpack-dev-server --config build/webpack.dev.conf.js --hot --progress -d",
|
||||
"dev:docs": "vuepress dev docs",
|
||||
"serve": "webpack-dev-server --config build/webpack.dev.conf.js --hot --progress -d",
|
||||
"serve:docs": "vuepress dev docs",
|
||||
"build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.conf.js --progress",
|
||||
"build:docs": "vuepress build docs",
|
||||
"build:preview": "cross-env DEPLOY_PREVIEW=true vuepress build --debug docs",
|
||||
"test": "jest"
|
||||
},
|
||||
"repository": {
|
||||
@@ -29,29 +30,27 @@
|
||||
"@babel/plugin-transform-runtime": "^7.2.0",
|
||||
"@babel/preset-env": "^7.3.1",
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"@vue/cli-service": "^3.4.0",
|
||||
"@vue/test-utils": "^1.0.0-beta.29",
|
||||
"autoprefixer": "^9.4.7",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-loader": "^8.0.0",
|
||||
"chokidar": "^2.0.4",
|
||||
"coveralls": "^3.0.2",
|
||||
"cross-env": "^5.2.0",
|
||||
"css-loader": "^1.0.0",
|
||||
"eventsource-polyfill": "^0.9.6",
|
||||
"extract-text-webpack-plugin": "^3.0.0",
|
||||
"file-loader": "^3.0.0",
|
||||
"function-bind": "^1.0.2",
|
||||
"css-loader": "^2.1.0",
|
||||
"cssnano": "^4.1.10",
|
||||
"fuse.js": "^3.2.0",
|
||||
"gh-pages": "^0.11.0",
|
||||
"html-loader": "^0.5.0",
|
||||
"html-loader": "^0.5.5",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"jest": "^24.1.0",
|
||||
"jest-serializer-vue": "^2.0.2",
|
||||
"jest-transform-stub": "^2.0.0",
|
||||
"mini-css-extract-plugin": "^0.5.0",
|
||||
"node-sass": "^4.10.0",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-scss": "^2.0.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"shelljs": "^0.7.0",
|
||||
"url-loader": "^1.1.2",
|
||||
"vue": "^2.6.4",
|
||||
"vue-html-loader": "^1.2.4",
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('autoprefixer'),
|
||||
require('cssnano')({
|
||||
preset: 'default',
|
||||
}),
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
|
||||
<path d="M6.895455 5l2.842897-2.842898c.348864-.348863.348864-.914488 0-1.263636L9.106534.261648c-.348864-.348864-.914489-.348864-1.263636 0L5 3.104545 2.157102.261648c-.348863-.348864-.914488-.348864-1.263636 0L.261648.893466c-.348864.348864-.348864.914489 0 1.263636L3.104545 5 .261648 7.842898c-.348864.348863-.348864.914488 0 1.263636l.631818.631818c.348864.348864.914773.348864 1.263636 0L5 6.895455l2.842898 2.842897c.348863.348864.914772.348864 1.263636 0l.631818-.631818c.348864-.348864.348864-.914489 0-1.263636L6.895455 5z"/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="10">
|
||||
<path d="M9.211364 7.59931l4.48338-4.867229c.407008-.441854.407008-1.158247 0-1.60046l-.73712-.80023c-.407008-.441854-1.066904-.441854-1.474243 0L7 5.198617 2.51662.33139c-.407008-.441853-1.066904-.441853-1.474243 0l-.737121.80023c-.407008.441854-.407008 1.158248 0 1.600461l4.48338 4.867228L7 10l2.211364-2.40069z"/>
|
||||
</svg>
|
||||
</template>
|
||||
+52
-351
@@ -1,363 +1,69 @@
|
||||
<style>
|
||||
.v-select {
|
||||
position: relative;
|
||||
font-family: inherit;
|
||||
}
|
||||
.v-select,
|
||||
.v-select * {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Rtl support - Because we're using a flexbox-based layout, the `dir="rtl"` HTML
|
||||
attribute does most of the work for us by rearranging the child elements visually.
|
||||
*/
|
||||
.v-select[dir="rtl"] .vs__actions {
|
||||
padding: 0 3px 0 6px;
|
||||
}
|
||||
.v-select[dir="rtl"] .dropdown-toggle .clear {
|
||||
margin-left: 6px;
|
||||
margin-right: 0;
|
||||
}
|
||||
.v-select[dir="rtl"] .selected-tag .close {
|
||||
margin-left: 0;
|
||||
margin-right: 2px;
|
||||
}
|
||||
.v-select[dir="rtl"] .dropdown-menu {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Open Indicator */
|
||||
.v-select .open-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
transition: all 150ms cubic-bezier(1.000, -0.115, 0.975, 0.855);
|
||||
transition-timing-function: cubic-bezier(1.000, -0.115, 0.975, 0.855);
|
||||
opacity: 1;
|
||||
width: 12px; /* To account for extra width from rotating. */
|
||||
}
|
||||
.v-select .open-indicator:before {
|
||||
border-color: rgba(60, 60, 60, .5);
|
||||
border-style: solid;
|
||||
border-width: 3px 3px 0 0;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
vertical-align: text-top;
|
||||
transform: rotate(133deg);
|
||||
transition: all 150ms cubic-bezier(1.000, -0.115, 0.975, 0.855);
|
||||
transition-timing-function: cubic-bezier(1.000, -0.115, 0.975, 0.855);
|
||||
box-sizing: inherit;
|
||||
}
|
||||
/* Open Indicator States */
|
||||
.v-select.open .open-indicator:before {
|
||||
transform: rotate(315deg);
|
||||
}
|
||||
.v-select.loading .open-indicator {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Dropdown Toggle */
|
||||
.v-select .dropdown-toggle {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
display: flex;
|
||||
padding: 0 0 4px 0;
|
||||
background: none;
|
||||
border: 1px solid rgba(60, 60, 60, .26);
|
||||
border-radius: 4px;
|
||||
white-space: normal;
|
||||
}
|
||||
.v-select .vs__selected-options {
|
||||
display: flex;
|
||||
flex-basis: 100%;
|
||||
flex-grow: 1;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 2px;
|
||||
position: relative;
|
||||
}
|
||||
.v-select .vs__actions {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
padding: 0 6px 0 3px;
|
||||
}
|
||||
|
||||
/* Clear Button */
|
||||
.v-select .dropdown-toggle .clear {
|
||||
font-size: 23px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
color: rgba(60, 60, 60, 0.5);
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
/* Dropdown Toggle States */
|
||||
.v-select.searchable .dropdown-toggle {
|
||||
cursor: text;
|
||||
}
|
||||
.v-select.unsearchable .dropdown-toggle {
|
||||
cursor: pointer;
|
||||
}
|
||||
.v-select.open .dropdown-toggle {
|
||||
border-bottom-color: transparent;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
/* Dropdown Menu */
|
||||
.v-select .dropdown-menu {
|
||||
display:block;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
min-width: 160px;
|
||||
padding: 5px 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
border: 1px solid rgba(0, 0, 0, .26);
|
||||
box-shadow: 0px 3px 6px 0px rgba(0,0,0,.15);
|
||||
border-top: none;
|
||||
border-radius: 0 0 4px 4px;
|
||||
text-align: left;
|
||||
list-style: none;
|
||||
background: #fff;
|
||||
}
|
||||
.v-select .no-options {
|
||||
text-align: center;
|
||||
}
|
||||
/* Selected Tags */
|
||||
.v-select .selected-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #f0f0f0;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
color: #333;
|
||||
line-height: 1.42857143; /* Normalize line height */
|
||||
margin: 4px 2px 0px 2px;
|
||||
padding: 0 0.25em;
|
||||
transition: opacity .25s;
|
||||
}
|
||||
.v-select.single .selected-tag {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
.v-select.single.open .selected-tag {
|
||||
position: absolute;
|
||||
opacity: .4;
|
||||
}
|
||||
.v-select.single.searching .selected-tag {
|
||||
display: none;
|
||||
}
|
||||
.v-select .selected-tag .close {
|
||||
margin-left: 2px;
|
||||
font-size: 1.25em;
|
||||
appearance: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
background: 0 0;
|
||||
border: 0;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
color: #000;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
filter: alpha(opacity=20);
|
||||
opacity: .2;
|
||||
}
|
||||
.v-select.single.searching:not(.open):not(.loading) input[type="search"] {
|
||||
opacity: .2;
|
||||
}
|
||||
/* Search Input */
|
||||
.v-select input[type="search"]::-webkit-search-decoration,
|
||||
.v-select input[type="search"]::-webkit-search-cancel-button,
|
||||
.v-select input[type="search"]::-webkit-search-results-button,
|
||||
.v-select input[type="search"]::-webkit-search-results-decoration {
|
||||
display: none;
|
||||
}
|
||||
.v-select input[type="search"]::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
.v-select input[type="search"],
|
||||
.v-select input[type="search"]:focus {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
line-height: 1.42857143;
|
||||
font-size: 1em;
|
||||
display: inline-block;
|
||||
border: 1px solid transparent;
|
||||
border-left: none;
|
||||
outline: none;
|
||||
margin: 4px 0 0 0;
|
||||
padding: 0 7px;
|
||||
max-width: 100%;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
flex-grow: 1;
|
||||
width: 0;
|
||||
}
|
||||
.v-select.unsearchable input[type="search"] {
|
||||
opacity: 0;
|
||||
}
|
||||
.v-select.unsearchable input[type="search"]:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* List Items */
|
||||
.v-select li {
|
||||
line-height: 1.42857143; /* Normalize line height */
|
||||
}
|
||||
.v-select li > a {
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
color: #333; /* Overrides most CSS frameworks */
|
||||
white-space: nowrap;
|
||||
}
|
||||
.v-select li:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.v-select .dropdown-menu .active > a {
|
||||
color: #333;
|
||||
background: rgba(50, 50, 50, .1);
|
||||
}
|
||||
.v-select .dropdown-menu > .highlight > a {
|
||||
/*
|
||||
* required to override bootstrap 3's
|
||||
* .dropdown-menu > li > a:hover {} styles
|
||||
*/
|
||||
background: #5897fb;
|
||||
color: #fff;
|
||||
}
|
||||
.v-select .highlight:not(:last-child) {
|
||||
margin-bottom: 0; /* Fixes Bulma Margin */
|
||||
}
|
||||
/* Loading Spinner */
|
||||
.v-select .spinner {
|
||||
align-self: center;
|
||||
opacity: 0;
|
||||
font-size: 5px;
|
||||
text-indent: -9999em;
|
||||
overflow: hidden;
|
||||
border-top: .9em solid rgba(100, 100, 100, .1);
|
||||
border-right: .9em solid rgba(100, 100, 100, .1);
|
||||
border-bottom: .9em solid rgba(100, 100, 100, .1);
|
||||
border-left: .9em solid rgba(60, 60, 60, .45);
|
||||
transform: translateZ(0);
|
||||
animation: vSelectSpinner 1.1s infinite linear;
|
||||
transition: opacity .1s;
|
||||
}
|
||||
.v-select .spinner,
|
||||
.v-select .spinner:after {
|
||||
border-radius: 50%;
|
||||
width: 5em;
|
||||
height: 5em;
|
||||
}
|
||||
|
||||
/* Disabled state */
|
||||
.v-select.disabled .dropdown-toggle,
|
||||
.v-select.disabled .dropdown-toggle .clear,
|
||||
.v-select.disabled .dropdown-toggle input,
|
||||
.v-select.disabled .selected-tag .close,
|
||||
.v-select.disabled .open-indicator {
|
||||
cursor: not-allowed;
|
||||
background-color: rgb(248, 248, 248);
|
||||
}
|
||||
|
||||
/* Loading Spinner States */
|
||||
.v-select.loading .spinner {
|
||||
opacity: 1;
|
||||
}
|
||||
/* KeyFrames */
|
||||
@-webkit-keyframes vSelectSpinner {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes vSelectSpinner {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
/* Dropdown Default Transition */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity .15s cubic-bezier(1.0, 0.5, 0.8, 1.0);
|
||||
}
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
<style lang="scss">
|
||||
@import '../scss/vue-select.scss';
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div :dir="dir" class="dropdown v-select" :class="dropdownClasses">
|
||||
<div ref="toggle" @mousedown.prevent="toggleDropdown" class="dropdown-toggle">
|
||||
<div :dir="dir" class="v-select" :class="stateClasses">
|
||||
<div ref="toggle" @mousedown.prevent="toggleDropdown" class="vs__dropdown-toggle">
|
||||
|
||||
<div class="vs__selected-options" ref="selectedOptions">
|
||||
<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 v-for="option in valueAsArray"
|
||||
name="selected-option-container"
|
||||
:option="(typeof option === 'object')?option:{[label]: option}"
|
||||
:deselect="deselect"
|
||||
:multiple="multiple"
|
||||
:disabled="disabled">
|
||||
<span class="vs__selected" 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">×</span>
|
||||
<button v-if="multiple" :disabled="disabled" @click="deselect(option)" type="button" class="vs__deselect" aria-label="Deselect option">
|
||||
<deselect />
|
||||
</button>
|
||||
</span>
|
||||
</slot>
|
||||
|
||||
<slot name="search" v-bind="scope.search">
|
||||
<input v-bind="scope.search.attributes" v-on="scope.search.events">
|
||||
<input class="vs__search" v-bind="scope.search.attributes" v-on="scope.search.events">
|
||||
</slot>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="vs__actions">
|
||||
<button
|
||||
v-show="showClearButton"
|
||||
:disabled="disabled"
|
||||
@click="clearSelection"
|
||||
type="button"
|
||||
class="clear"
|
||||
class="vs__clear"
|
||||
title="Clear selection"
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
<deselect />
|
||||
</button>
|
||||
|
||||
<i v-if="!noDrop" ref="openIndicator" role="presentation" class="open-indicator"></i>
|
||||
<open-indicator v-if="!noDrop" ref="openIndicator" role="presentation" class="vs__open-indicator" />
|
||||
|
||||
<slot name="spinner">
|
||||
<div class="spinner" v-show="mutableLoading">Loading...</div>
|
||||
<slot name="spinner" v-bind="scope.spinner">
|
||||
<div class="vs__spinner" v-show="mutableLoading">Loading...</div>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<transition :name="transition">
|
||||
<ul ref="dropdownMenu" v-if="dropdownOpen" class="dropdown-menu" :style="{ 'max-height': maxHeight }" role="listbox" @mousedown="onMousedown">
|
||||
<li role="option" v-for="(option, index) in filteredOptions" v-bind:key="index" :class="{ active: isOptionSelected(option), highlight: index === typeAheadPointer }" @mouseover="typeAheadPointer = index">
|
||||
<a @mousedown.prevent.stop="select(option)">
|
||||
<ul ref="dropdownMenu" v-if="dropdownOpen" class="vs__dropdown-menu" role="listbox" @mousedown="onMousedown">
|
||||
<li
|
||||
role="option"
|
||||
v-for="(option, index) in filteredOptions"
|
||||
:key="index"
|
||||
class="vs__dropdown-option"
|
||||
:class="{ active: isOptionSelected(option), 'vs__dropdown-option--highlight': index === typeAheadPointer }"
|
||||
@mouseover="typeAheadPointer = index"
|
||||
@mousedown.prevent.stop="select(option)"
|
||||
>
|
||||
<slot name="option" v-bind="(typeof option === 'object')?option:{[label]: option}">
|
||||
{{ getOptionLabel(option) }}
|
||||
</slot>
|
||||
</a>
|
||||
</li>
|
||||
<li v-if="!filteredOptions.length" class="no-options" @mousedown.stop="">
|
||||
<li v-if="!filteredOptions.length" class="vs__no-options" @mousedown.stop="">
|
||||
<slot name="no-options">Sorry, no matching options.</slot>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -369,8 +75,12 @@
|
||||
import pointerScroll from '../mixins/pointerScroll'
|
||||
import typeAheadPointer from '../mixins/typeAheadPointer'
|
||||
import ajax from '../mixins/ajax'
|
||||
import Deselect from './Deselect'
|
||||
import OpenIndicator from './OpenIndicator'
|
||||
|
||||
export default {
|
||||
components: {Deselect, OpenIndicator},
|
||||
|
||||
mixins: [pointerScroll, typeAheadPointer, ajax],
|
||||
|
||||
props: {
|
||||
@@ -416,16 +126,6 @@
|
||||
default: true
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the max-height property on the dropdown list.
|
||||
* @deprecated
|
||||
* @type {String}
|
||||
*/
|
||||
maxHeight: {
|
||||
type: String,
|
||||
default: '400px'
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable/disable filtering the options.
|
||||
* @type {Boolean}
|
||||
@@ -454,13 +154,12 @@
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets a Vue transition property on the `.dropdown-menu`. vue-select
|
||||
* does not include CSS for transitions, you'll need to add them yourself.
|
||||
* Sets a Vue transition property on the `.vs__dropdown-menu`.
|
||||
* @type {String}
|
||||
*/
|
||||
transition: {
|
||||
type: String,
|
||||
default: 'fade'
|
||||
default: 'vs__fade'
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -902,7 +601,7 @@
|
||||
*/
|
||||
toggleDropdown(e) {
|
||||
if (e.target === this.$refs.openIndicator || e.target === this.searchEl || e.target === this.$refs.toggle ||
|
||||
e.target.classList.contains('selected-tag') || e.target === this.$el) {
|
||||
e.target.classList.contains('vs__selected') || e.target === this.$el) {
|
||||
if (this.open) {
|
||||
this.searchEl.blur() // dropdown will close on blur
|
||||
} else {
|
||||
@@ -1147,7 +846,7 @@
|
||||
'role': 'combobox',
|
||||
'type': 'search',
|
||||
'autocomplete': 'off',
|
||||
'class': 'form-control',
|
||||
'value': this.search,
|
||||
},
|
||||
events: {
|
||||
'keydown': this.onSearchKeyDown,
|
||||
@@ -1157,23 +856,25 @@
|
||||
'input': (e) => this.search = e.target.value,
|
||||
},
|
||||
},
|
||||
spinner: {
|
||||
loading: this.mutableLoading
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Classes to be output on .dropdown
|
||||
* Holds the current state of the component.
|
||||
* @return {Object}
|
||||
*/
|
||||
dropdownClasses() {
|
||||
stateClasses() {
|
||||
return {
|
||||
open: this.dropdownOpen,
|
||||
single: !this.multiple,
|
||||
searching: this.searching,
|
||||
searchable: this.searchable,
|
||||
unsearchable: !this.searchable,
|
||||
loading: this.mutableLoading,
|
||||
rtl: this.dir === 'rtl', // This can be removed - styling is handled by `dir="rtl"` attribute
|
||||
disabled: this.disabled
|
||||
'vs--open': this.dropdownOpen,
|
||||
'vs--single': !this.multiple,
|
||||
'vs--searching': this.searching,
|
||||
'vs--searchable': this.searchable,
|
||||
'vs--unsearchable': !this.searchable,
|
||||
'vs--loading': this.mutableLoading,
|
||||
'vs--disabled': this.disabled
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1191,7 +892,7 @@
|
||||
* @return {Boolean} True if non empty value
|
||||
*/
|
||||
searching() {
|
||||
return !!this.search
|
||||
return !! this.search
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
$transition-timing-function: cubic-bezier(1.0, 0.5, 0.8, 1.0);
|
||||
$transition-duration: .15s;
|
||||
|
||||
/* KeyFrames */
|
||||
@-webkit-keyframes vSelectSpinner {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes vSelectSpinner {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Dropdown Default Transition */
|
||||
.vs__fade-enter-active,
|
||||
.vs__fade-leave-active {
|
||||
transition: opacity $transition-duration $transition-timing-function;
|
||||
}
|
||||
.vs__fade-enter,
|
||||
.vs__fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
.v-select {
|
||||
position: relative;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.v-select,
|
||||
.v-select * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/** Component States */
|
||||
|
||||
/*
|
||||
* Disabled
|
||||
*
|
||||
* When the component is disabled, all interaction
|
||||
* should be prevented. Here we modify the bg color,
|
||||
* and change the cursor displayed on the interactive
|
||||
* components.
|
||||
*/
|
||||
|
||||
$disabled-bg: $vs-state-disabled-bg;
|
||||
$disabled-color: $vs-state-disabled-color;
|
||||
$disabled-cursor: $vs-state-disabled-cursor;
|
||||
|
||||
.vs--disabled {
|
||||
.vs__dropdown-toggle,
|
||||
.vs__clear,
|
||||
.vs__search,
|
||||
.vs__selected,
|
||||
.vs__open-indicator {
|
||||
cursor: $disabled-cursor;
|
||||
background-color: $disabled-bg;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* RTL - Right to Left Support
|
||||
*
|
||||
* Because we're using a flexbox layout, the `dir="rtl"`
|
||||
* HTML attribute does most of the work for us by
|
||||
* rearranging the child elements visually.
|
||||
*/
|
||||
|
||||
.v-select[dir="rtl"] {
|
||||
.vs__actions {
|
||||
padding: 0 3px 0 6px;
|
||||
}
|
||||
|
||||
.vs__clear {
|
||||
margin-left: 6px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.vs__deselect {
|
||||
margin-left: 0;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.vs__dropdown-menu {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
$vs-colors: (
|
||||
lightest: rgba(60, 60, 60, 0.26),
|
||||
light: rgba(60, 60, 60, 0.5),
|
||||
dark: #333,
|
||||
darkest: rgba(0, 0, 0, .15),
|
||||
) !default;
|
||||
|
||||
// Global Component Variables
|
||||
$vs-component-line-height: 1.4 !default;
|
||||
$vs-component-placeholder-color: inherit !default;
|
||||
|
||||
// Active State
|
||||
$vs-state-active-bg: #5897fb !default;
|
||||
$vs-state-active-color: #fff !default;
|
||||
|
||||
// Disabled State
|
||||
$vs-state-disabled-bg: rgb(248, 248, 248) !default;
|
||||
$vs-state-disabled-color: map_get($vs-colors, 'light') !default;
|
||||
$vs-state-disabled-controls-color: map_get($vs-colors, 'light') !default;
|
||||
$vs-state-disabled-cursor: not-allowed !default;
|
||||
|
||||
// Borders
|
||||
$vs-border-width: 1px !default;
|
||||
$vs-border-style: solid !default;
|
||||
$vs-border-radius: 4px !default;
|
||||
$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;
|
||||
|
||||
// Selected
|
||||
$vs-selected-bg: #f0f0f0 !default;
|
||||
$vs-selected-border-color: $vs-border-color !default;
|
||||
$vs-selected-border-style: $vs-border-style !default;
|
||||
$vs-selected-border-width: $vs-border-width !default;
|
||||
|
||||
// Dropdown
|
||||
$vs-dropdown-z-index: 1000 !default;
|
||||
$vs-dropdown-min-width: 160px !default;
|
||||
$vs-dropdown-max-height: 350px !default;
|
||||
$vs-dropdown-box-shadow: 0px 3px 6px 0px map_get($vs-colors, 'darkest') !default;
|
||||
$vs-dropdown-bg: #fff !default;
|
||||
@@ -0,0 +1,10 @@
|
||||
/* Clear Button */
|
||||
|
||||
.vs__clear {
|
||||
fill: $vs-controls-color;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
margin-right: 8px;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* Dropdown Menu */
|
||||
|
||||
$border-width: $vs-border-width;
|
||||
$border-style: solid;
|
||||
$border-color: $vs-border-color;
|
||||
$border-radius: $vs-border-radius;
|
||||
$box-shadow: $vs-dropdown-box-shadow;
|
||||
|
||||
$bg-color: $vs-dropdown-bg;
|
||||
$z-index: $vs-dropdown-z-index;
|
||||
$min-width: $vs-dropdown-min-width;
|
||||
$max-height: $vs-dropdown-max-height;
|
||||
|
||||
.vs__dropdown-menu {
|
||||
display: block;
|
||||
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;
|
||||
z-index: $z-index;
|
||||
padding: 5px 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
max-height: $max-height;
|
||||
min-width: $min-width;
|
||||
overflow-y: auto;
|
||||
box-shadow: $box-shadow;
|
||||
border: $border-width $border-style $border-color;
|
||||
border-top-style: none;
|
||||
border-radius: 0 0 $border-radius $border-radius;
|
||||
text-align: left;
|
||||
list-style: none;
|
||||
background: $bg-color;
|
||||
}
|
||||
|
||||
.vs__no-options {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/* List Items */
|
||||
.vs__dropdown-option {
|
||||
line-height: 1.42857143; /* Normalize line height */
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
color: #333; /* Overrides most CSS frameworks */
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.vs__dropdown-option--highlight {
|
||||
background: $vs-state-active-bg;
|
||||
color: $vs-state-active-color;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
Dropdown Toggle
|
||||
|
||||
The dropdown toggle is the primary wrapper of the component. It
|
||||
has two direct descendants: .vs__selected-options, and .vs__actions.
|
||||
|
||||
.vs__selected-options holds the .vs__selected's as well as the
|
||||
main search input.
|
||||
|
||||
.vs__actions holds the clear button and dropdown toggle.
|
||||
*/
|
||||
|
||||
$border-width: $vs-border-width;
|
||||
$border-style: $vs-border-style;
|
||||
$border-color: $vs-border-color;
|
||||
$border-radius: $vs-border-radius;
|
||||
|
||||
.vs__dropdown-toggle {
|
||||
appearance: none;
|
||||
display: flex;
|
||||
padding: 0 0 4px 0;
|
||||
background: none;
|
||||
border: $border-width $border-style $border-color;
|
||||
border-radius: $border-radius;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.vs__selected-options {
|
||||
display: flex;
|
||||
flex-basis: 100%;
|
||||
flex-grow: 1;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 2px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.vs__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 6px 0 3px;
|
||||
}
|
||||
|
||||
/* Dropdown Toggle States */
|
||||
.vs--searchable .vs__dropdown-toggle {
|
||||
cursor: text;
|
||||
}
|
||||
.vs--unsearchable .vs__dropdown-toggle {
|
||||
cursor: pointer;
|
||||
}
|
||||
.vs--open .vs__dropdown-toggle {
|
||||
border-bottom-color: transparent;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Open Indicator
|
||||
|
||||
// The open indicator appears as a down facing
|
||||
// caret on the right side of the select.
|
||||
|
||||
$transition-timing-function: cubic-bezier(1.000, -0.115, 0.975, 0.855);
|
||||
$transition-duration: 150ms;
|
||||
|
||||
$open-indicator-color: $vs-controls-color;
|
||||
$open-indicator-size: $vs-controls-size;
|
||||
|
||||
.vs__open-indicator {
|
||||
fill: $open-indicator-color;
|
||||
transform: scale($open-indicator-size);
|
||||
transition: transform $transition-duration $transition-timing-function;
|
||||
transition-timing-function: $transition-timing-function;
|
||||
}
|
||||
|
||||
// Open State
|
||||
|
||||
.vs--open .vs__open-indicator {
|
||||
transform: rotate(180deg) scale($open-indicator-size);
|
||||
}
|
||||
|
||||
// Loading State
|
||||
|
||||
.vs--loading .vs__open-indicator {
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/* Search Input */
|
||||
|
||||
$line-height: $vs-component-line-height;
|
||||
$font-size: 1em;
|
||||
|
||||
.vs__search::-webkit-search-decoration,
|
||||
.vs__search::-webkit-search-cancel-button,
|
||||
.vs__search::-webkit-search-results-button,
|
||||
.vs__search::-webkit-search-results-decoration,
|
||||
.vs__search::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vs__search,
|
||||
.vs__search:focus {
|
||||
appearance: none;
|
||||
line-height: $line-height;
|
||||
font-size: $font-size;
|
||||
border: 1px solid transparent;
|
||||
border-left: none;
|
||||
outline: none;
|
||||
margin: 4px 0 0 0;
|
||||
padding: 0 7px;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
width: 0;
|
||||
max-width: 100%;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.vs__search::placeholder {
|
||||
color: $vs-component-placeholder-color;
|
||||
}
|
||||
|
||||
/**
|
||||
States
|
||||
*/
|
||||
|
||||
// Unsearchable
|
||||
.vs--unsearchable {
|
||||
.vs__search {
|
||||
opacity: 0;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Single, when searching but not loading or open
|
||||
.vs--single.vs--searching:not(.vs--open):not(.vs--loading) {
|
||||
.vs__search {
|
||||
opacity: .2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Selected Tags */
|
||||
.vs__selected {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: $vs-selected-bg;
|
||||
border: $vs-selected-border-width $vs-selected-border-style $vs-selected-border-color;
|
||||
border-radius: $vs-border-radius;
|
||||
color: map_get($vs-colors, 'dark');
|
||||
line-height: $vs-component-line-height;
|
||||
margin: 4px 2px 0px 2px;
|
||||
padding: 0 0.25em;
|
||||
}
|
||||
|
||||
.vs__deselect {
|
||||
display: inline-flex;
|
||||
appearance: none;
|
||||
margin-left: 4px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
fill: $vs-controls-color;
|
||||
text-shadow: $vs-controls-deselect-text-shadow;
|
||||
}
|
||||
|
||||
/* States */
|
||||
|
||||
.vs--single {
|
||||
.vs__selected {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
&.vs--open .vs__selected {
|
||||
position: absolute;
|
||||
opacity: .4;
|
||||
}
|
||||
&.vs--searching .vs__selected {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* Loading Spinner */
|
||||
.vs__spinner {
|
||||
align-self: center;
|
||||
opacity: 0;
|
||||
font-size: 5px;
|
||||
text-indent: -9999em;
|
||||
overflow: hidden;
|
||||
border-top: .9em solid rgba(100, 100, 100, .1);
|
||||
border-right: .9em solid rgba(100, 100, 100, .1);
|
||||
border-bottom: .9em solid rgba(100, 100, 100, .1);
|
||||
border-left: .9em solid rgba(60, 60, 60, .45);
|
||||
transform: translateZ(0);
|
||||
animation: vSelectSpinner 1.1s infinite linear;
|
||||
transition: opacity .1s;
|
||||
}
|
||||
.vs__spinner,
|
||||
.vs__spinner:after {
|
||||
border-radius: 50%;
|
||||
width: 5em;
|
||||
height: 5em;
|
||||
}
|
||||
|
||||
/* Loading Spinner States */
|
||||
.vs--loading .vs__spinner {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
@import "global/variables";
|
||||
@import "global/component";
|
||||
@import "global/animations";
|
||||
@import "global/states";
|
||||
|
||||
@import "modules/dropdown-toggle";
|
||||
@import "modules/open-indicator";
|
||||
@import "modules/clear";
|
||||
@import "modules/dropdown-menu";
|
||||
@import "modules/dropdown-option";
|
||||
@import "modules/selected";
|
||||
@import "modules/search-input";
|
||||
@import "modules/spinner";
|
||||
@@ -4,7 +4,7 @@ describe("Removing values", () => {
|
||||
it("can remove the given tag when its close icon is clicked", () => {
|
||||
const Select = selectWithProps({ multiple: true, value: ["foo"] });
|
||||
|
||||
Select.find(".close").trigger("click");
|
||||
Select.find(".vs__deselect").trigger("click");
|
||||
expect(Select.vm.mutableValue).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ describe("Removing values", () => {
|
||||
disabled: true
|
||||
});
|
||||
|
||||
Select.find(".close").trigger("click");
|
||||
Select.find(".vs__deselect").trigger("click");
|
||||
expect(Select.vm.mutableValue).toEqual(["one"]);
|
||||
});
|
||||
|
||||
@@ -68,7 +68,7 @@ describe("Removing values", () => {
|
||||
});
|
||||
|
||||
expect(Select.vm.mutableValue).toEqual("foo");
|
||||
Select.find("button.clear").trigger("click");
|
||||
Select.find("button.vs__clear").trigger("click");
|
||||
expect(Select.vm.mutableValue).toEqual(null);
|
||||
});
|
||||
|
||||
@@ -79,7 +79,7 @@ describe("Removing values", () => {
|
||||
disabled: true
|
||||
});
|
||||
|
||||
expect(Select.find("button.clear").attributes().disabled).toEqual(
|
||||
expect(Select.find("button.vs__clear").attributes().disabled).toEqual(
|
||||
"disabled"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ describe("Toggling Dropdown", () => {
|
||||
options: [{ label: "one" }]
|
||||
});
|
||||
|
||||
const selectedTag = Select.find(".selected-tag").element;
|
||||
const selectedTag = Select.find(".vs__selected").element;
|
||||
|
||||
Select.vm.toggleDropdown({ target: selectedTag });
|
||||
expect(Select.vm.open).toEqual(true);
|
||||
@@ -127,9 +127,9 @@ describe("Toggling Dropdown", () => {
|
||||
it("should have an open class when dropdown is active", () => {
|
||||
const Select = selectWithProps();
|
||||
|
||||
expect(Select.vm.dropdownClasses.open).toEqual(false);
|
||||
expect(Select.vm.stateClasses['vs--open']).toEqual(false);
|
||||
|
||||
Select.vm.open = true;
|
||||
expect(Select.vm.dropdownClasses.open).toEqual(true);
|
||||
expect(Select.vm.stateClasses['vs--open']).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ describe("Labels", () => {
|
||||
label: "name",
|
||||
value: { name: "Foo" }
|
||||
});
|
||||
expect(Select.find(".selected-tag").text()).toBe("Foo");
|
||||
expect(Select.find(".vs__selected").text()).toBe("Foo");
|
||||
});
|
||||
|
||||
it("will console.warn when options contain objects without a valid label key", () => {
|
||||
|
||||
@@ -137,7 +137,7 @@ describe("When index prop is defined", () => {
|
||||
}
|
||||
});
|
||||
|
||||
expect(Select.find(".selected-tag").text()).toContain("Baz");
|
||||
expect(Select.find(".vs__selected").text()).toContain("Baz");
|
||||
});
|
||||
|
||||
it("will console.warn when attempting to select an option with an undefined index", () => {
|
||||
|
||||
Reference in New Issue
Block a user