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

Merge remote-tracking branch 'origin/release/v3.0'

* origin/release/v3.0: (67 commits)
  ensure #222 stays closed (#828)
  add guide for reduce and taggable (#827)
  Remove foo bar (#826)
  WIP: v3 – remove onSearch callback prop (#811)
  doc content updates
  overhaul selecting docs, updated navigation and URL structure
  bump vue-server-renderer (#807)
  WIP: V3 - Remove index prop, add reduce prop (#800)
  V3/update list items slot (#799)
  update vuepress (#797)
  Update sandbox
  Add patch from #787
  update package deps, remove extra dev options
  V3 - Remove `mutable` class properties plus other misc changes (#781)
  refactor deselect method (#768)
  Sass & Class Renames (#759)
  fix coveralls coverage reporter (#766)
  remove: - express - gitbook - json-loader - markdown-loader - normalize.css - prismjs - vue-resource
  use test-utils beta-20
  fix failing test
  ...

# Conflicts:
#	docs/gitbook/README.md
#	docs/homepage/home.html
#	src/components/Select.vue
This commit is contained in:
Jeff
2019-04-13 19:06:06 -05:00
116 changed files with 13739 additions and 7234 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
{
"presets": ["es2015", "stage-2"],
"plugins": ["transform-runtime"],
"presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-transform-runtime"],
"comments": false
}
-25
View File
@@ -1,25 +0,0 @@
---
engines:
csslint:
enabled: true
duplication:
enabled: true
config:
languages:
- ruby
- javascript
- python
- php
eslint:
enabled: true
fixme:
enabled: true
ratings:
paths:
- "src/components/Select.vue"
exclude_paths:
- dist/
- test/
- docs/
- config/
- build/
-2
View File
@@ -1,2 +0,0 @@
--exclude-exts=.min.css
--ignore=adjoining-classes,box-model,ids,order-alphabetical,unqualified-attributes
-1
View File
@@ -1 +0,0 @@
**/*{.,-}min.js
-185
View File
@@ -1,185 +0,0 @@
{
"plugins": [
"html"
],
"env": {
"browser": true,
"node": true
},
"ecmaFeatures": {
"arrowFunctions": true,
"destructuring": true,
"classes": true,
"defaultParams": true,
"blockBindings": true,
"modules": true,
"objectLiteralComputedProperties": true,
"objectLiteralShorthandMethods": true,
"objectLiteralShorthandProperties": true,
"restParams": true,
"spread": true,
"forOf": true,
"generators": true,
"templateStrings": true,
"superInFunctions": true,
"experimentalObjectRestSpread": true
},
"rules": {
"accessor-pairs": 2,
"array-bracket-spacing": 0,
"block-scoped-var": 0,
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
"camelcase": 0,
"comma-dangle": [2, "never"],
"comma-spacing": [2, { "before": false, "after": true }],
"comma-style": [2, "last"],
"complexity": 0,
"computed-property-spacing": 0,
"consistent-return": 0,
"consistent-this": 0,
"constructor-super": 2,
"curly": [2, "multi-line"],
"default-case": 0,
"dot-location": [2, "property"],
"dot-notation": 0,
"eol-last": 2,
"eqeqeq": [2, "allow-null"],
"func-names": 0,
"func-style": 0,
"generator-star-spacing": [2, { "before": true, "after": true }],
"guard-for-in": 0,
"handle-callback-err": [2, "^(err|error)$" ],
"indent": [2, 2, { "SwitchCase": 1 }],
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
"linebreak-style": 0,
"lines-around-comment": 0,
"max-nested-callbacks": 0,
"new-cap": [2, { "newIsCap": true, "capIsNew": false }],
"new-parens": 2,
"newline-after-var": 0,
"no-alert": 0,
"no-array-constructor": 2,
"no-caller": 2,
"no-catch-shadow": 0,
"no-cond-assign": 2,
"no-console": 0,
"no-constant-condition": 0,
"no-continue": 0,
"no-control-regex": 2,
"no-debugger": 2,
"no-delete-var": 2,
"no-div-regex": 0,
"no-dupe-args": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-else-return": 0,
"no-empty": 0,
"no-empty-character-class": 2,
"no-empty-label": 2,
"no-eq-null": 0,
"no-eval": 2,
"no-ex-assign": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": 0,
"no-extra-semi": 0,
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-func-assign": 2,
"no-implied-eval": 2,
"no-inline-comments": 0,
"no-inner-declarations": [2, "functions"],
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-iterator": 2,
"no-label-var": 2,
"no-labels": 2,
"no-lone-blocks": 2,
"no-lonely-if": 0,
"no-loop-func": 0,
"no-mixed-requires": 0,
"no-mixed-spaces-and-tabs": 2,
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-multiple-empty-lines": [2, { "max": 1 }],
"no-native-reassign": 2,
"no-negated-in-lhs": 2,
"no-nested-ternary": 0,
"no-new": 2,
"no-new-func": 0,
"no-new-object": 2,
"no-new-require": 2,
"no-new-wrappers": 2,
"no-obj-calls": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-param-reassign": 0,
"no-path-concat": 0,
"no-process-env": 0,
"no-process-exit": 0,
"no-proto": 0,
"no-redeclare": 2,
"no-regex-spaces": 2,
"no-restricted-modules": 0,
"no-return-assign": 2,
"no-script-url": 0,
"no-self-compare": 2,
"no-sequences": 2,
"no-shadow": 0,
"no-shadow-restricted-names": 2,
"no-spaced-func": 2,
"no-sparse-arrays": 2,
"no-sync": 0,
"no-ternary": 0,
"no-this-before-super": 2,
"no-throw-literal": 2,
"no-trailing-spaces": 2,
"no-undef": 2,
"no-undef-init": 2,
"no-undefined": 0,
"no-underscore-dangle": 0,
"no-unexpected-multiline": 2,
"no-unneeded-ternary": 2,
"no-unreachable": 2,
"no-unused-expressions": 0,
"no-unused-vars": [2, { "vars": "all", "args": "none" }],
"no-use-before-define": 0,
"no-var": 0,
"no-void": 0,
"no-warning-comments": 0,
"no-with": 2,
"object-curly-spacing": 0,
"object-shorthand": 0,
"one-var": [2, { "initialized": "never" }],
"operator-assignment": 0,
"operator-linebreak": [2, "after", { "overrides": { "?": "before", ":": "before" } }],
"padded-blocks": 0,
"prefer-const": 0,
"quote-props": 0,
"quotes": [2, "single", "avoid-escape"],
"radix": 2,
"semi": [2, "never"],
"semi-spacing": 0,
"sort-vars": 0,
"space-after-keywords": [2, "always"],
"space-before-blocks": [2, "always"],
"space-before-function-paren": [2, "always"],
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-return-throw-case": 2,
"space-unary-ops": [2, { "words": true, "nonwords": false }],
"spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"] }],
"strict": 0,
"use-isnan": 2,
"valid-jsdoc": 0,
"valid-typeof": 2,
"vars-on-top": 0,
"wrap-iife": [2, "any"],
"wrap-regex": 0,
"yoda": [2, "never"]
}
}
+24 -8
View File
@@ -1,12 +1,28 @@
.DS_Store
node_modules
npm-debug.log
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
test/unit/coverage
.coveralls.yml
.flowconfig
package-lock.json
docs/gitbook/_book
docs/node_modules
site
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
# Project specific
coverage
dist
test/unit/coverage
package-lock.json
dev/dist
docs/.vuepress/dist
+5 -2
View File
@@ -1,5 +1,8 @@
language: node_js
cache: npm
node_js:
- node
after_success:
- codeclimate-test-reporter < ./test/unit/coverage/lcov.info
script:
- yarn test --coverage --coverageReporters=text-lcov | coveralls
+2 -2
View File
@@ -1,9 +1,9 @@
## Pull Requests
Looks like you want to help out on vue-select.. awesome! Here's a couple things to keep in mind when contributing.
Looks like you want to help out on vue-select.. awesome! Here's a few things to keep in mind when contributing.
1. If your PR contains multiple commits, try to keep those commits succinct, with descriptive messages. This makes it easier to understand your thought process.
2. Don't run the build before submitting. The build is only run and committed immediately before a new release, and submitting a PR with a build included is just more to parse.
2. **Don't run the build** before submitting. The build is only run and committed immediately before a new release.
3. If your PR fixes or references an open issue, be sure to reference it in your message.
4. If you're adding new functionality, make sure your code has good test coverage.
+2 -2
View File
@@ -1,6 +1,6 @@
# vue-select [![Build Status](https://travis-ci.org/sagalbot/vue-select.svg?branch=master)](https://travis-ci.org/sagalbot/vue-select) ![MIT License](https://img.shields.io/github/license/sagalbot/vue-select.svg?style=flat-square) ![Current Release](https://img.shields.io/github/release/sagalbot/vue-select.svg?style=flat-square)
# vue-select [![Build Status](https://travis-ci.org/sagalbot/vue-select.svg?branch=master)](https://travis-ci.org/sagalbot/vue-select) [![Coverage Status](https://coveralls.io/repos/github/sagalbot/vue-select/badge.svg)](https://coveralls.io/github/sagalbot/vue-select) ![MIT License](https://img.shields.io/github/license/sagalbot/vue-select.svg?style=flat-square) ![Current Release](https://img.shields.io/github/release/sagalbot/vue-select.svg?style=flat-square)
> A native Vue.js select component that provides similar functionality to Select2 without the overhead of jQuery.
> Everything you wish the native `<select>` element could do, wrapped up into a zero dependency, highly extensible Vue component.
Want to help out as a primary contributor? [Get in touch](https://github.com/sagalbot/vue-select/issues/581)!
-24
View File
@@ -1,24 +0,0 @@
{
"title": "vue-select",
"gitbook": ">3.0.0",
"root": "./docs/gitbook",
"plugins": ["edit-link", "-fontsettings", "codepen", "include-csv"],
"pluginsConfig": {
"edit-link": {
"base": "https://github.com/sagalbot/vue-select/edit/gitbook/docs/gitbook",
"label": "Edit This Page"
},
"github": {
"url": "https://github.com/sagalbot/vue-select/"
},
"codepen": {
"theme": 32252
}
},
"links": {
"sharing": {
"facebook": false,
"twitter": false
}
}
}
-9
View File
@@ -1,9 +0,0 @@
// https://github.com/shelljs/shelljs
const shell = require('shelljs');
shell.exec('gitbook build', (code, stdout, stderr) => {
if( code === 0 ) {
shell.rm('-rf', 'site/docs');
shell.exec('mv _book site/docs')
}
});
-35
View File
@@ -1,35 +0,0 @@
// https://github.com/shelljs/shelljs
require('shelljs/global');
env.NODE_ENV = 'production';
var path = require('path');
var config = require('../config');
var ora = require('ora');
var webpack = require('webpack');
var utils = require('./utils');
var webpackConfig = utils.shouldBuildHomepage() ? require('./webpack.site.conf') : require('./webpack.prod.conf');
var text = utils.shouldBuildHomepage() ? 'homepage' : 'vue-select UMD module';
var spinner = ora(`building ${text}...`);
spinner.start();
var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory);
if (!utils.shouldBuildHomepage()) {
rm('-rf', assetsPath);
mkdir('-p', assetsPath);
}
/**
* Build the /dist/ folder
*/
webpack(webpackConfig, function (err, stats) {
spinner.stop();
if (err) throw err;
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n')
});
-9
View File
@@ -1,9 +0,0 @@
/* eslint-disable */
require('eventsource-polyfill')
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(function (event) {
if (event.action === 'reload') {
window.location.reload()
}
})
-65
View File
@@ -1,65 +0,0 @@
var path = require('path')
var express = require('express')
var webpack = require('webpack')
var config = require('../config')
var proxyMiddleware = require('http-proxy-middleware')
var webpackConfig = process.env.NODE_ENV === 'testing'
? require('./webpack.prod.conf')
: require('./webpack.dev.conf')
// default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port
// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = config.dev.proxyTable
var app = express()
var compiler = webpack(webpackConfig)
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
stats: {
colors: true,
chunks: false
}
})
var hotMiddleware = require('webpack-hot-middleware')(compiler)
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
var options = proxyTable[context]
if (typeof options === 'string') {
options = { target: options }
}
app.use(proxyMiddleware(context, options))
})
// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')())
// serve webpack bundle output
app.use(devMiddleware)
// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware)
// serve pure static assets
var staticPath = path.posix.join(config.build.assetsPublicPath, config.build.assetsSubDirectory)
app.use(staticPath, express.static('./static'))
module.exports = app.listen(port, function (err) {
if (err) {
console.log(err)
return
}
console.log('Listening at http://localhost:' + port + '\n')
})
-83
View File
@@ -1,83 +0,0 @@
var path = require('path')
var config = require('../config')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
/**
* Get the path to the assetsSubDirectory
* @param _path
* @returns {*|string}
*/
exports.assetsPath = function (_path) {
return path.posix.join(config.build.assetsSubDirectory, _path)
}
/**
* Generate loader string to be used with extract text plugin
* @param options
* @returns {{css: *, postcss: *, less: *, sass: *, scss: *, stylus: *, styl: *}}
*/
exports.cssLoaders = function (options) {
options = options || {}
function generateLoaders (loaders) {
var sourceLoader = loaders.map(function (loader) {
var extraParamChar
if (/\?/.test(loader)) {
loader = loader.replace(/\?/, '-loader?')
extraParamChar = '&'
} else {
loader = loader + '-loader'
extraParamChar = '?'
}
return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '')
}).join('!')
if (options.extract) {
return ExtractTextPlugin.extract('vue-style-loader', sourceLoader)
} else {
return ['vue-style-loader', sourceLoader].join('!')
}
}
// http://vuejs.github.io/vue-loader/configurations/extract-css.html
return {
css: generateLoaders(['css']),
postcss: generateLoaders(['css']),
less: generateLoaders(['css', 'less']),
sass: generateLoaders(['css', 'sass?indentedSyntax']),
scss: generateLoaders(['css', 'sass']),
stylus: generateLoaders(['css', 'stylus']),
styl: generateLoaders(['css', 'stylus'])
}
}
/**
* Generate loaders for standalone style files (outside of .vue)
* @param options
* @returns {Array}
*/
exports.styleLoaders = function (options) {
var output = []
var loaders = exports.cssLoaders(options)
for (var extension in loaders) {
var loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
loader: loader
})
}
return output
}
/**
* Are we serving the docs or
* the dev environment?
* @returns {boolean}
*/
exports.shouldServeHomepage = function () {
return process.argv.indexOf('--docs') > 0
}
exports.shouldBuildHomepage = function () {
return process.argv.indexOf('--homepage') > 0
}
+48 -49
View File
@@ -1,71 +1,70 @@
var path = require('path')
var config = require('../config')
var utils = require('./utils')
var projectRoot = path.resolve(__dirname, '../')
const path = require('path');
const webpack = require('webpack');
const VueLoaderPlugin = require('vue-loader').VueLoaderPlugin;
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const env = process.env.NODE_ENV === 'production'
? 'production'
: 'development';
const devtool = env === 'production' ? 'source-map' : 'eval-source-map';
const extractOrInjectStyles = env !== 'production'
? 'vue-style-loader'
: MiniCssExtractPlugin.loader;
module.exports = {
entry: {
app: utils.shouldServeHomepage() ? './docs/homepage/home.js' : './dev/dev.js',
},
mode: env,
output: {
path: config.build.assetsRoot,
publicPath: config.build.assetsPublicPath,
filename: '[name].js'
path: path.resolve(__dirname, '../dist'),
publicPath: '/',
filename: '[name].js',
},
devtool,
resolve: {
extensions: ['', '.js', '.vue'],
fallback: [path.join(__dirname, '../node_modules')],
extensions: ['.js', '.vue'],
alias: {
'src': path.resolve(__dirname, '../src'),
'assets': path.resolve(__dirname, '../docs/assets'),
'mixins': path.resolve(__dirname, '../src/mixins'),
'components': path.resolve(__dirname, '../src/components'),
'docs': path.resolve(__dirname, '../docs'),
'vue$': 'vue/dist/vue.common.js',
}
},
resolveLoader: {
fallback: [path.join(__dirname, '../node_modules')]
'vue$': 'vue/dist/vue.esm.js',
},
},
module: {
loaders: [
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
loader: 'vue-loader',
},
{
test: /\.js$/,
loader: 'babel-loader',
include: projectRoot,
exclude: /node_modules/
include: path.resolve(__dirname, '../'),
exclude: /node_modules/,
},
{
test: /\.json$/,
loader: 'json-loader'
test: /\.s?css$/,
use: [
extractOrInjectStyles,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
{
test: /\.html$/,
loader: 'vue-html-loader'
},
{
test: /\.(png|jpe?g|gif)(\?.*)?$/,
loader: 'url',
query: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/,
loader: 'url',
query: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
],
},
vue: {
loaders: utils.cssLoaders()
}
}
plugins: [
new webpack.DefinePlugin({
'process.env': env,
}),
new MiniCssExtractPlugin({
filename: 'vue-select.css',
}),
new VueLoaderPlugin(),
],
stats: {
children: false,
modules: false,
},
};
+34 -34
View File
@@ -1,39 +1,39 @@
var config = require('../config')
var webpack = require('webpack')
var merge = require('webpack-merge')
var utils = require('./utils')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
// add hot-reload related code to entry chunks
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})
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, {
module: {
loaders: utils.styleLoaders().concat({ test: /\.md$/, loader: "html!markdown" })
},
// eval-source-map is faster for development
devtool: '#eval-source-map',
entry: './dev/dev.js',
plugins: [
new webpack.DefinePlugin({
'process.env': config.dev.env
}),
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: utils.shouldServeHomepage() ? './docs/homepage/home.html' : './dev/dev.html',
inject: true
})
template: './dev/dev.html',
inject: true,
}),
],
markdownLoader: {
highlight: function (code) {
return require('highlight.js').highlightAuto(code).value;
}
}
})
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');
});
},
},
});
+24 -31
View File
@@ -1,32 +1,25 @@
const webpack = require('webpack')
const base = require('./webpack.base.conf')
const config = require('../config')
const TerserPlugin = require('terser-webpack-plugin');
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
base.entry = {
lib: './src/index.js'
}
base.output = {
path: config.build.assetsRoot,
publicPath: config.build.assetsPublicPath,
filename: 'vue-select.js',
library: 'VueSelect',
libraryTarget: 'umd'
}
var webpackConfig = Object.assign({}, base)
webpackConfig.devtool = '#source-map'
webpackConfig.plugins = (webpackConfig.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
compress: { warnings: false }
}),
new webpack.optimize.OccurenceOrderPlugin(),
])
module.exports = webpackConfig
module.exports = merge(baseWebpackConfig, {
entry: './src/index.js',
output: {
filename: 'vue-select.js',
library: 'VueSelect',
libraryTarget: 'umd',
globalObject: 'typeof self !== \'undefined\' ? self : this',
},
optimization: {
minimizer: [
new TerserPlugin({
sourceMap: true,
terserOptions: {
compress: {
drop_console: true,
},
},
}),
],
}
});
-83
View File
@@ -1,83 +0,0 @@
var path = require('path')
var config = require('../config')
var utils = require('./utils')
var webpack = require('webpack')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var env = process.env.NODE_ENV === 'testing'
? require('../config/test.env')
: config.homepage.env
module.exports = merge(baseWebpackConfig, {
entry: {
app: config.homepage.entry
},
module: {
loaders: utils.styleLoaders({ sourceMap: config.homepage.productionSourceMap, extract: true })
},
devtool: config.homepage.productionSourceMap ? '#source-map' : false,
output: {
publicPath: config.homepage.assetsPublicPath,
path: config.homepage.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
vue: {
loaders: utils.cssLoaders({
sourceMap: config.homepage.productionSourceMap,
extract: true
})
},
plugins: [
// http://vuejs.github.io/vue-loader/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
new webpack.optimize.OccurenceOrderPlugin(),
// extract css into its own file
new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: './docs/homepage/home.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module, count) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['vendor']
})
]
})
-6
View File
@@ -1,6 +0,0 @@
var merge = require('webpack-merge')
var prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"'
})
-30
View File
@@ -1,30 +0,0 @@
// see http://vuejs-templates.github.io/webpack for documentation.
var path = require('path');
module.exports = {
build: {
env: require('./prod.env'),
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: '',
assetsPublicPath: '/',
productionSourceMap: true
},
dev: {
env: require('./dev.env'),
port: 8080,
proxyTable: {}
},
umd: {
assetsRoot: path.resolve(__dirname, '../umd'),
assetsPublicPath: '/'
},
homepage: {
env: require('./prod.env'),
entry: './docs/homepage/home.js',
assetsRoot: path.resolve(__dirname, '../site'),
assetsSubDirectory: '',
assetsPublicPath: '',
productionSourceMap: true
}
};
-3
View File
@@ -1,3 +0,0 @@
module.exports = {
NODE_ENV: '"production"'
}
-6
View File
@@ -1,6 +0,0 @@
var merge = require('webpack-merge')
var devEnv = require('./dev.env')
module.exports = merge(devEnv, {
NODE_ENV: '"testing"'
})
+39
View File
@@ -0,0 +1,39 @@
<template>
<div id="app">
<sandbox hide-help v-slot="config">
<v-select v-bind="config" />
</sandbox>
</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';
export default {
components: {Sandbox, vSelect},
};
</script>
<style>
html,
body {
margin: 0;
height: 100%;
font-family: -apple-system, sans-serif;
}
#app {
height: 100%;
}
hr {
border: none;
border-bottom: 1px solid #cacaca;
margin-bottom: 1em;
padding-top: 1em;
width: 90%;
}
</style>
-1
View File
@@ -1 +0,0 @@
export default ["Afghanistan", "Åland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", "Congo, The Democratic Republic of The", "Cook Islands", "Costa Rica", "Cote D'ivoire", "Croatia", "Cuba", "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)", "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", "Guinea-bissau", "Guyana", "Haiti", "Heard Island and Mcdonald Islands", "Holy See (Vatican City State)", "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran, Islamic Republic of", "Iraq", "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan", "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya", "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia, The Former Yugoslav Republic of", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of", "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", "Palestinian Territory, Occupied", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", "Rwanda", "Saint Helena", "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon", "Saint Vincent and The Grenadines", "Samoa", "San Marino", "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", "South Georgia and The South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic", "Taiwan, Province of China", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Timor-leste", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", "Viet Nam", "Virgin Islands, British", "Virgin Islands, U.S.", "Wallis and Futuna", "Western Sahara", "Yemen", "Zambia", "Zimbabwe"];
+7 -80
View File
@@ -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">&times;</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>
+5 -47
View File
@@ -1,50 +1,8 @@
import Vue from 'vue'
import Fuse from 'fuse.js'
import debounce from 'lodash/debounce'
import resource from 'vue-resource'
import vSelect from '../src/components/Select.vue'
import countries from './data/countryCodes'
import fuseSearchOptions from './data/books'
import Vue from "vue";
import Dev from "./Dev.vue";
Vue.use(resource)
Vue.component('v-select', vSelect)
Vue.config.productionTip = false;
/* eslint-disable no-new */
new Vue({
el: '#app',
data: {
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);
}
}
});
render: h => h(Dev)
}).$mount("#app");
+28
View File
@@ -0,0 +1,28 @@
<template>
<p :data-height="height"
data-theme-id="32252"
:data-slug-hash="url"
data-default-tab="result"
data-user="sagalbot"
class="codepen">
</p>
</template>
<script>
import mountCodePen from '../utils/codePen';
export default {
props: {
url: {
type: String,
required: true,
},
height: {
default: 250
}
},
mounted () {
mountCodePen();
},
};
</script>
@@ -0,0 +1,18 @@
<template>
<v-select :options="options"></v-select>
</template>
<script>
import countryCodes from '../data/countryCodes';
export default {
name: 'CountrySelect',
data: () => ({
options: countryCodes,
}),
};
</script>
<style scoped>
</style>
@@ -0,0 +1,60 @@
<template>
<div class="flex">
<div>
<v-select
label="country"
v-model="selected"
:reduce="opt => opt.meta.id"
:options="options"
/>
</div>
<div>
<pre><code>v-model value: {{ selected || 'null' }}</code></pre>
</div>
</div>
</template>
<script>
export default {
name: 'ReducerNestedValue',
data: () => ({
selected: null,
options: [
{
country: 'canada',
meta: {
id: '1',
code: 'ca',
},
}],
}),
};
</script>
<style scoped>
.flex {
margin-bottom: 2rem;
border: 1px solid #eaecef;
/*padding: 1rem;*/
display: flex;
justify-content: center;
align-items: center;
}
.flex > div {
flex-grow: 1;
width: 50%;
padding: 0 1rem;
}
pre {
margin: 0;
background: #fff;
}
code {
color: #635762 !important;
color: #5b2d2d !important;
/*color: #7ec699 !important;*/
}
</style>
+281
View File
@@ -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: 'Select a Country...',
});
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>
+104
View File
@@ -0,0 +1,104 @@
const isDeployPreview = process.env.hasOwnProperty('DEPLOY_PREVIEW');
const meta = {
title: 'Vue Select | VueJS Select2/Chosen Component',
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',
};
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' }]
];
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'}],
['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}],
);
}
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: {
'/': [
{
title: 'Getting Started',
collapsable: false,
children: [
['guide/install', 'Installation'],
['guide/options', 'Dropdown Options'],
['guide/values', 'Selecting Values'],
['guide/localization', 'Localization'],
],
},
{
title: 'Digging Deeper',
collapsable: false,
children: [
['guide/templating', 'Templating'],
['guide/vuex', 'Vuex'],
['guide/ajax', 'AJAX'],
],
},
{
title: 'API',
collapsable: false,
children: [
['api/props', 'Props'],
['api/slots', 'Slots'],
['api/events', 'Events'],
],
},
],
},
},
};
@@ -1,3 +1,4 @@
/* eslint-disable */
export default [
{
title: "Old Man's War",
@@ -160,4 +161,4 @@ export default [
lastName: "Maine"
}
}
]
]
+246
View File
@@ -0,0 +1,246 @@
/* eslint-disable */
export default [
'Afghanistan',
'Åland Islands',
'Albania',
'Algeria',
'American Samoa',
'Andorra',
'Angola',
'Anguilla',
'Antarctica',
'Antigua and Barbuda',
'Argentina',
'Armenia',
'Aruba',
'Australia',
'Austria',
'Azerbaijan',
'Bahamas',
'Bahrain',
'Bangladesh',
'Barbados',
'Belarus',
'Belgium',
'Belize',
'Benin',
'Bermuda',
'Bhutan',
'Bolivia',
'Bosnia and Herzegovina',
'Botswana',
'Bouvet Island',
'Brazil',
'British Indian Ocean Territory',
'Brunei Darussalam',
'Bulgaria',
'Burkina Faso',
'Burundi',
'Cambodia',
'Cameroon',
'Canada',
'Cape Verde',
'Cayman Islands',
'Central African Republic',
'Chad',
'Chile',
'China',
'Christmas Island',
'Cocos (Keeling) Islands',
'Colombia',
'Comoros',
'Congo',
'Congo, The Democratic Republic of The',
'Cook Islands',
'Costa Rica',
'Cote D\'ivoire',
'Croatia',
'Cuba',
'Cyprus',
'Czech Republic',
'Denmark',
'Djibouti',
'Dominica',
'Dominican Republic',
'Ecuador',
'Egypt',
'El Salvador',
'Equatorial Guinea',
'Eritrea',
'Estonia',
'Ethiopia',
'Falkland Islands (Malvinas)',
'Faroe Islands',
'Fiji',
'Finland',
'France',
'French Guiana',
'French Polynesia',
'French Southern Territories',
'Gabon',
'Gambia',
'Georgia',
'Germany',
'Ghana',
'Gibraltar',
'Greece',
'Greenland',
'Grenada',
'Guadeloupe',
'Guam',
'Guatemala',
'Guernsey',
'Guinea',
'Guinea-bissau',
'Guyana',
'Haiti',
'Heard Island and Mcdonald Islands',
'Holy See (Vatican City State)',
'Honduras',
'Hong Kong',
'Hungary',
'Iceland',
'India',
'Indonesia',
'Iran, Islamic Republic of',
'Iraq',
'Ireland',
'Isle of Man',
'Israel',
'Italy',
'Jamaica',
'Japan',
'Jersey',
'Jordan',
'Kazakhstan',
'Kenya',
'Kiribati',
'Korea, Democratic People\'s Republic of',
'Korea, Republic of',
'Kuwait',
'Kyrgyzstan',
'Lao People\'s Democratic Republic',
'Latvia',
'Lebanon',
'Lesotho',
'Liberia',
'Libyan Arab Jamahiriya',
'Liechtenstein',
'Lithuania',
'Luxembourg',
'Macao',
'Macedonia, The Former Yugoslav Republic of',
'Madagascar',
'Malawi',
'Malaysia',
'Maldives',
'Mali',
'Malta',
'Marshall Islands',
'Martinique',
'Mauritania',
'Mauritius',
'Mayotte',
'Mexico',
'Micronesia, Federated States of',
'Moldova, Republic of',
'Monaco',
'Mongolia',
'Montenegro',
'Montserrat',
'Morocco',
'Mozambique',
'Myanmar',
'Namibia',
'Nauru',
'Nepal',
'Netherlands',
'Netherlands Antilles',
'New Caledonia',
'New Zealand',
'Nicaragua',
'Niger',
'Nigeria',
'Niue',
'Norfolk Island',
'Northern Mariana Islands',
'Norway',
'Oman',
'Pakistan',
'Palau',
'Palestinian Territory, Occupied',
'Panama',
'Papua New Guinea',
'Paraguay',
'Peru',
'Philippines',
'Pitcairn',
'Poland',
'Portugal',
'Puerto Rico',
'Qatar',
'Reunion',
'Romania',
'Russian Federation',
'Rwanda',
'Saint Helena',
'Saint Kitts and Nevis',
'Saint Lucia',
'Saint Pierre and Miquelon',
'Saint Vincent and The Grenadines',
'Samoa',
'San Marino',
'Sao Tome and Principe',
'Saudi Arabia',
'Senegal',
'Serbia',
'Seychelles',
'Sierra Leone',
'Singapore',
'Slovakia',
'Slovenia',
'Solomon Islands',
'Somalia',
'South Africa',
'South Georgia and The South Sandwich Islands',
'Spain',
'Sri Lanka',
'Sudan',
'Suriname',
'Svalbard and Jan Mayen',
'Swaziland',
'Sweden',
'Switzerland',
'Syrian Arab Republic',
'Taiwan, Province of China',
'Tajikistan',
'Tanzania, United Republic of',
'Thailand',
'Timor-leste',
'Togo',
'Tokelau',
'Tonga',
'Trinidad and Tobago',
'Tunisia',
'Turkey',
'Turkmenistan',
'Turks and Caicos Islands',
'Tuvalu',
'Uganda',
'Ukraine',
'United Arab Emirates',
'United Kingdom',
'United States',
'United States Minor Outlying Islands',
'Uruguay',
'Uzbekistan',
'Vanuatu',
'Venezuela',
'Viet Nam',
'Virgin Islands, British',
'Virgin Islands, U.S.',
'Wallis and Futuna',
'Western Sahara',
'Yemen',
'Zambia',
'Zimbabwe'];
@@ -1,3 +1,4 @@
/* eslint-disable */
export default [
{value: "AF", label: "Afghanistan"},
{value: "AX", label: "Åland Islands"},
+5
View File
@@ -0,0 +1,5 @@
import vSelect from '../../src/components/Select';
export default ({Vue, options, router, siteData}) => {
Vue.component('v-select', vSelect);
}
+111
View File
@@ -0,0 +1,111 @@
export default () => {
document.getElementsByClassName || (document.getElementsByClassName = function (e) {
var n, t, r, a = document, o = [];
if (a.querySelectorAll) return a.querySelectorAll('.' + e);
if (a.evaluate) for (t = './/*[contains(concat(\' \', @class, \' \'), \' ' + e + ' \')]', n = a.evaluate(t, a,
null, 0,
null); r = n.iterateNext();) o.push(r); else for (n = a.getElementsByTagName('*'), t = new RegExp(
'(^|\\s)' + e + '(\\s|$)'), r = 0; r < n.length; r++) t.test(n[r].className) && o.push(n[r]);
return o;
}), function () {
function e () {
function e () {
for (var e = document.getElementsByClassName('codepen'), t = e.length - 1; t > -1; t--) {
var u = a(e[t]);
if (0 !== Object.keys(u).length && (u = o(u), u.user = n(u, e[t]), r(u))) {
var c = i(u), l = s(u, c);
f(e[t], l);
}
}
m();
}
function n (e, n) {
if ('string' == typeof e.user) return e.user;
for (var t = 0, r = n.children.length; t < r; t++) {
var a = n.children[t], o = a.href || '', i = o.match(/codepen\.(io|dev)\/(\w+)\/pen\//i);
if (i) return i[2];
}
return 'anon';
}
function r (e) {return e['slug-hash'];}
function a (e) {
for (var n = {}, t = e.attributes, r = 0, a = t.length; r < a; r++) {
var o = t[r].name;
0 === o.indexOf('data-') && (n[o.replace('data-', '')] = t[r].value);
}
return n;
}
function o (e) {
return e.href && (e['slug-hash'] = e.href), e.type && (e['default-tab'] = e.type), e.safe &&
('true' === e.safe ? e.animations = 'run' : e.animations = 'stop-after-5'), e;
}
function i (e) {
var n = u(e), t = e.user ? e.user : 'anon', r = '?' + l(e),
a = e.preview && 'true' === e.preview ? 'embed/preview' : 'embed',
o = [n, t, a, e['slug-hash'] + r].join('/');
return o.replace(/\/\//g, '//');
}
function u (e) {
return e.host ? c(e.host) : 'file:' === document.location.protocol
? 'https://codepen.io'
: '//codepen.io';
}
function c (e) {return e.match(/^\/\//) || !e.match(/https?:/) ? document.location.protocol + '//' + e : e;}
function l (e) {
var n = '';
for (var t in e) '' !== n && (n += '&'), n += t + '=' + encodeURIComponent(e[t]);
return n;
}
function s (e, n) {
var r;
e['pen-title'] ? r = e['pen-title'] : (r = 'CodePen Embed ' + t, t++);
var a = {
id: 'cp_embed_' + e['slug-hash'].replace('/', '_'),
src: n,
scrolling: 'no',
frameborder: '0',
height: d(e),
allowTransparency: 'true',
allowfullscreen: 'true',
allowpaymentrequest: 'true',
name: 'CodePen Embed',
title: r,
'class': 'cp_embed_iframe ' + (e['class'] ? e['class'] : ''),
style: 'width: ' + p + '; overflow: hidden;',
}, o = '<iframe ';
for (var i in a) o += i + '="' + a[i] + '" ';
return o += '></iframe>';
}
function d (e) {return e.height ? e.height : 300;}
function f (e, n) {
if (e.parentNode) {
var t = document.createElement('div');
t.className = 'cp_embed_wrapper', t.innerHTML = n, e.parentNode.replaceChild(t, e);
} else e.innerHTML = n;
}
function m () {'function' == typeof __CodePenIFrameAddedToPage && __CodePenIFrameAddedToPage();}
var p = '100%';
e();
}
function n (e) {/in/.test(document.readyState) ? setTimeout('window.__cp_domReady(' + e + ')', 9) : e();}
var t = 1;
window.__cp_domReady = n, window.__CPEmbed = e, n(function () {new __CPEmbed;});
}();
}
+38
View File
@@ -0,0 +1,38 @@
# Vue Select
![Current Release](https://img.shields.io/github/release/sagalbot/vue-select.svg?style=flat-square)
![Bundle Size](https://img.shields.io/bundlephobia/min/vue-select.svg?style=flat-square)
![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)
> Everything you wish the HTML `<select>` element could do, wrapped
up into a zero dependency, highly 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:
<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).
#### Features
- Tagging
- Filtering/Searching
- Vuex Support
- AJAX Support
- SSR Support
- Select Single/Multiple Options
- Tested with Bootstrap 3/4, Bulma, Foundation
- +95% Test Coverage
- ~20kb Total / ~5kb CSS / ~15kb JS
- Zero dependencies
#### Resources
- **[CodePen Template](http://codepen.io/sagalbot/pen/NpwrQO)**
- **[Trello Roadmap](https://trello.com/b/vWvITNzS/vue-select)**
- **[GitHub](https://github.com/sagalbot/vue-select)**
+39
View File
@@ -0,0 +1,39 @@
## `input`
Triggered when the selected value changes. Used internally for `v-model`.
```js
/**
* @param val {Object|String}` - selected option.
*/
this.$emit("input", val);
```
## `option:created`
Triggered when `taggable` is `true` and a new option has been created.
```js
/**
* @param newOption {Object} - created option
*/
this.$emit("option:created", newOption);
```
## `search:blur`
Triggered when the text input loses focus. The dropdown will close immediately before this
event is triggered.
```js
this.$emit("search:blur");
```
## `search:focus`
Triggered when the text input gains focus. The dropdown will open immediately before this
event is triggered.
```js
this.$emit("search:focus");
```
+359
View File
@@ -0,0 +1,359 @@
## 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
},
```
## 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
options: {
type: Array,
default() {
return [];
}
},
```
## 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"
},
```
## 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
open (useful when combined with multi-select, for example)
```js
closeOnSelect: {
type: Boolean,
default: true
},
```
## label
Tells vue-select what key to use when generating option
labels when each `option` is an object.
```js
label: {
type: String,
default: "label"
},
```
## index
Tells vue-select what key to use when generating option
values when each `option` is an object.
```js
index: {
type: String,
default: null
},
```
## 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 (this.index) {
option = this.findOptionByIndexValue(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` +
"http://sagalbot.github.io/vue-select/#ex-labels"
);
}
return option[this.label];
}
return option;
}
},
```
## onTab
Select the current value if `selectOnTab` is enabled
```js
onTab: {
type: Function,
default: function() {
if (this.selectOnTab) {
this.typeAheadSelect();
}
}
},
```
## taggable
Enable/disable creating options from searchInput.
```js
taggable: {
type: Boolean,
default: false
},
```
## tabindex
Set the tabindex for the input field.
```js
tabindex: {
type: Number,
default: null
},
```
## pushTags
When true, newly created tags will be added to
the options list.
```js
pushTags: {
type: Boolean,
default: false
},
```
## 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;
}
},
```
## 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: {
type: Function,
default(newOption) {
if (typeof this.optionList[0] === "object") {
newOption = { [this.label]: newOption };
}
this.$emit("option:created", newOption);
return newOption;
}
},
```
## resetOnOptionsChange
When false, updating the options will not reset the select value
```js
resetOnOptionsChange: {
type: Boolean,
default: false
},
```
## noDrop
Disable the dropdown entirely.
```js
noDrop: {
type: Boolean,
default: false
},
```
## inputId
Sets the id of the input element.
```js
inputId: {
type: String
},
```
## dir
Sets RTL support. Accepts `ltr`, `rtl`, `auto`.
```js
dir: {
type: String,
default: "auto"
},
```
## selectOnTab
When true, hitting the 'tab' key will select the current select value
```js
selectOnTab: {
type: Boolean,
default: false
}
+65
View File
@@ -0,0 +1,65 @@
::: tip
VueSelect leverages scoped slots to allow for total customization of the presentation layer.
Slots can be used to change the look and feel of the UI, or to simply swap out text.
:::
## Selected Option(s)
### `selected-option`
#### Scope:
- `option {Object}` - A selected option
```html
<slot name="selected-option" v-bind="(typeof option === 'object')?option:{[label]: option}">
{{ getOptionLabel(option) }}
</slot>
```
### `selected-option-container`
#### Scope:
- `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
```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>
```
## Component Actions
### `spinner`
```html
<slot name="spinner">
<div class="spinner" v-show="mutableLoading">Loading...</div>
</slot>
```
## Dropdown
### `option`
#### Scope:
- `option {Object}` - The currently iterated option from `filteredOptions`
```html
<slot name="option" v-bind="(typeof option === 'object')?option:{[label]: option}">
{{ getOptionLabel(option) }}
</slot>
```
-60
View File
@@ -1,60 +0,0 @@
## AJAX Remote Option Loading
[](codepen://sagalbot/POMeOX?height=400)
The `onSearch` prop allows you to load options via ajax in a parent component
when the search text is updated. It is invoked with two parameters, `search` & `loading`.
```js
/**
* Accepts a callback function that will be run
* when the search text changes. The callback
* will be invoked with these parameters:
*
* @param {search} String Current search text
* @param {loading} Function Toggle loading class
*/
onSearch: {
type: Function,
default: false
},
```
The `loading` function accepts a boolean parameter that will be assigned
to the vue-select internal `loading` property. Call `loading(true)` to set the
`loading` property to `true` - toggling the loading spinner. After your
asynchronous operation completes, call `loading(false)` to toggle it off.
#### Disabling Filtering
When loading server side options, it can be useful to disable the
client side filtering. Use the `filterable` prop to disable filtering.
```js
/**
* When true, existing options will be filtered
* by the search text. Should not be used in
* conjunction with taggable.
*
* @type {Boolean}
*/
filterable: {
type: Boolean,
default: true
},
```
#### Loading Spinner
Vue Select includes a default loading spinner that appears when the loading class is present. The `spinner` slot allows you to implement your own spinner.
```html
<div class="spinner" v-show="spinner">Loading...</div>
```
#### Library Agnostic
Since Vue.js does not ship with ajax functionality as part of the core library, it's up to you to process the ajax requests in your parent component.
I recommend using [axios](https://github.com/axios/axios) for creating your applications HTTP layer,
or [`fetch()`](https://github.com/github/fetch) for simple requests.
-1
View File
@@ -1 +0,0 @@
[](codepen://sagalbot/zZQJKW?height=600)
View File
View File
View File
-311
View File
@@ -1,311 +0,0 @@
## Select
```js
/**
* 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
* @type {Object||String||null}
*/
value: {
default: null
},
/**
* 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: 'This is Foo', value: 'foo'}]). A
* custom label key can be set with the `label` prop.
* @type {Array}
*/
options: {
type: Array,
default() {
return []
},
},
/**
* Disable the entire component.
* @type {Boolean}
*/
disabled: {
type: Boolean,
default: false
},
/**
* Value of the 'autocomplete' field of the input
* element.
* @type {String}
*/
autocomplete: {
type: String,
default: 'off'
},
/**
* Sets the max-height property on the dropdown list.
* @deprecated
* @type {String}
*/
maxHeight: {
type: String,
default: '400px'
},
/**
* Enable/disable filtering the options.
* @type {Boolean}
*/
searchable: {
type: Boolean,
default: true
},
/**
* Equivalent to the `multiple` attribute on a `<select>` input.
* @type {Boolean}
*/
multiple: {
type: Boolean,
default: false
},
/**
* Equivalent to the `placeholder` attribute on an `<input>`.
* @type {String}
*/
placeholder: {
type: String,
default: ''
},
/**
* Sets a Vue transition property on the `.dropdown-menu`. vue-select
* does not include CSS for transitions, you'll need to add them yourself.
* @type {String}
*/
transition: {
type: String,
default: 'fade'
},
/**
* Enables/disables clearing the search text when an option is selected.
* @type {Boolean}
*/
clearSearchOnSelect: {
type: Boolean,
default: true
},
/**
* Enables/disables allowing the user to clear the selected property.
* @type {Boolean}
*/
clearable: {
type: Boolean,
default: true
},
/**
* Close a dropdown when an option is chosen. Set to false to keep the dropdown
* open (useful when combined with multi-select, for example)
* @type {Boolean}
*/
closeOnSelect: {
type: Boolean,
default: true
},
/**
* Tells vue-select what key to use when generating option
* labels when each `option` is an object.
* @type {String}
*/
label: {
type: String,
default: 'label'
},
/**
* Callback to generate the label text. If {option}
* is an object, returns option[this.label] by default.
* @type {Function}
* @param {Object || String} option
* @return {String}
*/
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` +
'http://sagalbot.github.io/vue-select/#ex-labels'
)
}
if (this.label && option[this.label]) {
return option[this.label]
}
}
return option;
}
},
/**
* Callback to determine if the provided option should
* match the current search text. Used to determine
* if the option should be displayed.
* @type {Function}
* @param {Object || String} option
* @param {String} label
* @param {String} search
* @return {Boolean}
*/
filterBy: {
type: Function,
default(option, label, search) {
return (label || '').toLowerCase().indexOf(search.toLowerCase()) > -1
}
},
/**
* An optional callback function that is called each time the selected
* value(s) change. When integrating with Vuex, use this callback to trigger
* an action, rather than using :value.sync to retreive the selected value.
* @type {Function}
* @param {Object || String} val
*/
onChange: {
type: Function,
default: function (val) {
this.$emit('input', val)
}
},
/**
* Enable/disable creating options from searchInput.
* @type {Boolean}
*/
taggable: {
type: Boolean,
default: false
},
/**
* Set the tabindex for the input field.
* @type {Number}
*/
tabindex: {
type: Number,
default: null
},
/**
* When true, newly created tags will be added to
* the options list.
* @type {Boolean}
*/
pushTags: {
type: Boolean,
default: false
},
/**
* When true, existing options will be filtered
* by the search text. Should not be used in conjunction
* with taggable.
* @type {Boolean}
*/
filterable: {
type: Boolean,
default: true
},
/**
* User defined function for adding Options
* @type {Function}
*/
createOption: {
type: Function,
default(newOption) {
if (typeof this.mutableOptions[0] === 'object') {
newOption = {[this.label]: newOption}
}
this.$emit('option:created', newOption)
return newOption
}
},
/**
* When false, updating the options will not reset the select value
* @type {Boolean}
*/
resetOnOptionsChange: {
type: Boolean,
default: false
},
/**
* Disable the dropdown entirely.
* @type {Boolean}
*/
noDrop: {
type: Boolean,
default: false
},
/**
* Sets the id of the input element.
* @type {String}
* @default {null}
*/
inputId: {
type: String
},
/**
* Sets RTL support. Accepts 'ltr', 'rtl', 'auto'.
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir
* @type {String}
* @default 'auto'
*/
dir: {
type: String,
default: 'auto'
},
```
## AJAX
```js
/**
* Toggles the adding of a 'loading' class to the main
* .v-select wrapper. Useful to control UI state when
* results are being processed through AJAX.
*/
loading: {
type: Boolean,
default: false
},
/**
* Accept a callback function that will be
* run when the search text changes.
*
* loading() accepts a boolean value, and can
* be used to toggle a loading class from
* the onSearch callback.
*
* @param {search} String Current search text
* @param {loading} Function(bool) Toggle loading class
*/
onSearch: {
type: Function,
default: function(search, loading){}
}
```
View File
-25
View File
@@ -1,25 +0,0 @@
name,type
value,Object||String||null
options,Array
disabled,Boolean
maxHeight,String
searchable,Boolean
multiple,Boolean
placeholder,String
transition,String
clearSearchOnSelect,Boolean
closeOnSelect,Boolean
label,String
getOptionLabel,Function
filterFunction,Function
filter,Function
onChange,Function
taggable,Boolean
tabindex,Number
pushTags,Boolean
filterable,Boolean
createOption,Function
resetOnOptionsChange,Boolean
noDrop,Boolean
inputId,String
dir,String
1 name type
2 value Object||String||null
3 options Array
4 disabled Boolean
5 maxHeight String
6 searchable Boolean
7 multiple Boolean
8 placeholder String
9 transition String
10 clearSearchOnSelect Boolean
11 closeOnSelect Boolean
12 label String
13 getOptionLabel Function
14 filterFunction Function
15 filter Function
16 onChange Function
17 taggable Boolean
18 tabindex Number
19 pushTags Boolean
20 filterable Boolean
21 createOption Function
22 resetOnOptionsChange Boolean
23 noDrop Boolean
24 inputId String
25 dir String
-1
View File
@@ -1 +0,0 @@
## Getting Started
-59
View File
@@ -1,59 +0,0 @@
## Dropdown Options {#options}
`vue-select` accepts arrays of strings or objects to use as options through the `options` prop:
```html
<v-select :options="['foo','bar']"></v-select>
```
When provided an array of objects, `vue-select` will display a single value of the object. By default, `vue-select` will look for a key named `label` on the object to use as display text.
```html
<v-select :options="[{label: 'foo', value: 'Foo'}]"></v-select>
```
### Option Labels {#labels}
When the `options` array contains objects, `vue-select` looks for the `label` key to display by default. You can set your own label to match your source data using the `label` prop.
For example, consider an object with `countryCode` and `countryName` properties:
```json
{
countryCode: "CA",
countryName: "Canada"
}
```
If you wanted to display `Canada` in the dropdown, you'd use the `countryName` key:
```html
<v-select label="countryName" :options="countries"></v-select>
```
[](codepen://sagalbot/aEjLPB?height=500)
### Option index {#values}
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:
```json
{
value: "CA",
label: "Canada"
}
```
If you wanted to return `CA` in the dropdown when `Canada` is selected, you'd use the `index` key:
```html
<v-select index="value" :options="countries"></v-select>
```
### Null / Empty Options {#null}
`vue-select` requires the `option` property to be an `array`. If you are using Vue in development mode, you will get warnings attempting to pass anything other than an `array` to the `options` prop. If you need a `null`/`empty` value, use an empty array `[]`.
-38
View File
@@ -1,38 +0,0 @@
## Selecting Values {#single}
The most common use case for `vue-select` is to have the chosen value synced with a parent component. `vue-select` takes advantage of the `v-model` syntax to sync values with a parent.
```html
<v-select v-model="selected"></v-select>
```
[](codepen://sagalbot/Kqxbjw?height=250)
If you don't require the `value` to be synced, you can also pass the prop directly:
```html
<v-select :value="selected"></v-select>
```
This method allows you to pre-select a value(s), without syncing any changes to the parent component. This is also very useful when using a state management tool, like Vuex.
### Single/Multiple Selection {#multiple}
By default, `vue-select` supports choosing a single value. If you need multiple values, use the `multiple` prop:
```html
<v-select multiple v-model="selected"></v-select>
```
[](codepen://sagalbot/opMGro?height=250)
### Tagging {#tagging}
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://sagalbot/XVoWxm?height=350)
-43
View File
@@ -1,43 +0,0 @@
## Vue Compatibility
- `vue ~2.0` use `vue-select ~2.0`
- `vue ~1.0` use `vue-select ~1.0`
## Yarn / NPM
Install with yarn:
```bash
yarn add vue-select
```
or, using NPM:
```
npm install vue-select
```
Then, import and register the component:
```js
import Vue from 'vue'
import vSelect from 'vue-select'
Vue.component('v-select', vSelect)
```
## CDN
Include `vue` & `vue-select.js` - I recommend using [unpkg.com](https://unpkg.com/#/).
```html
<!-- include VueJS first -->
<script src="https://unpkg.com/vue@latest"></script>
<!-- use the latest release -->
<script src="https://unpkg.com/vue-select@latest"></script>
<!-- or point to a specific release -->
<script src="https://unpkg.com/vue-select@1.30"></script>
```
Then register the component in your javascript:
```js
Vue.component('v-select', VueSelect.VueSelect);
```
[](codepen://sagalbot/dJjzeP)
-25
View File
@@ -1,25 +0,0 @@
# vue-select
![Build Status](https://travis-ci.org/sagalbot/vue-select.svg?branch=master)
![Monthly Downloads](https://img.shields.io/npm/dm/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)
![Current Release](https://img.shields.io/github/release/sagalbot/vue-select.svg?style=flat-square)
> A Vue.js select component that provides similar functionality to Select2 without the overhead of jQuery.
#### Features
- AJAX Support
- Tagging
- List Filtering/Searching
- Supports Vuex
- Select Single/Multiple Options
- Tested with Bootstrap 3/4, Bulma, Foundation
- +95% Test Coverage
- ~33kb minified with CSS
- Zero dependencies
#### Resources
- **[CodePen Template](http://codepen.io/sagalbot/pen/NpwrQO)**
- **[Trello Roadmap](https://trello.com/b/vWvITNzS/vue-select)**
- **[GitHub](https://github.com/sagalbot/vue-select)**
-21
View File
@@ -1,21 +0,0 @@
# Summary
- Getting Started
- [Installation](Install.md)
- [Dropdown Options](Basics/Options.md)
- [Option Labels](Basics/Options.md#labels)
- [Null Options](Basics/Options.md#null)
- [Selecting Values](Basics/Values.md#values)
- [Single](Basics/Values.md#single)
- [Multiple](Basics/Values.md#multiple)
- [Tagging](Basics/Values.md#tagging)
- [Localization](Basics/Localization.md)
- Digging Deeper
- [Templating](Advanced/Templating.md)
- [Vuex](Advanced/Vuex.md)
- [AJAX](Advanced/Ajax.md)
- [Examples](Examples.md)
- API
- [Props](Api/Props.md)
-228
View File
@@ -1,228 +0,0 @@
@import url('https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600');
body {
letter-spacing: 0;
color: #34495e;
font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
font-size: 15px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #34495e;
background-color: #fff;
margin: 0;
}
/* LANGS.md index page */
.book-langs-index {
font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
}
.book-langs-index .inner .languages {
padding: 20px 0px;
}
.book-langs-index .inner .languages li {
float: none;
}
li a {
color: #42b983;
font-weight: 600;
}
/* set correct fonts on sidebar and main page */
.book .book-body .page-wrapper .page-inner section.normal, .book-summary { font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif; }
/* sidebar */
.book-summary ul.summary li a,
.book-summary ul.summary li span {
color: #7f8c8d;
font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
}
.book .book-summary ul.summary li span {
opacity: 0.6;
cursor: not-allowed;
}
.book-summary ul.summary li.active>a {
color: #42b983;
font-weight: 600;
}
#book-search-input { background-color: #fafafa; }
.book-summary { background-color: #fff; }
/* markdown content found on pages */
.markdown-section h1,
.markdown-section h2,
.markdown-section h3,
.markdown-section h4,
.markdown-section strong {
font-weight: 600;
color: #2c3e50;
}
.markdown-section a {
color: #42b983;
font-weight: 600;
}
.markdown-section p,
.markdown-section ul,
.markdown-section ol {
word-spacing: 0.05em;
}
.markdown-section em {
color: #7f8c8d;
}
.markdown-section pre {
padding: 1.2em 1.4em;
line-height: 1.5em;
margin: 0;
}
.markdown-section code, .markdown-section pre {
font-family: 'Roboto Mono', Monaco, courier, monospace;
-webkit-font-smoothing: initial;
-moz-osx-font-smoothing: initial;
background-color: #f8f8f8;
}
code span.css,
code span.javascript,
code span.html,
span[class^="hljs-"] {
-webkit-font-smoothing: initial;
-moz-osx-font-smoothing: initial;
}
.markdown-section pre>code {
font-size: 0.8em;
display: block;
}
.markdown-section code:after, .markdown-section code:before {
content: none;
letter-spacing: 0.05em;
}
code, pre {
font-family: 'Roboto Mono', Monaco, courier, monospace;
font-size: 0.8em;
background-color: #f8f8f8;
-webkit-font-smoothing: initial;
-moz-osx-font-smoothing: initial;
}
code {
color: #e96900;
padding: 3px 5px;
margin: 0 2px;
border-radius: 2px;
white-space: nowrap;
}
code .token {
min-height: 1.5em;
-webkit-font-smoothing: initial;
-moz-osx-font-smoothing: initial;
}
pre code { position: relative; }
pre code.lang-html:after,
pre code.lang-js:after,
pre code.lang-bash:after,
pre code.lang-css:after {
position: absolute;
top: 0;
right: 0;
color: #ccc;
text-align: right;
font-size: 0.75em;
padding: 5px 10px 0;
line-height: 15px;
height: 15px;
font-weight: 600;
}
pre code.lang-html:after {
content: 'HTML';
}
pre code.lang-js:after {
content: 'JS';
}
pre code.lang-bash:after {
content: 'Shell';
}
pre code.lang-css:after {
content: 'CSS';
}
.content img {
max-width: 100%;
}
.content span.light {
color: #7f8c8d;
}
.content span.info {
font-size: 0.85em;
display: inline-block;
vertical-align: middle;
width: 280px;
margin-left: 20px;
}
.markdown-section h1 {
margin: 0 0 1em;
}
.markdown-section h2 {
margin: 45px 0 0.8em;
padding-bottom: 0.7em;
border-bottom: 1px solid #ddd;
}
.markdown-section h3 {
margin: 52px 0 1.2em;
}
.markdown-section figure,
.markdown-section p,
.markdown-section ul,
.markdown-section ol {
margin: 1.2em 0;
}
.markdown-section p,
.markdown-section ul,
.markdown-section ol {
line-height: 1.6em;
}
.markdown-section ul,
.markdown-section ol {
padding-left: 1.5em;
}
.markdown-section a {
color: #42b983;
font-weight: 600;
}
.markdown-section blockquote {
margin: 2em 0;
padding-left: 20px;
border-left: 4px solid #42b983;
}
.markdown-section blockquote p {
font-weight: 600;
margin-left: 0;
}
.markdown-section iframe {
margin: 1em 0;
}
/* these aren't in gitbook at the moment, but leaving them in for future reference */
img {
border: none;
}
.highlight {
overflow-x: auto;
position: relative;
padding: 0;
background-color: #f8f8f8;
padding: 0.8em 0.8em 0.4em;
line-height: 1.1em;
border-radius: 2px;
}
.highlight table,
.highlight tr,
.highlight td {
width: 100%;
border-collapse: collapse;
padding: 0;
margin: 0;
}
.highlight .gutter {
width: 1.5em;
}
+72
View File
@@ -0,0 +1,72 @@
## Loading Options with AJAX
The `search` event provides a hook to load options from a parent component
when the search text is updated. It is emitted with two parameters:
**Search Event Parameters**
- `search {String}` The current search string
- `loading {Function}` Accepts a boolean parameter to toggle the loading state
```html
<v-select @search="fetchOptions" />
```
```js
/**
* Triggered when the search text changes.
*
* @param search {String} Current search text
* @param loading {Function} Toggle loading class
*/
fetchOptions (search, loading) {
// ... do some asynchronous stuff!
},
```
The `loading` function accepts a boolean parameter that will be assigned
to the vue-select internal `loading` property. Call `loading(true)` to set the
`loading` property to `true` - toggling the loading spinner. After your
asynchronous operation completes, call `loading(false)` to toggle it off.
## Disabling Filtering
When loading server side options, it can be useful to disable the
client side filtering. Use the `filterable` prop to disable filtering.
```js
/**
* When true, existing options will be filtered
* by the search text. Should not be used in
* conjunction with taggable.
*
* @type {Boolean}
*/
filterable: {
type: Boolean,
default: true
},
```
## Loading Spinner
Vue Select includes a default loading spinner that appears when the loading class is present. The
`spinner` slot allows you to implement your own spinner.
```html
<div class="spinner" v-show="spinner">Loading...</div>
```
## Library Agnostic
Since Vue.js does not ship with ajax functionality as part of the core library, it's up to you to
process the ajax requests in your parent component.
I recommend using [axios](https://github.com/axios/axios) for creating your applications HTTP layer,
or [`fetch()`](https://github.com/github/fetch) for simple requests.
## Example
The [codepen example](https://codepen.io/sagalbot/pen/POMeOX) wraps up all the above concepts and
searches GitHub repositories. It also uses scoped slots to add some custom templating.
<CodePen url="POMeOX" height="400"/>
+60
View File
@@ -0,0 +1,60 @@
## Yarn / NPM
Install with yarn:
```bash
yarn add vue-select
# or, using NPM
npm install vue-select
```
Then, import and register the component:
```js
import Vue from 'vue'
import vSelect from 'vue-select'
Vue.component('v-select', vSelect)
```
The component itself does not include any CSS. You'll need to include it separately:
```js
import 'vue-select/dist/vue-select.css';
```
Alternatively, you can import the scss for complete control of the component styles:
```scss
@import "vue-select/src/scss/vue-select.scss";
```
## In the Browser
vue-select ships as an UMD module that is accessible in the browser. When loaded
in the browser, you can access the component through the `VueSelect.VueSelect`
global variable. You'll need to load Vue.js, vue-select JS & vue-select CSS.
```html
<!-- include VueJS first -->
<script src="https://unpkg.com/vue@latest"></script>
<!-- use the latest vue-select release -->
<script src="https://unpkg.com/vue-select@latest"></script>
<link rel="stylesheet" href="https://unpkg.com/vue-select@latest/dist/vue-select.css">
<!-- or point to a specific vue-select release -->
<script src="https://unpkg.com/vue-select@2.6.0"></script>
<link rel="stylesheet" href="https://unpkg.com/vue-select@2.6.0/dist/vue-select.css">
```
Then register the component in your javascript:
```js
Vue.component('v-select', VueSelect.VueSelect);
```
<CodePen url="dJjzeP" />
## Vue Compatibility
If you're on Vue `1.x`, use vue-select `1.x`. The `1.x` branch has not received updates
since the 2.0 release.
@@ -1,4 +1,4 @@
### RTL
## RTL
vue-select supports RTL using the standard HTML API using the `dir` attribute.
@@ -8,11 +8,11 @@ vue-select supports RTL using the standard HTML API using the `dir` attribute.
The `dir` attribute accepts the same values as the [HTML spec](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir): `rtl`,`ltr`, and `auto`.
### Component Text
## Component Text
All of the text within the component has been wrapped within [slots](https://vuejs.org/v2/guide/components.html#Content-Distribution-with-Slots) and can be replaced in your app.
##### Loading Spinner
### Loading Spinner
*Slot Definition:*
```html
<slot name="spinner">
@@ -26,7 +26,7 @@ All of the text within the component has been wrapped within [slots](https://vue
</v-select>
```
##### No Options Text
### No Options Text
*Slot Definition:*
```html
<slot name="no-options">Sorry, no matching options.</slot>
@@ -38,6 +38,4 @@ All of the text within the component has been wrapped within [slots](https://vue
</v-select>
```
For a full list of component slots, view the [slots API docs](Api/Slots.md).
[](codepen://sagalbot/oZmLVN?height=250)
For a full list of component slots, view the [slots API docs](../api/slots.md).
+54
View File
@@ -0,0 +1,54 @@
## Options Prop
`vue-select` accepts arrays of primitive values or objects to use as options through the `options` prop:
```html
<!-- array of strings or numbers -->
<v-select :options="['Canada', 'United States']"></v-select>
```
<v-select :options="['Canada', 'United States']"></v-select>
```html
<!-- or, an array of objects -->
<v-select :options="[{label: 'Canada', code: 'ca'}]"></v-select>
```
<v-select :options="[{label: 'Canada', code: 'ca'}]"></v-select>
## Option Labels
#### Options as Primitives (strings, numbers, boolean)
When `options` contains strings or numbers, they'll be used as the label for the option within the
component. No further configuration is necessary.
#### Options as Objects
When `options` is an array of objects, the component must generate a label to be shown as the
options text. By default, `vue-select` will attempt to render `option.label` as the option label.
You might not have a `label` key in your objects, so you can set your own label to match your
source data using the `label {String}` prop.
For example, consider an object with `countryCode` and `countryName` properties:
```json
{
countryCode: "CA",
countryName: "Canada"
}
```
If you wanted to display `Canada` in the dropdown, you'd use the `countryName` key:
```html
<v-select label="countryName" :options="countries"></v-select>
```
<country-select />
## Null / Empty Options
`vue-select` requires the `options` prop to be an `array`. If you are using Vue in development
mode, you will get warnings attempting to pass anything other than an `array` to the `options` prop.
If you need a `null`/`empty` value, use an empty array `[]`.
@@ -14,4 +14,4 @@ vue-select provides the scoped `option` slot in order to create custom dropdown
Using the `option` slot with `slot-scope="option"` gives the
provides the current option variable to the template.
[](codepen://sagalbot/NXBwYG?height=500)
<CodePen url="NXBwYG" height="500"/>
+176
View File
@@ -0,0 +1,176 @@
## Getting and Setting
### `v-model`
The most common use case for vue-select is to have the chosen value synced with a parent component. vue-select
takes advantage of the `v-model` syntax to sync values with a parent. The `v-model` syntax works with
primitives and objects.
```html
<v-select v-model="selected" />
```
Note that when using the `multiple` prop, the `v-model` value will always be an array.
### Props and Events
Sometimes `v-model` might not fit your use case. For example, when working with [Vuex](https://vuex.vuejs.org),
you'll need to trigger a mutation rather than mutating a value directly. In that case, maybe you need
to bind a pre-selected value, and trigger a mutation when it changes.
vue-select exposes the `value` prop and an `input` event to enable this. This combo of props and
events is also how Vue wires up the `v-model` syntax internally.
#### Prop: `value`
The `value` prop lets vue-select know what value is currently selected. It will accept strings,
numbers or objects. If you're using a `multiple` v-select, you'll want to pass an array.
```html
<v-select :value="selected" />
```
::: tip 🤓
Anytime you bind the `value` prop directly, you're responsible for updating the bound variable
in your code using the `@input` event.
:::
#### Event: `input`
The `input` event is triggered anytime the value state changes, and is emitted with the `value`
state as it's only parameter.
#### Vuex Support
The `value` prop and `emit` event are very useful when using a state management tool, like Vuex.
You can bind the selected value with `:value="$store.myValue"`, and use the `input` event to
trigger a mutation, or dispatch an action or anything else you might need to do when the selection
changes.
```html
<v-select :value="$store.myValue" @input="setSelected" />
```
```js
methods: {
setSelected(value) {
// trigger a mutation, or dispatch an action
}
}
```
## Transforming Selections
When the `options` array contains objects, vue-select returns the whole object as dropdown value
upon selection. This approach makes no assumptions about the data you need, and provides a lot of
flexibility. However, there will be situations where maybe you just need to return a single key
from an object.
### Returning a single key with `reduce`
If you need to return a single key, or transform the selection before it is synced, vue-select
provides a `reduce` callback that allows you to transform a selected option before it is passed to
the `@input` event. Consider this data structure:
```js
let options = [{code: 'CA', country: 'Canada'}];
```
If we want to display the `country`, but return the `code` to `v-model`, we can use the `reduce`
prop to receive only the data that's required.
```html
<v-select :options="options" :reduce="country => country.code" label="country" />
```
### Deep Nested Values
The `reduce` property also works well when you have a deeply nested value:
```
{
country: 'canada',
meta: {
code: 'ca'
provinces: [...],
}
}
```
```html
<v-select :options="options" :reduce="country => country.meta.code" label="country" />
```
<reducer-nested-value />
## Single/Multiple Selection
By default, vue-select supports choosing a single value. If you need multiple values, use the
`multiple` boolean prop, much the same way you would on an HTML `<select>` element. When `multiple`
is true, `v-model` and `value` must be an array.
```html
<v-select multiple v-model="selected" :options="['Canada','United States']" />
```
<v-select multiple :options="['Canada','United States']" />
## Tagging
To allow input that's not present within the options, set the `taggable` prop to true.
```html
<v-select taggable multiple />
```
<v-select taggable multiple />
If you want added tags to be pushed to the options array, set `push-tags` to true.
```html
<v-select taggable multiple />
```
<v-select taggable multiple push-tags />
### Using `taggable` & `reduce` together
When combining `taggable` with `reduce`, you must define the `createOption` prop. The
`createOption` function is responsible for defining the structure of the objects that Vue Select
will create for you when adding a tag. It should return a value that has the same properties as the
rest of your `options`.
If you don't define `createOption`, Vue Select will construct a simple object following this structure:
`{[this.label]: searchText}`. If you're using `reduce`, this is probably not what your options look
like, which is why you'll need to set the function yourself.
**Example**
We have a taggable select for adding books to a collection. We're just concerned about getting the
book title added, and our server side code will add the author details in a background process. The
user has already selected a book.
```js
const options = [
{
title: "HTML5",
author: {
firstName: "Remy",
lastName: "Sharp"
}
}
];
```
```html
<v-select
taggable
multiple
label="title"
:options="options"
:create-option="book => ({ title: book, author: { firstName: '', lastName: '' } })"
:reduce="book => `${book.author.firstName} ${book.author.lastName}`"
/>
```
@@ -13,4 +13,4 @@ action, or trigger a mutation.
></v-select>
```
[](codepen://sagalbot/aJQJyp?height=500)
<CodePen url="aJQJyp" height="350"/>
@@ -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
-17
View File
@@ -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;
-3
View File
@@ -1,3 +0,0 @@
@import '~normalize.css';
@import 'demo';
@import 'cyan_theme';
-117
View File
@@ -1,117 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Vue Select | VueJS Select2 Component</title>
<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 href="https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/font/octicons.min.css" rel="stylesheet" type="text/css">
<meta name="title" content="Vue Select | VueJS Select2/Chosen Component">
<meta name="description"
content="A well-tested, native Vue.js component that provides similar functionality to Select2/Chosen without the overhead of jQuery.">
<link rel="icon" href="static/vue-logo.png" type="image/png">
<meta property="og:image" content="static/vue-logo.png">
<meta property="twitter:image" content="static/vue-logo.png">
<meta name="description"
content="A native Vue.js component that provides similar functionality to Select2/Chosen without the overhead of jQuery.">
<meta property="og:description"
content="A native Vue.js component that provides similar functionality to Select2/Chosen without the overhead of jQuery.">
<meta property="twitter:description"
content="A native Vue.js component that provides similar functionality to Select2/Chosen without the overhead of jQuery.">
<meta property="twitter:title" content="Vue Select | VueJS Select2/Chosen Component">
<meta property="og:title" content="Vue Select | VueJS Select2/Chosen Component">
<meta property="og:site_name" content="Vue Select | VueJS Select2/Chosen Component">
<meta property="og:url" content="http://sagalbot.github.io/vue-select/">
</head>
<body>
<div id="app">
<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>
<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>
<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>
<a class="btn btn-primary btn-outline btn-lg" href="docs/">
<span class="octicon octicon-book"></span> Read the Docs
</a>
</div>
</section>
</div>
<script>
(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
i[r] = i[r] || function () {
(i[r].q = i[r].q || []).push(arguments)
}, i[r].l = 1 * new Date();
a = s.createElement(o),
m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m)
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
ga('create', 'UA-12818324-8', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>
-49
View File
@@ -1,49 +0,0 @@
import Vue from 'vue'
import vSelect from 'vue-select'
import './assets/scss/home.scss'
Vue.component('v-select', vSelect);
/* eslint-disable no-new */
new Vue({
el: '#app',
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) {
window.location = option.url;
}
}
});
+6
View File
@@ -0,0 +1,6 @@
---
sidebar: false
editLink: false
layout: Sandbox
---
+1
View File
@@ -0,0 +1 @@
<CodePen url="zZQJKW" height="60"/>
+15
View File
@@ -0,0 +1,15 @@
# Global settings applied to the whole site.
#
# “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"
+81 -66
View File
@@ -7,17 +7,12 @@
"main": "dist/vue-select.js",
"license": "MIT",
"scripts": {
"start": "npm run dev",
"dev": "node build/dev-server.js",
"dev:homepage": "node build/dev-server.js --docs",
"dev:docs": "gitbook serve",
"build": "node build/build.js",
"build:homepage": "node build/build.js --homepage",
"build:docs": "yarn run build:homepage && node build/build-docs.js",
"deploy": "yarn run build:homepage && node build/build-docs.js && gh-pages -d site",
"lint": "eslint --ext .js,.vue src test/unit/specs",
"test": "karma start test/unit/karma.conf.js --single-run",
"test-watch": "karma start test/unit/karma.conf.js --single-run=false --auto-watch=true"
"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": {
"type": "git",
@@ -26,63 +21,83 @@
"peerDependencies": {
"vue": "2.x"
},
"resolutions": {
"ajv": "6.8.1"
},
"devDependencies": {
"babel-core": "^6.0.0",
"babel-loader": "^6.0.0",
"babel-plugin-transform-runtime": "^6.0.0",
"babel-preset-es2015": "^6.0.0",
"babel-preset-stage-2": "^6.0.0",
"codeclimate-test-reporter": "^0.3.1",
"connect-history-api-fallback": "^1.1.0",
"css-loader": "^0.23.0",
"eventsource-polyfill": "^0.9.6",
"express": "^4.13.3",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.8.4",
"function-bind": "^1.0.2",
"@babel/core": "^7.4.0",
"@babel/plugin-transform-runtime": "^7.4.0",
"@babel/preset-env": "^7.4.2",
"@babel/runtime": "^7.4.2",
"@vue/test-utils": "^1.0.0-beta.29",
"autoprefixer": "^9.4.7",
"babel-core": "^7.0.0-bridge.0",
"babel-loader": "^8.0.5",
"chokidar": "^2.1.5",
"coveralls": "^3.0.2",
"cross-env": "^5.2.0",
"css-loader": "^2.1.0",
"cssnano": "^4.1.10",
"fuse.js": "^3.2.0",
"gh-pages": "^0.11.0",
"gitbook-plugin-codepen": "^0.1.2",
"gitbook-plugin-edit-link": "^2.0.2",
"gitbook-plugin-github": "^3.0.0",
"highlight.js": "^9.9.0",
"html-loader": "^0.4.4",
"html-webpack-plugin": "^2.8.1",
"http-proxy-middleware": "^0.15.2",
"inject-loader": "^2.0.1",
"isparta-loader": "^2.0.0",
"jasmine": "^2.4.1",
"json-loader": "^0.5.4",
"karma": "^0.13.15",
"karma-coverage": "^1.0.0",
"karma-jasmine": "^1.0.2",
"karma-phantomjs-launcher": "^1.0.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-spec-reporter": "0.0.26",
"karma-webpack": "^1.7.0",
"lolex": "^1.4.0",
"markdown-loader": "^0.1.7",
"node-sass": "^3.7.0",
"normalize.css": "^7.0.0",
"ora": "^0.2.0",
"phantomjs-prebuilt": "^2.1.3",
"prismjs": "^1.5.0",
"sass-loader": "^3.2.0",
"shelljs": "^0.7.0",
"url-loader": "^0.5.7",
"vue": "^2.1.8",
"vue-hot-reload-api": "^2.0.7",
"vue-html-loader": "^1.2.3",
"vue-loader": "^10.0.2",
"vue-markdown-loader": "^0.6.1",
"vue-resource": "^1.0.3",
"vue-select": "*",
"vue-style-loader": "^1.0.0",
"vue-template-compiler": "^2.1.8",
"vuex": "^2.1.1",
"webpack": "^1.12.2",
"webpack-dev-middleware": "^1.4.0",
"webpack-hot-middleware": "^2.6.0",
"webpack-merge": "^0.13.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",
"terser-webpack-plugin": "^1.2.3",
"url-loader": "^1.1.2",
"vue": "^2.6.10",
"vue-html-loader": "^1.2.4",
"vue-jest": "^3.0.4",
"vue-loader": "^15.7.0",
"vue-server-renderer": "^2.6.10",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.10",
"vuepress": "^0.14.10",
"vuex": "^3.1.0",
"webpack": "^4.29.6",
"webpack-cli": "^3.3.0",
"webpack-dev-server": "^3.2.1",
"webpack-merge": "^4.2.1"
},
"jest": {
"moduleFileExtensions": [
"js",
"jsx",
"json",
"vue"
],
"transform": {
"^.+\\.vue$": "vue-jest",
".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub",
"^.+\\.jsx?$": "babel-jest"
},
"moduleNameMapper": {
"^@/(.*)$": "<rootDir>/src/$1"
},
"snapshotSerializers": [
"jest-serializer-vue"
],
"testMatch": [
"**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"
],
"testURL": "http://localhost/",
"collectCoverage": true,
"collectCoverageFrom": [
"src/mixins/*.js",
"!src/mixins/index.js",
"src/components/Select.vue",
"!**/node_modules/**"
],
"coverageReporters": [
"html",
"text-summary"
]
}
}
+8
View File
@@ -0,0 +1,8 @@
module.exports = {
plugins: [
require('autoprefixer'),
require('cssnano')({
preset: 'default',
}),
],
};
+5
View File
@@ -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>
+5
View File
@@ -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>
+364 -602
View File
File diff suppressed because it is too large Load Diff
+55 -68
View File
@@ -1,72 +1,59 @@
module.exports = {
props: {
/**
* Toggles the adding of a 'loading' class to the main
* .v-select wrapper. Useful to control UI state when
* results are being processed through AJAX.
*/
loading: {
type: Boolean,
default: false
},
/**
* Accept a callback function that will be
* run when the search text changes.
*
* loading() accepts a boolean value, and can
* be used to toggle a loading class from
* the onSearch callback.
*
* @param {search} String Current search text
* @param {loading} Function(bool) Toggle loading class
*/
onSearch: {
type: Function,
default: function(search, loading){}
}
},
data() {
return {
mutableLoading: false
}
},
watch: {
/**
* If a callback & search text has been provided,
* invoke the onSearch callback.
*/
search() {
if (this.search.length > 0) {
this.onSearch(this.search, this.toggleLoading)
this.$emit('search', this.search, this.toggleLoading)
}
},
export default {
props: {
/**
* Sync the loading prop with the internal
* mutable loading value.
* Toggles the adding of a 'loading' class to the main
* .v-select wrapper. Useful to control UI state when
* results are being processed through AJAX.
*/
loading: {
type: Boolean,
default: false,
},
},
data () {
return {
mutableLoading: false,
};
},
watch: {
/**
* 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
*/
search () {
this.$emit('search', this.search, this.toggleLoading);
},
/**
* Sync the loading prop with the internal
* mutable loading value.
* @param val
*/
loading(val) {
this.mutableLoading = val
}
},
loading (val) {
this.mutableLoading = val;
},
},
methods: {
/**
* Toggle this.loading. Optionally pass a boolean
* value. If no value is provided, this.loading
* will be set to the opposite of it's current value.
* @param toggle Boolean
* @returns {*}
*/
toggleLoading(toggle = null) {
if (toggle == null) {
return this.mutableLoading = !this.mutableLoading
}
return this.mutableLoading = toggle
}
}
}
methods: {
/**
* Toggle this.loading. Optionally pass a boolean
* value. If no value is provided, this.loading
* will be set to the opposite of it's current value.
* @param toggle Boolean
* @returns {*}
*/
toggleLoading (toggle = null) {
if (toggle == null) {
return (this.mutableLoading = !this.mutableLoading);
}
return (this.mutableLoading = toggle);
},
},
};
+28 -22
View File
@@ -1,9 +1,7 @@
// flow
module.exports = {
export default {
watch: {
typeAheadPointer() {
this.maybeAdjustScroll()
this.maybeAdjustScroll();
}
},
@@ -15,13 +13,13 @@ module.exports = {
* @returns {*}
*/
maybeAdjustScroll() {
let pixelsToPointerTop = this.pixelsToPointerTop()
let pixelsToPointerBottom = this.pixelsToPointerBottom()
let pixelsToPointerTop = this.pixelsToPointerTop();
let pixelsToPointerBottom = this.pixelsToPointerBottom();
if ( pixelsToPointerTop <= this.viewport().top) {
return this.scrollTo( pixelsToPointerTop )
if (pixelsToPointerTop <= this.viewport().top) {
return this.scrollTo(pixelsToPointerTop);
} else if (pixelsToPointerBottom >= this.viewport().bottom) {
return this.scrollTo( this.viewport().top + this.pointerHeight() )
return this.scrollTo(this.viewport().top + this.pointerHeight());
}
},
@@ -31,13 +29,14 @@ module.exports = {
* @returns {number}
*/
pixelsToPointerTop() {
let pixelsToPointerTop = 0
if( this.$refs.dropdownMenu ) {
let pixelsToPointerTop = 0;
if (this.$refs.dropdownMenu) {
for (let i = 0; i < this.typeAheadPointer; i++) {
pixelsToPointerTop += this.$refs.dropdownMenu.children[i].offsetHeight
pixelsToPointerTop += this.$refs.dropdownMenu.children[i]
.offsetHeight;
}
}
return pixelsToPointerTop
return pixelsToPointerTop;
},
/**
@@ -46,7 +45,7 @@ module.exports = {
* @returns {*}
*/
pixelsToPointerBottom() {
return this.pixelsToPointerTop() + this.pointerHeight()
return this.pixelsToPointerTop() + this.pointerHeight();
},
/**
@@ -54,8 +53,10 @@ module.exports = {
* @returns {number}
*/
pointerHeight() {
let element = this.$refs.dropdownMenu ? this.$refs.dropdownMenu.children[this.typeAheadPointer] : false
return element ? element.offsetHeight : 0
let element = this.$refs.dropdownMenu
? this.$refs.dropdownMenu.children[this.typeAheadPointer]
: false;
return element ? element.offsetHeight : 0;
},
/**
@@ -64,9 +65,12 @@ module.exports = {
*/
viewport() {
return {
top: this.$refs.dropdownMenu ? this.$refs.dropdownMenu.scrollTop: 0,
bottom: this.$refs.dropdownMenu ? this.$refs.dropdownMenu.offsetHeight + this.$refs.dropdownMenu.scrollTop : 0
}
top: this.$refs.dropdownMenu ? this.$refs.dropdownMenu.scrollTop : 0,
bottom: this.$refs.dropdownMenu
? this.$refs.dropdownMenu.offsetHeight +
this.$refs.dropdownMenu.scrollTop
: 0
};
},
/**
@@ -75,7 +79,9 @@ module.exports = {
* @returns {*}
*/
scrollTo(position) {
return this.$refs.dropdownMenu ? this.$refs.dropdownMenu.scrollTop = position : null
},
return this.$refs.dropdownMenu
? (this.$refs.dropdownMenu.scrollTop = position)
: null;
}
}
}
};
+2 -2
View File
@@ -1,4 +1,4 @@
module.exports = {
export default {
data() {
return {
typeAheadPointer: -1
@@ -57,4 +57,4 @@ module.exports = {
}
},
}
}
}
+31
View File
@@ -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;
}
+9
View File
@@ -0,0 +1,9 @@
.v-select {
position: relative;
font-family: inherit;
}
.v-select,
.v-select * {
box-sizing: border-box;
}
+53
View File
@@ -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;
}
}
+44
View File
@@ -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;
+10
View File
@@ -0,0 +1,10 @@
/* Clear Button */
.vs__clear {
fill: $vs-controls-color;
padding: 0;
border: 0;
background-color: transparent;
cursor: pointer;
margin-right: 8px;
}
+37
View File
@@ -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;
}
+18
View File
@@ -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;
}
+54
View File
@@ -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;
}
+29
View File
@@ -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;
}
+54
View File
@@ -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: 1;
&: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;
}
}
+40
View File
@@ -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;
}
}
+26
View File
@@ -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;
}
+13
View File
@@ -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";
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

