mirror of
https://github.com/Choices-js/Choices.git
synced 2024-05-02 22:03:11 +02:00
Initial styling + ability to highlight all and remove highlighted items
This commit is contained in:
parent
ecee8cff47
commit
449b735461
2
assets/scripts/dist/bundle.js
vendored
2
assets/scripts/dist/bundle.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
import { wrap, getSiblings, isType } from './lib/utils.js';
|
||||
import { hasClass, wrap, getSiblings, isType } from './lib/utils.js';
|
||||
|
||||
class Choices {
|
||||
constructor(options) {
|
||||
|
@ -9,8 +9,10 @@ class Choices {
|
|||
const DEFAULT_OPTIONS = {
|
||||
element: document.querySelector('[data-choice]'),
|
||||
disabled: false,
|
||||
maxItems: 5,
|
||||
debug: true,
|
||||
create: true,
|
||||
maxItems: 10,
|
||||
allowDuplicates: false,
|
||||
debug: false,
|
||||
placeholder: false,
|
||||
callbackOnInit: function(){},
|
||||
callbackOnRender: function(){},
|
||||
|
@ -20,8 +22,6 @@ class Choices {
|
|||
callbackOnRemove: function(){}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Merge options with user options
|
||||
this.options = this.extend(DEFAULT_OPTIONS, USER_OPTIONS || {});
|
||||
this.initialised = false;
|
||||
|
@ -95,31 +95,72 @@ class Choices {
|
|||
|
||||
/* Event handling */
|
||||
|
||||
onKeyUp(e) {
|
||||
// console.log('Keyup');
|
||||
}
|
||||
|
||||
onKeyDown(e) {
|
||||
let ctrlDown = e.ctrlKey || e.metaKey;
|
||||
|
||||
// Handle select all
|
||||
if(ctrlDown && e.keyCode === 65) {
|
||||
for (let i = 0; i < this.list.children.length; i++) {
|
||||
let listItem = this.list.children[i];
|
||||
|
||||
listItem.classList.add('is-selected');
|
||||
}
|
||||
}
|
||||
|
||||
// Handle enter key
|
||||
if(e.keyCode === 13 && e.target.value) {
|
||||
let value = this.input.value;
|
||||
|
||||
let handleEnterKey = () => {
|
||||
this.addItem(value);
|
||||
this.updateInputValue(value);
|
||||
this.clearInput(this.element);
|
||||
let canUpdate = true;
|
||||
|
||||
// If there is a max entry limit and we have reached that limit
|
||||
// don't update
|
||||
if(this.options.maxItems && this.options.maxItems <= this.list.children.length) {
|
||||
canUpdate = false;
|
||||
}
|
||||
|
||||
// If no duplicates are allowed, and the value already exists
|
||||
// in the array, don't update
|
||||
if(this.options.allowDuplicates === false && this.element.value) {
|
||||
let currentValues = JSON.parse(this.element.value);
|
||||
|
||||
if(currentValues.indexOf(value) > -1) {
|
||||
canUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
// All is good, update
|
||||
if(canUpdate) {
|
||||
this.addItem(value);
|
||||
this.updateInputValue(value);
|
||||
this.clearInput(this.element);
|
||||
}
|
||||
};
|
||||
|
||||
if(this.options.maxItems) {
|
||||
if(this.options.maxItems > this.list.children.length) {
|
||||
handleEnterKey();
|
||||
}
|
||||
} else {
|
||||
handleEnterKey();
|
||||
}
|
||||
handleEnterKey();
|
||||
}
|
||||
|
||||
if(e.keyCode === 8 && !e.target.value) {
|
||||
if((e.keyCode === 8 || e.keyCode === 46) && !e.target.value) {
|
||||
|
||||
let handleBackspaceKey = () => {
|
||||
let lastItem = this.list.children[this.list.children.length - 1];
|
||||
lastItem.parentNode.removeChild(lastItem);
|
||||
let currentListItems = this.list.querySelectorAll('.choices__item');
|
||||
let lastItem = currentListItems[currentListItems.length - 1];
|
||||
|
||||
lastItem.classList.add('is-selected');
|
||||
|
||||
for (let i = 0; i < currentListItems.length; i++) {
|
||||
let listItem = currentListItems[i];
|
||||
|
||||
if(listItem.classList.contains('is-selected')) {
|
||||
this.removeItem(listItem);
|
||||
this.removeInputValue(listItem.textContent);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
handleBackspaceKey();
|
||||
|
@ -144,6 +185,7 @@ class Choices {
|
|||
|
||||
addEventListeners(el) {
|
||||
el.addEventListener('click', this.onClick);
|
||||
el.addEventListener('keyup', this.onKeyUp);
|
||||
el.addEventListener('keydown', this.onKeyDown);
|
||||
el.addEventListener('change', this.onChange);
|
||||
el.addEventListener('focus', this.onFocus);
|
||||
|
@ -152,6 +194,7 @@ class Choices {
|
|||
|
||||
removeEventListeners(el) {
|
||||
el.removeEventListener('click', this.onClick);
|
||||
el.removeEventListener('keyup', this.onKeyUp);
|
||||
el.removeEventListener('keydown', this.onKeyDown);
|
||||
el.removeEventListener('change', this.onChange);
|
||||
el.removeEventListener('focus', this.onFocus);
|
||||
|
@ -189,34 +232,34 @@ class Choices {
|
|||
this.element.value = JSON.stringify(this.valueArray);
|
||||
}
|
||||
|
||||
removeInputValue(value) {
|
||||
if(this.options.debug) console.debug('Remove input value');
|
||||
|
||||
let index = this.valueArray.indexOf(value);
|
||||
this.valueArray.splice(index, 1);
|
||||
|
||||
this.element.value = JSON.stringify(this.valueArray);
|
||||
}
|
||||
|
||||
addItem(value) {
|
||||
if(this.options.debug) console.debug('Add item');
|
||||
|
||||
// Create new list element
|
||||
let item = document.createElement('li');
|
||||
item.classList.add('choice__item');
|
||||
item.classList.add('choices__item');
|
||||
item.textContent = value;
|
||||
|
||||
// Append it to list
|
||||
this.list.appendChild(item);
|
||||
}
|
||||
|
||||
removeItem() {
|
||||
|
||||
removeItem(item) {
|
||||
if(item) item.parentNode.removeChild(item);
|
||||
}
|
||||
|
||||
removeAllItems() {
|
||||
|
||||
}
|
||||
|
||||
createItemList() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
init() {
|
||||
if(!this.supports) console.error('Your browser doesn\'nt support shit');
|
||||
this.initialised = true;
|
||||
|
||||
this.render(this.element);
|
||||
}
|
||||
|
||||
|
@ -224,31 +267,37 @@ class Choices {
|
|||
if(this.options.debug) console.debug('Render');
|
||||
|
||||
// Create DOM elements
|
||||
let container = document.createElement('div');
|
||||
let containerOuter = document.createElement('div');
|
||||
let containerInner = document.createElement('div');
|
||||
let input = document.createElement('input');
|
||||
let list = document.createElement('ul');
|
||||
|
||||
container.className = 'choice choice--active';
|
||||
containerOuter.className = 'choices choices--active';
|
||||
containerInner.className = 'choices__inner';
|
||||
|
||||
// Hide passed input
|
||||
this.element.classList.add('choice__input', 'choice__input--hidden');
|
||||
this.element.classList.add('choices__input', 'choices__input--hidden');
|
||||
this.element.tabIndex = '-1';
|
||||
this.element.setAttribute('style', 'display:none;');
|
||||
this.element.setAttribute('aria-hidden', 'true');
|
||||
|
||||
// Wrap input in container
|
||||
wrap(this.element, container);
|
||||
// Wrap input in container preserving DOM ordering
|
||||
wrap(this.element, containerInner);
|
||||
|
||||
list.className = 'choice__list';
|
||||
wrap(containerInner, containerOuter);
|
||||
|
||||
list.className = 'choices__list';
|
||||
|
||||
input.type = 'text';
|
||||
input.placeholder = this.element.placeholder;
|
||||
input.className = 'choice__input choice__input--cloned';
|
||||
input.className = 'choices__input choices__input--cloned';
|
||||
|
||||
container.appendChild(list);
|
||||
container.appendChild(input);
|
||||
containerInner.appendChild(list);
|
||||
containerInner.appendChild(input);
|
||||
containerOuter.appendChild(containerInner);
|
||||
|
||||
this.container = container;
|
||||
this.containerOuter = containerOuter;
|
||||
this.containerInner = containerInner;
|
||||
this.input = input;
|
||||
this.list = list;
|
||||
|
||||
|
@ -265,7 +314,7 @@ class Choices {
|
|||
destroy() {
|
||||
this.options = null;
|
||||
this.element = null;
|
||||
|
||||
this.initialised = null;
|
||||
this.removeEventListeners(this.input);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
export let hasClass = (elem, className) => {
|
||||
return new RegExp(' ' + className + ' ').test(' ' + elem.className + ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalises the first letter of each word in a string
|
||||
* @param {String} str String to capitalise
|
||||
|
|
63
assets/styles/css/choices.css
Normal file
63
assets/styles/css/choices.css
Normal file
|
@ -0,0 +1,63 @@
|
|||
*, *:before, *:after {
|
||||
box-sizing: border-box; }
|
||||
|
||||
html {
|
||||
font-size: 62.5%; }
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
widows: 100%; }
|
||||
|
||||
body {
|
||||
background-color: #FAFAFA;
|
||||
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
|
||||
font-size: 1.6rem;
|
||||
color: #222222;
|
||||
padding: 2.4rem; }
|
||||
|
||||
.choices {
|
||||
margin-bottom: 2.4rem;
|
||||
position: relative; }
|
||||
|
||||
.choices__inner {
|
||||
background-color: #FFFFFF;
|
||||
padding: .75rem .75rem .375rem;
|
||||
border: 1px solid #DDDDDD;
|
||||
border-radius: .25rem;
|
||||
font-size: 1.4rem; }
|
||||
.choices__inner:focus {
|
||||
outline: 1px solid #00BCD4;
|
||||
outline-offset: -1px; }
|
||||
|
||||
.choices__list {
|
||||
margin: 0;
|
||||
padding-left: 0;
|
||||
list-style-type: none;
|
||||
display: inline; }
|
||||
|
||||
.choices__input {
|
||||
font-size: 1.4rem;
|
||||
padding: 0;
|
||||
margin-bottom: .5rem;
|
||||
display: inline-block;
|
||||
vertical-align: baseline;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
max-width: 100%; }
|
||||
.choices__input:focus {
|
||||
outline: 0; }
|
||||
|
||||
.choices__item {
|
||||
display: inline-block;
|
||||
border-radius: .4rem;
|
||||
padding: .4rem .8rem;
|
||||
font-size: 1.4rem;
|
||||
margin-right: .375rem;
|
||||
margin-bottom: .375rem;
|
||||
background-color: #00BCD4;
|
||||
text-shadow: 0px 1px 0px #008fa1;
|
||||
border: 1px solid #00a5bb;
|
||||
color: #FFFFFF; }
|
||||
.choices__item.is-selected {
|
||||
background-color: #00a5bb; }
|
1
assets/styles/css/choices.min.css
vendored
Normal file
1
assets/styles/css/choices.min.css
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*,:after,:before{box-sizing:border-box}body,html{margin:0;height:100%;widows:100%}html{font-size:62.5%}body{background-color:#fafafa;font-family:'Helvetica Neue','Helvetica','Arial',sans-serif;font-size:1.6rem;color:#222;padding:2.4rem}.choices{margin-bottom:2.4rem;position:relative}.choices__inner{background-color:#fff;padding:.75rem .75rem .375rem;border:1px solid #ddd;border-radius:.25rem;font-size:1.4rem}.choices__inner:focus{outline:1px solid #00bcd4;outline-offset:-1px}.choices__list{margin:0;padding-left:0;list-style-type:none;display:inline}.choices__input{font-size:1.4rem;padding:0;margin-bottom:.5rem;display:inline-block;vertical-align:baseline;border:0;border-radius:0;max-width:100%}.choices__input:focus{outline:0}.choices__item{display:inline-block;border-radius:.4rem;padding:.4rem .8rem;font-size:1.4rem;margin-right:.375rem;margin-bottom:.375rem;background-color:#00bcd4;text-shadow:0 1px 0 #008fa1;border:1px solid #00a5bb;color:#fff}.choices__item.is-selected{background-color:#00a5bb}
|
72
assets/styles/scss/choices.scss
Normal file
72
assets/styles/scss/choices.scss
Normal file
|
@ -0,0 +1,72 @@
|
|||
*, *:before, *:after {
|
||||
box-sizing: border-box
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 62.5%;
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
widows: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #FAFAFA;
|
||||
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
|
||||
font-size: 1.6rem;
|
||||
color: #222222;
|
||||
padding: 2.4rem;
|
||||
}
|
||||
|
||||
|
||||
.choices {
|
||||
margin-bottom: 2.4rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.choices__inner {
|
||||
background-color: #FFFFFF;
|
||||
padding: .75rem .75rem .375rem;
|
||||
border: 1px solid #DDDDDD;
|
||||
border-radius: .25rem;
|
||||
font-size: 1.4rem;
|
||||
&:focus {
|
||||
outline: 1px solid #00BCD4;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
.choices__list {
|
||||
margin: 0;
|
||||
padding-left: 0;
|
||||
list-style-type: none;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.choices__input {
|
||||
font-size: 1.4rem;
|
||||
padding: 0;
|
||||
margin-bottom: .5rem;
|
||||
display: inline-block;
|
||||
vertical-align: baseline;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
max-width: 100%;
|
||||
&:focus { outline: 0; }
|
||||
}
|
||||
|
||||
.choices__item {
|
||||
display: inline-block;
|
||||
border-radius: .4rem;
|
||||
padding: .4rem .8rem;
|
||||
font-size: 1.4rem;
|
||||
margin-right: .375rem;
|
||||
margin-bottom: .375rem;
|
||||
background-color: #00BCD4;
|
||||
text-shadow: 0px 1px 0px darken(#00BCD4, 10%);
|
||||
border: 1px solid darken(#00BCD4, 5%);
|
||||
color: #FFFFFF;
|
||||
&.is-selected { background-color: darken(#00BCD4, 5%); }
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Choices</title>
|
||||
<link rel="stylesheet" href="assets/styles/css/choices.css">
|
||||
</head>
|
||||
<body>
|
||||
<input id="1" type="text" data-choice value='["preset-1", "preset-2"]' placeholder="This is a placeholder" class="custom class">
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
"lint": "eslint ./assets/scripts/src",
|
||||
"css:watch": "nodemon -e scss -x \"npm run css:build\"",
|
||||
"css:build": "npm run css:sass -s && npm run css:prefix -s && npm run css:min -s",
|
||||
"css:sass": "node-sass --include-path scss assets/styles/scss/main.scss assets/styles/css/main.css",
|
||||
"css:sass": "node-sass --include-path scss assets/styles/scss/*.scss assets/styles/css/choices.css",
|
||||
"css:prefix": "postcss --use autoprefixer -b 'last 2 versions' assets/styles/css/*.css -d assets/styles/css/",
|
||||
"css:min": "csso assets/styles/css/main.css assets/styles/css/main.min.css",
|
||||
"css:min": "csso assets/styles/css/choices.css assets/styles/css/choices.min.css",
|
||||
"js:build": "webpack --config webpack.config.prod.js",
|
||||
"js:test": "./node_modules/karma/bin/karma start --single-run --no-auto-watch tests/karma.config.js",
|
||||
"js:test_watch": "./node_modules/karma/bin/karma start --auto-watch --no-single-run tests/karma.config.js"
|
||||
|
|
Loading…
Reference in a new issue