├── .babelrc
├── .gitignore
├── assets
├── images
│ ├── favicon.ico
│ ├── favicon.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── mstile-150x150.png
│ ├── apple-touch-icon.png
│ ├── android-chrome-192x192.png
│ ├── manifest.json
│ ├── browserconfig.xml
│ ├── safari-pinned-tab.svg
│ └── logo.svg
├── scripts
│ ├── dist
│ │ ├── choices.min.js.map
│ │ └── choices.min.js
│ └── src
│ │ ├── reducers
│ │ ├── groups.js
│ │ ├── index.js
│ │ ├── items.js
│ │ └── choices.js
│ │ ├── actions
│ │ └── index.js
│ │ ├── lib
│ │ ├── polyfills.js
│ │ └── utils.js
│ │ └── store
│ │ └── index.js
├── icons
│ ├── cross.svg
│ └── cross-inverse.svg
└── styles
│ ├── css
│ ├── base.min.css
│ ├── base.css
│ ├── choices.min.css
│ └── choices.css
│ └── scss
│ ├── base.scss
│ └── choices.scss
├── .editorconfig
├── .travis.yml
├── server.js
├── .eslintrc
├── bower.json
├── LICENSE
├── webpack.config.dev.js
├── tests
├── karma.config.js
└── spec
│ └── choices_spec.js
├── webpack.config.prod.js
├── package.json
├── index.html
└── README.md
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | .DS_Store
4 |
5 | # Test
6 | tests/reports
7 | tests/results
--------------------------------------------------------------------------------
/assets/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rstacruz/Choices/master/assets/images/favicon.ico
--------------------------------------------------------------------------------
/assets/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rstacruz/Choices/master/assets/images/favicon.png
--------------------------------------------------------------------------------
/assets/images/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rstacruz/Choices/master/assets/images/favicon-16x16.png
--------------------------------------------------------------------------------
/assets/images/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rstacruz/Choices/master/assets/images/favicon-32x32.png
--------------------------------------------------------------------------------
/assets/images/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rstacruz/Choices/master/assets/images/mstile-150x150.png
--------------------------------------------------------------------------------
/assets/images/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rstacruz/Choices/master/assets/images/apple-touch-icon.png
--------------------------------------------------------------------------------
/assets/scripts/dist/choices.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"choices.min.js","sources":[],"mappings":";;","sourceRoot":""}
--------------------------------------------------------------------------------
/assets/images/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rstacruz/Choices/master/assets/images/android-chrome-192x192.png
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 |
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.10"
4 | before_install:
5 | - '[ "${TRAVIS_NODE_VERSION}" != "0.8" ] || npm install -g npm@1.4.28'
6 | - npm install -g npm@latest
7 | install:
8 | - npm install
9 | script: npm run js:test
--------------------------------------------------------------------------------
/assets/icons/cross.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons/cross-inverse.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Choices.js",
3 | "icons": [
4 | {
5 | "src": "\/assets\/images\/android-chrome-192x192.png",
6 | "sizes": "192x192",
7 | "type": "image\/png"
8 | }
9 | ],
10 | "theme_color": "#ffffff",
11 | "display": "standalone"
12 | }
13 |
--------------------------------------------------------------------------------
/assets/images/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #ffffff
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/assets/scripts/src/reducers/groups.js:
--------------------------------------------------------------------------------
1 | const groups = (state = [], action) => {
2 | switch (action.type) {
3 | case 'ADD_GROUP': {
4 | return [...state, {
5 | id: action.id,
6 | value: action.value,
7 | active: action.active,
8 | disabled: action.disabled,
9 | }];
10 | }
11 |
12 | case 'CLEAR_CHOICES': {
13 | return state.groups = [];
14 | }
15 |
16 | default: {
17 | return state;
18 | }
19 | }
20 | };
21 |
22 | export default groups;
23 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var WebpackDevServer = require('webpack-dev-server');
3 | var config = require('./webpack.config.dev');
4 | var opn = require('opn');
5 |
6 | new WebpackDevServer(webpack(config), {
7 | publicPath: config.output.publicPath,
8 | historyApiFallback: true,
9 | quiet: true, // lets WebpackDashboard do its thing
10 | }).listen(3000, 'localhost', function(err, result) {
11 | if (err) console.log(err);
12 | opn('http://localhost:3000');
13 | console.log('Listening at localhost:3000');
14 | });
--------------------------------------------------------------------------------
/assets/scripts/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import items from './items';
3 | import groups from './groups';
4 | import choices from './choices';
5 |
6 | const appReducer = combineReducers({
7 | items,
8 | groups,
9 | choices,
10 | });
11 |
12 | const rootReducer = (passedState, action) => {
13 | let state = passedState;
14 | // If we are clearing all items, groups and options we reassign
15 | // state and then pass that state to our proper reducer. This isn't
16 | // mutating our actual state
17 | // See: http://stackoverflow.com/a/35641992
18 | if (action.type === 'CLEAR_ALL') {
19 | state = undefined;
20 | }
21 |
22 | return appReducer(state, action);
23 | };
24 |
25 | export default rootReducer;
26 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "ecmaFeatures": {
3 | "modules": true
4 | },
5 | "env": {
6 | "browser": true,
7 | "node": true
8 | },
9 | "parser": "babel-eslint",
10 | "rules": {
11 | "quotes": [2, "single"],
12 | "strict": [2, "never"],
13 | "indent": ["error", 2, {"SwitchCase": 1}],
14 | "eol-last": "off",
15 | "arrow-body-style": "off",
16 | "no-underscore-dangle": "off",
17 | "no-new": 0,
18 | "max-len": "off",
19 | "no-console": "off",
20 | "consistent-return": "off",
21 | "no-param-reassign": ["error", { "props": false }],
22 | "no-unused-vars": ["error", { "args": "none" }],
23 | "no-lonely-if": "off",
24 | "class-methods-use-this": "error",
25 | "react/require-extension": "off",
26 | },
27 | }
28 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "choices.js",
3 | "version": "2.5.1",
4 | "description": "A vanilla JS customisable text input/select box plugin",
5 | "main": [
6 | "./assets/scripts/dist/choices.js",
7 | "./assets/styles/css/choices.css"
8 | ],
9 | "authors": [
10 | "Josh Johnson"
11 | ],
12 | "license": "MIT",
13 | "homepage": "https://joshuajohnson.co.uk/Choices/",
14 | "ignore": [
15 | "**/.*",
16 | "node_modules",
17 | "bower_components",
18 | "test",
19 | "tests",
20 | "assets/images",
21 | "webpack.config.dev.js",
22 | "webpack.config.prod.js",
23 | "server.js"
24 | ],
25 | "keywords": [
26 | "customisable",
27 | "input",
28 | "select",
29 | "vanilla",
30 | "plugin",
31 | "js"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Josh Johnson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var Dashboard = require('webpack-dashboard');
4 | var DashboardPlugin = require('webpack-dashboard/plugin');
5 | var dashboard = new Dashboard();
6 |
7 | module.exports = {
8 | devtool: 'eval',
9 | entry: [
10 | 'webpack-dev-server/client?http://localhost:3000',
11 | './assets/scripts/src/choices'
12 | ],
13 | output: {
14 | path: path.join(__dirname, 'dist'),
15 | filename: 'choices.min.js',
16 | publicPath: '/assets/scripts/dist/',
17 | library: 'Choices',
18 | libraryTarget: 'umd',
19 | },
20 | eslint: {
21 | configFile: '.eslintrc'
22 | },
23 | plugins: [
24 | new DashboardPlugin(dashboard.setData),
25 | new webpack.HotModuleReplacementPlugin(),
26 | new webpack.DefinePlugin({
27 | 'process.env': {
28 | 'NODE_ENV': JSON.stringify('development')
29 | }
30 | })
31 | ],
32 | module: {
33 | loaders: [{
34 | test: /\.js$/,
35 | exclude: /(node_modules|bower_components)/,
36 | loaders: ['babel', 'eslint-loader'],
37 | include: path.join(__dirname, 'assets/scripts/src')
38 | }]
39 | }
40 | };
41 |
--------------------------------------------------------------------------------
/tests/karma.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('karma-webpack');
2 |
3 | module.exports = function(config) {
4 | config.set({
5 | frameworks: ['jasmine'],
6 | files: [
7 | '../tests/**/*_spec.js',
8 | ],
9 | plugins: [
10 | webpack,
11 | 'karma-jasmine',
12 | 'karma-phantomjs-launcher',
13 | 'karma-coverage',
14 | 'karma-spec-reporter',
15 | 'karma-htmlfile-reporter',
16 | 'es6-shim'
17 | ],
18 | browsers: ['PhantomJS'],
19 | preprocessors: {
20 | '**/*_spec.js': ['webpack'],
21 | 'src/**/*.js': ['webpack']
22 | },
23 | reporters: ['spec', 'coverage', 'html'],
24 | coverageReporter: {
25 | dir: '../tests/reports/coverage',
26 | reporters: [{
27 | type: 'html',
28 | }]
29 | },
30 | webpack: {
31 | module: {
32 | loaders: [{
33 | test: /\.(js|jsx)$/,
34 | exclude: /(bower_components|node_modules)/,
35 | loader: 'babel-loader'
36 | }],
37 | }
38 | },
39 | colors: true,
40 | webpackMiddleware: {
41 | noInfo: true
42 | },
43 | htmlReporter: {
44 | outputFile: 'results/unit-tests.html'
45 | }
46 | });
47 | };
--------------------------------------------------------------------------------
/assets/scripts/src/reducers/items.js:
--------------------------------------------------------------------------------
1 | const items = (state = [], action) => {
2 | switch (action.type) {
3 | case 'ADD_ITEM': {
4 | // Add object to items array
5 | const newState = [...state, {
6 | id: action.id,
7 | choiceId: action.choiceId,
8 | groupId: action.groupId,
9 | value: action.value,
10 | label: action.label,
11 | active: true,
12 | highlighted: false,
13 | }];
14 |
15 | return newState.map((item) => {
16 | if (item.highlighted) {
17 | item.highlighted = false;
18 | }
19 | return item;
20 | });
21 | }
22 |
23 | case 'REMOVE_ITEM': {
24 | // Set item to inactive
25 | return state.map((item) => {
26 | if (item.id === action.id) {
27 | item.active = false;
28 | }
29 | return item;
30 | });
31 | }
32 |
33 | case 'HIGHLIGHT_ITEM': {
34 | return state.map((item) => {
35 | if (item.id === action.id) {
36 | item.highlighted = action.highlighted;
37 | }
38 | return item;
39 | });
40 | }
41 |
42 | default: {
43 | return state;
44 | }
45 | }
46 | };
47 |
48 | export default items;
49 |
--------------------------------------------------------------------------------
/assets/styles/css/base.min.css:
--------------------------------------------------------------------------------
1 | *{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*,:after,:before{box-sizing:border-box}body,html{position:relative;margin:0;width:100%;height:100%}body{font-family:"Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;font-size:16px;line-height:1.4;color:#fff;background-color:#333;overflow-x:hidden}hr,label{display:block}label{margin-bottom:8px;font-size:14px;font-weight:500;cursor:pointer}p{margin-top:0}hr{margin:36px 0;border:0;border-bottom:1px solid #eaeaea;height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:12px;font-weight:400;line-height:1.2}a,a:focus,a:visited{color:#fff;text-decoration:none;font-weight:600}.form-control{display:block;width:100%;background-color:#f9f9f9;padding:12px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;-webkit-appearance:none;-moz-appearance:none;appearance:none;margin-bottom:24px}.h1,h1{font-size:32px}.h2,h2{font-size:24px}.h3,h3{font-size:20px}.h4,h4{font-size:18px}.h5,h5{font-size:16px}.h6,h6{font-size:14px}.container{display:block;margin:auto;max-width:40em;padding:48px}@media (max-width:620px){.container{padding:0}}.section{background-color:#fff;padding:24px;color:#333}.section a,.section a:focus,.section a:visited{color:#00bcd4}.logo{display:block;margin-bottom:12px}.logo__img{width:100%;height:auto;display:inline-block;max-width:100%;vertical-align:top;padding:6px 0}.visible-ie{display:none}
--------------------------------------------------------------------------------
/assets/scripts/src/actions/index.js:
--------------------------------------------------------------------------------
1 | export const addItem = (value, label, id, choiceId, groupId) => {
2 | return {
3 | type: 'ADD_ITEM',
4 | value,
5 | label,
6 | id,
7 | choiceId,
8 | groupId,
9 | };
10 | };
11 |
12 | export const removeItem = (id, choiceId) => {
13 | return {
14 | type: 'REMOVE_ITEM',
15 | id,
16 | choiceId,
17 | };
18 | };
19 |
20 | export const highlightItem = (id, highlighted) => {
21 | return {
22 | type: 'HIGHLIGHT_ITEM',
23 | id,
24 | highlighted,
25 | };
26 | };
27 |
28 | export const addChoice = (value, label, id, groupId, disabled) => {
29 | return {
30 | type: 'ADD_CHOICE',
31 | value,
32 | label,
33 | id,
34 | groupId,
35 | disabled,
36 | };
37 | };
38 |
39 | export const filterChoices = (results) => {
40 | return {
41 | type: 'FILTER_CHOICES',
42 | results,
43 | };
44 | };
45 |
46 | export const activateChoices = (active = true) => {
47 | return {
48 | type: 'ACTIVATE_CHOICES',
49 | active,
50 | };
51 | };
52 |
53 | export const clearChoices = () => {
54 | return {
55 | type: 'CLEAR_CHOICES',
56 | };
57 | };
58 |
59 | export const addGroup = (value, id, active, disabled) => {
60 | return {
61 | type: 'ADD_GROUP',
62 | value,
63 | id,
64 | active,
65 | disabled,
66 | };
67 | };
68 |
69 | export const clearAll = () => {
70 | return {
71 | type: 'CLEAR_ALL',
72 | };
73 | };
74 |
--------------------------------------------------------------------------------
/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var package = require('./package.json');
3 | var webpack = require('webpack');
4 | var wrapperPlugin = require('wrapper-webpack-plugin');
5 | var banner = `/*! ${ package.name } v${ package.version } | (c) ${ new Date().getFullYear() } ${ package.author } | ${ package.homepage } */ \n`;
6 | var minimize = process.argv.indexOf('--minimize') !== -1;
7 |
8 | var config = {
9 | devtool: 'cheap-module-source-map',
10 | entry: [
11 | './assets/scripts/src/choices'
12 | ],
13 | output: {
14 | path: path.join(__dirname, '/assets/scripts/dist'),
15 | filename: minimize ? 'choices.min.js' : 'choices.js',
16 | publicPath: '/assets/scripts/dist/',
17 | library: 'Choices',
18 | libraryTarget: 'umd',
19 | },
20 | libraryTarget: 'umd',
21 | plugins: [
22 | new webpack.DefinePlugin({
23 | 'process.env': {
24 | // This has effect on the react lib size
25 | 'NODE_ENV': JSON.stringify('production'),
26 | }
27 | }),
28 | new wrapperPlugin({
29 | header: banner,
30 | }),
31 | ],
32 | module: {
33 | loaders: [{
34 | test: /\.js$/,
35 | exclude: /(node_modules|bower_components)/,
36 | loaders: ['babel'],
37 | include: path.join(__dirname, 'assets/scripts/src')
38 | }]
39 | }
40 | };
41 |
42 | if (minimize) {
43 | config.plugins.unshift(new webpack.optimize.UglifyJsPlugin({
44 | sourceMap: false,
45 | mangle: true,
46 | output: {
47 | comments: false
48 | },
49 | compress: {
50 | warnings: false,
51 | screw_ie8: true
52 | }
53 | }));
54 | }
55 |
56 | module.exports = config;
57 |
--------------------------------------------------------------------------------
/assets/images/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 | Created by potrace 1.11, written by Peter Selinger 2001-2013
9 |
10 |
12 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/assets/styles/css/base.css:
--------------------------------------------------------------------------------
1 | /*=============================================
2 | = Generic styling =
3 | =============================================*/
4 | * {
5 | -webkit-font-smoothing: antialiased;
6 | -moz-osx-font-smoothing: grayscale;
7 | }
8 |
9 | *, *:before, *:after {
10 | box-sizing: border-box;
11 | }
12 |
13 | html, body {
14 | position: relative;
15 | margin: 0;
16 | width: 100%;
17 | height: 100%;
18 | }
19 |
20 | body {
21 | font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
22 | font-size: 16px;
23 | line-height: 1.4;
24 | color: #FFFFFF;
25 | background-color: #333;
26 | overflow-x: hidden;
27 | }
28 |
29 | label {
30 | display: block;
31 | margin-bottom: 8px;
32 | font-size: 14px;
33 | font-weight: 500;
34 | cursor: pointer;
35 | }
36 |
37 | p {
38 | margin-top: 0;
39 | }
40 |
41 | hr {
42 | display: block;
43 | margin: 36px 0;
44 | border: 0;
45 | border-bottom: 1px solid #eaeaea;
46 | height: 1px;
47 | }
48 |
49 | h1, h2, h3, h4, h5, h6 {
50 | margin-top: 0;
51 | margin-bottom: 12px;
52 | font-weight: 400;
53 | line-height: 1.2;
54 | }
55 |
56 | a, a:visited, a:focus {
57 | color: #FFFFFF;
58 | text-decoration: none;
59 | font-weight: 600;
60 | }
61 |
62 | .form-control {
63 | display: block;
64 | width: 100%;
65 | background-color: #f9f9f9;
66 | padding: 12px;
67 | border: 1px solid #ddd;
68 | border-radius: 2.5px;
69 | font-size: 14px;
70 | -webkit-appearance: none;
71 | -moz-appearance: none;
72 | appearance: none;
73 | margin-bottom: 24px;
74 | }
75 |
76 | h1, .h1 {
77 | font-size: 32px;
78 | }
79 |
80 | h2, .h2 {
81 | font-size: 24px;
82 | }
83 |
84 | h3, .h3 {
85 | font-size: 20px;
86 | }
87 |
88 | h4, .h4 {
89 | font-size: 18px;
90 | }
91 |
92 | h5, .h5 {
93 | font-size: 16px;
94 | }
95 |
96 | h6, .h6 {
97 | font-size: 14px;
98 | }
99 |
100 | .container {
101 | display: block;
102 | margin: auto;
103 | max-width: 40em;
104 | padding: 48px;
105 | }
106 |
107 | @media (max-width: 620px) {
108 | .container {
109 | padding: 0;
110 | }
111 | }
112 |
113 | .section {
114 | background-color: #FFFFFF;
115 | padding: 24px;
116 | color: #333;
117 | }
118 |
119 | .section a, .section a:visited, .section a:focus {
120 | color: #00bcd4;
121 | }
122 |
123 | .logo {
124 | display: block;
125 | margin-bottom: 12px;
126 | }
127 |
128 | .logo__img {
129 | width: 100%;
130 | height: auto;
131 | display: inline-block;
132 | max-width: 100%;
133 | vertical-align: top;
134 | padding: 6px 0;
135 | }
136 |
137 | .visible-ie {
138 | display: none;
139 | }
140 |
141 | /*===== End of Section comment block ======*/
142 |
--------------------------------------------------------------------------------
/assets/scripts/src/reducers/choices.js:
--------------------------------------------------------------------------------
1 | const choices = (state = [], action) => {
2 | switch (action.type) {
3 | case 'ADD_CHOICE': {
4 | /*
5 | A disabled choice appears in the choice dropdown but cannot be selected
6 | A selected choice has been added to the passed input's value (added as an item)
7 | An active choice appears within the choice dropdown
8 | */
9 | return [...state, {
10 | id: action.id,
11 | groupId: action.groupId,
12 | value: action.value,
13 | label: action.label,
14 | disabled: action.disabled,
15 | selected: false,
16 | active: true,
17 | score: 9999,
18 | }];
19 | }
20 |
21 | case 'ADD_ITEM': {
22 | let newState = state;
23 |
24 | // If all choices need to be activated
25 | if (action.activateOptions) {
26 | newState = state.map((choice) => {
27 | choice.active = action.active;
28 | return choice;
29 | });
30 | }
31 | // When an item is added and it has an associated choice,
32 | // we want to disable it so it can't be chosen again
33 | if (action.choiceId > -1) {
34 | newState = state.map((choice) => {
35 | if (choice.id === parseInt(action.choiceId, 10)) {
36 | choice.selected = true;
37 | }
38 | return choice;
39 | });
40 | }
41 |
42 | return newState;
43 | }
44 |
45 | case 'REMOVE_ITEM': {
46 | // When an item is removed and it has an associated choice,
47 | // we want to re-enable it so it can be chosen again
48 | if (action.choiceId > -1) {
49 | return state.map((choice) => {
50 | if (choice.id === parseInt(action.choiceId, 10)) {
51 | choice.selected = false;
52 | }
53 | return choice;
54 | });
55 | }
56 |
57 | return state;
58 | }
59 |
60 | case 'FILTER_CHOICES': {
61 | const filteredResults = action.results;
62 | const filteredState = state.map((choice) => {
63 | // Set active state based on whether choice is
64 | // within filtered results
65 |
66 | choice.active = filteredResults.some((result) => {
67 | if (result.item.id === choice.id) {
68 | choice.score = result.score;
69 | return true;
70 | }
71 | return false;
72 | });
73 |
74 | return choice;
75 | });
76 |
77 | return filteredState;
78 | }
79 |
80 | case 'ACTIVATE_CHOICES': {
81 | return state.map((choice) => {
82 | choice.active = action.active;
83 | return choice;
84 | });
85 | }
86 |
87 | case 'CLEAR_CHOICES': {
88 | return state.choices = [];
89 | }
90 |
91 | default: {
92 | return state;
93 | }
94 | }
95 | };
96 |
97 | export default choices;
98 |
--------------------------------------------------------------------------------
/assets/styles/scss/base.scss:
--------------------------------------------------------------------------------
1 | $global-guttering: 24px;
2 | $global-font-size-h1: 32px;
3 | $global-font-size-h2: 24px;
4 | $global-font-size-h3: 20px;
5 | $global-font-size-h4: 18px;
6 | $global-font-size-h5: 16px;
7 | $global-font-size-h6: 14px;
8 |
9 | /*=============================================
10 | = Generic styling =
11 | =============================================*/
12 |
13 | * {
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale
16 | }
17 |
18 |
19 | *, *:before, *:after {
20 | box-sizing: border-box
21 | }
22 |
23 |
24 | html, body {
25 | position: relative;
26 | margin: 0;
27 | width: 100%;
28 | height: 100%;
29 | }
30 |
31 | body {
32 | font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
33 | font-size: 16px;
34 | line-height: 1.4;
35 | color: #FFFFFF;
36 | background-color: #333;
37 | overflow-x: hidden;
38 | }
39 |
40 | label {
41 | display: block;
42 | margin-bottom: 8px;
43 | font-size: 14px;
44 | font-weight: 500;
45 | cursor: pointer;
46 | }
47 |
48 | p { margin-top: 0; }
49 |
50 | hr {
51 | display: block;
52 | margin: $global-guttering*1.5 0;
53 | border: 0;
54 | border-bottom: 1px solid #eaeaea;
55 | height: 1px;
56 | }
57 |
58 | h1, h2, h3, h4, h5, h6 {
59 | margin-top: 0;
60 | margin-bottom: $global-guttering/2;
61 | font-weight: 400;
62 | line-height: 1.2;
63 | }
64 |
65 | a, a:visited, a:focus {
66 | color: #FFFFFF;
67 | text-decoration: none;
68 | font-weight: 600;
69 | }
70 |
71 | .form-control {
72 | display: block;
73 | width: 100%;
74 | background-color: #f9f9f9;
75 | padding: 12px;
76 | border: 1px solid #ddd;
77 | border-radius: 2.5px;
78 | font-size: 14px;
79 | -webkit-appearance: none;
80 | appearance: none;
81 | margin-bottom: $global-guttering;
82 | }
83 |
84 | h1, .h1 { font-size: $global-font-size-h1; }
85 | h2, .h2 { font-size: $global-font-size-h2; }
86 | h3, .h3 { font-size: $global-font-size-h3; }
87 | h4, .h4 { font-size: $global-font-size-h4; }
88 | h5, .h5 { font-size: $global-font-size-h5; }
89 | h6, .h6 { font-size: $global-font-size-h6; }
90 |
91 | .container {
92 | display: block;
93 | margin: auto;
94 | max-width: 40em;
95 | padding: $global-guttering*2;
96 | @media (max-width: 620px) { padding: 0; }
97 | }
98 |
99 | .section {
100 | background-color: #FFFFFF;
101 | padding: $global-guttering;
102 | color: #333;
103 | a, a:visited, a:focus { color: #00bcd4; }
104 | }
105 |
106 | .logo {
107 | display: block;
108 | margin-bottom: $global-guttering/2;
109 | }
110 |
111 | .logo__img {
112 | width: 100%;
113 | height: auto;
114 | display: inline-block;
115 | max-width: 100%;
116 | vertical-align: top;
117 | padding: $global-guttering/4 0;
118 | }
119 |
120 | .visible-ie { display: none; }
121 |
122 | /*===== End of Section comment block ======*/
123 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "choices.js",
3 | "version": "2.5.1",
4 | "description": "A vanilla JS customisable text input/select box plugin",
5 | "main": "./assets/scripts/dist/choices.min.js",
6 | "scripts": {
7 | "start": "node server.js",
8 | "css:watch": "nodemon -e scss -x \"npm run css:build\"",
9 | "css:build": "npm run css:sass -s && npm run css:prefix -s && npm run css:min -s",
10 | "css:sass": "node-sass --output-style expanded --include-path scss assets/styles/scss/base.scss assets/styles/css/base.css && node-sass --output-style expanded --include-path scss assets/styles/scss/choices.scss assets/styles/css/choices.css",
11 | "css:prefix": "postcss --use autoprefixer -b 'last 2 versions' assets/styles/css/*.css -d assets/styles/css/",
12 | "css:min": "csso assets/styles/css/base.css assets/styles/css/base.min.css && csso assets/styles/css/choices.css assets/styles/css/choices.min.css",
13 | "js:build": "concurrently --prefix-colors yellow,green \"webpack --minimize --config webpack.config.prod.js\" \"webpack --config webpack.config.prod.js\"",
14 | "js:test": "./node_modules/karma/bin/karma start --single-run --no-auto-watch tests/karma.config.js",
15 | "js:test:watch": "./node_modules/karma/bin/karma start --auto-watch --no-single-run tests/karma.config.js"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/jshjohnson/Choices.git"
20 | },
21 | "author": "Josh Johnson",
22 | "license": "MIT",
23 | "bugs": {
24 | "url": "https://github.com/jshjohnson/Choices/issues"
25 | },
26 | "homepage": "https://github.com/jshjohnson/Choices#readme",
27 | "devDependencies": {
28 | "autoprefixer": "^6.3.3",
29 | "babel-core": "^6.7.2",
30 | "babel-eslint": "^6.1.2",
31 | "babel-loader": "^6.2.4",
32 | "babel-preset-es2015": "^6.6.0",
33 | "concurrently": "^3.1.0",
34 | "csso": "^1.8.2",
35 | "es6-promise": "^3.2.1",
36 | "eslint": "^3.3.0",
37 | "eslint-config-airbnb": "^10.0.1",
38 | "eslint-loader": "^1.5.0",
39 | "eslint-plugin-import": "^1.13.0",
40 | "eslint-plugin-jsx-a11y": "^2.2.3",
41 | "eslint-plugin-react": "^6.4.1",
42 | "jasmine-core": "2.4.1",
43 | "karma": "^1.1.0",
44 | "karma-coverage": "^1.0.0",
45 | "karma-es6-shim": "^1.0.0",
46 | "karma-htmlfile-reporter": "^0.3.4",
47 | "karma-jasmine": "^1.0.2",
48 | "karma-phantomjs-launcher": "^1.0.1",
49 | "karma-spec-reporter": "0.0.26",
50 | "karma-webpack": "^1.7.0",
51 | "node-sass": "^3.4.2",
52 | "nodemon": "^1.9.1",
53 | "opn-cli": "^3.1.0",
54 | "postcss-cli": "^2.5.1",
55 | "webpack": "^1.12.14",
56 | "webpack-dashboard": "^0.1.8",
57 | "webpack-dev-server": "^1.14.1",
58 | "whatwg-fetch": "^1.0.0",
59 | "wrapper-webpack-plugin": "^0.1.7"
60 | },
61 | "dependencies": {
62 | "redux": "^3.3.1",
63 | "fuse.js": "^2.2.2"
64 | },
65 | "npmName": "choices.js",
66 | "npmFileMap": [
67 | {
68 | "basePath": "assets",
69 | "files": [
70 | "scripts/dist/*",
71 | "styles/css/*",
72 | "icons/*"
73 | ]
74 | }
75 | ]
76 | }
77 |
--------------------------------------------------------------------------------
/assets/scripts/src/lib/polyfills.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // Production steps of ECMA-262, Edition 6, 22.1.2.1
3 | // Reference: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from
4 | if (!Array.from) {
5 | Array.from = (function() {
6 | var toStr = Object.prototype.toString;
7 |
8 | var isCallable = function(fn) {
9 | return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
10 | };
11 |
12 | var toInteger = function(value) {
13 | var number = Number(value);
14 | if (isNaN(number)) {
15 | return 0;
16 | }
17 | if (number === 0 || !isFinite(number)) {
18 | return number;
19 | }
20 | return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
21 | };
22 |
23 | var maxSafeInteger = Math.pow(2, 53) - 1;
24 |
25 | var toLength = function(value) {
26 | var len = toInteger(value);
27 | return Math.min(Math.max(len, 0), maxSafeInteger);
28 | };
29 |
30 | // The length property of the from method is 1.
31 | return function from(arrayLike /*, mapFn, thisArg */ ) {
32 | // 1. Let C be the this value.
33 | var C = this;
34 |
35 | // 2. Let items be ToObject(arrayLike).
36 | var items = Object(arrayLike);
37 |
38 | // 3. ReturnIfAbrupt(items).
39 | if (arrayLike == null) {
40 | throw new TypeError("Array.from requires an array-like object - not null or undefined");
41 | }
42 |
43 | // 4. If mapfn is undefined, then let mapping be false.
44 | var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
45 | var T;
46 | if (typeof mapFn !== 'undefined') {
47 | // 5. else
48 | // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
49 | if (!isCallable(mapFn)) {
50 | throw new TypeError('Array.from: when provided, the second argument must be a function');
51 | }
52 |
53 | // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
54 | if (arguments.length > 2) {
55 | T = arguments[2];
56 | }
57 | }
58 |
59 | // 10. Let lenValue be Get(items, "length").
60 | // 11. Let len be ToLength(lenValue).
61 | var len = toLength(items.length);
62 |
63 | // 13. If IsConstructor(C) is true, then
64 | // 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
65 | // 14. a. Else, Let A be ArrayCreate(len).
66 | var A = isCallable(C) ? Object(new C(len)) : new Array(len);
67 |
68 | // 16. Let k be 0.
69 | var k = 0;
70 | // 17. Repeat, while k < len… (also steps a - h)
71 | var kValue;
72 | while (k < len) {
73 | kValue = items[k];
74 | if (mapFn) {
75 | A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
76 | } else {
77 | A[k] = kValue;
78 | }
79 | k += 1;
80 | }
81 | // 18. Let putStatus be Put(A, "length", len, true).
82 | A.length = len;
83 | // 20. Return A.
84 | return A;
85 | };
86 | }());
87 | }
88 |
89 | // Reference: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/find
90 | if (!Array.prototype.find) {
91 | Array.prototype.find = function(predicate) {
92 | 'use strict';
93 | if (this == null) {
94 | throw new TypeError('Array.prototype.find called on null or undefined');
95 | }
96 | if (typeof predicate !== 'function') {
97 | throw new TypeError('predicate must be a function');
98 | }
99 | var list = Object(this);
100 | var length = list.length >>> 0;
101 | var thisArg = arguments[1];
102 | var value;
103 |
104 | for (var i = 0; i < length; i++) {
105 | value = list[i];
106 | if (predicate.call(thisArg, value, i, list)) {
107 | return value;
108 | }
109 | }
110 | return undefined;
111 | };
112 | }
--------------------------------------------------------------------------------
/assets/scripts/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from 'redux';
2 | import rootReducer from './../reducers/index.js';
3 |
4 | export default class Store {
5 | constructor() {
6 | this.store = createStore(
7 | rootReducer
8 | , window.devToolsExtension ? window.devToolsExtension() : undefined
9 | );
10 | }
11 |
12 | /**
13 | * Get store object (wrapping Redux method)
14 | * @return {Object} State
15 | */
16 | getState() {
17 | return this.store.getState();
18 | }
19 |
20 | /**
21 | * Dispatch event to store (wrapped Redux method)
22 | * @param {Function} action Action function to trigger
23 | * @return
24 | */
25 | dispatch(action) {
26 | this.store.dispatch(action);
27 | }
28 |
29 | /**
30 | * Subscribe store to function call (wrapped Redux method)
31 | * @param {Function} onChange Function to trigger when state changes
32 | * @return
33 | */
34 | subscribe(onChange) {
35 | this.store.subscribe(onChange);
36 | }
37 |
38 | /**
39 | * Get items from store
40 | * @return {Array} Item objects
41 | */
42 | getItems() {
43 | const state = this.store.getState();
44 | return state.items;
45 | }
46 |
47 | /**
48 | * Get active items from store
49 | * @return {Array} Item objects
50 | */
51 | getItemsFilteredByActive() {
52 | const items = this.getItems();
53 | const values = items.filter((item) => {
54 | return item.active === true;
55 | }, []);
56 |
57 | return values;
58 | }
59 |
60 | /**
61 | * Get items from store reduced to just their values
62 | * @return {Array} Item objects
63 | */
64 | getItemsReducedToValues(items = this.getItems()) {
65 | const values = items.reduce((prev, current) => {
66 | prev.push(current.value);
67 | return prev;
68 | }, []);
69 |
70 | return values;
71 | }
72 |
73 | /**
74 | * Get choices from store
75 | * @return {Array} Option objects
76 | */
77 | getChoices() {
78 | const state = this.store.getState();
79 | return state.choices;
80 | }
81 |
82 | /**
83 | * Get active choices from store
84 | * @return {Array} Option objects
85 | */
86 | getChoicesFilteredByActive() {
87 | const choices = this.getChoices();
88 | const values = choices.filter((choice) => {
89 | return choice.active === true;
90 | }, []);
91 |
92 | return values;
93 | }
94 |
95 | /**
96 | * Get selectable choices from store
97 | * @return {Array} Option objects
98 | */
99 | getChoicesFilteredBySelectable() {
100 | const choices = this.getChoices();
101 | const values = choices.filter((choice) => {
102 | return choice.disabled !== true;
103 | }, []);
104 |
105 | return values;
106 | }
107 |
108 | /**
109 | * Get single choice by it's ID
110 | * @return {Object} Found choice
111 | */
112 | getChoiceById(id) {
113 | if (id) {
114 | const choices = this.getChoicesFilteredByActive();
115 | const foundChoice = choices.find((choice) => choice.id === parseInt(id, 10));
116 | return foundChoice;
117 | }
118 | return false;
119 | }
120 |
121 | /**
122 | * Get groups from store
123 | * @return {Array} Group objects
124 | */
125 | getGroups() {
126 | const state = this.store.getState();
127 | return state.groups;
128 | }
129 |
130 | /**
131 | * Get active groups from store
132 | * @return {Array} Group objects
133 | */
134 | getGroupsFilteredByActive() {
135 | const groups = this.getGroups();
136 | const choices = this.getChoices();
137 |
138 | const values = groups.filter((group) => {
139 | const isActive = group.active === true && group.disabled === false;
140 | const hasActiveOptions = choices.some((choice) => {
141 | return choice.active === true && choice.disabled === false;
142 | });
143 | return isActive && hasActiveOptions;
144 | }, []);
145 |
146 | return values;
147 | }
148 |
149 | /**
150 | * Get group by group id
151 | * @param {Number} id Group ID
152 | * @return {Object} Group data
153 | */
154 | getGroupById(id) {
155 | const groups = this.getGroups();
156 | const foundGroup = groups.find((group) => {
157 | return group.id === id;
158 | });
159 |
160 | return foundGroup;
161 | }
162 | }
163 |
164 | module.exports = Store;
165 |
--------------------------------------------------------------------------------
/assets/styles/css/choices.min.css:
--------------------------------------------------------------------------------
1 | .choices{position:relative;margin-bottom:24px;font-size:16px}.choices:focus{outline:none}.choices:last-child{margin-bottom:0}.choices.is-disabled .choices__inner,.choices.is-disabled .choices__input{background-color:#eaeaea;cursor:not-allowed;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.choices.is-disabled .choices__item{cursor:not-allowed}.choices[data-type*=select-one]{cursor:pointer}.choices[data-type*=select-one] .choices__inner{padding-bottom:7.5px}.choices[data-type*=select-one] .choices__input{display:block;width:100%;padding:10px;border-bottom:1px solid #ddd;background-color:#fff;margin:0}.choices[data-type*=select-one] .choices__button{background-image:url(../../icons//cross-inverse.svg);padding:0;background-size:8px;position:absolute;top:50%;right:0;margin-top:-10px;margin-right:25px;height:20px;width:20px;border-radius:10em;opacity:.5}.choices[data-type*=select-one] .choices__button:focus,.choices[data-type*=select-one] .choices__button:hover{opacity:1}.choices[data-type*=select-one] .choices__button:focus{box-shadow:0 0 0 2px #00bcd4}.choices[data-type*=select-one]:after{content:"";height:0;width:0;border-style:solid;border-color:#333 transparent transparent transparent;border-width:5px;position:absolute;right:11.5px;top:50%;margin-top:-2.5px;pointer-events:none}.choices[data-type*=select-one].is-open:after{border-color:transparent transparent #333 transparent;margin-top:-7.5px}.choices[data-type*=select-one][dir=rtl]:after{left:11.5px;right:auto}.choices[data-type*=select-one][dir=rtl] .choices__button{right:auto;left:0;margin-left:25px;margin-right:0}.choices[data-type*=select-multiple] .choices__inner,.choices[data-type*=text] .choices__inner{cursor:text}.choices[data-type*=select-multiple] .choices__button,.choices[data-type*=text] .choices__button{position:relative;display:inline-block;margin:0 -4px 0 8px;padding-left:16px;border-left:1px solid #008fa1;background-image:url(../../icons//cross.svg);background-size:8px;width:8px;line-height:1;opacity:.75}.choices[data-type*=select-multiple] .choices__button:focus,.choices[data-type*=select-multiple] .choices__button:hover,.choices[data-type*=text] .choices__button:focus,.choices[data-type*=text] .choices__button:hover{opacity:1}.choices__inner{display:inline-block;vertical-align:top;width:100%;background-color:#f9f9f9;padding:7.5px 7.5px 3.75px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;min-height:44px;overflow:hidden}.is-focused .choices__inner,.is-open .choices__inner{border-color:#b7b7b7}.is-open .choices__inner{border-radius:2.5px 2.5px 0 0}.is-flipped.is-open .choices__inner{border-radius:0 0 2.5px 2.5px}.choices__list{margin:0;padding-left:0;list-style:none}.choices__list--single{display:inline-block;padding:4px 16px 4px 4px;width:100%}[dir=rtl] .choices__list--single{padding-right:4px;padding-left:16px}.choices__list--single .choices__item{width:100%}.choices__list--multiple{display:inline}.choices__list--multiple .choices__item{display:inline-block;vertical-align:middle;border-radius:20px;padding:4px 10px;font-size:12px;font-weight:500;margin-right:3.75px;margin-bottom:3.75px;background-color:#00bcd4;border:1px solid #00a5bb;color:#fff;word-break:break-all}.choices__list--multiple .choices__item[data-deletable]{padding-right:5px}[dir=rtl] .choices__list--multiple .choices__item{margin-right:0;margin-left:3.75px}.choices__list--multiple .choices__item.is-highlighted{background-color:#00a5bb;border:1px solid #008fa1}.is-disabled .choices__list--multiple .choices__item{background-color:#aaa;border:1px solid #919191}.choices__list--dropdown{display:none;z-index:1;position:absolute;width:100%;background-color:#fff;border:1px solid #ddd;top:100%;margin-top:-1px;border-bottom-left-radius:2.5px;border-bottom-right-radius:2.5px;overflow:hidden;word-break:break-all}.choices__list--dropdown.is-active{display:block}.is-open .choices__list--dropdown{border-color:#b7b7b7}.is-flipped .choices__list--dropdown{top:auto;bottom:100%;margin-top:0;margin-bottom:-1px;border-radius:.25rem .25rem 0 0}.choices__list--dropdown .choices__list{position:relative;max-height:300px;overflow:auto;-webkit-overflow-scrolling:touch;will-change:scroll-position}.choices__list--dropdown .choices__item{position:relative;padding:10px;font-size:14px}[dir=rtl] .choices__list--dropdown .choices__item{text-align:right}@media (min-width:640px){.choices__list--dropdown .choices__item--selectable{padding-right:100px}.choices__list--dropdown .choices__item--selectable:after{content:attr(data-select-text);font-size:12px;opacity:0;position:absolute;right:10px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}[dir=rtl] .choices__list--dropdown .choices__item--selectable{text-align:right;padding-left:100px;padding-right:10px}[dir=rtl] .choices__list--dropdown .choices__item--selectable:after{right:auto;left:10px}}.choices__list--dropdown .choices__item--selectable.is-highlighted{background-color:#f2f2f2}.choices__list--dropdown .choices__item--selectable.is-highlighted:after{opacity:.5}.choices__item{cursor:default}.choices__item--selectable{cursor:pointer}.choices__item--disabled{cursor:not-allowed;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;opacity:.5}.choices__heading{font-weight:600;font-size:12px;padding:10px;border-bottom:1px solid #f7f7f7;color:gray}.choices__button{text-indent:-9999px;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:0;background-color:transparent;background-repeat:no-repeat;background-position:center;cursor:pointer}.choices__button:focus{outline:none}.choices__input{display:inline-block;vertical-align:baseline;background-color:#f9f9f9;font-size:14px;margin-bottom:5px;border:0;border-radius:0;max-width:100%;padding:4px 0 4px 2px}.choices__input:focus{outline:0}[dir=rtl] .choices__input{padding-right:2px;padding-left:0}.choices__placeholder{opacity:.5}
--------------------------------------------------------------------------------
/assets/styles/css/choices.css:
--------------------------------------------------------------------------------
1 | /*===============================
2 | = Choices =
3 | ===============================*/
4 | .choices {
5 | position: relative;
6 | margin-bottom: 24px;
7 | font-size: 16px;
8 | }
9 |
10 | .choices:focus {
11 | outline: none;
12 | }
13 |
14 | .choices:last-child {
15 | margin-bottom: 0;
16 | }
17 |
18 | .choices.is-disabled .choices__inner, .choices.is-disabled .choices__input {
19 | background-color: #EAEAEA;
20 | cursor: not-allowed;
21 | -webkit-user-select: none;
22 | -moz-user-select: none;
23 | -ms-user-select: none;
24 | user-select: none;
25 | }
26 |
27 | .choices.is-disabled .choices__item {
28 | cursor: not-allowed;
29 | }
30 |
31 | .choices[data-type*="select-one"] {
32 | cursor: pointer;
33 | }
34 |
35 | .choices[data-type*="select-one"] .choices__inner {
36 | padding-bottom: 7.5px;
37 | }
38 |
39 | .choices[data-type*="select-one"] .choices__input {
40 | display: block;
41 | width: 100%;
42 | padding: 10px;
43 | border-bottom: 1px solid #DDDDDD;
44 | background-color: #FFFFFF;
45 | margin: 0;
46 | }
47 |
48 | .choices[data-type*="select-one"] .choices__button {
49 | background-image: url("../../icons//cross-inverse.svg");
50 | padding: 0;
51 | background-size: 8px;
52 | height: 100%;
53 | position: absolute;
54 | top: 50%;
55 | right: 0;
56 | margin-top: -10px;
57 | margin-right: 25px;
58 | height: 20px;
59 | width: 20px;
60 | border-radius: 10em;
61 | opacity: .5;
62 | }
63 |
64 | .choices[data-type*="select-one"] .choices__button:hover, .choices[data-type*="select-one"] .choices__button:focus {
65 | opacity: 1;
66 | }
67 |
68 | .choices[data-type*="select-one"] .choices__button:focus {
69 | box-shadow: 0px 0px 0px 2px #00BCD4;
70 | }
71 |
72 | .choices[data-type*="select-one"]:after {
73 | content: "";
74 | height: 0;
75 | width: 0;
76 | border-style: solid;
77 | border-color: #333333 transparent transparent transparent;
78 | border-width: 5px;
79 | position: absolute;
80 | right: 11.5px;
81 | top: 50%;
82 | margin-top: -2.5px;
83 | pointer-events: none;
84 | }
85 |
86 | .choices[data-type*="select-one"].is-open:after {
87 | border-color: transparent transparent #333333 transparent;
88 | margin-top: -7.5px;
89 | }
90 |
91 | .choices[data-type*="select-one"][dir="rtl"]:after {
92 | left: 11.5px;
93 | right: auto;
94 | }
95 |
96 | .choices[data-type*="select-one"][dir="rtl"] .choices__button {
97 | right: auto;
98 | left: 0;
99 | margin-left: 25px;
100 | margin-right: 0;
101 | }
102 |
103 | .choices[data-type*="select-multiple"] .choices__inner, .choices[data-type*="text"] .choices__inner {
104 | cursor: text;
105 | }
106 |
107 | .choices[data-type*="select-multiple"] .choices__button, .choices[data-type*="text"] .choices__button {
108 | position: relative;
109 | display: inline-block;
110 | margin-top: 0;
111 | margin-right: -4px;
112 | margin-bottom: 0;
113 | margin-left: 8px;
114 | padding-left: 16px;
115 | border-left: 1px solid #008fa1;
116 | background-image: url("../../icons//cross.svg");
117 | background-size: 8px;
118 | width: 8px;
119 | line-height: 1;
120 | opacity: .75;
121 | }
122 |
123 | .choices[data-type*="select-multiple"] .choices__button:hover, .choices[data-type*="select-multiple"] .choices__button:focus, .choices[data-type*="text"] .choices__button:hover, .choices[data-type*="text"] .choices__button:focus {
124 | opacity: 1;
125 | }
126 |
127 | .choices__inner {
128 | display: inline-block;
129 | vertical-align: top;
130 | width: 100%;
131 | background-color: #f9f9f9;
132 | padding: 7.5px 7.5px 3.75px;
133 | border: 1px solid #DDDDDD;
134 | border-radius: 2.5px;
135 | font-size: 14px;
136 | min-height: 44px;
137 | overflow: hidden;
138 | }
139 |
140 | .is-focused .choices__inner, .is-open .choices__inner {
141 | border-color: #b7b7b7;
142 | }
143 |
144 | .is-open .choices__inner {
145 | border-radius: 2.5px 2.5px 0 0;
146 | }
147 |
148 | .is-flipped.is-open .choices__inner {
149 | border-radius: 0 0 2.5px 2.5px;
150 | }
151 |
152 | .choices__list {
153 | margin: 0;
154 | padding-left: 0;
155 | list-style: none;
156 | }
157 |
158 | .choices__list--single {
159 | display: inline-block;
160 | padding: 4px 16px 4px 4px;
161 | width: 100%;
162 | }
163 |
164 | [dir="rtl"] .choices__list--single {
165 | padding-right: 4px;
166 | padding-left: 16px;
167 | }
168 |
169 | .choices__list--single .choices__item {
170 | width: 100%;
171 | }
172 |
173 | .choices__list--multiple {
174 | display: inline;
175 | }
176 |
177 | .choices__list--multiple .choices__item {
178 | display: inline-block;
179 | vertical-align: middle;
180 | border-radius: 20px;
181 | padding: 4px 10px;
182 | font-size: 12px;
183 | font-weight: 500;
184 | margin-right: 3.75px;
185 | margin-bottom: 3.75px;
186 | background-color: #00BCD4;
187 | border: 1px solid #00a5bb;
188 | color: #FFFFFF;
189 | word-break: break-all;
190 | }
191 |
192 | .choices__list--multiple .choices__item[data-deletable] {
193 | padding-right: 5px;
194 | }
195 |
196 | [dir="rtl"] .choices__list--multiple .choices__item {
197 | margin-right: 0;
198 | margin-left: 3.75px;
199 | }
200 |
201 | .choices__list--multiple .choices__item.is-highlighted {
202 | background-color: #00a5bb;
203 | border: 1px solid #008fa1;
204 | }
205 |
206 | .is-disabled .choices__list--multiple .choices__item {
207 | background-color: #aaaaaa;
208 | border: 1px solid #919191;
209 | }
210 |
211 | .choices__list--dropdown {
212 | display: none;
213 | z-index: 1;
214 | position: absolute;
215 | width: 100%;
216 | background-color: #FFFFFF;
217 | border: 1px solid #DDDDDD;
218 | top: 100%;
219 | margin-top: -1px;
220 | border-bottom-left-radius: 2.5px;
221 | border-bottom-right-radius: 2.5px;
222 | overflow: hidden;
223 | word-break: break-all;
224 | }
225 |
226 | .choices__list--dropdown.is-active {
227 | display: block;
228 | }
229 |
230 | .is-open .choices__list--dropdown {
231 | border-color: #b7b7b7;
232 | }
233 |
234 | .is-flipped .choices__list--dropdown {
235 | top: auto;
236 | bottom: 100%;
237 | margin-top: 0;
238 | margin-bottom: -1px;
239 | border-radius: .25rem .25rem 0 0;
240 | }
241 |
242 | .choices__list--dropdown .choices__list {
243 | position: relative;
244 | max-height: 300px;
245 | overflow: auto;
246 | -webkit-overflow-scrolling: touch;
247 | will-change: scroll-position;
248 | }
249 |
250 | .choices__list--dropdown .choices__item {
251 | position: relative;
252 | padding: 10px;
253 | font-size: 14px;
254 | }
255 |
256 | [dir="rtl"] .choices__list--dropdown .choices__item {
257 | text-align: right;
258 | }
259 |
260 | @media (min-width: 640px) {
261 | .choices__list--dropdown .choices__item--selectable {
262 | padding-right: 100px;
263 | }
264 | .choices__list--dropdown .choices__item--selectable:after {
265 | content: attr(data-select-text);
266 | font-size: 12px;
267 | opacity: 0;
268 | position: absolute;
269 | right: 10px;
270 | top: 50%;
271 | -webkit-transform: translateY(-50%);
272 | transform: translateY(-50%);
273 | }
274 | [dir="rtl"] .choices__list--dropdown .choices__item--selectable {
275 | text-align: right;
276 | padding-left: 100px;
277 | padding-right: 10px;
278 | }
279 | [dir="rtl"] .choices__list--dropdown .choices__item--selectable:after {
280 | right: auto;
281 | left: 10px;
282 | }
283 | }
284 |
285 | .choices__list--dropdown .choices__item--selectable.is-highlighted {
286 | background-color: #f2f2f2;
287 | }
288 |
289 | .choices__list--dropdown .choices__item--selectable.is-highlighted:after {
290 | opacity: .5;
291 | }
292 |
293 | .choices__item {
294 | cursor: default;
295 | }
296 |
297 | .choices__item--selectable {
298 | cursor: pointer;
299 | }
300 |
301 | .choices__item--disabled {
302 | cursor: not-allowed;
303 | -webkit-user-select: none;
304 | -moz-user-select: none;
305 | -ms-user-select: none;
306 | user-select: none;
307 | opacity: .5;
308 | }
309 |
310 | .choices__heading {
311 | font-weight: 600;
312 | font-size: 12px;
313 | padding: 10px;
314 | border-bottom: 1px solid #f7f7f7;
315 | color: gray;
316 | }
317 |
318 | .choices__button {
319 | text-indent: -9999px;
320 | -webkit-appearance: none;
321 | -moz-appearance: none;
322 | appearance: none;
323 | border: 0;
324 | background-color: transparent;
325 | background-repeat: no-repeat;
326 | background-position: center;
327 | cursor: pointer;
328 | }
329 |
330 | .choices__button:focus {
331 | outline: none;
332 | }
333 |
334 | .choices__input {
335 | display: inline-block;
336 | vertical-align: baseline;
337 | background-color: #f9f9f9;
338 | font-size: 14px;
339 | margin-bottom: 5px;
340 | border: 0;
341 | border-radius: 0;
342 | max-width: 100%;
343 | padding: 4px 0 4px 2px;
344 | }
345 |
346 | .choices__input:focus {
347 | outline: 0;
348 | }
349 |
350 | [dir="rtl"] .choices__input {
351 | padding-right: 2px;
352 | padding-left: 0;
353 | }
354 |
355 | .choices__placeholder {
356 | opacity: .5;
357 | }
358 |
359 | /*===== End of Choices ======*/
360 |
--------------------------------------------------------------------------------
/assets/styles/scss/choices.scss:
--------------------------------------------------------------------------------
1 | /*===============================
2 | = Choices =
3 | ===============================*/
4 |
5 | $choices-selector: 'choices' !default;
6 | $choices-font-size-lg: 16px !default;
7 | $choices-font-size-md: 14px !default;
8 | $choices-font-size-sm: 12px !default;
9 | $choices-guttering: 24px !default;
10 | $choices-border-radius: 2.5px !default;
11 | $choices-border-radius-item: 20px !default;
12 | $choices-bg-color: #f9f9f9 !default;
13 | $choices-bg-color-disabled: #EAEAEA !default;
14 | $choices-bg-color-dropdown: #FFFFFF !default;
15 | $choices-text-color: #333333 !default;
16 | $choices-keyline-color: #DDDDDD !default;
17 | $choices-primary-color: #00BCD4 !default;
18 | $choices-disabled-color: #eaeaea !default;
19 | $choices-highlight-color: $choices-primary-color !default;
20 | $choices-button-icon-path: '../../icons/' !default;
21 | $choices-button-dimension: 8px !default;
22 | $choices-button-offset: 8px !default;
23 |
24 | .#{$choices-selector} {
25 | position: relative;
26 | margin-bottom: $choices-guttering;
27 | font-size: $choices-font-size-lg;
28 | &:focus { outline: none; }
29 | &:last-child { margin-bottom: 0; }
30 | &.is-disabled {
31 | .#{$choices-selector}__inner, .#{$choices-selector}__input {
32 | background-color: $choices-bg-color-disabled;
33 | cursor: not-allowed;
34 | user-select: none;
35 | }
36 | .#{$choices-selector}__item { cursor: not-allowed; }
37 | }
38 | }
39 |
40 | .#{$choices-selector}[data-type*="select-one"] {
41 | cursor: pointer;
42 | .#{$choices-selector}__inner { padding-bottom: 7.5px; }
43 | .#{$choices-selector}__input {
44 | display: block;
45 | width: 100%;
46 | padding: 10px;
47 | border-bottom: 1px solid $choices-keyline-color;
48 | background-color: #FFFFFF;
49 | margin: 0;
50 | }
51 | .#{$choices-selector}__button {
52 | background-image: url($choices-button-icon-path + '/cross-inverse.svg');
53 | padding: 0;
54 | background-size: 8px;
55 | height: 100%;
56 | position: absolute;
57 | top: 50%;
58 | right: 0;
59 | margin-top: -10px;
60 | margin-right: 25px;
61 | height: 20px;
62 | width: 20px;
63 | border-radius: 10em;
64 | opacity: .5;
65 | &:hover, &:focus { opacity: 1; }
66 | &:focus { box-shadow: 0px 0px 0px 2px $choices-highlight-color; }
67 | }
68 | &:after {
69 | content: "";
70 | height: 0;
71 | width: 0;
72 | border-style: solid;
73 | border-color: $choices-text-color transparent transparent transparent;
74 | border-width: 5px;
75 | position: absolute;
76 | right: 11.5px;
77 | top: 50%;
78 | margin-top: -2.5px;
79 | pointer-events: none;
80 | }
81 | &.is-open:after {
82 | border-color: transparent transparent $choices-text-color transparent;
83 | margin-top: -7.5px;
84 | }
85 | &[dir="rtl"] {
86 | &:after {
87 | left: 11.5px;
88 | right: auto;
89 | }
90 | .#{$choices-selector}__button {
91 | right: auto;
92 | left: 0;
93 | margin-left: 25px;
94 | margin-right: 0;
95 | }
96 | }
97 | }
98 |
99 | .#{$choices-selector}[data-type*="select-multiple"], .#{$choices-selector}[data-type*="text"] {
100 | .#{$choices-selector}__inner { cursor: text; }
101 | .#{$choices-selector}__button {
102 | position: relative;
103 | display: inline-block;
104 | margin-top: 0;
105 | margin-right: -$choices-button-offset/2;
106 | margin-bottom: 0;
107 | margin-left: $choices-button-offset;
108 | padding-left: $choices-button-offset*2;
109 | border-left: 1px solid darken($choices-primary-color, 10%);
110 | background-image: url($choices-button-icon-path + '/cross.svg');
111 | background-size: $choices-button-dimension;
112 | width: $choices-button-dimension;
113 | line-height: 1;
114 | opacity: .75;
115 | &:hover, &:focus { opacity: 1; }
116 | }
117 | }
118 |
119 | .#{$choices-selector}__inner {
120 | display: inline-block;
121 | vertical-align: top;
122 | width: 100%;
123 | background-color: $choices-bg-color;
124 | padding: 7.5px 7.5px 3.75px;
125 | border: 1px solid $choices-keyline-color;
126 | border-radius: $choices-border-radius;
127 | font-size: $choices-font-size-md;
128 | min-height: 44px;
129 | overflow: hidden;
130 | .is-focused &, .is-open & { border-color: darken($choices-keyline-color, 15%); }
131 | .is-open & { border-radius: $choices-border-radius $choices-border-radius 0 0; }
132 | .is-flipped.is-open & { border-radius: 0 0 $choices-border-radius $choices-border-radius; }
133 | }
134 |
135 | .#{$choices-selector}__list {
136 | margin: 0;
137 | padding-left: 0;
138 | list-style: none;
139 | }
140 |
141 | .#{$choices-selector}__list--single {
142 | display: inline-block;
143 | padding: 4px 16px 4px 4px;
144 | width: 100%;
145 | [dir="rtl"] & {
146 | padding-right: 4px;
147 | padding-left: 16px;
148 | }
149 | .#{$choices-selector}__item { width: 100%; }
150 | }
151 |
152 | .#{$choices-selector}__list--multiple {
153 | display: inline;
154 | .#{$choices-selector}__item {
155 | display: inline-block;
156 | vertical-align: middle;
157 | border-radius: $choices-border-radius-item;
158 | padding: 4px 10px;
159 | font-size: $choices-font-size-sm;
160 | font-weight: 500;
161 | margin-right: 3.75px;
162 | margin-bottom: 3.75px;
163 | background-color: $choices-primary-color;
164 | border: 1px solid darken($choices-primary-color, 5%);
165 | color: #FFFFFF;
166 | word-break: break-all;
167 | &[data-deletable] { padding-right: 5px; }
168 | [dir="rtl"] & {
169 | margin-right: 0;
170 | margin-left: 3.75px;
171 | }
172 | &.is-highlighted {
173 | background-color: darken($choices-primary-color, 5%);
174 | border: 1px solid darken($choices-primary-color, 10%);
175 | }
176 | .is-disabled & {
177 | background-color: darken($choices-disabled-color, 25%);
178 | border: 1px solid darken($choices-disabled-color, 35%);
179 | }
180 | }
181 | }
182 |
183 | .#{$choices-selector}__list--dropdown {
184 | display: none;
185 | z-index: 1;
186 | position: absolute;
187 | width: 100%;
188 | background-color: $choices-bg-color-dropdown;
189 | border: 1px solid $choices-keyline-color;
190 | top: 100%;
191 | margin-top: -1px;
192 | border-bottom-left-radius: $choices-border-radius;
193 | border-bottom-right-radius: $choices-border-radius;
194 | overflow: hidden;
195 | word-break: break-all;
196 | &.is-active { display: block; }
197 | .is-open & { border-color: darken($choices-keyline-color, 15%); }
198 | .is-flipped & {
199 | top: auto;
200 | bottom: 100%;
201 | margin-top: 0;
202 | margin-bottom: -1px;
203 | border-radius: .25rem .25rem 0 0;
204 | }
205 | .#{$choices-selector}__list {
206 | position: relative;
207 | max-height: 300px;
208 | overflow: auto;
209 | -webkit-overflow-scrolling: touch;
210 | will-change: scroll-position;
211 | }
212 | .#{$choices-selector}__item {
213 | position: relative;
214 | padding: 10px;
215 | font-size: $choices-font-size-md;
216 | [dir="rtl"] & { text-align: right; }
217 | }
218 | .#{$choices-selector}__item--selectable {
219 | @media (min-width: 640px) {
220 | padding-right: 100px;
221 | &:after {
222 | content: attr(data-select-text);
223 | font-size: $choices-font-size-sm;
224 | opacity: 0;
225 | position: absolute;
226 | right: 10px;
227 | top: 50%;
228 | transform: translateY(-50%);
229 | }
230 | [dir="rtl"] & {
231 | text-align: right;
232 | padding-left: 100px;
233 | padding-right: 10px;
234 | &:after {
235 | right: auto;
236 | left: 10px;
237 | }
238 | }
239 | }
240 | &.is-highlighted {
241 | background-color: mix(#000000, #FFFFFF, 5%);
242 | &:after { opacity: .5; }
243 | }
244 | }
245 | }
246 |
247 | .#{$choices-selector}__item { cursor: default; }
248 | .#{$choices-selector}__item--selectable { cursor: pointer; }
249 | .#{$choices-selector}__item--disabled {
250 | cursor: not-allowed;
251 | user-select: none;
252 | opacity: .5;
253 | }
254 |
255 | .#{$choices-selector}__heading {
256 | font-weight: 600;
257 | font-size: $choices-font-size-sm;
258 | padding: 10px;
259 | border-bottom: 1px solid lighten($choices-keyline-color, 10%);
260 | color: lighten(#333, 30%);
261 | }
262 |
263 | .#{$choices-selector}__button {
264 | text-indent: -9999px;
265 | -webkit-appearance: none;
266 | appearance: none;
267 | border: 0;
268 | background-color: transparent;
269 | background-repeat: no-repeat;
270 | background-position: center;
271 | cursor: pointer;
272 | &:focus { outline: none; }
273 | }
274 |
275 | .#{$choices-selector}__input {
276 | display: inline-block;
277 | vertical-align: baseline;
278 | background-color: mix(#000000, #FFFFFF, 2.5%);
279 | font-size: $choices-font-size-md;
280 | margin-bottom: 5px;
281 | border: 0;
282 | border-radius: 0;
283 | max-width: 100%;
284 | padding: 4px 0 4px 2px;
285 | &:focus { outline: 0; }
286 | [dir="rtl"] & {
287 | padding-right: 2px;
288 | padding-left: 0;
289 | }
290 | }
291 |
292 | .#{$choices-selector}__placeholder { opacity: .5; }
293 |
294 | /*===== End of Choices ======*/
295 |
--------------------------------------------------------------------------------
/assets/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Slice 1
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/assets/scripts/src/lib/utils.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /**
3 | * Capitalises the first letter of each word in a string
4 | * @param {String} str String to capitalise
5 | * @return {String} Capitalised string
6 | */
7 | export const capitalise = function(str) {
8 | return str.replace(/\w\S*/g, function(txt) {
9 | return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
10 | });
11 | };
12 |
13 | /**
14 | * Tests the type of an object
15 | * @param {String} type Type to test object against
16 | * @param {Object} obj Object to be tested
17 | * @return {Boolean}
18 | */
19 | export const isType = function(type, obj) {
20 | var clas = Object.prototype.toString.call(obj).slice(8, -1);
21 | return obj !== undefined && obj !== null && clas === type;
22 | };
23 |
24 | /**
25 | * Tests to see if a passed object is a node
26 | * @param {Object} obj Object to be tested
27 | * @return {Boolean}
28 | */
29 | export const isNode = (o) => {
30 | return (
31 | typeof Node === "object" ? o instanceof Node :
32 | o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string"
33 | );
34 | };
35 |
36 | /**
37 | * Tests to see if a passed object is an element
38 | * @param {Object} obj Object to be tested
39 | * @return {Boolean}
40 | */
41 | export const isElement = (o) => {
42 | return (
43 | typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
44 | o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string"
45 | );
46 | };
47 |
48 | /**
49 | * Merges unspecified amount of objects into new object
50 | * @private
51 | * @return {Object} Merged object of arguments
52 | */
53 | export const extend = function() {
54 | let extended = {};
55 | let length = arguments.length;
56 |
57 | /**
58 | * Merge one object into another
59 | * @param {Object} obj Object to merge into extended object
60 | */
61 | let merge = function(obj) {
62 | for (let prop in obj) {
63 | if (Object.prototype.hasOwnProperty.call(obj, prop)) {
64 | // If deep merge and property is an object, merge properties
65 | if (isType('Object', obj[prop])) {
66 | extended[prop] = extend(true, extended[prop], obj[prop]);
67 | } else {
68 | extended[prop] = obj[prop];
69 | }
70 | }
71 | }
72 | };
73 |
74 | // Loop through each passed argument
75 | for (let i = 0; i < length; i++) {
76 | // store argument at position i
77 | let obj = arguments[i];
78 |
79 | // If we are in fact dealing with an object, merge it.
80 | if (isType('Object', obj)) {
81 | merge(obj);
82 | }
83 | }
84 |
85 | return extended;
86 | };
87 |
88 | /**
89 | * CSS transition end event listener
90 | * @return
91 | */
92 | export const whichTransitionEvent = function() {
93 | var t,
94 | el = document.createElement("fakeelement");
95 |
96 | var transitions = {
97 | "transition": "transitionend",
98 | "OTransition": "oTransitionEnd",
99 | "MozTransition": "transitionend",
100 | "WebkitTransition": "webkitTransitionEnd"
101 | }
102 |
103 | for (t in transitions) {
104 | if (el.style[t] !== undefined) {
105 | return transitions[t];
106 | }
107 | }
108 | };
109 |
110 | /**
111 | * CSS animation end event listener
112 | * @return
113 | */
114 | export const whichAnimationEvent = function() {
115 | var t,
116 | el = document.createElement('fakeelement');
117 |
118 | var animations = {
119 | 'animation': 'animationend',
120 | 'OAnimation': 'oAnimationEnd',
121 | 'MozAnimation': 'animationend',
122 | 'WebkitAnimation': 'webkitAnimationEnd'
123 | };
124 |
125 | for (t in animations) {
126 | if (el.style[t] !== undefined) {
127 | return animations[t];
128 | }
129 | }
130 | };
131 |
132 | /**
133 | * Get the ancestors of each element in the current set of matched elements,
134 | * up to but not including the element matched by the selector
135 | * @param {NodeElement} elem Element to begin search from
136 | * @param {NodeElement} parent Parent to find
137 | * @param {String} selector Class to find
138 | * @return {Array} Array of parent elements
139 | */
140 | export const getParentsUntil = function(elem, parent, selector) {
141 | var parents = [];
142 | // Get matches
143 | for (; elem && elem !== document; elem = elem.parentNode) {
144 |
145 | // Check if parent has been reached
146 | if (parent) {
147 |
148 | var parentType = parent.charAt(0);
149 |
150 | // If parent is a class
151 | if (parentType === '.') {
152 | if (elem.classList.contains(parent.substr(1))) {
153 | break;
154 | }
155 | }
156 |
157 | // If parent is an ID
158 | if (parentType === '#') {
159 | if (elem.id === parent.substr(1)) {
160 | break;
161 | }
162 | }
163 |
164 | // If parent is a data attribute
165 | if (parentType === '[') {
166 | if (elem.hasAttribute(parent.substr(1, parent.length - 1))) {
167 | break;
168 | }
169 | }
170 |
171 | // If parent is a tag
172 | if (elem.tagName.toLowerCase() === parent) {
173 | break;
174 | }
175 |
176 | }
177 | if (selector) {
178 | var selectorType = selector.charAt(0);
179 |
180 | // If selector is a class
181 | if (selectorType === '.') {
182 | if (elem.classList.contains(selector.substr(1))) {
183 | parents.push(elem);
184 | }
185 | }
186 |
187 | // If selector is an ID
188 | if (selectorType === '#') {
189 | if (elem.id === selector.substr(1)) {
190 | parents.push(elem);
191 | }
192 | }
193 |
194 | // If selector is a data attribute
195 | if (selectorType === '[') {
196 | if (elem.hasAttribute(selector.substr(1, selector.length - 1))) {
197 | parents.push(elem);
198 | }
199 | }
200 |
201 | // If selector is a tag
202 | if (elem.tagName.toLowerCase() === selector) {
203 | parents.push(elem);
204 | }
205 |
206 | } else {
207 | parents.push(elem);
208 | }
209 | }
210 |
211 | // Return parents if any exist
212 | if (parents.length === 0) {
213 | return null;
214 | } else {
215 | return parents;
216 | }
217 | };
218 |
219 | export const wrap = function(element, wrapper) {
220 | wrapper = wrapper || document.createElement('div');
221 | if (element.nextSibling) {
222 | element.parentNode.insertBefore(wrapper, element.nextSibling);
223 | } else {
224 | element.parentNode.appendChild(wrapper);
225 | }
226 | return wrapper.appendChild(element);
227 | };
228 |
229 | export const getSiblings = function(elem) {
230 | var siblings = [];
231 | var sibling = elem.parentNode.firstChild;
232 | for (; sibling; sibling = sibling.nextSibling) {
233 | if (sibling.nodeType === 1 && sibling !== elem) {
234 | siblings.push(sibling);
235 | }
236 | }
237 | return siblings;
238 | };
239 |
240 | /**
241 | * Find ancestor in DOM tree
242 | * @param {NodeElement} el Element to start search from
243 | * @param {[type]} cls Class of parent
244 | * @return {NodeElement} Found parent element
245 | */
246 | export const findAncestor = function(el, cls) {
247 | while ((el = el.parentElement) && !el.classList.contains(cls));
248 | return el;
249 | };
250 |
251 | /**
252 | * Debounce an event handler.
253 | * @param {Function} func Function to run after wait
254 | * @param {Number} wait The delay before the function is executed
255 | * @param {Boolean} immediate If passed, trigger the function on the leading edge, instead of the trailing.
256 | * @return {Function} A function will be called after it stops being called for a given delay
257 | */
258 | export const debounce = function(func, wait, immediate) {
259 | var timeout;
260 | return function() {
261 | var context = this,
262 | args = arguments;
263 | var later = function() {
264 | timeout = null;
265 | if (!immediate) func.apply(context, args);
266 | };
267 | var callNow = immediate && !timeout;
268 | clearTimeout(timeout);
269 | timeout = setTimeout(later, wait);
270 | if (callNow) func.apply(context, args);
271 | };
272 | };
273 |
274 | /**
275 | * Get an element's distance from the top of the page
276 | * @private
277 | * @param {NodeElement} el Element to test for
278 | * @return {Number} Elements Distance from top of page
279 | */
280 | export const getElemDistance = function(el) {
281 | var location = 0;
282 | if (el.offsetParent) {
283 | do {
284 | location += el.offsetTop;
285 | el = el.offsetParent;
286 | } while (el);
287 | }
288 | return location >= 0 ? location : 0;
289 | };
290 |
291 | /**
292 | * Determine element height multiplied by any offsets
293 | * @private
294 | * @param {HTMLElement} el Element to test for
295 | * @return {Number} Height of element
296 | */
297 | export const getElementOffset = function(el, offset) {
298 | var elOffset = offset;
299 | if (elOffset > 1) elOffset = 1;
300 | if (elOffset > 0) elOffset = 0;
301 |
302 | return Math.max(el.offsetHeight * elOffset);
303 | };
304 |
305 | /**
306 | * Get the next or previous element from a given start point
307 | * @param {HTMLElement} startEl Element to start position from
308 | * @param {String} className The class we will look through
309 | * @param {Number} direction Positive next element, negative previous element
310 | * @return {[HTMLElement} Found element
311 | */
312 | export const getAdjacentEl = (startEl, className, direction = 1) => {
313 | if (!startEl || !className) return;
314 |
315 | const parent = startEl.parentNode.parentNode;
316 | const children = Array.from(parent.querySelectorAll(className));
317 |
318 | const startPos = children.indexOf(startEl);
319 | const operatorDirection = direction > 0 ? 1 : -1;
320 |
321 | return children[startPos + operatorDirection];
322 | };
323 |
324 | /**
325 | * Get scroll position based on top/bottom position
326 | * @private
327 | * @return {String} Position of scroll
328 | */
329 | export const getScrollPosition = function(position) {
330 | if (position === 'bottom') {
331 | // Scroll position from the bottom of the viewport
332 | return Math.max((window.scrollY || window.pageYOffset) + (window.innerHeight || document.documentElement.clientHeight));
333 | } else {
334 | // Scroll position from the top of the viewport
335 | return (window.scrollY || window.pageYOffset);
336 | }
337 | };
338 |
339 | /**
340 | * Determine whether an element is within the viewport
341 | * @param {HTMLElement} el Element to test
342 | * @return {String} Position of scroll
343 | * @return {Boolean}
344 | */
345 | export const isInView = function(el, position, offset) {
346 | // If the user has scrolled further than the distance from the element to the top of its parent
347 | return this.getScrollPosition(position) > (this.getElemDistance(el) + this.getElementOffset(el, offset)) ? true : false;
348 | };
349 |
350 | /**
351 | * Determine whether an element is within
352 | * @param {HTMLElement} el Element to test
353 | * @param {HTMLElement} parent Scrolling parent
354 | * @param {Number} direction Whether element is visible from above or below
355 | * @return {Boolean}
356 | */
357 | export const isScrolledIntoView = (el, parent, direction = 1) => {
358 | if (!el) return;
359 |
360 | let isVisible;
361 |
362 | if (direction > 0) {
363 | // In view from bottom
364 | isVisible = (parent.scrollTop + parent.offsetHeight) >= (el.offsetTop + el.offsetHeight);
365 | } else {
366 | // In view from top
367 | isVisible = el.offsetTop >= parent.scrollTop;
368 | }
369 |
370 | return isVisible;
371 | };
372 |
373 | /**
374 | * Remove html tags from a string
375 | * @param {String} Initial string/html
376 | * @return {String} Sanitised string
377 | */
378 | export const stripHTML = function(html) {
379 | let el = document.createElement("DIV");
380 | el.innerHTML = html;
381 | return el.textContent || el.innerText || "";
382 | };
383 |
384 | /**
385 | * Adds animation to an element and removes it upon animation completion
386 | * @param {Element} el Element to add animation to
387 | * @param {String} animation Animation class to add to element
388 | * @return
389 | */
390 | export const addAnimation = (el, animation) => {
391 | let animationEvent = whichAnimationEvent();
392 |
393 | let removeAnimation = () => {
394 | el.classList.remove(animation);
395 | el.removeEventListener(animationEvent, removeAnimation, false);
396 | };
397 |
398 | el.classList.add(animation);
399 | el.addEventListener(animationEvent, removeAnimation, false);
400 | };
401 |
402 |
403 | /**
404 | * Get a random number between a range
405 | * @param {Number} min Minimum range
406 | * @param {Number} max Maximum range
407 | * @return {Number} Random number
408 | */
409 | export const getRandomNumber = function(min, max) {
410 | return Math.floor(Math.random() * (max - min) + min);
411 | };
412 |
413 | /**
414 | * Turn a string into a node
415 | * @param {String} String to convert
416 | * @return {HTMLElement} Converted node element
417 | */
418 | export const strToEl = (function() {
419 | var tmpEl = document.createElement('div');
420 | return function(str) {
421 | var r;
422 | tmpEl.innerHTML = str;
423 | r = tmpEl.children[0];
424 |
425 | while (tmpEl.firstChild) {
426 | tmpEl.removeChild(tmpEl.firstChild);
427 | }
428 |
429 | return r;
430 | };
431 | }());
432 |
433 | /**
434 | * Sets the width of a passed input based on its value
435 | * @return {Number} Width of input
436 | */
437 | export const getWidthOfInput = (input) => {
438 | const value = input.value || input.placeholder;
439 | let width = input.offsetWidth;
440 |
441 | if (value) {
442 | const testEl = strToEl(`${ value } `);
443 | testEl.style.position = 'absolute';
444 | testEl.style.padding = '0';
445 | testEl.style.top = '-9999px';
446 | testEl.style.left = '-9999px';
447 | testEl.style.width = 'auto';
448 | testEl.style.whiteSpace = 'pre';
449 |
450 | document.body.appendChild(testEl);
451 |
452 | if (value && testEl.offsetWidth !== input.offsetWidth) {
453 | width = testEl.offsetWidth + 4;
454 | }
455 |
456 | document.body.removeChild(testEl);
457 | }
458 |
459 | return `${width}px`;
460 | };
461 |
462 | export const sortByAlpha = (a, b) => {
463 | const labelA = (a.label || a.value).toLowerCase();
464 | const labelB = (b.label || b.value).toLowerCase();
465 |
466 | if (labelA < labelB) return -1;
467 | if (labelA > labelB) return 1;
468 | return 0;
469 | };
470 |
471 | export const sortByScore = (a, b) => {
472 | return a.score - b.score;
473 | };
474 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Choices
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | Choices.js
43 |
44 |
Choices.js is a lightweight, configurable select box/text input plugin. Similar to Select2 and Selectize but without the jQuery dependency.
45 |
For all config options, visit the GitHub repo .
46 |
47 |
48 |
49 |
Text inputs
50 |
Limited to 5 values with remove button
51 |
52 |
53 |
Unique values only, no pasting
54 |
55 |
56 |
Email addresses only
57 |
58 |
59 |
Disabled
60 |
61 |
62 |
Prepends and appends a value to each items return value
63 |
64 |
65 |
Preset values passed through options
66 |
67 |
68 |
I18N labels
69 |
70 |
71 |
Right-to-left
72 |
73 |
74 |
75 |
76 |
Multiple select input
77 |
Default
78 |
79 | Dropdown item 1
80 | Dropdown item 2
81 | Dropdown item 3
82 | Dropdown item 4
83 |
84 |
85 |
With remove button
86 |
87 | Dropdown item 1
88 | Dropdown item 2
89 | Dropdown item 3
90 | Dropdown item 4
91 |
92 |
93 |
Option groups
94 |
95 |
96 | London
97 | Manchester
98 | Liverpool
99 |
100 |
101 | Paris
102 | Lyon
103 | Marseille
104 |
105 |
106 | Hamburg
107 | Munich
108 | Berlin
109 |
110 |
111 | New York
112 | Washington
113 | Michigan
114 |
115 |
116 | Madrid
117 | Barcelona
118 | Malaga
119 |
120 |
121 | Montreal
122 | Toronto
123 | Vancouver
124 |
125 |
126 |
127 |
If the following example do not load, the Discogs rate limit has probably been reached. Try again later!
128 |
129 |
Options from remote source (Fetch API) & limited to 5
130 |
131 |
132 |
Right-to-left
133 |
134 | Dropdown item 1
135 | Dropdown item 2
136 | Dropdown item 3
137 | Dropdown item 4
138 |
139 |
140 |
141 |
142 |
Single select input
143 |
Default
144 |
145 | This is a placeholder
146 | Dropdown item 1
147 | Dropdown item 2
148 | Dropdown item 3
149 |
150 |
151 |
If the following two examples do not load, the Discogs rate limit has probably been reached. Try again later!
152 |
153 |
Options from remote source (Fetch API)
154 |
155 |
156 |
Options from remote source (XHR) & remove button
157 |
158 |
159 |
Option groups
160 |
161 |
162 | London
163 | Manchester
164 | Liverpool
165 |
166 |
167 | Paris
168 | Lyon
169 | Marseille
170 |
171 |
172 | Hamburg
173 | Munich
174 | Berlin
175 |
176 |
177 | New York
178 | Washington
179 | Michigan
180 |
181 |
182 | Madrid
183 | Barcelona
184 | Malaga
185 |
186 |
187 | Montreal
188 | Toronto
189 | Vancouver
190 |
191 |
192 |
193 |
Right-to-left
194 |
195 | Dropdown item 1
196 | Dropdown item 2
197 | Dropdown item 3
198 |
199 |
200 |
Options added via config with no search
201 |
202 | Zero
203 |
204 |
205 |
Option and option groups added via config
206 |
207 |
208 |
Option selected via config
209 |
210 |
211 |
Options without sorting
212 |
213 | Madrid
214 | Toronto
215 | Vancouver
216 | London
217 | Manchester
218 | Liverpool
219 | Paris
220 | Malaga
221 | Washington
222 | Lyon
223 | Marseille
224 | Hamburg
225 | Munich
226 | Barcelona
227 | Berlin
228 | Montreal
229 | New York
230 | Michigan
231 |
232 |
233 |
Custom templates
234 |
235 | React
236 | Angular
237 | Ember
238 | Vue
239 |
240 |
241 |
Below is an example of how you could have two select inputs depend on eachother. 'Boroughs' will only be enabled if the value of 'States' is 'New York'
242 |
States
243 |
244 | Michigan
245 | Texas
246 | Chicago
247 | New York
248 | Washington
249 |
250 |
251 |
Boroughs
252 |
253 | The Bronx
254 | Brooklyn
255 | Manhattan
256 | Queens
257 | Staten Island
258 |
259 |
260 |
261 |
472 |
473 |
474 |
479 |
480 |
481 |
482 |
483 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Choices.js [](https://travis-ci.org/jshjohnson/Choices)
2 | A vanilla, lightweight (~15kb gzipped 🎉), configurable select box/text input plugin. Similar to Select2 and Selectize but without the jQuery dependency.
3 |
4 | [Demo](https://joshuajohnson.co.uk/Choices/)
5 |
6 | ## TL;DR
7 | * Lightweight
8 | * No jQuery dependency
9 | * Configurable sorting
10 | * Flexible styling
11 | * Fast search/filtering
12 | * Clean API
13 | * Right-to-left support
14 | * Custom templates
15 |
16 | ## Installation
17 | With [NPM](https://www.npmjs.com/package/choices.js):
18 | ```zsh
19 | npm install choices.js --save
20 | ```
21 | With [Bower](https://bower.io/):
22 | ```zsh
23 | bower install choices.js --save
24 | ```
25 | Or include Choices directly:
26 | ```html
27 |
28 |
29 |
30 |
31 |
32 |
33 | ```
34 | ## Setup
35 |
36 | ```js
37 | // Pass multiple elements:
38 | const choices = new Choices(elements);
39 |
40 | // Pass single element:
41 | const choices = new Choices(element);
42 |
43 | // Pass reference
44 | const choices = new Choices('[data-trigger']);
45 | const choices = new Choices('.js-choice');
46 |
47 | // Pass jQuery element
48 | const choices = new Choices($('.js-choice')[0]);
49 |
50 | // Passing options (with default options)
51 | const choices = new Choices(elements, {
52 | items: [],
53 | choices: [],
54 | maxItemCount: -1,
55 | addItems: true,
56 | removeItems: true,
57 | removeItemButton: false,
58 | editItems: false,
59 | duplicateItems: true,
60 | delimiter: ',',
61 | paste: true,
62 | search: true,
63 | searchFloor: 1,
64 | flip: true,
65 | regexFilter: null,
66 | shouldSort: true,
67 | sortFilter: () => {...},
68 | sortFields: ['label', 'value'],
69 | placeholder: true,
70 | placeholderValue: null,
71 | prependValue: null,
72 | appendValue: null,
73 | loadingText: 'Loading...',
74 | noResultsText: 'No results found',
75 | noChoicesText: 'No choices to choose from',
76 | itemSelectText: 'Press to select',
77 | addItemText: (value) => {
78 | return `Press Enter to add "${value}" `;
79 | },
80 | maxItemText: (maxItemCount) => {
81 | return `Only ${maxItemCount} values can be added.`;
82 | },
83 | classNames: {
84 | containerOuter: 'choices',
85 | containerInner: 'choices__inner',
86 | input: 'choices__input',
87 | inputCloned: 'choices__input--cloned',
88 | list: 'choices__list',
89 | listItems: 'choices__list--multiple',
90 | listSingle: 'choices__list--single',
91 | listDropdown: 'choices__list--dropdown',
92 | item: 'choices__item',
93 | itemSelectable: 'choices__item--selectable',
94 | itemDisabled: 'choices__item--disabled',
95 | itemChoice: 'choices__item--choice',
96 | group: 'choices__group',
97 | groupHeading : 'choices__heading',
98 | button: 'choices__button',
99 | activeState: 'is-active',
100 | focusState: 'is-focused',
101 | openState: 'is-open',
102 | disabledState: 'is-disabled',
103 | highlightedState: 'is-highlighted',
104 | hiddenState: 'is-hidden',
105 | flippedState: 'is-flipped',
106 | loadingState: 'is-loading',
107 | },
108 | callbackOnInit: null,
109 | callbackOnAddItem: null,
110 | callbackOnRemoveItem: null,
111 | callbackOnHighlightItem: null,
112 | callbackOnUnhighlightItem: null,
113 | callbackOnCreateTemplates: null,
114 | callbackOnChange: null,
115 | callbackOnSearch: null,
116 | });
117 | ```
118 |
119 | ## Terminology
120 | | Word | Definition |
121 | | ------ | ---------- |
122 | | Choice | A choice is a value a user can select. A choice would be equivelant to the ` ` element within a select input. |
123 | | Group | A group is a collection of choices. A group should be seen as equivalent to a ` ` element within a select input.|
124 | | Item | An item is an inputted value (text input) or a selected choice (select element). In the context of a select element, an item is equivelent to a selected option element: ` ` whereas in the context of a text input an item is equivelant to ` `|
125 |
126 |
127 | ## Configuration options
128 | ### items
129 | **Type:** `Array` **Default:** `[]`
130 |
131 | **Input types affected:** `text`
132 |
133 | **Usage:** Add pre-selected items (see terminology) to text input.
134 |
135 | Pass an array of strings:
136 |
137 | `['value 1', 'value 2', 'value 3']`
138 |
139 | Pass an array of objects:
140 |
141 | ```
142 | [{
143 | value: 'Value 1',
144 | label: 'Label 1',
145 | id: 1
146 | },
147 | {
148 | value: 'Value 2',
149 | label: 'Label 2',
150 | id: 2
151 | }]
152 | ```
153 |
154 | ### choices
155 | **Type:** `Array` **Default:** `[]`
156 |
157 | **Input types affected:** `select-one`, `select-multiple`
158 |
159 | **Usage:** Add choices (see terminology) to select input.
160 |
161 | Pass an array of objects:
162 |
163 | ```
164 | [{
165 | value: 'Option 1',
166 | label: 'Option 1',
167 | selected: true,
168 | disabled: false,
169 | },
170 | {
171 | value: 'Option 2',
172 | label: 'Option 2',
173 | selected: false,
174 | disabled: true,
175 | }]
176 | ```
177 |
178 | ### maxItemCount
179 | **Type:** `Number` **Default:** `-1`
180 |
181 | **Input types affected:** `text`, `select-multiple`
182 |
183 | **Usage:** The amount of items a user can input/select ("-1" indicates no limit).
184 |
185 | ### addItems
186 | **Type:** `Boolean` **Default:** `true`
187 |
188 | **Input types affected:** `text`
189 |
190 | **Usage:** Whether a user can add items.
191 |
192 | ### removeItems
193 | **Type:** `Boolean` **Default:** `true`
194 |
195 | **Input types affected:** `text`, `select-multiple`
196 |
197 | **Usage:** Whether a user can remove items.
198 |
199 | ### removeItemButton
200 | **Type:** `Boolean` **Default:** `false`
201 |
202 | **Input types affected:** `text`, `select-one`, `select-multiple`
203 |
204 | **Usage:** Whether each item should have a remove button.
205 |
206 | ### editItems
207 | **Type:** `Boolean` **Default:** `false`
208 |
209 | **Input types affected:** `text`
210 |
211 | **Usage:** Whether a user can edit items. An item's value can be edited by pressing the backspace.
212 |
213 | ### duplicateItems
214 | **Type:** `Boolean` **Default:** `true`
215 |
216 | **Input types affected:** `text`, `select-multiple`
217 |
218 | **Usage:** Whether each inputted/chosen item should be unique.
219 |
220 | ### delimiter
221 | **Type:** `String` **Default:** `,`
222 |
223 | **Input types affected:** `text`
224 |
225 | **Usage:** What divides each value. The default delimiter seperates each value with a comma: `"Value 1, Value 2, Value 3"`.
226 |
227 | ### paste
228 | **Type:** `Boolean` **Default:** `true`
229 |
230 | **Input types affected:** `text`, `select-multiple`
231 |
232 | **Usage:** Whether a user can paste into the input.
233 |
234 | ### search
235 | **Type:** `Boolean` **Default:** `true`
236 |
237 | **Input types affected:** `select-one`
238 |
239 | **Usage:** Whether a user should be allowed to search avaiable choices. Note that multiple select boxes will always show search inputs.
240 |
241 | ### searchFloor
242 | **Type:** `Number` **Default:** `1`
243 |
244 | **Input types affected:** `select-one`, `select-multiple`
245 |
246 | **Usage:** The minimum length a search value should be before choices are searched.
247 |
248 | ### flip
249 | **Type:** `Boolean` **Default:** `true`
250 |
251 | **Input types affected:** `select-one`, `select-multiple`
252 |
253 | **Usage:** Whether the dropdown should appear above the input (rather than beneath) if there is not enough space within the window.
254 |
255 | ### regexFilter
256 | **Type:** `Regex` **Default:** `null`
257 |
258 | **Input types affected:** `text`
259 |
260 | **Usage:** A filter that will need to pass for a user to successfully add an item.
261 |
262 | ### shouldSort
263 | **Type:** `Boolean` **Default:** `true`
264 |
265 | **Input types affected:** `select-one`, `select-multiple`
266 |
267 | **Usage:** Whether choices should be sorted. If false, choices will appear in the order they were given.
268 |
269 | ### sortFilter
270 | **Type:** `Function` **Default:** sortByAlpha
271 |
272 | **Input types affected:** `select-one`, `select-multiple`
273 |
274 | **Usage:** The function that will sort choices before they are displayed (unless a user is searching). By default choices are sorted by alphabetical order.
275 |
276 | **Example:**
277 |
278 | ```js
279 | // Sorting via length of label from largest to smallest
280 | const example = new Choices(element, {
281 | sortFilter: function(a, b) {
282 | return b.label.length - a.label.length;
283 | },
284 | };
285 | ```
286 |
287 | ### sortFields
288 | **Type:** `Array/String` **Default:** `['label', 'value']`
289 |
290 | **Input types affected:**`select-one`, `select-multiple`
291 |
292 | **Usage:** Specify which fields should be used for sorting when a user is searching. If a user is not searching and sorting is enabled, only the choice's label will be sorted.
293 |
294 | ### placeholder
295 | **Type:** `Boolean` **Default:** `true`
296 |
297 | **Input types affected:** `text`, `select-one`, `select-multiple`
298 |
299 | **Usage:** Whether the input should show a placeholder. Used in conjunction with `placeholderValue`. If `placeholder` is set to true and no value is passed to `placeholderValue`, the passed input's placeholder attribute will be used as the placeholder value.
300 |
301 | ### placeholderValue
302 | **Type:** `String` **Default:** `null`
303 |
304 | **Input types affected:** `text`, `select-one`, `select-multiple`
305 |
306 | **Usage:** The value of the inputs placeholder.
307 |
308 | ### prependValue
309 | **Type:** `String` **Default:** `null`
310 |
311 | **Input types affected:** `text`, `select-one`, `select-multiple`
312 |
313 | **Usage:** Prepend a value to each item added/selected.
314 |
315 | ### appendValue
316 | **Type:** `String` **Default:** `null`
317 |
318 | **Input types affected:** `text`, `select-one`, `select-multiple`
319 |
320 | **Usage:** Append a value to each item added/selected.
321 |
322 | ### loadingText
323 | **Type:** `String` **Default:** `Loading...`
324 |
325 | **Input types affected:** `select-one`, `select-multiple`
326 |
327 | **Usage:** The text that is shown whilst choices are being populated via AJAX.
328 |
329 | ### noResultsText
330 | **Type:** `String` **Default:** `No results found`
331 |
332 | **Input types affected:** `select-one`, `select-multiple`
333 |
334 | **Usage:** The text that is shown when a user's search has returned no results.
335 |
336 | ### noChoicesText
337 | **Type:** `String` **Default:** `No choices to choose from`
338 |
339 | **Input types affected:** `select-multiple`
340 |
341 | **Usage:** The text that is shown when a user has selected all possible choices.
342 |
343 | ### itemSelectText
344 | **Type:** `String` **Default:** `Press to select`
345 |
346 | **Input types affected:** `select-multiple`, `select-one`
347 |
348 | **Usage:** The text that is shown when a user hovers over a selectable choice.
349 |
350 | ### addItemText
351 | **Type:** `String/Function` **Default:** `Press Enter to add "${value}"`
352 |
353 | **Input types affected:** `text`
354 |
355 | **Usage:** The text that is shown when a user has inputted a new item but has not pressed the enter key. To access the current input value, pass a function with a `value` argument (see the [default config](https://github.com/jshjohnson/Choices#setup) for an example), otherwise pass a string.
356 |
357 | ### maxItemText
358 | **Type:** `String/Function` **Default:** `Only ${maxItemCount} values can be added.`
359 |
360 | **Input types affected:** `text`
361 |
362 | **Usage:** The text that is shown when a user has focus on the input but has already reached the [max item count](https://github.com/jshjohnson/Choices#maxitemcount). To access the max item count, pass a function with a `maxItemCount` argument (see the [default config](https://github.com/jshjohnson/Choices#setup) for an example), otherwise pass a string.
363 |
364 | ### classNames
365 | **Type:** `Object` **Default:**
366 |
367 | ```
368 | classNames: {
369 | containerOuter: 'choices',
370 | containerInner: 'choices__inner',
371 | input: 'choices__input',
372 | inputCloned: 'choices__input--cloned',
373 | list: 'choices__list',
374 | listItems: 'choices__list--multiple',
375 | listSingle: 'choices__list--single',
376 | listDropdown: 'choices__list--dropdown',
377 | item: 'choices__item',
378 | itemSelectable: 'choices__item--selectable',
379 | itemDisabled: 'choices__item--disabled',
380 | itemOption: 'choices__item--choice',
381 | group: 'choices__group',
382 | groupHeading : 'choices__heading',
383 | button: 'choices__button',
384 | activeState: 'is-active',
385 | focusState: 'is-focused',
386 | openState: 'is-open',
387 | disabledState: 'is-disabled',
388 | highlightedState: 'is-highlighted',
389 | hiddenState: 'is-hidden',
390 | flippedState: 'is-flipped',
391 | selectedState: 'is-highlighted',
392 | }
393 | ```
394 |
395 | **Input types affected:** `text`, `select-one`, `select-multiple`
396 |
397 | **Usage:** Classes added to HTML generated by Choices. By default classnames follow the [BEM](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/) notation.
398 |
399 | ## Callbacks
400 | **Note:** For each callback, `this` refers to the current instance of Choices. This can be useful if you need access to methods (`this.disable()`) or the config object (`this.config`).
401 |
402 | ### callbackOnInit
403 | **Type:** `Function` **Default:** `null`
404 |
405 | **Input types affected:** `text`, `select-one`, `select-multiple`
406 |
407 | **Usage:** Function to run once Choices initialises.
408 |
409 | ### callbackOnAddItem
410 | **Type:** `Function` **Default:** `null` **Arguments:** `id, value, groupValue`
411 |
412 | **Input types affected:** `text`, `select-one`, `select-multiple`
413 |
414 | **Usage:** Function to run each time an item is added (programmatically or by the user).
415 |
416 | **Example:**
417 |
418 | ```js
419 | const example = new Choices(element, {
420 | callbackOnAddItem: (id, value, groupValue) => {
421 | // do something creative here...
422 | },
423 | };
424 | ```
425 |
426 | ### callbackOnRemoveItem
427 | **Type:** `Function` **Default:** `null` **Arguments:** `id, value, groupValue`
428 |
429 | **Input types affected:** `text`, `select-one`, `select-multiple`
430 |
431 | **Usage:** Function to run each time an item is removed (programmatically or by the user).
432 |
433 | ### callbackOnHighlightItem
434 | **Type:** `Function` **Default:** `null` **Arguments:** `id, value, groupValue`
435 |
436 | **Input types affected:** `text`, `select-multiple`
437 |
438 | **Usage:** Function to run each time an item is highlighted.
439 |
440 | ### callbackOnUnhighlightItem
441 | **Type:** `Function` **Default:** `null` **Arguments:** `id, value, groupValue`
442 |
443 | **Input types affected:** `text`, `select-multiple`
444 |
445 | **Usage:** Function to run each time an item is unhighlighted.
446 |
447 | ### callbackOnCreateTemplates
448 | **Type:** `Function` **Default:** `null` **Arguments:** `template`
449 |
450 | **Input types affected:** `text`, `select-one`, `select-multiple`
451 |
452 | **Usage:** Function to run on template creation. Through this callback it is possible to provide custom templates for the various components of Choices (see terminology). For Choices to work with custom templates, it is important you maintain the various data attributes defined [here](https://github.com/jshjohnson/Choices/blob/67f29c286aa21d88847adfcd6304dc7d068dc01f/assets/scripts/src/choices.js#L1993-L2067).
453 |
454 | **Example:**
455 |
456 | ```js
457 | const example = new Choices(element, {
458 | callbackOnCreateTemplates: function (template) {
459 | var classNames = this.config.classNames;
460 | return {
461 | item: (data) => {
462 | return template(`
463 |
464 | ★ ${data.label}
465 |
466 | `);
467 | },
468 | choice: (data) => {
469 | return template(`
470 | 0 ? 'role="treeitem"' : 'role="option"'}>
471 | ★ ${data.label}
472 |
473 | `);
474 | },
475 | };
476 | }
477 | });
478 | ```
479 |
480 | ### callbackOnChange
481 | **Type:** `Function` **Default:** `null` **Arguments:** `value`
482 |
483 | **Input types affected:** `text`, `select-one`, `select-multiple`
484 |
485 | **Usage:** Function to run each time an item is added/removed by a user.
486 |
487 | ### callbackOnSearch
488 | **Type:** `Function` **Default:** `null` **Arguments:** `value`
489 |
490 | **Input types affected:** `select-one`, `select-multiple`
491 |
492 | **Usage:** Function to run when a user types into an input to search choices.
493 |
494 | ## Methods
495 | Methods can be called either directly or by chaining:
496 |
497 | ```js
498 | // Calling a method by chaining
499 | const choices = new Choices(element, {
500 | addItems: false,
501 | removeItems: false,
502 | }).setValue(['Set value 1', 'Set value 2']).disable();
503 |
504 | // Calling a method directly
505 | const choices = new Choices(element, {
506 | addItems: false,
507 | removeItems: false,
508 | });
509 |
510 | choices.setValue(['Set value 1', 'Set value 2'])
511 | choices.disable();
512 | ```
513 |
514 | ### destroy();
515 | **Input types affected:** `text`, `select-multiple`, `select-one`
516 |
517 | **Usage:** Kills the instance of Choices, removes all event listeners and returns passed input to its initial state.
518 |
519 | ### init();
520 | **Input types affected:** `text`, `select-multiple`, `select-one`
521 |
522 | **Usage:** Creates a new instance of Choices, adds event listeners, creates templates and renders a Choices element to the DOM.
523 |
524 | **Note:** This is called implicitly when a new instance of Choices is created. This would be used after a Choices instance had already been destroyed (using `destroy()`).
525 |
526 | ### highlightAll();
527 | **Input types affected:** `text`, `select-multiple`
528 |
529 | **Usage:** Highlight each chosen item (selected items can be removed).
530 |
531 |
532 | ### unhighlightAll();
533 | **Input types affected:** `text`, `select-multiple`
534 |
535 | **Usage:** Un-highlight each chosen item.
536 |
537 |
538 | ### removeItemsByValue(value);
539 | **Input types affected:** `text`, `select-multiple`
540 |
541 | **Usage:** Remove each item by a given value.
542 |
543 |
544 | ### removeActiveItems(excludedId);
545 | **Input types affected:** `text`, `select-multiple`
546 |
547 | **Usage:** Remove each selectable item.
548 |
549 |
550 | ### removeHighlightedItems();
551 | **Input types affected:** `text`, `select-multiple`
552 |
553 | **Usage:** Remove each item the user has selected.
554 |
555 |
556 | ### showDropdown();
557 | **Input types affected:** `select-one`, `select-multiple`
558 |
559 | **Usage:** Show option list dropdown (only affects select inputs).
560 |
561 |
562 | ### hideDropdown();
563 | **Input types affected:** `text`, `select-multiple`
564 |
565 | **Usage:** Hide option list dropdown (only affects select inputs).
566 |
567 |
568 | ### toggleDropdown();
569 | **Input types affected:** `text`, `select-multiple`
570 |
571 | **Usage:** Toggle dropdown between showing/hidden.
572 |
573 | ### setChoices(choices, value, label, replaceChoices);
574 | **Input types affected:** `select-one`, `select-multiple`
575 |
576 | **Usage:** Set choices of select input via an array of objects, a value name and a label name. This behaves the same as passing items via the `choices` option but can be called after initialising Choices. This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices.
577 |
578 | **Example 1:**
579 |
580 | ```js
581 | const example = new Choices(element);
582 |
583 | example.setChoices([
584 | {value: 'One', label: 'Label One', disabled: true},
585 | {value: 'Two', label: 'Label Two' selected: true},
586 | {value: 'Three', label: 'Label Three'},
587 | ], 'value', 'label', false);
588 | ```
589 |
590 | **Example 2:**
591 |
592 | ```js
593 | const example = new Choices(element);
594 |
595 | example.setChoices([{
596 | label: 'Group one',
597 | id: 1,
598 | disabled: false,
599 | choices: [
600 | {value: 'Child One', label: 'Child One', selected: true},
601 | {value: 'Child Two', label: 'Child Two', disabled: true},
602 | {value: 'Child Three', label: 'Child Three'},
603 | ]
604 | },
605 | {
606 | label: 'Group two',
607 | id: 2,
608 | disabled: false,
609 | choices: [
610 | {value: 'Child Four', label: 'Child Four', disabled: true},
611 | {value: 'Child Five', label: 'Child Five'},
612 | {value: 'Child Six', label: 'Child Six'},
613 | ]
614 | }], 'value', 'label', false);
615 | ```
616 |
617 | ### getValue(valueOnly)
618 | **Input types affected:** `text`, `select-one`, `select-multiple`
619 |
620 | **Usage:** Get value(s) of input (i.e. inputted items (text) or selected choices (select)). Optionally pass an argument of `true` to only return values rather than value objects.
621 |
622 | **Example:**
623 |
624 | ```js
625 | const example = new Choices(element);
626 | const values = example.getValue(true); // returns ['value 1', 'value 2'];
627 | const valueArray = example.getValue(); // returns [{ active: true, choiceId: 1, highlighted: false, id: 1, label: 'Label 1', value: 'Value 1'}, { active: true, choiceId: 2, highlighted: false, id: 2, label: 'Label 2', value: 'Value 2'}];
628 | ```
629 |
630 | ### setValue(args);
631 | **Input types affected:** `text`
632 |
633 | **Usage:** Set value of input based on an array of objects or strings. This behaves exactly the same as passing items via the `items` option but can be called after initialising Choices.
634 |
635 | **Example:**
636 |
637 | ```js
638 | const example = new Choices(element);
639 |
640 | // via an array of objects
641 | example.setValue([
642 | {value: 'One', label: 'Label One'},
643 | {value: 'Two', label: 'Label Two'},
644 | {value: 'Three', label: 'Label Three'},
645 | ]);
646 |
647 | // or via an array of strings
648 | example.setValue(['Four','Five','Six']);
649 | ```
650 |
651 | ### setValueByChoice(value);
652 | **Input types affected:** `select-one`, `select-multiple`
653 |
654 | **Usage:** Set value of input based on existing Choice.
655 |
656 | **Example:**
657 |
658 | ```js
659 | const example = new Choices(element, {
660 | choices: [
661 | {value: 'One', label: 'Label One'},
662 | {value: 'Two', label: 'Label Two', disabled: true},
663 | {value: 'Three', label: 'Label Three'},
664 | ],
665 | });
666 |
667 | example.setValueByChoice('Two'); // Choice with value of 'Two' has now been selected.
668 | ```
669 |
670 | ### clearStore();
671 | **Input types affected:** `text`, `select-one`, `select-multiple`
672 |
673 | **Usage:** Removes all items, choices and groups. Use with caution.
674 |
675 |
676 | ### clearInput();
677 | **Input types affected:** `text`
678 |
679 | **Usage:** Clear input of any user inputted text.
680 |
681 |
682 | ### disable();
683 | **Input types affected:** `text`, `select-one`, `select-multiple`
684 |
685 | **Usage:** Disables input from accepting new value/sselecting further choices.
686 |
687 | ### enable();
688 | **Input types affected:** `text`, `select-one`, `select-multiple`
689 |
690 | **Usage:** Enables input to accept new values/select further choices.
691 |
692 |
693 | ### ajax(fn);
694 | **Input types affected:** `select-one`, `select-multiple`
695 |
696 | **Usage:** Populate choices/groups via a callback.
697 |
698 | **Example:**
699 |
700 | ```js
701 | var example = new Choices(element);
702 |
703 | example.ajax(function(callback) {
704 | fetch(url)
705 | .then(function(response) {
706 | response.json().then(function(data) {
707 | callback(data, 'value', 'label');
708 | });
709 | })
710 | .catch(function(error) {
711 | console.log(error);
712 | });
713 | });
714 | ```
715 |
716 |
717 | ## Browser compatibility
718 | Choices is compiled using [Babel](https://babeljs.io/) to enable support for [ES5 browsers](http://caniuse.com/#feat=es5). If you need to support a browser that does not support one of the features listed below, I suggest including a polyfill from the very good [polyfill.io](https://cdn.polyfill.io/v2/docs/):
719 |
720 | **Polyfill example used for the demo:**
721 |
722 | ```html
723 |
724 | ```
725 |
726 | **Features used in Choices:**
727 |
728 | * Array.prototype.forEach
729 | * Array.prototype.map
730 | * Array.prototype.find
731 | * Array.prototype.some
732 | * Array.prototype.reduce
733 | * Array.prototype.indexOf
734 | * Element.prototype.classList
735 | * window.requestAnimationFrame
736 |
737 | ## Development
738 | To setup a local environment: clone this repo, navigate into it's directory in a terminal window and run the following command:
739 |
740 | ```npm install```
741 |
742 | ### NPM tasks
743 | | Task | Usage |
744 | | ------------------- | ------------------------------------------------------------ |
745 | | `npm start` | Fire up local server for development |
746 | | `npm run js:build` | Compile Choices to an uglified JavaScript file |
747 | | `npm run css:watch` | Watch SCSS files for changes. On a change, run build process |
748 | | `npm run css:build` | Compile, minify and prefix SCSS files to CSS |
749 |
750 | ## Contributions
751 | In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using npm scripts...bla bla bla
752 |
753 | ## License
754 | MIT License
755 |
756 | ## Misc
757 | Thanks to [@mikefrancis](https://github.com/mikefrancis/) for [sending me on a hunt](https://twitter.com/_mikefrancis/status/701797835826667520) for a non-jQuery solution for select boxes that eventually led to this being built!
758 |
--------------------------------------------------------------------------------
/tests/spec/choices_spec.js:
--------------------------------------------------------------------------------
1 | import 'whatwg-fetch';
2 | import 'es6-promise';
3 | import Choices from '../../assets/scripts/src/choices.js';
4 |
5 | describe('Choices', () => {
6 |
7 | afterEach(function() {
8 | this.choices.destroy();
9 | });
10 |
11 | describe('should initialize Choices', () => {
12 |
13 | beforeEach(function() {
14 | this.input = document.createElement('input');
15 | this.input.type = "text";
16 | this.input.className = 'js-choices';
17 |
18 | document.body.appendChild(this.input);
19 | this.choices = new Choices(this.input);
20 | });
21 |
22 | it('should be defined', function() {
23 | expect(this.choices).toBeDefined();
24 | });
25 |
26 | it('should have initalised', function() {
27 | expect(this.choices.initialised).toBe(true);
28 | });
29 |
30 | it('should not re-initialise if passed element again', function() {
31 | const reinitialise = new Choices(this.choices.passedElement);
32 | spyOn(reinitialise, '_createTemplates');
33 | expect(reinitialise._createTemplates).not.toHaveBeenCalled();
34 | })
35 |
36 | it('should have a blank state', function() {
37 | expect(this.choices.currentState.items.length).toEqual(0);
38 | expect(this.choices.currentState.groups.length).toEqual(0);
39 | expect(this.choices.currentState.choices.length).toEqual(0);
40 | });
41 |
42 | it('should have config options', function() {
43 | expect(this.choices.config.items).toEqual(jasmine.any(Array));
44 | expect(this.choices.config.choices).toEqual(jasmine.any(Array));
45 | expect(this.choices.config.maxItemCount).toEqual(jasmine.any(Number));
46 | expect(this.choices.config.addItems).toEqual(jasmine.any(Boolean));
47 | expect(this.choices.config.removeItems).toEqual(jasmine.any(Boolean));
48 | expect(this.choices.config.removeItemButton).toEqual(jasmine.any(Boolean));
49 | expect(this.choices.config.editItems).toEqual(jasmine.any(Boolean));
50 | expect(this.choices.config.duplicateItems).toEqual(jasmine.any(Boolean));
51 | expect(this.choices.config.delimiter).toEqual(jasmine.any(String));
52 | expect(this.choices.config.paste).toEqual(jasmine.any(Boolean));
53 | expect(this.choices.config.search).toEqual(jasmine.any(Boolean));
54 | expect(this.choices.config.searchFloor).toEqual(jasmine.any(Number));
55 | expect(this.choices.config.flip).toEqual(jasmine.any(Boolean));
56 | expect(this.choices.config.regexFilter).toEqual(null);
57 | expect(this.choices.config.sortFilter).toEqual(jasmine.any(Function));
58 | expect(this.choices.config.sortFields).toEqual(jasmine.any(Array) || jasmine.any(String));
59 | expect(this.choices.config.shouldSort).toEqual(jasmine.any(Boolean));
60 | expect(this.choices.config.placeholder).toEqual(jasmine.any(Boolean));
61 | expect(this.choices.config.placeholderValue).toEqual(null);
62 | expect(this.choices.config.prependValue).toEqual(null);
63 | expect(this.choices.config.appendValue).toEqual(null);
64 | expect(this.choices.config.loadingText).toEqual(jasmine.any(String));
65 | expect(this.choices.config.noResultsText).toEqual(jasmine.any(String));
66 | expect(this.choices.config.noChoicesText).toEqual(jasmine.any(String));
67 | expect(this.choices.config.itemSelectText).toEqual(jasmine.any(String));
68 | expect(this.choices.config.classNames).toEqual(jasmine.any(Object));
69 | expect(this.choices.config.callbackOnInit).toEqual(null);
70 | expect(this.choices.config.callbackOnAddItem).toEqual(null);
71 | expect(this.choices.config.callbackOnRemoveItem).toEqual(null);
72 | expect(this.choices.config.callbackOnHighlightItem).toEqual(null);
73 | expect(this.choices.config.callbackOnUnhighlightItem).toEqual(null);
74 | expect(this.choices.config.callbackOnChange).toEqual(null);
75 | expect(this.choices.config.callbackOnSearch).toEqual(null);
76 | });
77 |
78 | it('should expose public methods', function() {
79 | expect(this.choices.init).toEqual(jasmine.any(Function));
80 | expect(this.choices.destroy).toEqual(jasmine.any(Function));
81 | expect(this.choices.render).toEqual(jasmine.any(Function));
82 | expect(this.choices.renderGroups).toEqual(jasmine.any(Function));
83 | expect(this.choices.renderItems).toEqual(jasmine.any(Function));
84 | expect(this.choices.renderChoices).toEqual(jasmine.any(Function));
85 | expect(this.choices.highlightItem).toEqual(jasmine.any(Function));
86 | expect(this.choices.unhighlightItem).toEqual(jasmine.any(Function));
87 | expect(this.choices.highlightAll).toEqual(jasmine.any(Function));
88 | expect(this.choices.unhighlightAll).toEqual(jasmine.any(Function));
89 | expect(this.choices.removeItemsByValue).toEqual(jasmine.any(Function));
90 | expect(this.choices.removeActiveItems).toEqual(jasmine.any(Function));
91 | expect(this.choices.removeHighlightedItems).toEqual(jasmine.any(Function));
92 | expect(this.choices.showDropdown).toEqual(jasmine.any(Function));
93 | expect(this.choices.hideDropdown).toEqual(jasmine.any(Function));
94 | expect(this.choices.toggleDropdown).toEqual(jasmine.any(Function));
95 | expect(this.choices.getValue).toEqual(jasmine.any(Function));
96 | expect(this.choices.setValue).toEqual(jasmine.any(Function));
97 | expect(this.choices.setValueByChoice).toEqual(jasmine.any(Function));
98 | expect(this.choices.setChoices).toEqual(jasmine.any(Function));
99 | expect(this.choices.disable).toEqual(jasmine.any(Function));
100 | expect(this.choices.enable).toEqual(jasmine.any(Function));
101 | expect(this.choices.ajax).toEqual(jasmine.any(Function));
102 | expect(this.choices.clearStore).toEqual(jasmine.any(Function));
103 | expect(this.choices.clearInput).toEqual(jasmine.any(Function));
104 | });
105 |
106 | it('should hide passed input', function() {
107 | expect(this.choices.passedElement.style.display).toEqual('none');
108 | });
109 |
110 | it('should create an outer container', function() {
111 | expect(this.choices.containerOuter).toEqual(jasmine.any(HTMLElement));
112 | });
113 |
114 | it('should create an inner container', function() {
115 | expect(this.choices.containerInner).toEqual(jasmine.any(HTMLElement));
116 | });
117 |
118 | it('should create a choice list', function() {
119 | expect(this.choices.choiceList).toEqual(jasmine.any(HTMLElement));
120 | });
121 |
122 | it('should create an item list', function() {
123 | expect(this.choices.itemList).toEqual(jasmine.any(HTMLElement));
124 | });
125 |
126 | it('should create an input', function() {
127 | expect(this.choices.input).toEqual(jasmine.any(HTMLElement));
128 | });
129 |
130 | // it('should create a dropdown', function() {
131 | // expect(this.choices.dropdown).toEqual(jasmine.any(HTMLElement));
132 | // });
133 | });
134 |
135 | describe('should accept text inputs', function() {
136 | beforeEach(function() {
137 | this.input = document.createElement('input');
138 | this.input.type = "text";
139 | this.input.className = 'js-choices';
140 | this.input.placeholder = 'Placeholder text';
141 |
142 | document.body.appendChild(this.input);
143 | });
144 |
145 | it('should accept a user inputted value', function() {
146 | this.choices = new Choices(this.input);
147 |
148 | this.choices.input.focus();
149 | this.choices.input.value = 'test';
150 |
151 | this.choices._onKeyDown({
152 | target: this.choices.input,
153 | keyCode: 13,
154 | ctrlKey: false
155 | });
156 |
157 | expect(this.choices.currentState.items[0].value).toContain(this.choices.input.value);
158 | });
159 |
160 | it('should copy the passed placeholder to the cloned input', function() {
161 | this.choices = new Choices(this.input);
162 |
163 | expect(this.choices.input.placeholder).toEqual(this.input.placeholder);
164 | });
165 |
166 | it('should not allow duplicates if duplicateItems is false', function() {
167 | this.choices = new Choices(this.input, {
168 | duplicateItems: false,
169 | items: ['test 1'],
170 | });
171 |
172 | this.choices.input.focus();
173 | this.choices.input.value = 'test 1';
174 |
175 | this.choices._onKeyDown({
176 | target: this.choices.input,
177 | keyCode: 13,
178 | ctrlKey: false
179 | });
180 |
181 | expect(this.choices.currentState.items[this.choices.currentState.items.length - 1]).not.toContain(this.choices.input.value);
182 | });
183 |
184 | it('should filter input if regexFilter is passed', function() {
185 | this.choices = new Choices(this.input, {
186 | regexFilter: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
187 | });
188 |
189 | this.choices.input.focus();
190 | this.choices.input.value = 'josh@joshuajohnson.co.uk';
191 |
192 | this.choices._onKeyDown({
193 | target: this.choices.input,
194 | keyCode: 13,
195 | ctrlKey: false
196 | });
197 |
198 | this.choices.input.focus();
199 | this.choices.input.value = 'not an email address';
200 |
201 | this.choices._onKeyDown({
202 | target: this.choices.input,
203 | keyCode: 13,
204 | ctrlKey: false
205 | });
206 |
207 | const lastItem = this.choices.currentState.items[this.choices.currentState.items.length - 1];
208 |
209 | expect(lastItem.value).toEqual('josh@joshuajohnson.co.uk');
210 | expect(lastItem.value).not.toEqual('not an email address');
211 | });
212 |
213 | it('should prepend and append values if passed', function() {
214 | this.choices = new Choices(this.input, {
215 | prependValue: 'item-',
216 | appendValue: '-value',
217 | });
218 |
219 | this.choices.input.focus();
220 | this.choices.input.value = 'test';
221 |
222 | this.choices._onKeyDown({
223 | target: this.choices.input,
224 | keyCode: 13,
225 | ctrlKey: false
226 | });
227 |
228 | const lastItem = this.choices.currentState.items[this.choices.currentState.items.length - 1];
229 |
230 | expect(lastItem.value).not.toEqual('test');
231 | expect(lastItem.value).toEqual('item-test-value');
232 | });
233 | });
234 |
235 | describe('should accept single select inputs', function() {
236 | beforeEach(function() {
237 | this.input = document.createElement('select');
238 | this.input.className = 'js-choices';
239 | this.input.placeholder = 'Placeholder text';
240 |
241 | for (let i = 1; i < 4; i++) {
242 | const option = document.createElement('option');
243 |
244 | option.value = `Value ${i}`;
245 | option.innerHTML = `Label ${i}`;
246 |
247 | this.input.appendChild(option);
248 | }
249 |
250 | document.body.appendChild(this.input);
251 | });
252 |
253 | it('should open the choice list on focussing', function() {
254 | this.choices = new Choices(this.input);
255 | this.choices.input.focus();
256 | expect(this.choices.dropdown.classList).toContain(this.choices.config.classNames.activeState);
257 | });
258 |
259 | it('should select the first choice', function() {
260 | this.choices = new Choices(this.input);
261 | expect(this.choices.currentState.items[0].value).toContain('Value 1');
262 | });
263 |
264 | it('should highlight the choices on keydown', function() {
265 | this.choices = new Choices(this.input);
266 | this.choices.input.focus();
267 |
268 | for (var i = 0; i < 2; i++) {
269 | // Key down to third choice
270 | this.choices._onKeyDown({
271 | target: this.choices.input,
272 | keyCode: 40,
273 | ctrlKey: false,
274 | preventDefault: () => {}
275 | });
276 | }
277 |
278 | expect(this.choices.highlightPosition).toBe(2);
279 | });
280 |
281 | it('should select choice on enter key press', function() {
282 | this.choices = new Choices(this.input);
283 | this.choices.input.focus();
284 |
285 | // Key down to second choice
286 | this.choices._onKeyDown({
287 | target: this.choices.input,
288 | keyCode: 40,
289 | ctrlKey: false,
290 | preventDefault: () => {}
291 | });
292 |
293 | // Key down to select choice
294 | this.choices._onKeyDown({
295 | target: this.choices.input,
296 | keyCode: 13,
297 | ctrlKey: false,
298 | preventDefault: () => {}
299 | });
300 |
301 | expect(this.choices.currentState.items.length).toBe(2);
302 | });
303 |
304 | it('should trigger a change callback on selection', function() {
305 | this.choices = new Choices(this.input);
306 | spyOn(this.choices.config, 'callbackOnChange');
307 | this.choices.input.focus();
308 |
309 | // Key down to second choice
310 | this.choices._onKeyDown({
311 | target: this.choices.input,
312 | keyCode: 40,
313 | ctrlKey: false,
314 | preventDefault: () => {}
315 | });
316 |
317 | // Key down to select choice
318 | this.choices._onKeyDown({
319 | target: this.choices.input,
320 | keyCode: 13,
321 | ctrlKey: false,
322 | preventDefault: () => {}
323 | });
324 |
325 | expect(this.choices.config.callbackOnChange).toHaveBeenCalledWith(jasmine.any(String));
326 | });
327 |
328 | it('should open the dropdown on click', function() {
329 | this.choices = new Choices(this.input);
330 | const container = this.choices.containerOuter;
331 | this.choices._onClick({
332 | target: container,
333 | ctrlKey: false,
334 | preventDefault: () => {}
335 | });
336 |
337 | expect(document.activeElement === this.choices.input && container.classList.contains('is-open')).toBe(true);
338 | });
339 |
340 | it('should close the dropdown on double click', function() {
341 | this.choices = new Choices(this.input);
342 | const container = this.choices.containerOuter;
343 |
344 | this.choices._onClick({
345 | target: container,
346 | ctrlKey: false,
347 | preventDefault: () => {}
348 | });
349 |
350 | this.choices._onClick({
351 | target: container,
352 | ctrlKey: false,
353 | preventDefault: () => {}
354 | });
355 |
356 | expect(document.activeElement === this.choices.input && container.classList.contains('is-open')).toBe(false);
357 | });
358 |
359 | it('should filter choices when searching', function() {
360 | this.choices = new Choices(this.input);
361 | this.choices.input.focus();
362 | this.choices.input.value = 'Value 3';
363 |
364 | // Key down to search
365 | this.choices._onKeyUp({
366 | target: this.choices.input,
367 | keyCode: 13,
368 | ctrlKey: false
369 | });
370 |
371 | const mostAccurateResult = this.choices.currentState.choices[0];
372 |
373 | expect(this.choices.isSearching && mostAccurateResult.value === 'Value 3').toBeTruthy;
374 | });
375 |
376 | it('shouldn\'t sort choices if shouldSort is false', function() {
377 | this.choices = new Choices(this.input, {
378 | shouldSort: false,
379 | choices: [{
380 | value: 'Value 5',
381 | label: 'Label Five'
382 | }, {
383 | value: 'Value 6',
384 | label: 'Label Six'
385 | }, {
386 | value: 'Value 7',
387 | label: 'Label Seven'
388 | }, ],
389 | });
390 |
391 | expect(this.choices.currentState.choices[0].value).toEqual('Value 5');
392 | });
393 |
394 | it('should sort choices if shouldSort is false', function() {
395 | this.choices = new Choices(this.input, {
396 | shouldSort: true,
397 | choices: [{
398 | value: 'Value 5',
399 | label: 'Label Five'
400 | }, {
401 | value: 'Value 6',
402 | label: 'Label Six'
403 | }, {
404 | value: 'Value 7',
405 | label: 'Label Seven'
406 | }, ],
407 | });
408 |
409 | expect(this.choices.currentState.choices[0].value).toEqual('Value 1');
410 | });
411 | });
412 |
413 | describe('should accept multiple select inputs', function() {
414 | beforeEach(function() {
415 | this.input = document.createElement('select');
416 | this.input.className = 'js-choices';
417 | this.input.setAttribute('multiple', '');
418 |
419 | for (let i = 1; i < 4; i++) {
420 | const option = document.createElement('option');
421 |
422 | option.value = `Value ${i}`;
423 | option.innerHTML = `Value ${i}`;
424 |
425 | if (i % 2) {
426 | option.selected = true;
427 | }
428 |
429 | this.input.appendChild(option);
430 | }
431 |
432 | document.body.appendChild(this.input);
433 |
434 | this.choices = new Choices(this.input, {
435 | placeholderValue: 'Placeholder text',
436 | choices: [{
437 | value: 'One',
438 | label: 'Label One',
439 | selected: true,
440 | disabled: false
441 | }, {
442 | value: 'Two',
443 | label: 'Label Two',
444 | disabled: true
445 | }, {
446 | value: 'Three',
447 | label: 'Label Three'
448 | }, ],
449 | });
450 | });
451 |
452 | it('should add any pre-defined values', function() {
453 | expect(this.choices.currentState.items.length).toBeGreaterThan(1);
454 | });
455 |
456 | it('should add options defined in the config + pre-defined options', function() {
457 | expect(this.choices.currentState.choices.length).toEqual(6);
458 | });
459 |
460 | it('should add a placeholder defined in the config to the search input', function() {
461 | expect(this.choices.input.placeholder).toEqual('Placeholder text');
462 | });
463 | });
464 |
465 | describe('should handle public methods on select input types', function() {
466 | beforeEach(function() {
467 | this.input = document.createElement('select');
468 | this.input.className = 'js-choices';
469 | this.input.multiple = true;
470 | this.input.placeholder = 'Placeholder text';
471 |
472 | for (let i = 1; i < 10; i++) {
473 | const option = document.createElement('option');
474 |
475 | option.value = `Value ${i}`;
476 | option.innerHTML = `Value ${i}`;
477 |
478 | if (i % 2) {
479 | option.selected = true;
480 | }
481 |
482 | this.input.appendChild(option);
483 | }
484 |
485 | document.body.appendChild(this.input);
486 | this.choices = new Choices(this.input);
487 | });
488 |
489 | it('should handle highlightItem()', function() {
490 | const items = this.choices.currentState.items;
491 | const randomItem = items[Math.floor(Math.random() * items.length)];
492 |
493 | this.choices.highlightItem(randomItem);
494 |
495 | expect(randomItem.highlighted).toBe(true);
496 | });
497 |
498 | it('should handle unhighlightItem()', function() {
499 | const items = this.choices.currentState.items;
500 | const randomItem = items[Math.floor(Math.random() * items.length)];
501 |
502 | this.choices.unhighlightItem(randomItem);
503 |
504 | expect(randomItem.highlighted).toBe(false);
505 | });
506 |
507 | it('should handle highlightAll()', function() {
508 | const items = this.choices.currentState.items;
509 |
510 | this.choices.highlightAll();
511 |
512 | const unhighlightedItems = items.some((item) => item.highlighted === false);
513 |
514 | expect(unhighlightedItems).toBe(false);
515 | });
516 |
517 | it('should handle unhighlightAll()', function() {
518 | const items = this.choices.currentState.items;
519 |
520 | this.choices.unhighlightAll();
521 |
522 | const highlightedItems = items.some((item) => item.highlighted === true);
523 |
524 | expect(highlightedItems).toBe(false);
525 | });
526 |
527 | it('should handle removeHighlightedItems()', function() {
528 | const items = this.choices.currentState.items;
529 | this.choices.highlightAll();
530 | this.choices.removeHighlightedItems();
531 |
532 | const activeItems = items.some((item) => item.active === true);
533 |
534 | expect(activeItems).toBe(false);
535 | });
536 |
537 | it('should handle showDropdown()', function() {
538 | this.choices.showDropdown();
539 | const hasOpenState = this.choices.containerOuter.classList.contains(this.choices.config.classNames.openState);
540 | const hasAttr = this.choices.containerOuter.getAttribute('aria-expanded') === 'true';
541 | const hasActiveState = this.choices.dropdown.classList.contains(this.choices.config.classNames.activeState);
542 | expect(hasOpenState && hasAttr && hasActiveState).toBe(true);
543 | });
544 |
545 | it('should handle hideDropdown()', function() {
546 | this.choices.showDropdown();
547 | this.choices.hideDropdown();
548 | const hasOpenState = this.choices.containerOuter.classList.contains(this.choices.config.classNames.openState);
549 | const hasAttr = this.choices.containerOuter.getAttribute('aria-expanded') === 'true';
550 | const hasActiveState = this.choices.dropdown.classList.contains(this.choices.config.classNames.activeState);
551 |
552 | expect(hasOpenState && hasAttr && hasActiveState).toBe(false);
553 | });
554 |
555 | it('should handle toggleDropdown()', function() {
556 | spyOn(this.choices, 'hideDropdown');
557 | this.choices.showDropdown();
558 | this.choices.toggleDropdown();
559 | expect(this.choices.hideDropdown).toHaveBeenCalled();
560 | });
561 |
562 | it('should handle hideDropdown()', function() {
563 | this.choices.showDropdown();
564 | expect(this.choices.containerOuter.classList).toContain(this.choices.config.classNames.openState);
565 | });
566 |
567 | it('should handle getValue()', function() {
568 | const valueObjects = this.choices.getValue();
569 | const valueStrings = this.choices.getValue(true);
570 |
571 | expect(valueStrings[0]).toEqual(jasmine.any(String));
572 | expect(valueObjects[0]).toEqual(jasmine.any(Object));
573 | expect(valueObjects).toEqual(jasmine.any(Array));
574 | expect(valueObjects.length).toEqual(5);
575 | });
576 |
577 | it('should handle setValue()', function() {
578 | this.choices.setValue(['Set value 1', 'Set value 2', 'Set value 3']);
579 | const valueStrings = this.choices.getValue(true);
580 |
581 | expect(valueStrings[valueStrings.length - 1]).toBe('Set value 3');
582 | expect(valueStrings[valueStrings.length - 2]).toBe('Set value 2');
583 | expect(valueStrings[valueStrings.length - 3]).toBe('Set value 1');
584 | });
585 |
586 | it('should handle setValueByChoice()', function() {
587 | const choices = this.choices.store.getChoicesFilteredByActive();
588 | const randomChoice = choices[Math.floor(Math.random() * choices.length)];
589 |
590 | this.choices.highlightAll();
591 | this.choices.removeHighlightedItems();
592 | this.choices.setValueByChoice(randomChoice.value);
593 |
594 | const value = this.choices.getValue(true);
595 |
596 | expect(value[0]).toBe(randomChoice.value);
597 | });
598 |
599 | it('should handle setChoices()', function() {
600 | this.choices.setChoices([{
601 | label: 'Group one',
602 | id: 1,
603 | disabled: false,
604 | choices: [{
605 | value: 'Child One',
606 | label: 'Child One',
607 | selected: true
608 | }, {
609 | value: 'Child Two',
610 | label: 'Child Two',
611 | disabled: true
612 | }, {
613 | value: 'Child Three',
614 | label: 'Child Three'
615 | }, ]
616 | }, {
617 | label: 'Group two',
618 | id: 2,
619 | disabled: false,
620 | choices: [{
621 | value: 'Child Four',
622 | label: 'Child Four',
623 | disabled: true
624 | }, {
625 | value: 'Child Five',
626 | label: 'Child Five'
627 | }, {
628 | value: 'Child Six',
629 | label: 'Child Six'
630 | }, ]
631 | }], 'value', 'label');
632 |
633 |
634 | const groups = this.choices.currentState.groups;
635 | const choices = this.choices.currentState.choices;
636 |
637 | expect(groups[groups.length - 1].value).toEqual('Group two');
638 | expect(groups[groups.length - 2].value).toEqual('Group one');
639 | expect(choices[choices.length - 1].value).toEqual('Child Six');
640 | expect(choices[choices.length - 2].value).toEqual('Child Five');
641 | });
642 |
643 | it('should handle setChoices() with blank values', function() {
644 | this.choices.setChoices([{
645 | label: 'Choice one',
646 | value: 'one'
647 | }, {
648 | label: 'Choice two',
649 | value: ''
650 | }], 'value', 'label', true);
651 |
652 |
653 | const choices = this.choices.currentState.choices;
654 | expect(choices[0].value).toEqual('one');
655 | expect(choices[1].value).toEqual('');
656 | });
657 |
658 | it('should handle clearStore()', function() {
659 | this.choices.clearStore();
660 |
661 | expect(this.choices.currentState.items).toEqual([]);
662 | expect(this.choices.currentState.choices).toEqual([]);
663 | expect(this.choices.currentState.groups).toEqual([]);
664 | });
665 |
666 | it('should handle disable()', function() {
667 | this.choices.disable();
668 |
669 | expect(this.choices.input.disabled).toBe(true);
670 | expect(this.choices.containerOuter.classList.contains(this.choices.config.classNames.disabledState)).toBe(true);
671 | expect(this.choices.containerOuter.getAttribute('aria-disabled')).toBe('true');
672 | });
673 |
674 | it('should handle enable()', function() {
675 | this.choices.enable();
676 |
677 | expect(this.choices.input.disabled).toBe(false);
678 | expect(this.choices.containerOuter.classList.contains(this.choices.config.classNames.disabledState)).toBe(false);
679 | expect(this.choices.containerOuter.hasAttribute('aria-disabled')).toBe(false);
680 | });
681 |
682 | it('should handle ajax()', function() {
683 | spyOn(this.choices, 'ajax');
684 |
685 | this.choices.ajax((callback) => {
686 | fetch('https://restcountries.eu/rest/v1/all')
687 | .then((response) => {
688 | response.json().then((data) => {
689 | callback(data, 'alpha2Code', 'name');
690 | });
691 | })
692 | .catch((error) => {
693 | console.log(error);
694 | });
695 | });
696 |
697 | expect(this.choices.ajax).toHaveBeenCalledWith(jasmine.any(Function));
698 | });
699 | });
700 |
701 | describe('should handle public methods on text input types', function() {
702 | beforeEach(function() {
703 | this.input = document.createElement('input');
704 | this.input.type = "text";
705 | this.input.className = 'js-choices';
706 | this.input.value = "Value 1, Value 2, Value 3, Value 4";
707 |
708 | document.body.appendChild(this.input);
709 | this.choices = new Choices(this.input);
710 | });
711 |
712 | it('should handle clearInput()', function() {
713 | this.choices.clearInput();
714 | expect(this.choices.input.value).toBe('');
715 | });
716 |
717 | it('should handle removeItemsByValue()', function() {
718 | const items = this.choices.currentState.items;
719 | const randomItem = items[Math.floor(Math.random() * items.length)];
720 |
721 | this.choices.removeItemsByValue(randomItem.value);
722 | expect(randomItem.active).toBe(false);
723 | });
724 | });
725 | });
726 |
--------------------------------------------------------------------------------
/assets/scripts/dist/choices.min.js:
--------------------------------------------------------------------------------
1 | /*! choices.js v2.5.1 | (c) 2016 Josh Johnson | https://github.com/jshjohnson/Choices#readme */
2 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Choices=t():e.Choices=t()}(this,function(){return function(e){function t(n){if(i[n])return i[n].exports;var s=i[n]={exports:{},id:n,loaded:!1};return e[n].call(s.exports,s,s.exports,t),s.loaded=!0,s.exports}var i={};return t.m=e,t.c=i,t.p="/assets/scripts/dist/",t(0)}([function(e,t,i){e.exports=i(1)},function(e,t,i){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function s(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function o(e){if(Array.isArray(e)){for(var t=0,i=Array(e.length);t0&&void 0!==arguments[0]?arguments[0]:"[data-choice]",n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(r(this,e),(0,f.isType)("String",i)){var s=document.querySelectorAll(i);if(s.length>1)for(var o=1;o"'+e+'"'},maxItemText:function(e){return"Only "+e+" values can be added."},uniqueItemText:"Only unique values can be added.",classNames:{containerOuter:"choices",containerInner:"choices__inner",input:"choices__input",inputCloned:"choices__input--cloned",list:"choices__list",listItems:"choices__list--multiple",listSingle:"choices__list--single",listDropdown:"choices__list--dropdown",item:"choices__item",itemSelectable:"choices__item--selectable",itemDisabled:"choices__item--disabled",itemChoice:"choices__item--choice",placeholder:"choices__placeholder",group:"choices__group",groupHeading:"choices__heading",button:"choices__button",activeState:"is-active",focusState:"is-focused",openState:"is-open",disabledState:"is-disabled",highlightedState:"is-highlighted",hiddenState:"is-hidden",flippedState:"is-flipped",loadingState:"is-loading"},callbackOnInit:null,callbackOnAddItem:null,callbackOnRemoveItem:null,callbackOnHighlightItem:null,callbackOnUnhighlightItem:null,callbackOnCreateTemplates:null,callbackOnChange:null,callbackOnSearch:null};if(this.config=(0,f.extend)(c,n),this.store=new h.default(this.render),this.initialised=!1,this.currentState={},this.prevState={},this.currentValue="",this.element=i,this.passedElement=(0,f.isType)("String",i)?document.querySelector(i):i,this.isSelectElement="select-one"===this.passedElement.type||"select-multiple"===this.passedElement.type,this.isTextElement="text"===this.passedElement.type,!this.passedElement)return void console.error("Passed element not found");this.highlightPosition=0,this.canSearch=this.config.search,this.presetChoices=this.config.choices,this.presetItems=this.config.items,this.passedElement.value&&(this.presetItems=this.presetItems.concat(this.passedElement.value.split(this.config.delimiter))),this.init=this.init.bind(this),this.render=this.render.bind(this),this.destroy=this.destroy.bind(this),this.disable=this.disable.bind(this),this._onFocus=this._onFocus.bind(this),this._onBlur=this._onBlur.bind(this),this._onKeyUp=this._onKeyUp.bind(this),this._onKeyDown=this._onKeyDown.bind(this),this._onClick=this._onClick.bind(this),this._onTouchMove=this._onTouchMove.bind(this),this._onTouchEnd=this._onTouchEnd.bind(this),this._onMouseDown=this._onMouseDown.bind(this),this._onMouseOver=this._onMouseOver.bind(this),this._onPaste=this._onPaste.bind(this),this._onInput=this._onInput.bind(this),this.wasTap=!0;var l="classList"in document.documentElement;l||console.error("Choices: Your browser doesn't support Choices");var u=["select-one","select-multiple","text"].some(function(e){return e===t.passedElement.type}),d=(0,f.isElement)(this.passedElement)&&u;if(d){if("active"===this.passedElement.getAttribute("data-choice"))return;this.init()}else console.error("Incompatible input passed")}return a(e,[{key:"init",value:function(){if(this.initialised!==!0){var e=this.config.callbackOnInit;this.initialised=!0,this._createTemplates(),this._createInput(),this.store.subscribe(this.render),this.render(),this._addEventListeners(),e&&((0,f.isType)("Function",e)?e.call(this):console.error("callbackOnInit: Callback is not a function"))}}},{key:"destroy",value:function(){this.initialised!==!1&&(this._removeEventListeners(),this.passedElement.classList.remove(this.config.classNames.input,this.config.classNames.hiddenState),this.passedElement.removeAttribute("tabindex"),this.passedElement.removeAttribute("style","display:none;"),this.passedElement.removeAttribute("aria-hidden"),this.passedElement.removeAttribute("data-choice","active"),this.passedElement.value=this.passedElement.value,this.containerOuter.parentNode.insertBefore(this.passedElement,this.containerOuter),this.containerOuter.parentNode.removeChild(this.containerOuter),this.clearStore(),this.config.templates=null,this.initialised=!1)}},{key:"renderGroups",value:function(e,t,i){var n=this,s=i||document.createDocumentFragment(),o=this.config.sortFilter;return this.config.shouldSort&&e.sort(o),e.forEach(function(e){var i=t.filter(function(t){return"select-one"===n.passedElement.type?t.groupId===e.id:t.groupId===e.id&&!t.selected});if(i.length>=1){var o=n._getTemplate("choiceGroup",e);s.appendChild(o),n.renderChoices(i,s)}}),s}},{key:"renderChoices",value:function(e,t){var i=this,n=t||document.createDocumentFragment(),s=this.isSearching?f.sortByScore:this.config.sortFilter;return(this.config.shouldSort||this.isSearching)&&e.sort(s),e.forEach(function(e){var t=i._getTemplate("choice",e),s="select-one"===i.passedElement.type||!e.selected;s&&n.appendChild(t)}),n}},{key:"renderItems",value:function(e,t){var i=this,n=t||document.createDocumentFragment(),s=this.store.getItemsReducedToValues(e);return this.isTextElement?this.passedElement.setAttribute("value",s.join(this.config.delimiter)):!function(){var t=document.createDocumentFragment();e.forEach(function(e){var n=i._getTemplate("option",e);t.appendChild(n)}),i.passedElement.innerHTML="",i.passedElement.appendChild(t)}(),e.forEach(function(e){var t=i._getTemplate("item",e);n.appendChild(t)}),n}},{key:"render",value:function(){if(this.currentState=this.store.getState(),this.currentState!==this.prevState){if(!(this.currentState.choices===this.prevState.choices&&this.currentState.groups===this.prevState.groups||"select-multiple"!==this.passedElement.type&&"select-one"!==this.passedElement.type)){var e=this.store.getGroupsFilteredByActive(),t=this.store.getChoicesFilteredByActive(),i=document.createDocumentFragment();if(this.choiceList.innerHTML="",this.choiceList.scrollTop=0,e.length>=1&&this.isSearching!==!0?i=this.renderGroups(e,t,i):t.length>=1&&(i=this.renderChoices(t,i)),i.childNodes&&i.childNodes.length>0)this.choiceList.appendChild(i),this._highlightChoice();else{var n=this.isSearching?this._getTemplate("notice",this.config.noResultsText):this._getTemplate("notice",this.config.noChoicesText);this.choiceList.appendChild(n)}}if(this.currentState.items!==this.prevState.items){var s=this.store.getItemsFilteredByActive();if(s){var o=this.renderItems(s);this.itemList.innerHTML="",o.childNodes&&this.itemList.appendChild(o)}}this.prevState=this.currentState}}},{key:"highlightItem",value:function(e){if(e){var t=e.id,i=e.groupId,n=this.config.callbackOnHighlightItem;if(this.store.dispatch((0,d.highlightItem)(t,!0)),n)if((0,f.isType)("Function",n)){var s=i>=0?this.store.getGroupById(i):null;s&&s.value?n.call(this,t,e.value,s.value):n.call(this,t,e.value)}else console.error("callbackOnHighlightItem: Callback is not a function");return this}}},{key:"unhighlightItem",value:function(e){if(e){var t=e.id,i=e.groupId,n=this.config.callbackOnUnhighlightItem;if(this.store.dispatch((0,d.highlightItem)(t,!1)),n)if((0,f.isType)("Function",n)){var s=i>=0?this.store.getGroupById(i):null;s&&s.value?n.call(this,t,e.value,s.value):n.call(this,t,e.value)}else console.error("callbackOnUnhighlightItem: Callback is not a function");return this}}},{key:"highlightAll",value:function(){var e=this,t=this.store.getItems();return t.forEach(function(t){e.highlightItem(t)}),this}},{key:"unhighlightAll",value:function(){var e=this,t=this.store.getItems();return t.forEach(function(t){e.unhighlightItem(t)}),this}},{key:"removeItemsByValue",value:function(e){var t=this;if(!e||!(0,f.isType)("String",e))return void console.error("removeItemsByValue: No value was passed to be removed");var i=this.store.getItemsFilteredByActive();return i.forEach(function(i){i.value===e&&t._removeItem(i)}),this}},{key:"removeActiveItems",value:function(e){var t=this,i=this.store.getItemsFilteredByActive();return i.forEach(function(i){i.active&&e!==i.id&&t._removeItem(i)}),this}},{key:"removeHighlightedItems",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]&&arguments[0],i=this.store.getItemsFilteredByActive();return i.forEach(function(i){i.highlighted&&i.active&&(e._removeItem(i),t&&e._triggerChange(i.value))}),this}},{key:"showDropdown",value:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=document.body,i=document.documentElement,n=Math.max(t.scrollHeight,t.offsetHeight,i.clientHeight,i.scrollHeight,i.offsetHeight);this.containerOuter.classList.add(this.config.classNames.openState),this.containerOuter.setAttribute("aria-expanded","true"),this.dropdown.classList.add(this.config.classNames.activeState);var s=this.dropdown.getBoundingClientRect(),o=Math.ceil(s.top+window.scrollY+s.height),r=!!this.config.flip&&o>=n;return r?this.containerOuter.classList.add(this.config.classNames.flippedState):this.containerOuter.classList.remove(this.config.classNames.flippedState),e&&this.canSearch&&document.activeElement!==this.input&&this.input.focus(),this}},{key:"hideDropdown",value:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=this.containerOuter.classList.contains(this.config.classNames.flippedState);return this.containerOuter.classList.remove(this.config.classNames.openState),this.containerOuter.setAttribute("aria-expanded","false"),this.dropdown.classList.remove(this.config.classNames.activeState),t&&this.containerOuter.classList.remove(this.config.classNames.flippedState),e&&this.canSearch&&document.activeElement===this.input&&this.input.blur(),this}},{key:"toggleDropdown",value:function(){var e=this.dropdown.classList.contains(this.config.classNames.activeState);return e?this.hideDropdown():this.showDropdown(!0),this}},{key:"getValue",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]&&arguments[0],i=this.store.getItemsFilteredByActive(),n=[];return i.forEach(function(i){e.isTextElement?n.push(t?i.value:i):i.active&&n.push(t?i.value:i)}),"select-one"===this.passedElement.type?n[0]:n}},{key:"setValue",value:function(e){var t=this;return this.initialised===!0&&!function(){var i=[].concat(o(e)),n=t.passedElement.type;i.forEach(function(e){if((0,f.isType)("Object",e)){if(!e.value)return;"text"!==n?t._addChoice(!0,!1,e.value,e.label,-1):t._addItem(e.value,e.label,e.id)}else(0,f.isType)("String",e)&&("text"!==n?t._addChoice(!0,!1,e,e,-1):t._addItem(e))})}(),this}},{key:"setValueByChoice",value:function(e){var t=this;return"text"!==this.passedElement.type&&!function(){var i=t.store.getChoices(),n=(0,f.isType)("Array",e)?e:[e];n.forEach(function(e){var n=i.find(function(t){return t.value===e});n?n.selected?console.warn("Attempting to select choice already selected"):t._addItem(n.value,n.label,n.id,n.groupId):console.warn("Attempting to select choice that does not exist")})}(),this}},{key:"setChoices",value:function(e,t,i){var n=this,s=arguments.length>3&&void 0!==arguments[3]&&arguments[3];if(this.initialised===!0&&this.isSelectElement){if(!(0,f.isType)("Array",e)||!t)return;s&&this._clearChoices(),e&&e.length&&(this.containerOuter.classList.remove(this.config.classNames.loadingState),e.forEach(function(e,s){var o=!!e.selected&&e.selected,r=!!e.disabled&&e.disabled;e.choices?n._addGroup(e,s,t,i):n._addChoice(o,r,e[t],e[i])}))}return this}},{key:"clearStore",value:function(){return this.store.dispatch((0,d.clearAll)()),this}},{key:"clearInput",value:function(){return this.input.value&&(this.input.value=""),"select-one"!==this.passedElement.type&&this._setInputWidth(),"text"!==this.passedElement.type&&this.config.search&&(this.isSearching=!1,this.store.dispatch((0,d.activateChoices)(!0))),this}},{key:"enable",value:function(){this.passedElement.disabled=!1;var e=this.containerOuter.classList.contains(this.config.classNames.disabledState);return this.initialised&&e&&(this._addEventListeners(),this.passedElement.removeAttribute("disabled"),this.input.removeAttribute("disabled"),this.containerOuter.classList.remove(this.config.classNames.disabledState),this.containerOuter.removeAttribute("aria-disabled")),this}},{key:"disable",value:function(){this.passedElement.disabled=!0;var e=!this.containerOuter.classList.contains(this.config.classNames.disabledState);return this.initialised&&e&&(this._removeEventListeners(),this.passedElement.setAttribute("disabled",""),this.input.setAttribute("disabled",""),this.containerOuter.classList.add(this.config.classNames.disabledState),this.containerOuter.setAttribute("aria-disabled","true")),this}},{key:"ajax",value:function(e){return this.initialised===!0&&this.isSelectElement&&(this._handleLoadingState(!0),e(this._ajaxCallback())),this}},{key:"_triggerChange",value:function(e){if(e){var t=this.config.callbackOnChange;t&&((0,f.isType)("Function",t)?t.call(this,e):console.error("callbackOnChange: Callback is not a function"))}}},{key:"_handleButtonAction",value:function(e,t){var i=this;e&&t&&this.config.removeItems&&this.config.removeItemButton&&!function(){var n=t.parentNode.getAttribute("data-id"),s=e.find(function(e){return e.id===parseInt(n,10)});if(i._removeItem(s),i._triggerChange(s.value),"select-one"===i.passedElement.type){var o=!!i.config.placeholder&&(i.config.placeholderValue||i.passedElement.getAttribute("placeholder"));if(o){var r=i._getTemplate("placeholder",o);i.itemList.appendChild(r)}}}()}},{key:"_handleItemAction",value:function(e,t){var i=this,n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];e&&t&&this.config.removeItems&&"select-one"!==this.passedElement.type&&!function(){var s=t.getAttribute("data-id");e.forEach(function(e){e.id!==parseInt(s,10)||e.highlighted?n||e.highlighted&&i.unhighlightItem(e):i.highlightItem(e)}),document.activeElement!==i.input&&i.input.focus()}()}},{key:"_handleChoiceAction",value:function(e,t){if(e&&t){var i=t.getAttribute("data-id"),n=this.store.getChoiceById(i),s=this.dropdown.classList.contains(this.config.classNames.activeState);if(n&&!n.selected&&!n.disabled){var o=this._canAddItem(e,n.value);o.response&&(this._addItem(n.value,n.label,n.id,n.groupId),this._triggerChange(n.value))}this.clearInput(this.passedElement),s&&"select-one"===this.passedElement.type&&(this.hideDropdown(),this.containerOuter.focus())}}},{key:"_handleBackspace",value:function(e){if(this.config.removeItems&&e){var t=e[e.length-1],i=e.some(function(e){return e.highlighted===!0});this.config.editItems&&!i&&t?(this.input.value=t.value,this._setInputWidth(),this._removeItem(t),this._triggerChange(t.value)):(i||this.highlightItem(t),this.removeHighlightedItems(!0))}}},{key:"_canAddItem",value:function(e,t){var i=!0,n=(0,f.isType)("Function",this.config.addItemText)?this.config.addItemText(t):this.config.addItemText;if("select-multiple"!==this.passedElement.type&&"text"!==this.passedElement.type||this.config.maxItemCount>0&&this.config.maxItemCount<=this.itemList.children.length&&(i=!1,n=(0,f.isType)("Function",this.config.maxItemText)?this.config.maxItemText(this.config.maxItemCount):this.config.maxItemText),"text"===this.passedElement.type&&this.config.addItems){var s=!e.some(function(e){return e.value===t.trim()});this.config.regexFilter&&(i=this._regexFilter(t)),this.config.duplicateItems!==!1||s||(i=!1,n=(0,f.isType)("Function",this.config.uniqueItemText)?this.config.uniqueItemText(t):this.config.uniqueItemText)}return{response:i,notice:n}}},{key:"_handleLoadingState",value:function(){var e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],t=this.itemList.querySelector("."+this.config.classNames.placeholder);if(e)this.containerOuter.classList.add(this.config.classNames.loadingState),this.containerOuter.setAttribute("aria-busy","true"),"select-one"===this.passedElement.type?t?t.innerHTML=this.config.loadingText:(t=this._getTemplate("placeholder",this.config.loadingText),this.itemList.appendChild(t)):this.input.placeholder=this.config.loadingText;else{this.containerOuter.classList.remove(this.config.classNames.loadingState);var i=!!this.config.placeholder&&(this.config.placeholderValue||this.passedElement.getAttribute("placeholder"));"select-one"===this.passedElement.type?t.innerHTML=i||"":this.input.placeholder=i||""}}},{key:"_ajaxCallback",value:function(){var e=this;return function(t,i,n){if(t&&i){var s=(0,f.isType)("Object",t)?[t]:t;s&&(0,f.isType)("Array",s)&&s.length&&(e._handleLoadingState(!1),s.forEach(function(t,s){var o=!!t.selected&&t.selected,r=!!t.disabled&&t.disabled;t.choices?e._addGroup(t,s,i,n):e._addChoice(o,r,t[i],t[n])})),e.containerOuter.removeAttribute("aria-busy")}}}},{key:"_searchChoices",value:function(e){var t=(0,f.isType)("String",e)?e.trim():e,i=(0,f.isType)("String",this.currentValue)?this.currentValue.trim():this.currentValue;if(t.length>=1&&t!==i+" "){var n=this.store.getChoicesFilteredBySelectable(),s=t,o=(0,f.isType)("Array",this.config.sortFields)?this.config.sortFields:[this.config.sortFields],r=new l.default(n,{keys:o,shouldSort:!0,include:"score"}),a=r.search(s);this.currentValue=t,this.highlightPosition=0,this.isSearching=!0,this.store.dispatch((0,d.filterChoices)(a))}}},{key:"_handleSearch",value:function(e){if(e){var t=this.store.getChoices(),i=t.some(function(e){return e.active!==!0}),n=this.config.callbackOnSearch;this.input===document.activeElement&&(e&&e.length>this.config.searchFloor?(this._searchChoices(e),n&&((0,f.isType)("Function",n)?n.call(this,e):console.error("callbackOnSearch: Callback is not a function"))):i&&(this.isSearching=!1,this.store.dispatch((0,d.activateChoices)(!0))))}}},{key:"_addEventListeners",value:function(){document.addEventListener("keyup",this._onKeyUp),document.addEventListener("keydown",this._onKeyDown),document.addEventListener("click",this._onClick),document.addEventListener("touchmove",this._onTouchMove),document.addEventListener("touchend",this._onTouchEnd),document.addEventListener("mousedown",this._onMouseDown),document.addEventListener("mouseover",this._onMouseOver),this.passedElement.type&&"select-one"===this.passedElement.type&&(this.containerOuter.addEventListener("focus",this._onFocus),this.containerOuter.addEventListener("blur",this._onBlur)),this.input.addEventListener("input",this._onInput),this.input.addEventListener("paste",this._onPaste),this.input.addEventListener("focus",this._onFocus),this.input.addEventListener("blur",this._onBlur)}},{key:"_removeEventListeners",value:function(){document.removeEventListener("keyup",this._onKeyUp),document.removeEventListener("keydown",this._onKeyDown),document.removeEventListener("click",this._onClick),document.removeEventListener("touchmove",this._onTouchMove),document.removeEventListener("touchend",this._onTouchEnd),document.removeEventListener("mousedown",this._onMouseDown),document.removeEventListener("mouseover",this._onMouseOver),this.passedElement.type&&"select-one"===this.passedElement.type&&(this.containerOuter.removeEventListener("focus",this._onFocus),this.containerOuter.removeEventListener("blur",this._onBlur)),this.input.removeEventListener("input",this._onInput),this.input.removeEventListener("paste",this._onPaste),this.input.removeEventListener("focus",this._onFocus),this.input.removeEventListener("blur",this._onBlur)}},{key:"_setInputWidth",value:function(){if(this.config.placeholder&&(this.config.placeholderValue||this.passedElement.getAttribute("placeholder"))){var e=!!this.config.placeholder&&(this.config.placeholderValue||this.passedElement.getAttribute("placeholder"));this.input.value&&this.input.value.length>=e.length/1.25&&(this.input.style.width=(0,f.getWidthOfInput)(this.input))}else this.input.style.width=(0,f.getWidthOfInput)(this.input)}},{key:"_onKeyDown",value:function(e){var t,i=this;if(e.target===this.input||this.containerOuter.contains(e.target)){var n=e.target,o=this.passedElement.type,r=this.store.getItemsFilteredByActive(),a=this.input===document.activeElement,c=this.dropdown.classList.contains(this.config.classNames.activeState),l=this.itemList&&this.itemList.children,u=String.fromCharCode(e.keyCode),h=46,d=8,p=13,v=65,m=27,g=38,y=40,b=e.ctrlKey||e.metaKey;"text"!==o&&/[a-zA-Z0-9-_ ]/.test(u)&&!c&&this.showDropdown(!0),this.canSearch=this.config.search;var _=function(){b&&l&&(i.canSearch=!1,i.config.removeItems&&!i.input.value&&i.input===document.activeElement&&i.highlightAll(i.itemList.children))},E=function(){if("text"===o&&n.value){var t=i.input.value,s=i._canAddItem(r,t);s.response&&(c&&i.hideDropdown(),i._addItem(t),i._triggerChange(t),i.clearInput(i.passedElement))}if(n.hasAttribute("data-button")&&(i._handleButtonAction(r,n),e.preventDefault()),c){e.preventDefault();var a=i.dropdown.querySelector("."+i.config.classNames.highlightedState);a&&i._handleChoiceAction(r,a)}else"select-one"===o&&(c||(i.showDropdown(!0),e.preventDefault()))},I=function(){c&&i.toggleDropdown()},S=function(){if(c||"select-one"===o){c||i.showDropdown(!0);var t=i.dropdown.querySelector("."+i.config.classNames.highlightedState),n=e.keyCode===y?1:-1,s=void 0;i.canSearch=!1,s=t?(0,f.getAdjacentEl)(t,"[data-choice-selectable]",n):i.dropdown.querySelector("[data-choice-selectable]"),s&&((0,f.isScrolledIntoView)(s,i.choiceList,n)||i._scrollToChoice(s,n),i._highlightChoice(s)),e.preventDefault()}},w=function(){a&&!e.target.value&&"select-one"!==o&&(i._handleBackspace(r),e.preventDefault())},T=(t={},s(t,v,_),s(t,p,E),s(t,m,I),s(t,g,S),s(t,y,S),s(t,d,w),s(t,h,w),t);T[e.keyCode]&&T[e.keyCode]()}}},{key:"_onKeyUp",value:function(e){if(e.target===this.input)if(this.isTextElement){var t=this.dropdown.classList.contains(this.config.classNames.activeState),i=this.input.value;if(i){var n=this.store.getItemsFilteredByActive(),s=this._canAddItem(n,i);if(s.notice){var o=this._getTemplate("notice",s.notice);this.dropdown.innerHTML=o.outerHTML}s.response===!0?t||this.showDropdown():!s.notice&&t&&this.hideDropdown()}else t&&this.hideDropdown()}else{var r=46,a=8;e.keyCode!==r&&e.keyCode!==a||e.target.value?this.canSearch&&this._handleSearch(this.input.value):"text"!==this.passedElement.type&&this.isSearching&&(this.isSearching=!1,this.store.dispatch((0,d.activateChoices)(!0)))}}},{key:"_onInput",value:function(){"select-one"!==this.passedElement.type&&this._setInputWidth()}},{key:"_onTouchMove",value:function(){this.wasTap===!0&&(this.wasTap=!1)}},{key:"_onTouchEnd",value:function(e){var t=e.target||e.touches[0].target,i=this.dropdown.classList.contains(this.config.classNames.activeState);this.wasTap===!0&&this.containerOuter.contains(t)&&(t!==this.containerOuter&&t!==this.containerInner||"select-one"===this.passedElement.type||(this.isTextElement?document.activeElement!==this.input&&this.input.focus():i||this.showDropdown(!0)),e.stopPropagation()),this.wasTap=!0}},{key:"_onMouseDown",value:function(e){var t=e.target;if(this.containerOuter.contains(t)&&t!==this.input){var i=this.store.getItemsFilteredByActive(),n=e.shiftKey;t.hasAttribute("data-item")?this._handleItemAction(i,t,n):t.hasAttribute("data-choice")&&this._handleChoiceAction(i,t),e.preventDefault()}}},{key:"_onClick",value:function(e){var t=e.target,i=this.dropdown.classList.contains(this.config.classNames.activeState),n=this.store.getItemsFilteredByActive();if(this.containerOuter.contains(t))t.hasAttribute("data-button")&&this._handleButtonAction(n,t),i?"select-one"!==this.passedElement.type||t===this.input||this.dropdown.contains(t)||this.hideDropdown(!0):this.isTextElement?document.activeElement!==this.input&&this.input.focus():this.canSearch?this.showDropdown(!0):(this.showDropdown(),this.containerOuter.focus());else{var s=n.some(function(e){return e.highlighted===!0});s&&this.unhighlightAll(),this.containerOuter.classList.remove(this.config.classNames.focusState),i&&this.hideDropdown()}}},{key:"_onMouseOver",value:function(e){(e.target===this.dropdown||this.dropdown.contains(e.target))&&e.target.hasAttribute("data-choice")&&this._highlightChoice(e.target)}},{key:"_onPaste",value:function(e){e.target!==this.input||this.config.paste||e.preventDefault()}},{key:"_onFocus",value:function(e){var t=this,i=e.target;this.containerOuter.contains(i)&&!function(){var e=t.dropdown.classList.contains(t.config.classNames.activeState),n={text:function(){i===t.input&&t.containerOuter.classList.add(t.config.classNames.focusState)},"select-one":function(){t.containerOuter.classList.add(t.config.classNames.focusState),i===t.input&&(e||t.showDropdown())},"select-multiple":function(){i===t.input&&(t.containerOuter.classList.add(t.config.classNames.focusState),e||t.showDropdown(!0))}};n[t.passedElement.type]()}()}},{key:"_onBlur",value:function(e){var t=this,i=e.target;this.containerOuter.contains(i)&&!function(){var e=t.store.getItemsFilteredByActive(),n=t.dropdown.classList.contains(t.config.classNames.activeState),s=e.some(function(e){return e.highlighted===!0}),o={text:function(){i===t.input&&(t.containerOuter.classList.remove(t.config.classNames.focusState),s&&t.unhighlightAll(),n&&t.hideDropdown())},"select-one":function(){t.containerOuter.classList.remove(t.config.classNames.focusState),i===t.containerOuter&&n&&!t.canSearch&&t.hideDropdown(),i===t.input&&n&&t.hideDropdown()},"select-multiple":function(){i===t.input&&(t.containerOuter.classList.remove(t.config.classNames.focusState),n&&t.hideDropdown(),s&&t.unhighlightAll())}};o[t.passedElement.type]()}()}},{key:"_regexFilter",value:function(e){if(e){var t=this.config.regexFilter,i=new RegExp(t.source,"i");return i.test(e)}}},{key:"_scrollToChoice",value:function(e,t){var i=this;if(e){var n=this.choiceList.offsetHeight,s=e.offsetHeight,o=e.offsetTop+s,r=this.choiceList.scrollTop+n,a=t>0?this.choiceList.scrollTop+o-r:e.offsetTop,c=function e(){var n=4,s=i.choiceList.scrollTop,o=!1,r=void 0,c=void 0;t>0?(r=(a-s)/n,c=r>1?r:1,i.choiceList.scrollTop=s+c,s1?r:1,i.choiceList.scrollTop=s-c,s>a&&(o=!0)),o&&requestAnimationFrame(function(i){e(i,a,t)})};requestAnimationFrame(function(e){c(e,a,t)})}}},{key:"_highlightChoice",value:function(e){var t=this,i=Array.from(this.dropdown.querySelectorAll("[data-choice-selectable]"));if(i&&i.length){var n=Array.from(this.dropdown.querySelectorAll("."+this.config.classNames.highlightedState));if(n.forEach(function(e){e.classList.remove(t.config.classNames.highlightedState),e.setAttribute("aria-selected","false")}),e)e.classList.add(this.config.classNames.highlightedState),this.highlightPosition=i.indexOf(e);else{var s=void 0;s=i.length>this.highlightPosition?i[this.highlightPosition]:i[i.length-1],s||(s=i[0]),s.classList.add(this.config.classNames.highlightedState),s.setAttribute("aria-selected","true")}}}},{key:"_addItem",value:function(e,t){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:-1,n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:-1,s=(0,f.isType)("String",e)?e.trim():e,o=this.store.getItems(),r=t||s,a=parseInt(i,10)||-1,c=this.config.callbackOnAddItem;this.config.prependValue&&(s=this.config.prependValue+s.toString()),this.config.appendValue&&(s+=this.config.appendValue.toString());var l=o?o.length+1:1;if(this.store.dispatch((0,d.addItem)(s,r,l,a,n)),"select-one"===this.passedElement.type&&this.removeActiveItems(l),c){var u=n>=0?this.store.getGroupById(n):null;(0,f.isType)("Function",c)?u&&u.value?c.call(this,l,s,u.value):c.call(this,l,s):console.error("callbackOnAddItem: Callback is not a function")}return this}},{key:"_removeItem",value:function(e){if(!e||!(0,f.isType)("Object",e))return void console.error("removeItem: No item object was passed to be removed");var t=e.id,i=e.value,n=e.choiceId,s=e.groupId,o=this.config.callbackOnRemoveItem;if(this.store.dispatch((0,d.removeItem)(t,n)),o)if((0,f.isType)("Function",o)){var r=s>=0?this.store.getGroupById(s):null;r&&r.value?o.call(this,t,i,r.value):o.call(this,t,i)}else console.error("callbackOnRemoveItem: Callback is not a function");return this}},{key:"_addChoice",value:function(e,t,i,n){var s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:-1;if("undefined"!=typeof i&&null!==i){var o=this.store.getChoices(),r=n||i,a=o?o.length+1:1;this.store.dispatch((0,d.addChoice)(i,r,a,s,t)),e&&this._addItem(i,r,a)}}},{key:"_clearChoices",value:function(){this.store.dispatch((0,d.clearChoices)())}},{key:"_addGroup",value:function(e,t){var i=this,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"value",s=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"label",o=(0,f.isType)("Object",e)?e.choices:Array.from(e.getElementsByTagName("OPTION")),r=t,a=!!e.disabled&&e.disabled;o?(this.store.dispatch((0,d.addGroup)(e.label,r,!0,a)),o.forEach(function(e){var t=e.disabled||e.parentNode&&e.parentNode.disabled||!1,o=!!e.selected&&e.selected,a=void 0;a=(0,f.isType)("Object",e)?e[s]||e[n]:e.innerHTML,i._addChoice(o,t,e[n],a,r)})):this.store.dispatch((0,d.addGroup)(e.label,e.id,!1,e.disabled))}},{key:"_getTemplate",value:function(e){if(e){for(var t=this.config.templates,i=arguments.length,n=Array(i>1?i-1:0),s=1;s\n ')},containerInner:function(){return(0,f.strToEl)('\n
\n ')},itemList:function(){return(0,f.strToEl)('\n
\n ')},placeholder:function(e){return(0,f.strToEl)('\n '+e+"
\n ")},item:function(i){return e.config.removeItemButton?(0,f.strToEl)('\n \n "+i.label+'Remove item \n
\n '):(0,f.strToEl)('\n \n "+i.label+"\n
\n ");
3 | },choiceList:function(){return(0,f.strToEl)('\n
\n ")},choiceGroup:function(e){return(0,f.strToEl)('\n \n ")},choice:function(i){return(0,f.strToEl)('\n 0?'role="treeitem"':'role="option"')+">\n "+i.label+"\n
\n ")},input:function(){return(0,f.strToEl)('\n \n ')},dropdown:function(){return(0,f.strToEl)('\n
\n ')},notice:function(e){return(0,f.strToEl)('\n '+e+"
\n ")},option:function(e){return(0,f.strToEl)('\n '+e.label+" \n ")}},n=this.config.callbackOnCreateTemplates,s={};n&&(0,f.isType)("Function",n)&&(s=n.call(this,f.strToEl)),this.config.templates=(0,f.extend)(i,s)}},{key:"_createInput",value:function(){var e=this,t=this.passedElement.getAttribute("dir")||"ltr",i=this._getTemplate("containerOuter",t),n=this._getTemplate("containerInner"),s=this._getTemplate("itemList"),o=this._getTemplate("choiceList"),r=this._getTemplate("input"),a=this._getTemplate("dropdown"),c=!!this.config.placeholder&&(this.config.placeholderValue||this.passedElement.getAttribute("placeholder"));if(this.containerOuter=i,this.containerInner=n,this.input=r,this.choiceList=o,this.itemList=s,this.dropdown=a,this.passedElement.classList.add(this.config.classNames.input,this.config.classNames.hiddenState),this.passedElement.tabIndex="-1",this.passedElement.setAttribute("style","display:none;"),this.passedElement.setAttribute("aria-hidden","true"),this.passedElement.setAttribute("data-choice","active"),(0,f.wrap)(this.passedElement,n),(0,f.wrap)(n,i),c&&(r.placeholder=c,"select-one"!==this.passedElement.type&&(r.style.width=(0,f.getWidthOfInput)(r))),this.config.addItems||this.disable(),i.appendChild(n),i.appendChild(a),n.appendChild(s),"text"!==this.passedElement.type&&a.appendChild(o),"select-multiple"===this.passedElement.type||"text"===this.passedElement.type?n.appendChild(r):this.canSearch&&a.insertBefore(r,a.firstChild),"select-multiple"===this.passedElement.type||"select-one"===this.passedElement.type){var l=Array.from(this.passedElement.getElementsByTagName("OPTGROUP"));this.highlightPosition=0,this.isSearching=!1,l&&l.length?l.forEach(function(t,i){e._addGroup(t,i)}):!function(){var t=Array.from(e.passedElement.options),i=e.config.sortFilter,n=e.presetChoices;t.forEach(function(e){n.push({value:e.value,label:e.innerHTML,selected:e.selected,disabled:e.disabled||e.parentNode.disabled})}),e.config.shouldSort&&n.sort(i);var s=n.some(function(e){return e.selected===!0});n.forEach(function(t,i){var n=!!t.disabled&&t.disabled,o=!!t.selected&&t.selected;"select-one"===e.passedElement.type?s||!s&&i>0?e._addChoice(o,n,t.value,t.label):e._addChoice(!0,!1,t.value,t.label):e._addChoice(o,n,t.value,t.label)})}()}else this.isTextElement&&this.presetItems.forEach(function(t){if((0,f.isType)("Object",t)){if(!t.value)return;e._addItem(t.value,t.label,t.id)}else(0,f.isType)("String",t)&&e._addItem(t)})}}]),e}();e.exports=p},function(e,t,i){!function(t){"use strict";function i(){console.log.apply(console,arguments)}function n(e,t){var i;this.list=e,this.options=t=t||{};for(i in a)a.hasOwnProperty(i)&&("boolean"==typeof a[i]?this.options[i]=i in t?t[i]:a[i]:this.options[i]=t[i]||a[i])}function s(e,t,i){var n,r,a,c,l,u;if(t){if(a=t.indexOf("."),a!==-1?(n=t.slice(0,a),r=t.slice(a+1)):n=t,c=e[n],null!==c&&void 0!==c)if(r||"string"!=typeof c&&"number"!=typeof c)if(o(c))for(l=0,u=c.length;l1)throw new Error("Key weight has to be > 0 and <= 1");e=e.name}else this._keyMap[e]={weight:1};this._analyze(e,o(u,e,[]),u,i)}},n.prototype._analyze=function(e,t,n,s){var r,a,c,l,u,h,d,f,p,v,m,g,y,b,_,E=this.options,I=!1;if(void 0!==t&&null!==t){a=[];var S=0;if("string"==typeof t){if(r=t.split(E.tokenSeparator),E.verbose&&i("---------\nKey:",e),this.options.tokenize){for(b=0;b=this.tokenSearchers.length,E.verbose&&i("Check Matches",y),(I||d.isMatch)&&y&&(c=this.resultMap[s],c?c.output.push({key:e,score:u,matchedIndices:d.matchedIndices}):(this.resultMap[s]={item:n,output:[{key:e,score:u,matchedIndices:d.matchedIndices}]},this.results.push(this.resultMap[s])))}else if(o(t))for(b=0;b0){if(t={item:r.item},u.indexOf("matches")!==-1)for(n=r.output,t.matches=[],i=0;iw.maxPatternLength){if(y=e.match(new RegExp(this.pattern.replace(w.tokenSeparator,"|"))),b=!!y)for(E=[],t=0,I=y.length;t=h;i--)if(v=this.patternAlphabet[e.charAt(i-1)],v&&(_[i-1]=1),0===t?f[i]=(f[i+1]<<1|1)&v:f[i]=(f[i+1]<<1|1)&v|((p[i+1]|p[i])<<1|1)|p[i+1],f[i]&this.matchmask&&(m=this._bitapScore(t,i-1),m<=r)){if(r=m,a=i-1,g.push(a),!(a>o))break;h=Math.max(1,2*o-a)}if(this._bitapScore(t+1,o)>r)break;p=f}return E=this._getMatchedIndices(_),{isMatch:a>=0,score:0===m?.001:m,matchedIndices:E}},r.prototype._getMatchedIndices=function(e){for(var t,i=[],n=-1,s=-1,o=0,r=e.length;o=this.options.minMatchCharLength&&i.push([n,s]),n=-1);return e[o-1]&&o-1-n+1>=this.options.minMatchCharLength&&i.push([n,o-1]),i},e.exports=n}(this)},function(e,t,i){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function s(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e,t){for(var i=0;i0&&void 0!==arguments[0]?arguments[0]:this.getItems(),t=e.reduce(function(e,t){return e.push(t.value),e},[]);return t}},{key:"getChoices",value:function(){var e=this.store.getState();return e.choices}},{key:"getChoicesFilteredByActive",value:function(){var e=this.getChoices(),t=e.filter(function(e){return e.active===!0},[]);return t}},{key:"getChoicesFilteredBySelectable",value:function(){var e=this.getChoices(),t=e.filter(function(e){return e.disabled!==!0},[]);return t}},{key:"getChoiceById",value:function(e){if(e){var t=this.getChoicesFilteredByActive(),i=t.find(function(t){return t.id===parseInt(e,10)});return i}return!1}},{key:"getGroups",value:function(){var e=this.store.getState();return e.groups}},{key:"getGroupsFilteredByActive",value:function(){var e=this.getGroups(),t=this.getChoices(),i=e.filter(function(e){var i=e.active===!0&&e.disabled===!1,n=t.some(function(e){return e.active===!0&&e.disabled===!1});return i&&n},[]);return i}},{key:"getGroupById",value:function(e){var t=this.getGroups(),i=t.find(function(t){return t.id===e});return i}}]),e}();t.default=l,e.exports=l},function(e,t,i){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0,t.compose=t.applyMiddleware=t.bindActionCreators=t.combineReducers=t.createStore=void 0;var s=i(5),o=n(s),r=i(20),a=n(r),c=i(22),l=n(c),u=i(23),h=n(u),d=i(24),f=n(d),p=i(21);n(p);t.createStore=o.default,t.combineReducers=a.default,t.bindActionCreators=l.default,t.applyMiddleware=h.default,t.compose=f.default},function(e,t,i){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function s(e,t,i){function n(){g===m&&(g=m.slice())}function o(){return v}function a(e){if("function"!=typeof e)throw new Error("Expected listener to be a function.");var t=!0;return n(),g.push(e),function(){if(t){t=!1,n();var i=g.indexOf(e);g.splice(i,1)}}}function u(e){if(!(0,r.default)(e))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if("undefined"==typeof e.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(y)throw new Error("Reducers may not dispatch actions.");try{y=!0,v=p(v,e)}finally{y=!1}for(var t=m=g,i=0;i0&&void 0!==arguments[0]?arguments[0]:[],t=arguments[1];switch(t.type){case"ADD_ITEM":var n=[].concat(i(e),[{id:t.id,choiceId:t.choiceId,groupId:t.groupId,value:t.value,label:t.label,active:!0,highlighted:!1}]);return n.map(function(e){return e.highlighted&&(e.highlighted=!1),e});case"REMOVE_ITEM":return e.map(function(e){return e.id===t.id&&(e.active=!1),e});case"HIGHLIGHT_ITEM":return e.map(function(e){return e.id===t.id&&(e.highlighted=t.highlighted),e});default:return e}};t.default=n},function(e,t){"use strict";function i(e){if(Array.isArray(e)){for(var t=0,i=Array(e.length);t0&&void 0!==arguments[0]?arguments[0]:[],t=arguments[1];switch(t.type){case"ADD_GROUP":return[].concat(i(e),[{id:t.id,value:t.value,active:t.active,disabled:t.disabled}]);case"CLEAR_CHOICES":return e.groups=[];default:return e}};t.default=n},function(e,t){"use strict";function i(e){if(Array.isArray(e)){for(var t=0,i=Array(e.length);t0&&void 0!==arguments[0]?arguments[0]:[],t=arguments[1];switch(t.type){case"ADD_CHOICE":return[].concat(i(e),[{id:t.id,groupId:t.groupId,value:t.value,label:t.label,disabled:t.disabled,selected:!1,active:!0,score:9999}]);case"ADD_ITEM":var s=e;return t.activateOptions&&(s=e.map(function(e){return e.active=t.active,e})),t.choiceId>-1&&(s=e.map(function(e){return e.id===parseInt(t.choiceId,10)&&(e.selected=!0),e})),s;case"REMOVE_ITEM":return t.choiceId>-1?e.map(function(e){return e.id===parseInt(t.choiceId,10)&&(e.selected=!1),e}):e;case"FILTER_CHOICES":var o=function(){var i=t.results,n=e.map(function(e){return e.active=i.some(function(t){return t.item.id===e.id&&(e.score=t.score,!0)}),e});return{v:n}}();if("object"===("undefined"==typeof o?"undefined":n(o)))return o.v;case"ACTIVATE_CHOICES":return e.map(function(e){return e.active=t.active,e});case"CLEAR_CHOICES":return e.choices=[];default:return e}};t.default=s},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.addItem=function(e,t,i,n,s){return{type:"ADD_ITEM",value:e,label:t,id:i,choiceId:n,groupId:s}},t.removeItem=function(e,t){return{type:"REMOVE_ITEM",id:e,choiceId:t}},t.highlightItem=function(e,t){return{type:"HIGHLIGHT_ITEM",id:e,highlighted:t}},t.addChoice=function(e,t,i,n,s){return{type:"ADD_CHOICE",value:e,label:t,id:i,groupId:n,disabled:s}},t.filterChoices=function(e){return{type:"FILTER_CHOICES",results:e}},t.activateChoices=function(){var e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];return{type:"ACTIVATE_CHOICES",active:e}},t.clearChoices=function(){return{type:"CLEAR_CHOICES"}},t.addGroup=function(e,t,i,n){return{type:"ADD_GROUP",value:e,id:t,active:i,disabled:n}},t.clearAll=function(){return{type:"CLEAR_ALL"}}},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n=(t.capitalise=function(e){return e.replace(/\w\S*/g,function(e){return e.charAt(0).toUpperCase()+e.substr(1).toLowerCase()})},t.isType=function(e,t){var i=Object.prototype.toString.call(t).slice(8,-1);return void 0!==t&&null!==t&&i===e}),s=(t.isNode=function(e){return"object"===("undefined"==typeof Node?"undefined":i(Node))?e instanceof Node:e&&"object"===("undefined"==typeof e?"undefined":i(e))&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName},t.isElement=function(e){return"object"===("undefined"==typeof HTMLElement?"undefined":i(HTMLElement))?e instanceof HTMLElement:e&&"object"===("undefined"==typeof e?"undefined":i(e))&&null!==e&&1===e.nodeType&&"string"==typeof e.nodeName},t.extend=function e(){for(var t={},i=arguments.length,s=function(i){for(var s in i)Object.prototype.hasOwnProperty.call(i,s)&&(n("Object",i[s])?t[s]=e(!0,t[s],i[s]):t[s]=i[s])},o=0;o=0?t:0},t.getElementOffset=function(e,t){var i=t;return i>1&&(i=1),i>0&&(i=0),Math.max(e.offsetHeight*i)},t.getAdjacentEl=function(e,t){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;if(e&&t){var n=e.parentNode.parentNode,s=Array.from(n.querySelectorAll(t)),o=s.indexOf(e),r=i>0?1:-1;return s[o+r]}},t.getScrollPosition=function(e){return"bottom"===e?Math.max((window.scrollY||window.pageYOffset)+(window.innerHeight||document.documentElement.clientHeight)):window.scrollY||window.pageYOffset},t.isInView=function(e,t,i){return this.getScrollPosition(t)>this.getElemDistance(e)+this.getElementOffset(e,i)},t.isScrolledIntoView=function(e,t){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;if(e){var n=void 0;return n=i>0?t.scrollTop+t.offsetHeight>=e.offsetTop+e.offsetHeight:e.offsetTop>=t.scrollTop}},t.stripHTML=function(e){var t=document.createElement("DIV");return t.innerHTML=e,t.textContent||t.innerText||""},t.addAnimation=function(e,t){var i=s(),n=function n(){e.classList.remove(t),e.removeEventListener(i,n,!1)};e.classList.add(t),e.addEventListener(i,n,!1)},t.getRandomNumber=function(e,t){return Math.floor(Math.random()*(t-e)+e)},t.strToEl=function(){var e=document.createElement("div");return function(t){var i;for(e.innerHTML=t,i=e.children[0];e.firstChild;)e.removeChild(e.firstChild);return i}}());t.getWidthOfInput=function(e){var t=e.value||e.placeholder,i=e.offsetWidth;if(t){var n=o(""+t+" ");n.style.position="absolute",n.style.padding="0",n.style.top="-9999px",n.style.left="-9999px",n.style.width="auto",n.style.whiteSpace="pre",document.body.appendChild(n),t&&n.offsetWidth!==e.offsetWidth&&(i=n.offsetWidth+4),document.body.removeChild(n)}return i+"px"},t.sortByAlpha=function(e,t){var i=(e.label||e.value).toLowerCase(),n=(t.label||t.value).toLowerCase();return in?1:0},t.sortByScore=function(e,t){return e.score-t.score}},function(e,t){"use strict";Array.from||(Array.from=function(){var e=Object.prototype.toString,t=function(t){return"function"==typeof t||"[object Function]"===e.call(t)},i=function(e){var t=Number(e);return isNaN(t)?0:0!==t&&isFinite(t)?(t>0?1:-1)*Math.floor(Math.abs(t)):t},n=Math.pow(2,53)-1,s=function(e){var t=i(e);return Math.min(Math.max(t,0),n)};return function(e){var i=this,n=Object(e);if(null==e)throw new TypeError("Array.from requires an array-like object - not null or undefined");var o,r=arguments.length>1?arguments[1]:void 0;if("undefined"!=typeof r){if(!t(r))throw new TypeError("Array.from: when provided, the second argument must be a function");arguments.length>2&&(o=arguments[2])}for(var a,c=s(n.length),l=t(i)?Object(new i(c)):new Array(c),u=0;u>>0,s=arguments[1],o=0;o