-12
View File
@@ -1,12 +0,0 @@
// Polyfill fn.bind() for PhantomJS
/* eslint-disable no-extend-native */
Function.prototype.bind = require('function-bind')
// require all test files (files that ends with .spec.js)
var testsContext = require.context('./specs', true, /\.spec$/)
testsContext.keys().forEach(testsContext)
// require all src files except main.js for coverage.
// you can also change this to match only the subset of files that
// you want coverage for.
var srcContext = require('../../src/components/Select.vue')
-78
View File
@@ -1,78 +0,0 @@
// This is a karma config file. For more details see
// http://karma-runner.github.io/0.13/config/configuration-file.html
// we are also using it with karma-webpack
// https://github.com/webpack/karma-webpack
var path = require('path')
var merge = require('webpack-merge')
var baseConfig = require('../../build/webpack.base.conf')
var utils = require('../../build/utils')
var webpack = require('webpack')
var projectRoot = path.resolve(__dirname, '../../')
var webpackConfig = merge(baseConfig, {
// use inline sourcemap for karma-sourcemap-loader
module: {
loaders: utils.styleLoaders()
},
devtool: '#inline-source-map',
vue: {
loaders: {
js: 'isparta'
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../../config/test.env')
})
]
})
// no need for app entry during tests
delete webpackConfig.entry
// make sure isparta loader is applied before eslint
webpackConfig.module.preLoaders = webpackConfig.module.preLoaders || []
webpackConfig.module.preLoaders.unshift({
test: /\.js$/,
loader: 'isparta',
include: path.resolve(projectRoot, 'src')
})
// only apply babel for test files when using isparta
webpackConfig.module.loaders.some(function (loader, i) {
if (loader.loader === 'babel') {
loader.include = path.resolve(projectRoot, 'test/unit')
return true
}
})
module.exports = function (config) {
config.set({
// to run in additional browsers:
// 1. install corresponding karma launcher
// http://karma-runner.github.io/0.13/config/browsers.html
// 2. add it to the `browsers` array below.
browsers: ['PhantomJS'],
frameworks: ['jasmine'],
reporters: ['spec', 'coverage'],
files: ['./index.js'],
preprocessors: {
'./index.js': ['webpack', 'sourcemap']
},
webpack: webpackConfig,
webpackMiddleware: {
noInfo: true
},
specReporter: {
suppressSkipped: true
},
coverageReporter: {
dir: './coverage',
reporters: [
{ type: 'lcov', subdir: '.' },
{ type: 'text-summary' }
]
}
})
}

Some files were not shown because too many files have changed in this diff Show More