├── .gitignore ├── History.md ├── Makefile ├── Readme.md ├── component.json ├── example.html ├── index.css ├── index.js ├── template.html └── test ├── index.html └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | components 2 | build 3 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 0.5.1 / 2014-01-05 3 | ================== 4 | 5 | * NEED TO UPDATE THIS 6 | 7 | 0.5.0 / 2013-09-29 8 | ================== 9 | 10 | * update pillbox 11 | * dont search down, up or enter are down 12 | * make .select-single input 100% width and 100% height [jonathanong] 13 | * add .select-multiple class when multiple, closes #36 14 | * ignore enter on search, closes #38 15 | * meta viewport + border-box styling [jonathanong] 16 | 17 | 0.4.0 / 2013-09-24 18 | ================== 19 | 20 | * add .empty() to remove all options, closes #28 21 | * fix .remove(name) when option is selected 22 | * fix .deselect(name) on multiple 23 | * prevent propagation on touch start 24 | * show dropdown on search, closes #32 25 | * bubble .select(), should close #31 26 | * Use component/debounce [jonathanong] 27 | 28 | 0.3.0 / 2013-09-23 29 | ================== 30 | 31 | * add .unbind() 32 | * add .blur() 33 | * fix ios7 blur, closes #25 34 | * fix search on backspace, closes #26 35 | * add .focus() docs, closes #27 36 | * clear single select on blur, closes #21 37 | * use matthewmueller/debounce, closes #23 38 | * Load `template.html` directly [jonathanong] 39 | 40 | 0.2.0 / 2013-09-21 41 | ================== 42 | 43 | * add demo prop 44 | * use e.preventDefault() when necessary closes #15 45 | * show dropdown if hidden, on down / up, closes #16 46 | * dehighlight selected, closes #17 47 | * highlight on hover, closes #14 48 | * change single-select to have a single searchable input 49 | * fix mouse click / hide behavior, should close #10 50 | * escape shouldnt show dropdown, closes #12 51 | 52 | 0.1.0 / 2013-09-16 53 | ================== 54 | 55 | * add custom element support, closes #4 56 | * add .highlight(name) and .dehighlight(), closes #8 57 | * change .options to a map of name to option 58 | * hide dropdown on "esc" 59 | * fix in firefox 60 | 61 | 0.0.3 / 2013-09-14 62 | ================== 63 | 64 | * add .remove(name) 65 | * add search tests 66 | * add search input helper 67 | 68 | 0.0.2 / 2013-09-13 69 | ================== 70 | 71 | * add tests 72 | * better example 73 | * no need to use .bind within foreach [jonathanong] 74 | 75 | 0.0.1 / 2013-09-13 76 | ================== 77 | 78 | * Initial commit 79 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | build: components index.js 3 | @component build --dev 4 | 5 | components: component.json 6 | @component install --dev 7 | 8 | clean: 9 | rm -fr build components template.js 10 | 11 | test: build 12 | @open test/index.html 13 | 14 | .PHONY: clean test 15 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # select 2 | 3 | modern <select>. (WIP) 4 | 5 | [see the demo](http://yields.github.io/select/index.html) 6 | 7 | ```js 8 | var Select = require('select'); 9 | 10 | var select = Select() 11 | .label('Select a language') 12 | .multiple() 13 | .add('Javascript') 14 | .add('Google Go') 15 | .add('Bash') 16 | .add('Ruby') 17 | .add('Lua') 18 | .add('C++') 19 | .add('C', 200) 20 | .select('Jasvascript') 21 | .select('Google Go') 22 | .deselect('Google Go') 23 | .select('c'); 24 | 25 | document.body.appendChild(select.el); 26 | 27 | select.on('change', function(){ 28 | console.log(select.values()); 29 | // => ['javascript', 200] 30 | }); 31 | ``` 32 | 33 | 34 | ## Installation 35 | 36 | Install with [component(1)](http://component.io): 37 | 38 | $ component install yields/select 39 | 40 | ## API 41 | 42 | ### Select() 43 | 44 | Initialize a new `Select`. 45 | 46 | #### .unbind() 47 | 48 | Unbind internal events. 49 | 50 | #### .label(label) 51 | 52 | Set the label. 53 | 54 | #### .multiple([label]) 55 | 56 | Allow multiple selections. 57 | 58 | #### .searchable([label]) 59 | 60 | Allow search. 61 | 62 | #### add(name[, value [, el]]) 63 | 64 | Add an option with `name` and optional `value`. 65 | 66 | An option `el` can be given, this can be either `html` string 67 | or native `Element`. 68 | 69 | add('js', 0, '
  • Javascript') 70 | 71 | #### remove(name) 72 | 73 | Remove an option with `name`. 74 | 75 | #### empty() 76 | 77 | Remove all options. 78 | 79 | #### select(name) 80 | 81 | Select an option with `name`. 82 | 83 | #### deselect(name) 84 | 85 | De-select `name`. 86 | 87 | #### focus() 88 | 89 | Focus ``. 90 | 91 | #### blur() 92 | 93 | Blur ``. 94 | 95 | #### highlight(name) 96 | 97 | Highlight an option by `name`. 98 | 99 | When an option is in "highlight" mode, it will be selected when the 100 | user hits enter. 101 | 102 | #### dehighlight() 103 | 104 | De-highlight "highlight"ed option 105 | 106 | #### get(name) 107 | 108 | Get an option with `name`. 109 | 110 | #### show([name]) 111 | 112 | Show the dropdown or option `name`. 113 | 114 | #### hide([name]) 115 | 116 | Hide the dropdown or option `name`. 117 | 118 | #### visible([name]) 119 | 120 | Check if option `name` or dropdown are visible. 121 | 122 | #### toggle([name]) 123 | 124 | Toggle `.show([name])`, `.hide([name])`. 125 | 126 | #### disable(name) 127 | 128 | Disable an option `name`. 129 | 130 | #### enable(name) 131 | 132 | Enable an option `name`. 133 | 134 | #### selected([options]) 135 | 136 | Get / set selected options. 137 | 138 | #### values() 139 | 140 | Get selected values. 141 | 142 | #### search(term) 143 | 144 | Search options with `term`, if there are listeners for `search` event, the `.search()` method will do nothing. 145 | this allows you to set up custom search. 146 | 147 | var select = Select() 148 | .add('one') 149 | .add('two') 150 | .on('search', function(term){ 151 | ajax(term, function(opts){ 152 | opts.forEach(select.add, select); 153 | select.highlight(opts[0].name); 154 | }) 155 | }) 156 | 157 | ### Events 158 | 159 | #### "add" 160 | 161 | Emitted when you add an `option` with an object: 162 | 163 | { 164 | name: 165 | value: 166 | el: 167 | selected: 168 | } 169 | 170 | #### "remove" 171 | 172 | Emitted when an option is removed, `option` object is given as an argument. 173 | 174 | #### "select" 175 | 176 | Emitted when an option is selected. 177 | 178 | { 179 | name: 180 | value: 181 | el: 182 | selected: 183 | } 184 | 185 | #### "change" 186 | 187 | Emitted with `Select` instance. 188 | 189 | #### "deselect" 190 | 191 | Emitted with an `option` object. 192 | 193 | #### "show" 194 | 195 | Emitted when the select dropdown is shown. 196 | 197 | #### "hide" 198 | 199 | Emitted when the select dropdown is hidden. 200 | 201 | #### "search" 202 | 203 | Emitted when `.search(term)` is called, if there are listeners 204 | the method `.search(term)` will do nothing. 205 | 206 | #### "found" 207 | 208 | Emitted after a search was performed with number of matches. 209 | 210 | ### Tests 211 | 212 | ```bash 213 | $ make test 214 | ``` 215 | 216 | ## License 217 | 218 | MIT 219 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "select", 3 | "repo": "yields/select", 4 | "description": "modern 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | select 6 | 7 | 8 | 9 | 10 | 11 | 12 |
    13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 2 | var dom = require('dom'); 3 | var Pillbox = require('pillbox'); 4 | var select = require('select'); 5 | var assert = require('assert'); 6 | 7 | describe('select()', function(){ 8 | describe('.label(label)', function(){ 9 | it('should set the label', function(){ 10 | var s = select().label('label'); 11 | assert('label' == dom('.select-input', s.el).placeholder()); 12 | }) 13 | }) 14 | 15 | describe('.multiple()', function(){ 16 | var s; 17 | 18 | before(function(){ 19 | s = select().multiple(); 20 | }) 21 | 22 | it('should set ._multiple to `true`', function(){ 23 | assert(true == s._multiple); 24 | }) 25 | 26 | it('should create `pillbox` and set it to `.box`', function(){ 27 | assert(s.box instanceof Pillbox); 28 | }) 29 | 30 | it('should append the pillbox to `.select-box`', function(){ 31 | assert(dom('.pillbox', s.el).length()); 32 | }) 33 | }) 34 | 35 | describe('.add(name[, value])', function(){ 36 | var s = select(); 37 | 38 | it('should add to `.options`', function(){ 39 | assert(!s.options.one); 40 | s.add('one', 'two'); 41 | assert(s.options.one); 42 | }) 43 | 44 | it('should create a correct option object', function(){ 45 | var opt = s.options.one; 46 | assert('one' == opt.name); 47 | assert('two' == opt.value); 48 | assert('one' == dom(opt.el).text()); 49 | }) 50 | 51 | it('should emit `add` with the option', function(){ 52 | var opt; 53 | s.on('add', function(o){ opt = o; }); 54 | s.add('foo') 55 | assert('foo' == opt.name); 56 | assert('foo' == opt.value); 57 | }) 58 | }) 59 | 60 | describe('.remove(name)', function(){ 61 | var s = select(); 62 | 63 | it('should remove it from .options', function(){ 64 | s.add('a'); 65 | assert(s.options.a); 66 | s.remove('a'); 67 | assert(!s.options.a); 68 | }) 69 | 70 | it('should remove it from ._selected if its selected', function(){ 71 | s.add('a') 72 | assert(s.options.a); 73 | s.select('a'); 74 | assert(1 == s.selected().length); 75 | s.remove('a'); 76 | assert(!s.options.a); 77 | assert(0 == s.selected().length); 78 | }) 79 | 80 | it('should emit `remove` with the option', function(){ 81 | var opt; 82 | s.add('a'); 83 | s.on('remove', function(o){ opt = o; }); 84 | s.remove('a'); 85 | assert('a' == opt.name); 86 | }) 87 | }) 88 | 89 | describe('.get', function(){ 90 | describe('(name)', function(){ 91 | it('should get an option', function(){ 92 | var s = select().add('foo'); 93 | assert('foo' == s.get('foo').value); 94 | }) 95 | }) 96 | 97 | describe('()', function(){ 98 | it('should return the dropdown', function(){ 99 | var s = select().add('foo'); 100 | assert(s.dropdown == s.get().el); 101 | }) 102 | }) 103 | }) 104 | 105 | describe('.select(name)', function(){ 106 | it('should add `.selected` to `.el`', function(){ 107 | var s = select().add('o').select('o'); 108 | assert(dom(s.el).hasClass('selected')); 109 | }) 110 | 111 | it('should emit `selected` with the option', function(){ 112 | var opt; 113 | 114 | select() 115 | .add('o') 116 | .on('select', function(o){ opt = o; }) 117 | .select('o'); 118 | 119 | assert('o' == opt.name); 120 | }) 121 | 122 | it('should set `hidden` attribute on the selected element', function(){ 123 | var opt = select().add('o').select('o').get('o'); 124 | assert(opt.el.hasAttribute('hidden')); 125 | }) 126 | 127 | it('should add `.selected` class to the element', function(){ 128 | var opt = select().add('o').select('o').get('o'); 129 | assert(dom(opt.el).hasClass('selected')); 130 | }) 131 | 132 | it('should add .selected = true to the option object', function(){ 133 | assert(select().add('o').select('o').get('o').selected); 134 | }) 135 | 136 | describe('when single', function(){ 137 | var s = select(); 138 | 139 | it('should set ._selected to arr with the option', function(){ 140 | s.add('o').select('o'); 141 | assert(s.get('o') == s.selected()[0]); 142 | }) 143 | 144 | it('should override selected when selecting something else', function(){ 145 | s.add('b').select('b'); 146 | assert(s.get('b') == s.selected()[0]); 147 | assert(1 == s.selected().length); 148 | }) 149 | }) 150 | 151 | describe('when multiple', function(){ 152 | it('should push to ._selected', function(){ 153 | var s = select().add('a').add('b').multiple(); 154 | s.select('a').select('b'); 155 | assert(2 == s.selected().length); 156 | }) 157 | }) 158 | }) 159 | 160 | describe('.deselect(name)', function(){ 161 | it('should emit `deselect` with `opt`', function(){ 162 | var opt; 163 | 164 | select() 165 | .add('a') 166 | .select('a') 167 | .on('deselect', function(o){ opt = o; }) 168 | .deselect('a'); 169 | 170 | assert('a' == opt.name); 171 | }) 172 | 173 | it('should remove `hidden` attr', function(){ 174 | var opt; 175 | 176 | select() 177 | .add('a') 178 | .select('a') 179 | .on('deselect', function(o){ opt = o; }) 180 | .deselect('a'); 181 | 182 | assert(!opt.el.hasAttribute('hidden')); 183 | }) 184 | 185 | it('should remove `.selected` class', function(){ 186 | var opt; 187 | 188 | select() 189 | .add('a') 190 | .select('a') 191 | .on('deselect', function(o){ opt = o; }) 192 | .deselect('a'); 193 | 194 | assert(!dom(opt.el).hasClass('selected')); 195 | }) 196 | 197 | describe('when single', function(){ 198 | it('should set ._selected to []', function(){ 199 | assert(0 == select() 200 | .add('a') 201 | .select('a') 202 | .on('deselect', function(o){ opt = o; }) 203 | .deselect('a') 204 | .selected().length); 205 | }) 206 | }) 207 | 208 | describe('when multiple', function(){ 209 | it('should remove the option from `._selected`', function(){ 210 | assert(2 == select() 211 | .multiple() 212 | .add('a') 213 | .add('b') 214 | .add('c') 215 | .select('a') 216 | .select('b') 217 | .select('c') 218 | .deselect('a') 219 | .selected() 220 | .length); 221 | }) 222 | }) 223 | }) 224 | 225 | describe('.hide', function(){ 226 | describe('(name)', function(){ 227 | it('should add `hidden` attr to the opt', function(){ 228 | var el = select().add('a').hide('a').options.a.el; 229 | assert(el.hasAttribute('hidden')); 230 | }) 231 | }) 232 | 233 | describe('()', function(){ 234 | it('should add `hidden` attr to the dropdown', function(){ 235 | var el = select().add('a').hide().dropdown; 236 | assert(el.hasAttribute('hidden')); 237 | }) 238 | }) 239 | }) 240 | 241 | describe('.show', function(){ 242 | describe('(name)', function(){ 243 | it('should remove `hidden` attr from the opt', function(){ 244 | var el = select().add('a').hide('a').show('a').options.a.el; 245 | assert(!el.hasAttribute('hidden')); 246 | }) 247 | }) 248 | 249 | describe('()', function(){ 250 | it('should remove `hidden` attr from the dropdown', function(){ 251 | var el = select().add('a').hide().show().dropdown; 252 | assert(!el.hasAttribute('hidden')); 253 | }) 254 | }) 255 | }) 256 | 257 | describe('.visible', function(){ 258 | describe('(name)', function(){ 259 | it('should be true if opt is visible', function(){ 260 | assert(select().add('a').visible('a')); 261 | }) 262 | }) 263 | 264 | describe('()', function(){ 265 | it('should be `true` if dropdown is visible', function(){ 266 | assert(select().show().visible()); 267 | }) 268 | }) 269 | }) 270 | 271 | describe('.toggle', function(){ 272 | describe('(name)', function(){ 273 | it('should hide opt if visible', function(){ 274 | assert(select().add('a').toggle('a').get('a').el.hasAttribute('hidden')); 275 | }) 276 | 277 | it('should show if hidden', function(){ 278 | assert(!select().add('a').hide('a').toggle('a').get('a').el.hasAttribute('hidden')); 279 | }) 280 | }) 281 | }) 282 | 283 | describe('.disable(name)', function(){ 284 | it('should add `disabled` attr', function(){ 285 | assert(select().add('a').disable('a').get('a').el.hasAttribute('disabled')); 286 | }) 287 | }) 288 | 289 | describe('.enable', function(){ 290 | it('should remove `disabled` attr', function(){ 291 | assert(!select() 292 | .add('a') 293 | .disable('a') 294 | .enable('a') 295 | .get('a').el 296 | .hasAttribute('disabled')); 297 | }) 298 | }) 299 | 300 | describe('.selected', function(){ 301 | describe('([a, b])', function(){ 302 | it('should select a and b opts', function(){ 303 | assert(2 == select() 304 | .multiple() 305 | .add('a') 306 | .add('b') 307 | .selected(['a', 'b']) 308 | .selected() 309 | .length); 310 | }) 311 | }) 312 | describe('()', function(){ 313 | it('should return selected options', function(){ 314 | assert(0 == select().selected()); 315 | }) 316 | }) 317 | }) 318 | 319 | describe('.highlight(name)', function(){ 320 | it('should set `.active` to the option element', function(){ 321 | var sel = select().add('s').highlight('s'); 322 | assert(sel.active == sel.get('s').el); 323 | }) 324 | it('should add `.highlight` to the option element', function(){ 325 | var s = select().add('s').highlight('s'); 326 | assert(dom(s.active).hasClass('highlighted')); 327 | }) 328 | }) 329 | 330 | describe('.dehighlight()', function(){ 331 | it('should de-highlight the active option', function(){ 332 | var sel = select().add('s').highlight('s'); 333 | var el = sel.active; 334 | assert(sel.active); 335 | sel.dehighlight(); 336 | assert(!dom(el).hasClass('highlighted')); 337 | assert(!sel.active); 338 | }) 339 | }) 340 | 341 | describe('.values()', function(){ 342 | describe('when something is selected', function(){ 343 | describe('when single', function(){ 344 | it('should return an array with a single value', function(){ 345 | assert(1 == select() 346 | .add('one', 1) 347 | .add('two', 2) 348 | .select('two') 349 | .select('one') 350 | .values()[0]) 351 | }) 352 | }) 353 | 354 | describe('when multiple', function(){ 355 | it('should return an array with selected values', function(){ 356 | var vals = select() 357 | .multiple() 358 | .add('Go', 'golang') 359 | .add('Lua') 360 | .add('JS') 361 | .select('go') 362 | .select('lua') 363 | .values(); 364 | 365 | assert('golang' == vals[0]); 366 | assert('lua' == vals[1]); 367 | }) 368 | }) 369 | }) 370 | 371 | describe('when nothing is selected', function(){ 372 | it('should return an empty array', function(){ 373 | assert(0 == select().values()); 374 | }) 375 | }) 376 | }) 377 | 378 | describe('.search(term)', function(){ 379 | describe('when there are no listeners to `search`', function(){ 380 | it('should work', function(){ 381 | var opts = select() 382 | .add('one') 383 | .add('two') 384 | .add('three') 385 | .search('o') 386 | .opts; 387 | 388 | assert(2 == dom('.select-option:not([hidden])', opts).length()); 389 | }) 390 | }) 391 | 392 | describe('when there are listeners to `search` it shouldnt work', function(){ 393 | var opts = select() 394 | .add('one') 395 | .add('two') 396 | .add('three') 397 | .on('search', function(){}) 398 | .search('o') 399 | .opts; 400 | 401 | assert(3 == dom('.select-option:not([hidden])', opts).length()); 402 | }) 403 | }) 404 | }) 405 | --------------------------------------------------------------------------------