├── .bowerrc
├── .editorconfig
├── .ember-cli
├── .gitignore
├── .jshintrc
├── .npmignore
├── .nvmrc
├── .travis.yml
├── .watchmanconfig
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── addon
├── .gitkeep
└── mixins
│ ├── item-cursor.js
│ └── select-picker.js
├── app
├── .gitkeep
├── components
│ ├── keyboard-select-picker.js
│ ├── list-picker.js
│ └── select-picker.js
└── templates
│ └── components
│ ├── -native-select.hbs
│ ├── list-picker.hbs
│ └── select-picker.hbs
├── blueprints
└── ember-cli-select-picker
│ └── index.js
├── bower.json
├── config
├── ember-try.js
└── environment.js
├── ember-cli-build.js
├── index.js
├── package.json
├── testem.json
├── tests
├── .jshintrc
├── common
│ └── select-picker.js
├── dummy
│ ├── app
│ │ ├── app.js
│ │ ├── components
│ │ │ ├── .gitkeep
│ │ │ ├── highlight-code.js
│ │ │ └── modal-dialog.js
│ │ ├── controllers
│ │ │ ├── .gitkeep
│ │ │ ├── application.js
│ │ │ ├── index.js
│ │ │ ├── keyboard.js
│ │ │ ├── options.js
│ │ │ ├── searching.js
│ │ │ ├── test-list-picker.js
│ │ │ └── test-select-picker.js
│ │ ├── helpers
│ │ │ └── .gitkeep
│ │ ├── index.html
│ │ ├── mixins
│ │ │ └── choices.js
│ │ ├── models
│ │ │ └── .gitkeep
│ │ ├── modules
│ │ │ └── choices-props.js
│ │ ├── router.js
│ │ ├── routes
│ │ │ └── .gitkeep
│ │ ├── styles
│ │ │ └── app.css
│ │ ├── templates
│ │ │ ├── application.hbs
│ │ │ ├── components
│ │ │ │ ├── .gitkeep
│ │ │ │ ├── highlight-code.hbs
│ │ │ │ └── modal-dialog.hbs
│ │ │ ├── i18n.hbs
│ │ │ ├── index.hbs
│ │ │ ├── install.hbs
│ │ │ ├── keyboard.hbs
│ │ │ ├── options.hbs
│ │ │ ├── searching.hbs
│ │ │ ├── test-list-picker.hbs
│ │ │ └── test-select-picker.hbs
│ │ └── views
│ │ │ └── .gitkeep
│ ├── config
│ │ └── environment.js
│ └── public
│ │ ├── crossdomain.xml
│ │ ├── robots.txt
│ │ └── screen-shot.png
├── helpers
│ ├── resolver.js
│ └── start-app.js
├── index.html
├── integration
│ ├── list-picker-test.js
│ └── select-picker-test.js
├── test-helper.js
└── unit
│ ├── .gitkeep
│ └── components
│ ├── list-picker-test.js
│ └── select-picker-test.js
└── vendor
├── .gitkeep
└── select-picker.css
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components",
3 | "analytics": false
4 | }
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 | indent_style = space
14 | indent_size = 2
15 |
16 | [*.js]
17 | indent_style = space
18 | indent_size = 2
19 |
20 | [*.hbs]
21 | insert_final_newline = false
22 | indent_style = space
23 | indent_size = 2
24 |
25 | [*.css]
26 | indent_style = space
27 | indent_size = 2
28 |
29 | [*.html]
30 | indent_style = space
31 | indent_size = 2
32 |
33 | [*.{diff,md}]
34 | trim_trailing_whitespace = false
35 |
--------------------------------------------------------------------------------
/.ember-cli:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | Ember CLI sends analytics information by default. The data is completely
4 | anonymous, but there are times when you might want to disable this behavior.
5 |
6 | Setting `disableAnalytics` to true will prevent any data from being sent.
7 | */
8 | "disableAnalytics": false
9 | }
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | bower.json.ember-try
7 |
8 | # dependencies
9 | /node_modules
10 | /bower_components
11 |
12 | # misc
13 | /.sass-cache
14 | /connect.lock
15 | /coverage/*
16 | /libpeerconnection.log
17 | npm-debug.log
18 | testem.log
19 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "document",
4 | "window",
5 | "-Promise"
6 | ],
7 | "browser": true,
8 | "jquery": true,
9 | "boss": true,
10 | "curly": true,
11 | "debug": false,
12 | "devel": true,
13 | "eqeqeq": true,
14 | "evil": true,
15 | "forin": false,
16 | "immed": false,
17 | "laxbreak": false,
18 | "newcap": true,
19 | "noarg": true,
20 | "noempty": false,
21 | "nonew": false,
22 | "nomen": false,
23 | "onevar": false,
24 | "plusplus": false,
25 | "regexp": false,
26 | "undef": true,
27 | "sub": true,
28 | "strict": false,
29 | "white": false,
30 | "eqnull": true,
31 | "esnext": true,
32 | "unused": true
33 | }
34 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 | tests/
3 | tmp/
4 | dist/
5 |
6 | .bowerrc
7 | .editorconfig
8 | .ember-cli
9 | .travis.yml
10 | .npmignore
11 | **/.gitkeep
12 | bower.json
13 | ember-cli-build.js
14 | Brocfile.js
15 | testem.json
16 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 0.12
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | language: node_js
3 | node_js:
4 | - "0.12"
5 |
6 | sudo: false
7 |
8 | cache:
9 | directories:
10 | - node_modules
11 |
12 | env:
13 | - EMBER_TRY_SCENARIO=default
14 | - EMBER_TRY_SCENARIO=ember-release
15 | # - EMBER_TRY_SCENARIO=ember-beta
16 | # - EMBER_TRY_SCENARIO=ember-canary
17 |
18 | matrix:
19 | fast_finish: true
20 | allow_failures:
21 | - env: EMBER_TRY_SCENARIO=ember-canary
22 |
23 | before_install:
24 | - export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH
25 | - "npm config set spin false"
26 | - "npm install -g npm@^2"
27 |
28 | install:
29 | - npm install -g bower
30 | - npm install
31 | - bower install
32 |
33 | script:
34 | - ember try $EMBER_TRY_SCENARIO test
35 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {
2 | "ignore_dirs": ["tmp"]
3 | }
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # ember-cli-select-picker is an OPEN Open Source Project
2 |
3 | ## What?
4 |
5 | Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.
6 |
7 | ## Rules
8 |
9 | There are a few basic ground-rules for contributors:
10 |
11 | 1. **No `--force` pushes** or modifying the Git history in any way.
12 | 1. **Non-master branches** ought to be used for ongoing work.
13 | 1. **External API changes and significant modifications** ought to be subject to an **internal pull-request** to solicit feedback from other contributors.
14 | 1. Internal pull-requests to solicit feedback are *encouraged* for any other non-trivial contribution but left to the discretion of the contributor.
15 | 1. Contributors should attempt to adhere to the prevailing code-style.
16 |
17 | ## Releases
18 |
19 | Declaring formal releases remains the prerogative of the project maintainer.
20 |
21 | ## Changes to this arrangement
22 |
23 | This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change.
24 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ember-cli-select-picker [](https://npmjs.org/package/ember-cli-select-picker) [](https://travis-ci.org/sukima/ember-cli-select-picker)
2 |
3 |
4 |
5 | This is a reinvention of the select view. It is designed to offer a [Bootstrap][1] style and function. It is highly inspired from the jQuery plugin [bootstrap-select][2] but designed for Ember-CLI apps specifically. It supports single and multiple select. It adds select all/none, and search filtering for multiple selections.
6 |
7 | See the [demo][] for examples, usage, and code snippits.
8 |
9 | [1]: http://getbootstrap.com/
10 | [2]: http://silviomoreto.github.io/bootstrap-select/
11 |
12 | ## Dependencies
13 |
14 | Version 2.0 is designed for Ember CLI 1.13 or greater. If you want to use an older unsupported version of ember take a look at the last 1.x release.
15 |
16 | ## Installation
17 |
18 | * `ember install ember-cli-select-picker`
19 |
20 | ## Using
21 |
22 | In your templates simply replace the usual `{{select …}}` with `{{select-picker …}}`. This addon is implemented as a component since the core Ember team is deprecating views. It is down-grades (read: backwards compatible) to mobile by keeping a select view in sync under the hood.
23 |
24 | More options and examples are available on the [demo][] site.
25 |
26 | ```handlebars
27 | {{select-picker value=myModel.myAttr
28 | content=mySelectContents
29 | optionGroupPath="group"
30 | optionLabelPath="content.label"
31 | optionValuePath="content.value"}}
32 | ```
33 |
34 | ## Running Tests
35 |
36 | * `npm test` - Test with Ember release, beta, and canary
37 | * `ember test`
38 | * `ember test --server`
39 |
40 | ## Building
41 |
42 | * `ember build`
43 |
44 | For more information on using ember-cli, visit [http://www.ember-cli.com/](http://www.ember-cli.com/).
45 |
46 | [demo]: https://sukima.github.io/ember-cli-select-picker/
47 |
--------------------------------------------------------------------------------
/addon/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sukima/ember-cli-select-picker/ea014668ae740d2f72375e91aa3de3e9e2e48b80/addon/.gitkeep
--------------------------------------------------------------------------------
/addon/mixins/item-cursor.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.Mixin.create({
4 | activeCursor: null,
5 | previousActiveIndex: 0,
6 |
7 | updateActiveItem: Ember.observer(
8 | 'activeCursor', 'contentList.length',
9 | function() {
10 | const previousActiveIndex = this.get('previousActiveIndex');
11 | const activeIndex = this.get('activeIndex');
12 | if (Ember.typeOf(activeIndex) !== 'number') { return; }
13 | this.set(`contentList.${previousActiveIndex}.active`, false);
14 | this.set(`contentList.${activeIndex}.active`, true);
15 | this.set('previousActiveIndex', activeIndex);
16 | }
17 | ),
18 |
19 | activeIndex: Ember.computed(
20 | 'activeCursor', 'contentList.length',
21 | function() {
22 | var cursor = this.get('activeCursor');
23 | if (Ember.isNone(cursor)) {
24 | return null;
25 | }
26 | var len = this.get('contentList.length');
27 | return (cursor % len + len) % len;
28 | }
29 | ),
30 |
31 | activeItem: Ember.computed(
32 | 'activeIndex', 'contentList.[]',
33 | function() {
34 | return this.get('contentList').objectAt(this.get('activeIndex'));
35 | }
36 | ),
37 |
38 | actions: {
39 | activeNext() {
40 | if (Ember.isNone(this.get('activeCursor'))) {
41 | this.set('activeCursor', 0);
42 | } else {
43 | this.incrementProperty('activeCursor');
44 | }
45 | },
46 |
47 | activePrev() {
48 | if (Ember.isNone(this.get('activeCursor'))) {
49 | this.set('activeCursor', -1);
50 | } else {
51 | this.decrementProperty('activeCursor');
52 | }
53 | },
54 |
55 | selectActiveItem() {
56 | var item = this.get('activeItem');
57 | if (Ember.isPresent(item)) {
58 | this.send('selectItem', item);
59 | }
60 | },
61 | }
62 | });
63 |
--------------------------------------------------------------------------------
/addon/mixins/select-picker.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | // Ember Addons need to be coded as if Ember.EXTEND_PROTOTYPES = false
4 | // Because of this we need to make our own proxy functions to apply as one offs
5 | // to native arrays.
6 | const emberArrayFunc = function(method) {
7 | return function(ctx, ...args) {
8 | let enumeral = Ember.A(ctx);
9 | Ember.assert(
10 | `Ember.Enumerable has no method ${method}`,
11 | Ember.typeOf(enumeral[method]) === 'function'
12 | );
13 | let result = enumeral[method](...args);
14 | if (Ember.typeOf(result) === 'array') {
15 | return Ember.A(result);
16 | } else {
17 | return result;
18 | }
19 | };
20 | };
21 | const _contains = emberArrayFunc(
22 | // Backwards compatability for Ember < 2.x
23 | Ember.Enumerable.keys().indexOf('includes') !== -1 ?
24 | 'includes' :
25 | 'contains'
26 | );
27 | const _mapBy = emberArrayFunc('mapBy');
28 | const _filterBy = emberArrayFunc('filterBy');
29 | const _findBy = emberArrayFunc('findBy');
30 | const _uniq = emberArrayFunc('uniq');
31 | const _compact = emberArrayFunc('compact');
32 |
33 | const selectOneOf = function(someSelected,
34 | allSelected,
35 | noneSelected) {
36 | return Ember.computed(
37 | 'hasSelectedItems', 'allItemsSelected',
38 | function() {
39 | if (this.get('allItemsSelected')) {
40 | return allSelected.call(this);
41 | } else if (this.get('hasSelectedItems')) {
42 | return someSelected.call(this);
43 | } else {
44 | return noneSelected.call(this);
45 | }
46 | }
47 | );
48 | };
49 |
50 | const selectOneOfValue = function(someSelectedValue,
51 | allSelectedValue,
52 | noneSelectedValue) {
53 | return selectOneOf(
54 | function() { return someSelectedValue; },
55 | function() { return allSelectedValue; },
56 | function() { return noneSelectedValue; }
57 | );
58 | };
59 |
60 | const selectOneOfProperty = function(someSelectedKey,
61 | allSelectedKey,
62 | noneSelectedKey) {
63 | return selectOneOf(
64 | function() { return this.get(someSelectedKey); },
65 | function() { return this.get(allSelectedKey); },
66 | function() { return this.get(noneSelectedKey); }
67 | );
68 | };
69 |
70 | const isAdvancedSearch = function(liveSearch) {
71 | return (
72 | Ember.typeOf(liveSearch) === 'string' &&
73 | liveSearch.toLowerCase() === 'advanced'
74 | );
75 | };
76 |
77 | export default Ember.Mixin.create({
78 | liveSearch: false,
79 | showDropdown: false,
80 | promptMessage: 'Please select an option',
81 | prompt: Ember.computed.bool('promptMessage'),
82 |
83 | showNativePrompt: Ember.computed(
84 | 'multiple', 'prompt',
85 | function() {
86 | return !this.get('multiple') && Ember.isPresent(this.get('prompt'));
87 | }
88 | ),
89 |
90 | menuButtonId: Ember.computed(
91 | 'elementId',
92 | function() {
93 | return this.get('elementId') + '-dropdown-menu';
94 | }
95 | ),
96 |
97 | selectionAsArray: function() {
98 | return Ember.makeArray(this.get('selection'));
99 | },
100 |
101 | contentList: Ember.computed(
102 | 'selection.[]', 'content.[]', 'optionGroupPath',
103 | 'optionLabelPath', 'optionValuePath', 'searchFilter',
104 | function() {
105 | // Ember.Select does not include the content prefix for optionGroupPath
106 | var groupPath = this.get('optionGroupPath');
107 | // Ember.Select expects optionLabelPath and optionValuePath to have a
108 | // `content.` prefix
109 | var labelPath = this.contentPathName('optionLabelPath');
110 | var valuePath = this.contentPathName('optionValuePath');
111 | // selection is either an object or an array of object depending on the
112 | // value of the multiple property. Ember.Select maintains the value
113 | // property.
114 | var selection = this.selectionAsArray().map(function(item) {
115 | return valuePath ? Ember.get(item, valuePath) : item;
116 | });
117 | var searchMatcher = this.makeSearchMatcher();
118 |
119 | var result = _compact(Ember.makeArray(this.get('content'))
120 | .map(function(item, index) {
121 | const label = labelPath ? Ember.get(item, labelPath) : item;
122 | const value = valuePath ? Ember.get(item, valuePath) : item;
123 | const group = groupPath ? Ember.get(item, groupPath) : null;
124 | if (searchMatcher(group) || searchMatcher(label)) {
125 | return Ember.Object.create({
126 | item: item,
127 | itemId: index,
128 | group: group,
129 | label: label,
130 | value: value,
131 | selected: _contains(selection, value)
132 | });
133 | } else {
134 | return null;
135 | }
136 | }));
137 |
138 | if (Ember.isPresent(result)) {
139 | result.set('firstObject.first', true);
140 | }
141 |
142 | return result;
143 | }
144 | ),
145 |
146 | nestedGroupContentList: Ember.computed(
147 | 'contentList.[].group',
148 | function() {
149 | const contentList = this.get('contentList');
150 | const groups = _uniq(_mapBy(contentList, 'group'));
151 | const results = Ember.A();
152 | groups.forEach(function(group) {
153 | results.pushObject(Ember.Object.create({
154 | name: group,
155 | items: _filterBy(contentList, 'group', group)
156 | }));
157 | });
158 | return results;
159 | }
160 | ),
161 |
162 | contentPathName: function(pathName) {
163 | return this.getWithDefault(pathName, '').substr(8);
164 | },
165 |
166 | getByContentPath: function(obj, pathName) {
167 | return Ember.get(obj, this.contentPathName(pathName));
168 | },
169 |
170 | selectedContentList: Ember.computed.filterBy('contentList', 'selected'),
171 | unselectedContentList: Ember.computed.setDiff('contentList', 'selectedContentList'),
172 | hasSelectedItems: Ember.computed.gt('selection.length', 0),
173 | allItemsSelected: Ember.computed(
174 | 'selection.length', 'content.length',
175 | function() {
176 | return Ember.isEqual(this.get('selection.length'), this.get('content.length'));
177 | }
178 | ),
179 |
180 | glyphiconClass: selectOneOfValue('glyphicon-minus', 'glyphicon-ok', ''),
181 | selectAllNoneLabel: selectOneOfProperty('selectNoneLabel', 'selectNoneLabel', 'selectAllLabel'),
182 |
183 | makeSearchMatcher: function () {
184 | var searchFilter = this.get('searchFilter');
185 | // item can be null, string, or SafeString.
186 | // SafeString does not have toLowerCase() so use toString() to
187 | // normalize it.
188 | if (Ember.isEmpty(searchFilter)) {
189 | return function () {
190 | return true; // Show all
191 | };
192 | } else if (isAdvancedSearch(this.get('liveSearch'))) {
193 | searchFilter = new RegExp(searchFilter.split('').join('.*'), 'i');
194 | return function (item) {
195 | if (Ember.isNone(item)) {
196 | return false;
197 | } else {
198 | return searchFilter.test(item.toString());
199 | }
200 | };
201 | } else {
202 | searchFilter = searchFilter.toLowerCase();
203 | return function (item) {
204 | if (Ember.isNone(item)) {
205 | return false;
206 | } else {
207 | return item.toString().toLowerCase().indexOf(searchFilter) >= 0;
208 | }
209 | };
210 | }
211 | },
212 |
213 | selectionLabels: Ember.computed.mapBy('selectedContentList', 'label'),
214 |
215 | selectionSummary: Ember.computed(
216 | 'selectionLabels.[]', 'nothingSelectedMessage', 'multipleSelectedMessage',
217 | 'summaryMessage', 'summaryMessageKey',
218 | function() {
219 | var selection = this.get('selectionLabels');
220 | var count = selection.get('length');
221 | var messageKey = this.get('summaryMessageKey');
222 | var message = this.get('summaryMessage');
223 | if (Ember.I18n && Ember.isPresent(messageKey)) {
224 | // TODO: Allow an enablePrompt="false" feature
225 | if (count === 0) {
226 | return this.get('nothingSelectedMessage');
227 | }
228 | var item = selection.get('firstObject');
229 | var translation = Ember.I18n.t(messageKey, {
230 | count: count,
231 | item: item,
232 | list: selection.join(', ')
233 | });
234 | // I18n is returning a string that's been escaped, we don't want the
235 | // string to get escaped again.
236 | return Ember.String.htmlSafe(translation);
237 | } else if (Ember.isPresent(message)) {
238 | return message;
239 | } else {
240 | switch (count) {
241 | case 0:
242 | return this.get('nothingSelectedMessage');
243 | case 1:
244 | return selection.get('firstObject');
245 | default:
246 | return Ember.String.fmt(
247 | this.get('multipleSelectedMessage'),
248 | count,
249 | selection.get('firstObject'),
250 | selection.join(', ')
251 | );
252 | }
253 | }
254 | }
255 | ),
256 |
257 | clearSearchDisabled: Ember.computed.empty('searchFilter'),
258 |
259 | toggleSelection: function(value) {
260 | var selection = Ember.A(this.get('selection'));
261 | if (_contains(selection, value)) {
262 | selection.removeObject(value);
263 | } else {
264 | selection.pushObject(value);
265 | }
266 | this.set('selection', selection);
267 | },
268 |
269 | selectAnItem: function(selected) {
270 | if (!this.get('disabled')) {
271 | if (this.get('multiple')) {
272 | this.set('keepDropdownOpen', true);
273 | this.toggleSelection(selected.get('item'));
274 | } else {
275 | this.setProperties({
276 | // TODO: value will be removed in the future
277 | value: selected.get('value'),
278 | selection: selected.get('item')
279 | });
280 | }
281 | }
282 | },
283 |
284 | sendChangeAction: function() {
285 | const changeAction = Ember.get(this, 'attrs.action');
286 | if (changeAction) {
287 | changeAction(this.get('selection'));
288 | }
289 | },
290 |
291 | actions: {
292 | selectItem(selected) {
293 | if (this.get('disabled')) { return true; }
294 | this.selectAnItem(selected);
295 | this.sendChangeAction();
296 | return false;
297 | },
298 |
299 | selectAllNone(listName) {
300 | if (this.get('disabled')) { return true; }
301 | this.get(listName).forEach(Ember.run.bind(this, this.selectAnItem));
302 | this.sendChangeAction();
303 | return false;
304 | },
305 |
306 | selectByValue() {
307 | if (this.get('disabled')) { return true; }
308 | const hasPrompt = Ember.isPresent(this.get('prompt'));
309 | const contentList = this.get('contentList');
310 | const selectedValues = Ember.makeArray(this.$('select').val());
311 | if (this.get('multiple')) {
312 | this.set('selection', contentList.filter(function(item) {
313 | return selectedValues.indexOf(item.get('value')) !== -1;
314 | }));
315 | } else if (hasPrompt && Ember.isEmpty(selectedValues[0])) {
316 | this.setProperties({value: null, selection: null});
317 | } else {
318 | this.send('selectItem', _findBy(contentList, 'value', selectedValues[0]));
319 | }
320 | this.sendChangeAction();
321 | },
322 |
323 | toggleSelectAllNone() {
324 | var listName;
325 | if (this.get('hasSelectedItems')) {
326 | listName = 'selectedContentList';
327 | } else {
328 | listName = 'unselectedContentList';
329 | }
330 | this.send('selectAllNone', listName);
331 | return false;
332 | },
333 |
334 | clearFilter() {
335 | this.set('searchFilter', null);
336 | return false;
337 | }
338 | }
339 | });
340 |
--------------------------------------------------------------------------------
/app/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sukima/ember-cli-select-picker/ea014668ae740d2f72375e91aa3de3e9e2e48b80/app/.gitkeep
--------------------------------------------------------------------------------
/app/components/keyboard-select-picker.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import SelectPicker from './select-picker';
3 | import ItemCursorMixin from 'ember-cli-select-picker/mixins/item-cursor';
4 |
5 | const KEY_ENTER = 13;
6 | const KEY_ESC = 27;
7 | const KEY_UP = 38;
8 | const KEY_DOWN = 40;
9 |
10 | export default SelectPicker.extend(ItemCursorMixin, {
11 | layoutName: 'components/select-picker',
12 | classNames: ['select-picker', 'keyboard-select-picker'],
13 |
14 | didInsertElement() {
15 | this.$().on(`keydown.${this.get('elementId')}`,
16 | Ember.run.bind(this, 'handleKeyPress'));
17 | },
18 |
19 | willDestroyElement() {
20 | this.$().off(`keydown.${this.get('elementId')}`);
21 | },
22 |
23 | focusActiveItem() {
24 | this.$(`[data-itemid=${this.get('activeItem.itemId')}]`).focus();
25 | },
26 |
27 | handleKeyPress(e) {
28 | var actionName = (() => {
29 | switch (e.which) {
30 | case KEY_DOWN: return 'activeNext';
31 | case KEY_UP: return 'activePrev';
32 | case KEY_ESC: return 'closeDropdown';
33 | case KEY_ENTER:
34 | return this.get('showDropdown') ?
35 | 'selectActiveItem' :
36 | 'openDropdown';
37 | default: return null;
38 | }
39 | })();
40 |
41 | if (actionName) {
42 | e.preventDefault();
43 | Ember.run(() => { this.send(actionName); });
44 | this.focusActiveItem();
45 | return false;
46 | }
47 |
48 | return true;
49 | }
50 | });
51 |
--------------------------------------------------------------------------------
/app/components/list-picker.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import SelectPickerMixin from 'ember-cli-select-picker/mixins/select-picker';
3 |
4 | var I18nProps = (Ember.I18n && Ember.I18n.TranslateableProperties) || {};
5 |
6 | export default Ember.Component.extend(
7 | SelectPickerMixin, I18nProps,
8 | {
9 | classNames: ['select-picker', 'list-picker'],
10 | selectAllLabel: 'Select All',
11 | selectNoneLabel: 'Select None',
12 | nativeMobile: false
13 | }
14 | );
15 |
--------------------------------------------------------------------------------
/app/components/select-picker.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import SelectPickerMixin from 'ember-cli-select-picker/mixins/select-picker';
3 |
4 | var I18nProps = (Ember.I18n && Ember.I18n.TranslateableProperties) || {};
5 |
6 | export default Ember.Component.extend(
7 | SelectPickerMixin, I18nProps, {
8 |
9 | nothingSelectedMessage: 'Nothing Selected',
10 | multipleSelectedMessage: '%@ items selected',
11 | selectAllLabel: 'All',
12 | selectNoneLabel: 'None',
13 |
14 | nativeMobile: true,
15 |
16 | classNames: ['select-picker', 'btn-group'],
17 | buttonClass: 'btn-default',
18 |
19 | badgeEnabled: Ember.computed.and('showBadge', 'multiple'),
20 |
21 | selectionBadge: Ember.computed(
22 | 'selection.length', 'badgeEnabled',
23 | function() {
24 | const enabled = this.get('badgeEnabled');
25 | const selected = this.get('selection.length');
26 | return (enabled && selected && selected !== 0) ? selected : '';
27 | }
28 | ),
29 |
30 | setupDom: Ember.on('didInsertElement', function() {
31 | const id = this.get('elementId');
32 | Ember.run.scheduleOnce('afterRender', this, this.updateDropUp);
33 | $(document)
34 | .on(`click.${id}`, Ember.run.bind(this, this.hideDropdownMenu))
35 | .on(`touchstart.${id}`, Ember.run.bind(this, this.hideDropdownMenu))
36 | .on(`scroll.${id}`, Ember.run.bind(this, this.updateDropUp))
37 | .on(`resize.${id}`, Ember.run.bind(this, this.updateDropUp));
38 | }),
39 |
40 | hideDropdownMenu: function(evt) {
41 | if (this.get('keepDropdownOpen')) {
42 | this.set('keepDropdownOpen', false);
43 | return;
44 | }
45 | if (this.element && !$.contains(this.element, evt.target)) {
46 | this.send('closeDropdown');
47 | }
48 | },
49 |
50 | updateDropUp() {
51 | const windowHeight = $(window).height();
52 | const scrollTop = $(window).scrollTop();
53 | const buttonOffset = this.$().offset().top;
54 | const buttonHeight = this.$().height();
55 | const menuHeight = this.$('.dropdown-menu').height();
56 | const viewportOffset = buttonOffset - scrollTop;
57 | const menuBottom = viewportOffset + buttonHeight + menuHeight;
58 | this.set('isDropUp', menuBottom > windowHeight);
59 | },
60 |
61 | teardownDom: Ember.on('willDestroyElement', function() {
62 | $(document).off(`.${this.get('elementId')}`);
63 | }),
64 |
65 | actions: {
66 | showHide() {
67 | this.toggleProperty('showDropdown');
68 | },
69 |
70 | openDropdown() {
71 | this.set('showDropdown', true);
72 | },
73 |
74 | closeDropdown() {
75 | this.set('showDropdown', false);
76 | }
77 | }
78 | });
79 |
--------------------------------------------------------------------------------
/app/templates/components/-native-select.hbs:
--------------------------------------------------------------------------------
1 |
5 | {{#if showNativePrompt}}
6 | {{promptMessage}}
7 | {{/if}}
8 | {{#if nestedGroupContentList.firstObject.name}}
9 | {{#each nestedGroupContentList as |group|}}
10 |
11 | {{#each group.items as |item|}}
12 | {{item.label}}
13 | {{/each}}
14 |
15 | {{/each}}
16 | {{else}}
17 | {{#each contentList as |item|}}
18 | {{item.label}}
19 | {{/each}}
20 | {{/if}}
21 |
22 |
--------------------------------------------------------------------------------
/app/templates/components/list-picker.hbs:
--------------------------------------------------------------------------------
1 | {{#if nativeMobile}}
2 |
3 | {{yield}}
4 | {{partial "components/native-select"}}
5 |
6 | {{/if}}
7 |
8 |
9 | {{yield}}
10 |
11 | {{#if liveSearch}}
12 |
13 | {{input type="text" class="search-filter form-control" value=searchFilter focus="preventClosing"}}
14 |
15 |
19 |
20 |
21 |
22 |
23 | {{/if}}
24 | {{#if multiple}}
25 | {{#if splitAllNoneButtons}}
26 |
27 | {{selectAllLabel}}
28 | {{selectNoneLabel}}
29 |
30 | {{else}}
31 |
32 |
33 | {{selectAllNoneLabel}}
34 |
35 |
36 |
37 | {{/if}}
38 | {{/if}}
39 | {{#each nestedGroupContentList as |group|}}
40 | {{#if group.name}}
{{group.name}} {{/if}}
41 |
42 | {{#each group.items as |item|}}
43 |
46 | {{item.label}}
47 |
48 |
49 | {{/each}}
50 |
51 | {{/each}}
52 |
53 |
--------------------------------------------------------------------------------
/app/templates/components/select-picker.hbs:
--------------------------------------------------------------------------------
1 | {{#if nativeMobile}}
2 |
3 | {{yield}}
4 | {{partial "components/native-select"}}
5 |
6 | {{/if}}
7 |
8 |
10 |
21 |
76 |
77 |
--------------------------------------------------------------------------------
/blueprints/ember-cli-select-picker/index.js:
--------------------------------------------------------------------------------
1 | /* jshint node: true */
2 | module.exports = {
3 | // no-op since we're just adding dependencies
4 | normalizeEntityName: function() {},
5 |
6 | afterInstall: function() {
7 | return this.addBowerPackagesToProject([
8 | {name: 'bootstrap', target: '~3.3.4'}
9 | ]);
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ember-cli-select-picker",
3 | "dependencies": {
4 | "ember": "1.13.8",
5 | "ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3",
6 | "ember-cli-test-loader": "ember-cli-test-loader#0.1.3",
7 | "ember-load-initializers": "ember-cli/ember-load-initializers#0.1.5",
8 | "ember-qunit": "0.4.9",
9 | "ember-qunit-notifications": "0.0.7",
10 | "ember-resolver": "~0.1.18",
11 | "jquery": "~1.11.3",
12 | "loader.js": "ember-cli/loader.js#3.2.1",
13 | "qunit": "~1.18.0",
14 | "chance": "~0.7.3",
15 | "highlightjs": "~8.4.0",
16 | "bootstrap": "~3.3.4"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/config/ember-try.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | scenarios: [
3 | {
4 | name: 'default',
5 | dependencies: { }
6 | },
7 | {
8 | name: 'ember-release',
9 | dependencies: {
10 | 'ember': 'components/ember#release'
11 | },
12 | resolutions: {
13 | 'ember': 'release'
14 | }
15 | }
16 | /* TODO: Skipping, Current code does not work on beta/canary builds yet.
17 | {
18 | name: 'ember-beta',
19 | dependencies: {
20 | 'ember': 'components/ember#beta'
21 | },
22 | resolutions: {
23 | 'ember': 'beta'
24 | }
25 | },
26 | {
27 | name: 'ember-canary',
28 | dependencies: {
29 | 'ember': 'components/ember#canary'
30 | },
31 | resolutions: {
32 | 'ember': 'canary'
33 | }
34 | }
35 | */
36 | ]
37 | };
38 |
--------------------------------------------------------------------------------
/config/environment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(/* environment, appConfig */) {
4 | return { };
5 | };
6 |
--------------------------------------------------------------------------------
/ember-cli-build.js:
--------------------------------------------------------------------------------
1 | /* global require, module */
2 | var EmberApp = require('ember-cli/lib/broccoli/ember-addon');
3 | var Funnel = require('broccoli-funnel');
4 |
5 | module.exports = function(defaults) {
6 | var app = new EmberApp(defaults, {
7 | sourcemaps: {
8 | enabled: false
9 | },
10 | fingerprint: {
11 | customHash: 'dist',
12 | exclude: ['screen-shot.png']
13 | },
14 | minifyCSS: {
15 | enabled: false
16 | },
17 | minifyJS: {
18 | enabled: false
19 | }
20 | });
21 |
22 | /*
23 | This build file specifes the options for the dummy test app of this
24 | addon, located in `/tests/dummy`
25 | This build file does *not* influence how the addon or the app using it
26 | behave. You most likely want to be modifying `./index.js` or app's build file
27 | */
28 | app.import('bower_components/bootstrap/dist/css/bootstrap.css');
29 | app.import('bower_components/bootstrap/dist/js/bootstrap.js');
30 |
31 | app.import('bower_components/chance/chance.js');
32 |
33 | app.import('bower_components/highlightjs/styles/github.css');
34 | app.import('bower_components/highlightjs/highlight.pack.js');
35 |
36 | var fontTree = new Funnel(
37 | 'bower_components/bootstrap/dist/fonts',
38 | {destDir: '/fonts'}
39 | );
40 |
41 | return app.toTree([fontTree]);
42 | };
43 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /* jshint node: true */
2 | 'use strict';
3 |
4 | module.exports = {
5 | name: 'ember-cli-select-picker',
6 |
7 | included: function(app, parentAddon) {
8 | var target = (parentAddon || app);
9 |
10 | target.import('vendor/select-picker.css');
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ember-cli-select-picker",
3 | "version": "2.3.8",
4 | "description": "A bootstrap 3 compatible select component",
5 | "author": "Devin Weaver (@sukima) ",
6 | "homepage": "https://sukima.github.io/ember-cli-select-picker/",
7 | "directories": {
8 | "doc": "doc",
9 | "test": "tests"
10 | },
11 | "scripts": {
12 | "start": "ember server",
13 | "build": "ember build",
14 | "test": "ember try:testall",
15 | "deploy": "ember github-pages:commit --message \"Deploy gh-pages from commit $(git rev-parse HEAD)\""
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/sukima/ember-cli-select-picker"
20 | },
21 | "bugs": {
22 | "url": "https://github.com/sukima/ember-cli-select-picker/issues"
23 | },
24 | "engines": {
25 | "node": ">= 0.10.0"
26 | },
27 | "license": "MIT",
28 | "devDependencies": {
29 | "broccoli-asset-rev": "^2.1.2",
30 | "broccoli-funnel": "^1.0.1",
31 | "ember-cli": "1.13.8",
32 | "ember-cli-app-version": "0.5.0",
33 | "ember-cli-content-security-policy": "0.4.0",
34 | "ember-cli-dependency-checker": "^1.0.1",
35 | "ember-cli-github-pages": "0.0.6",
36 | "ember-cli-htmlbars": "0.7.9",
37 | "ember-cli-htmlbars-inline-precompile": "^0.2.0",
38 | "ember-cli-ic-ajax": "0.2.1",
39 | "ember-cli-inject-live-reload": "^1.3.1",
40 | "ember-cli-qunit": "^1.0.0",
41 | "ember-cli-release": "0.2.3",
42 | "ember-cli-sri": "^1.0.3",
43 | "ember-cli-uglify": "^1.2.0",
44 | "ember-disable-prototype-extensions": "^1.0.0",
45 | "ember-disable-proxy-controllers": "^1.0.0",
46 | "ember-export-application-global": "^1.0.3",
47 | "ember-try": "0.0.6"
48 | },
49 | "keywords": [
50 | "ember-addon",
51 | "bootstrap",
52 | "ember",
53 | "select",
54 | "component"
55 | ],
56 | "dependencies": {
57 | "ember-cli-babel": "^5.1.3"
58 | },
59 | "ember-addon": {
60 | "configPath": "tests/dummy/config",
61 | "demoURL": "http://sukima.github.io/ember-cli-select-picker/"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/testem.json:
--------------------------------------------------------------------------------
1 | {
2 | "framework": "qunit",
3 | "test_page": "tests/index.html?hidepassed",
4 | "disable_watching": true,
5 | "launch_in_ci": [
6 | "PhantomJS"
7 | ],
8 | "launch_in_dev": [
9 | "PhantomJS",
10 | "Chrome"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/tests/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "document",
4 | "window",
5 | "location",
6 | "setTimeout",
7 | "$",
8 | "-Promise",
9 | "define",
10 | "console",
11 | "visit",
12 | "exists",
13 | "fillIn",
14 | "click",
15 | "keyEvent",
16 | "triggerEvent",
17 | "find",
18 | "findWithAssert",
19 | "wait",
20 | "DS",
21 | "andThen",
22 | "currentURL",
23 | "currentPath",
24 | "currentRouteName",
25 | "Chance",
26 | "chance",
27 | "hljs"
28 | ],
29 | "node": false,
30 | "browser": false,
31 | "boss": true,
32 | "curly": true,
33 | "debug": false,
34 | "devel": false,
35 | "eqeqeq": true,
36 | "evil": true,
37 | "forin": false,
38 | "immed": false,
39 | "laxbreak": false,
40 | "newcap": true,
41 | "noarg": true,
42 | "noempty": false,
43 | "nonew": false,
44 | "nomen": false,
45 | "onevar": false,
46 | "plusplus": false,
47 | "regexp": false,
48 | "undef": true,
49 | "sub": true,
50 | "strict": false,
51 | "white": false,
52 | "eqnull": true,
53 | "esnext": true,
54 | "unused": true
55 | }
56 |
--------------------------------------------------------------------------------
/tests/common/select-picker.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import { test } from 'ember-qunit';
3 |
4 | const contentArray = [
5 | {name: 'First item', id: 1},
6 | {name: 'Second item', id: 2},
7 | {name: 'Third item', id: 3},
8 | {name: 'Another', id: 4}
9 | ];
10 |
11 | export default function() {
12 | test('sends action when there are updates to the selection', function(assert) {
13 | assert.expect(1);
14 |
15 | var results;
16 | var component = this.subject({
17 | content: contentArray,
18 | selection: Ember.A(),
19 | multiple: true,
20 | optionValuePath: 'content.id',
21 | optionLabelPath: 'content.name',
22 | attrs: {
23 | action: function(values) { results = values; }
24 | }
25 | });
26 |
27 | Ember.run(function() {
28 | component.send('selectItem', component.get('contentList.firstObject'));
29 | });
30 |
31 | assert.ok(
32 | Ember.isPresent(results),
33 | 'action should be triggered on change'
34 | );
35 | });
36 |
37 | test('updates selection when selectItem is triggered (single)', function(assert) {
38 | assert.expect(2);
39 |
40 | var component = this.subject({
41 | content: contentArray,
42 | selection: Ember.A(),
43 | optionValuePath: 'content.id',
44 | optionLabelPath: 'content.name'
45 | });
46 |
47 | assert.ok(
48 | Ember.isEmpty(component.get('selection')),
49 | 'no items should be selected'
50 | );
51 |
52 | Ember.run(function() {
53 | component.send('selectItem', component.get('contentList.firstObject'));
54 | });
55 |
56 | assert.ok(
57 | Ember.isPresent(component.get('selection')),
58 | 'one item should be selected'
59 | );
60 | });
61 |
62 | test('updates selection when selectItem is triggered (multiple)', function(assert) {
63 | assert.expect(3);
64 |
65 | var component = this.subject({
66 | multiple: true,
67 | content: contentArray,
68 | selection: Ember.A(),
69 | optionValuePath: 'content.id',
70 | optionLabelPath: 'content.name'
71 | });
72 |
73 | Ember.run(function() {
74 | component.send('selectAllNone', 'unselectedContentList');
75 | });
76 |
77 | assert.equal(
78 | component.get('selection.length'), contentArray.length,
79 | 'all items should be selected'
80 | );
81 |
82 | Ember.run(function() {
83 | component.send('selectAllNone', 'selectedContentList');
84 | });
85 |
86 | assert.equal(
87 | component.get('selection.length'), 0,
88 | 'no items should be selected'
89 | );
90 |
91 | Ember.run(function() {
92 | component.send('selectItem', component.get('contentList.firstObject'));
93 | });
94 |
95 | assert.equal(
96 | component.get('selection.length'), 1,
97 | 'one item should be selected'
98 | );
99 | });
100 |
101 | test('Normal Search (multiple)', function(assert) {
102 | assert.expect(5);
103 |
104 | var component = this.subject({
105 | multiple: true,
106 | liveSearch: true,
107 | content: contentArray,
108 | selection: Ember.A(),
109 | optionValuePath: 'content.id',
110 | optionLabelPath: 'content.name'
111 | });
112 |
113 | assert.equal(
114 | component.get('contentList.length'), contentArray.length,
115 | 'contentList should not be filtered initially'
116 | );
117 |
118 | Ember.run(function() {
119 | component.set('searchFilter', 'F');
120 | });
121 |
122 | // Only one Items has 'F' litter in it (First Item), So, Filtering should
123 | // result in one item being listed.
124 | assert.equal(
125 | component.get('contentList.length'), 1,
126 | 'contentList should be filtered to one result for "F"'
127 | );
128 |
129 | Ember.run(function() {
130 | component.set('searchFilter', 'S');
131 | });
132 |
133 | // The result should be 2 (First, Second)
134 | assert.equal(
135 | component.get('contentList.length'), 2,
136 | 'contentList should be filtered to two results for "S"'
137 | );
138 |
139 | Ember.run(function() {
140 | component.send('clearFilter');
141 | });
142 |
143 | assert.equal(
144 | component.get('contentList.length'), contentArray.length,
145 | 'contentList should not be filtered when searchFilter is cleared'
146 | );
147 |
148 | Ember.run(function() {
149 | component.set('searchFilter', 'itm');
150 | });
151 |
152 | // When search is set to normal (not advanced), searching for 'itm' should
153 | // give 0 results
154 | assert.equal(
155 | component.get('contentList.length'), 0,
156 | 'contentList should be filtered to no results for "itm"'
157 | );
158 | });
159 |
160 | test('Advanced Search (multiple)', function(assert) {
161 | assert.expect(2);
162 |
163 | var component = this.subject({
164 | multiple: true,
165 | liveSearch: 'advanced',
166 | content: contentArray,
167 | selection: Ember.A(),
168 | optionValuePath: 'content.id',
169 | optionLabelPath: 'content.name'
170 | });
171 |
172 | assert.equal(
173 | component.get('contentList.length'), contentArray.length,
174 | 'contentList should not be filtered initially'
175 | );
176 |
177 | Ember.run(function() {
178 | component.set('searchFilter', 'itm');
179 | });
180 |
181 | assert.equal(
182 | component.get('contentList.length'), 3,
183 | 'contentList should be filtered to three results for "itm"'
184 | );
185 | });
186 | }
187 |
--------------------------------------------------------------------------------
/tests/dummy/app/app.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import Resolver from 'ember/resolver';
3 | import loadInitializers from 'ember/load-initializers';
4 | import config from './config/environment';
5 |
6 | var App;
7 |
8 | Ember.MODEL_FACTORY_INJECTIONS = true;
9 |
10 | App = Ember.Application.extend({
11 | modulePrefix: config.modulePrefix,
12 | podModulePrefix: config.podModulePrefix,
13 | Resolver: Resolver
14 | });
15 |
16 | loadInitializers(App, config.modulePrefix);
17 |
18 | export default App;
19 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sukima/ember-cli-select-picker/ea014668ae740d2f72375e91aa3de3e9e2e48b80/tests/dummy/app/components/.gitkeep
--------------------------------------------------------------------------------
/tests/dummy/app/components/highlight-code.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.Component.extend({
4 | lang: 'nohighlight',
5 | highlightCodeBlocks: Ember.on('didInsertElement', function() {
6 | hljs.highlightBlock(this.$('code').get(0));
7 | })
8 | });
9 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/modal-dialog.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.Component.extend({
4 | isVisible: false,
5 | showHide: Ember.observer('isVisible', function() {
6 | this.$('.modal').css({display: (this.get('isVisible') ? 'block' : 'none')});
7 | })
8 | });
9 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sukima/ember-cli-select-picker/ea014668ae740d2f72375e91aa3de3e9e2e48b80/tests/dummy/app/controllers/.gitkeep
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/application.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import config from '../config/environment';
3 |
4 | var ApplicationController = Ember.Controller.extend({
5 | addonVersion: config.APP.addonVersion
6 | });
7 |
8 | export default ApplicationController;
9 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/index.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import ChoicesMixin from '../mixins/choices';
3 |
4 | function neighborhood() {
5 | return chance.pick(['East side', 'West side']);
6 | }
7 |
8 | export default Ember.Controller.extend(ChoicesMixin, {
9 | singleContent: Ember.computed(function() {
10 | return chance.unique(chance.street, 10)
11 | .map(function(street) {
12 | return {label: street, value: street};
13 | });
14 | }),
15 |
16 | multipleContent: Ember.computed(function() {
17 | return Ember.A(chance.unique(chance.street, 10)
18 | .map(function(street) {
19 | return {label: street, value: street, group: neighborhood()};
20 | }))
21 | .sortBy('group');
22 | }),
23 |
24 | listContent: Ember.computed(function() {
25 | return Ember.A(chance.unique(chance.street, 10)
26 | .map(function(street) {
27 | return {label: street, value: street, group: neighborhood()};
28 | }))
29 | .sortBy('group');
30 | })
31 | });
32 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/keyboard.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import ChoicesMixin from '../mixins/choices';
3 |
4 | function neighborhood() {
5 | return chance.pick(['East side', 'West side']);
6 | }
7 |
8 | var KeyboardController = Ember.Controller.extend(ChoicesMixin, {
9 | singleContent: Ember.computed(function() {
10 | return chance.unique(chance.street, 10)
11 | .map(function(street) {
12 | return {label: street, value: street};
13 | });
14 | }),
15 |
16 | multipleContent: Ember.computed(function() {
17 | return Ember.A(chance.unique(chance.street, 10)
18 | .map(function(street) {
19 | return {label: street, value: street, group: neighborhood()};
20 | }))
21 | .sortBy('group');
22 | })
23 | });
24 |
25 | export default KeyboardController;
26 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/options.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import ChoicesMixin from '../mixins/choices';
3 |
4 | function neighborhood() {
5 | return chance.pick(['East side', 'West side']);
6 | }
7 |
8 | export default Ember.Controller.extend(ChoicesMixin, {
9 | showHelloDialog: false,
10 |
11 | prepMultipleValue: Ember.on('init', function() {
12 | var sample = this.get('multipleContent').slice(0, 4);
13 | this.set('multipleValue', sample);
14 | }),
15 |
16 | singleContent: Ember.computed(function() {
17 | return chance.unique(chance.street, 10)
18 | .map(function(street) {
19 | return {label: street, value: street};
20 | });
21 | }),
22 |
23 | multipleContent: Ember.computed(function() {
24 | return Ember.A(chance.unique(chance.street, 10)
25 | .map(function(street) {
26 | return {label: street, value: street, group: neighborhood()};
27 | }))
28 | .sortBy('group');
29 | }),
30 |
31 | listContent: Ember.computed(function() {
32 | return Ember.A(chance.unique(chance.street, 10)
33 | .map(function(street) {
34 | return {label: street, value: street, group: neighborhood()};
35 | }))
36 | .sortBy('group');
37 | }),
38 |
39 | actions: {
40 | showHelloDialog() {
41 | this.set('showHelloDialog', true);
42 | },
43 |
44 | hideHelloDialog() {
45 | this.set('showHelloDialog', false);
46 | }
47 | }
48 | });
49 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/searching.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import { choicesToString, setChoicesAction } from '../modules/choices-props';
3 |
4 | function popularity() {
5 | return chance.pick(['Great states', 'Awesome states']);
6 | }
7 |
8 | function stateList() {
9 | return Ember.computed(function() {
10 | return Ember.A(chance.states()
11 | .map(function(state) {
12 | return {label: state.name, value: state.name, group: popularity()};
13 | }))
14 | .sortBy('group', 'label');
15 | });
16 | }
17 |
18 | var SearchingController = Ember.Controller.extend({
19 | simpleSearchContent: stateList(),
20 | simpleSearchChoices: [],
21 | simpleSearchChoicesStr: choicesToString('simpleSearchChoices'),
22 |
23 | advancedSearchContent: stateList(),
24 | advancedSearchChoices: [],
25 | advancedSearchChoicesStr: choicesToString('advancedSearchChoices'),
26 |
27 | listSearchContent: stateList(),
28 | listSearchChoices: [],
29 | listSearchChoicesStr: choicesToString('listSearchChoices'),
30 |
31 | actions: {
32 | setSimpleSearchChoices: setChoicesAction('simpleSearchValue'),
33 | setAdvancedSearchChoices: setChoicesAction('advancedSearchValue'),
34 | setListSearchChoices: setChoicesAction('listSearchValue')
35 | }
36 | });
37 |
38 | export default SearchingController;
39 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/test-list-picker.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.Controller.extend({
4 | theContent: [
5 | {name: 'First item', id: 1},
6 | {name: 'Second item', id: 2},
7 | {name: 'Third item', id: 3},
8 | {name: 'Another', id: 4}
9 | ]
10 | });
11 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/test-select-picker.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.Controller.extend({
4 | theContent: [
5 | {name: 'First item', id: 1},
6 | {name: 'Second item', id: 2},
7 | {name: 'Third item', id: 3},
8 | {name: 'Another', id: 4}
9 | ]
10 | });
11 |
--------------------------------------------------------------------------------
/tests/dummy/app/helpers/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sukima/ember-cli-select-picker/ea014668ae740d2f72375e91aa3de3e9e2e48b80/tests/dummy/app/helpers/.gitkeep
--------------------------------------------------------------------------------
/tests/dummy/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | TestSelectPicker
7 |
8 |
9 |
10 | {{content-for 'head'}}
11 |
12 |
13 |
14 |
15 | {{content-for 'head-footer'}}
16 |
17 |
18 | {{content-for 'body'}}
19 |
20 |
21 |
22 |
23 | {{content-for 'body-footer'}}
24 |
25 |
26 |
--------------------------------------------------------------------------------
/tests/dummy/app/mixins/choices.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import { setChoicesAction, choicesToString } from '../modules/choices-props';
3 |
4 | export default Ember.Mixin.create({
5 | singleChoice: [],
6 | singleChoiceStr: Ember.computed.readOnly('singleChoice.firstObject.value'),
7 |
8 | multipleChoices: [],
9 | multipleChoicesStr: choicesToString('multipleChoices'),
10 |
11 | listChoices: [],
12 | listChoicesStr: choicesToString('listChoices'),
13 |
14 | actions: {
15 | setSingleChoice: setChoicesAction('singleChoice'),
16 | setMultipleChoices: setChoicesAction('multipleChoices'),
17 | setListChoices: setChoicesAction('listChoices')
18 | }
19 | });
20 |
--------------------------------------------------------------------------------
/tests/dummy/app/models/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sukima/ember-cli-select-picker/ea014668ae740d2f72375e91aa3de3e9e2e48b80/tests/dummy/app/models/.gitkeep
--------------------------------------------------------------------------------
/tests/dummy/app/modules/choices-props.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export function setChoicesAction(prop) {
4 | return function(selection) {
5 | this.set(prop, Ember.makeArray(selection));
6 | };
7 | }
8 |
9 | export function choicesToString(dependentProp) {
10 | return Ember.computed(`${dependentProp}.[].value`, function() {
11 | return Ember.A(this.get(dependentProp)).mapBy('value').join(', ');
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/tests/dummy/app/router.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import config from './config/environment';
3 |
4 | var Router = Ember.Router.extend({
5 | location: config.locationType,
6 | rootURL: config.baseURL
7 | });
8 |
9 | Router.map(function() {
10 | this.route('test-select-picker');
11 | this.route('test-list-picker');
12 | this.route('install');
13 | this.route('searching');
14 | this.route('options');
15 | this.route('i18n');
16 | this.route('keyboard');
17 | });
18 |
19 | export default Router;
20 |
--------------------------------------------------------------------------------
/tests/dummy/app/routes/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sukima/ember-cli-select-picker/ea014668ae740d2f72375e91aa3de3e9e2e48b80/tests/dummy/app/routes/.gitkeep
--------------------------------------------------------------------------------
/tests/dummy/app/styles/app.css:
--------------------------------------------------------------------------------
1 | .search-examples code u {
2 | background-color: #F9F200;
3 | }
4 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/application.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Select Picker
4 |
An Ember based reinvention of the select picker for Bootstrap 3.
5 |
Version: {{addonVersion}}
6 |
7 |
8 | {{#link-to "index" tagName="li"}}
9 | {{#link-to "index" class="btn btn-lg"}}Intro{{/link-to}}
10 | {{/link-to}}
11 | {{#link-to "install" tagName="li"}}
12 | {{#link-to "install" class="btn btn-lg"}}Installing{{/link-to}}
13 | {{/link-to}}
14 | {{#link-to "searching" tagName="li"}}
15 | {{#link-to "searching" class="btn btn-lg"}}Searching{{/link-to}}
16 | {{/link-to}}
17 | {{#link-to "options" tagName="li"}}
18 | {{#link-to "options" class="btn btn-lg"}}Options{{/link-to}}
19 | {{/link-to}}
20 | {{#link-to "i18n" tagName="li"}}
21 | {{#link-to "i18n" class="btn btn-lg"}}Internationalization{{/link-to}}
22 | {{/link-to}}
23 | {{#link-to "keyboard" tagName="li"}}
24 | {{#link-to "keyboard" class="btn btn-lg"}}Keyboard Support{{/link-to}}
25 | {{/link-to}}
26 |
27 |
28 |
29 |
30 | {{outlet}}
31 |
32 |
33 |
34 | {{!--
35 | live search
36 | simple search
37 | advanced search
38 | options
39 | all or none
40 | class changes primary, info, success, warning, danger, inverse
41 | prompt
42 | I18n support
43 | Keyboard support
44 | --}}
45 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/components/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sukima/ember-cli-select-picker/ea014668ae740d2f72375e91aa3de3e9e2e48b80/tests/dummy/app/templates/components/.gitkeep
--------------------------------------------------------------------------------
/tests/dummy/app/templates/components/highlight-code.hbs:
--------------------------------------------------------------------------------
1 | {{yield}}
2 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/components/modal-dialog.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 | {{yield}}
10 |
11 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/i18n.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
I18n Support
4 |
5 |
6 |
To use i18n support you first need to install the ember-i18n
addon to your applicaiton project.
7 |
$ ember install ember-i18n
8 |
9 |
10 |
11 |
12 |
13 |
Template Example
14 |
15 |
16 | {{#highlight-code lang="handlebars"}}
17 | \{{select-picker content=multipleContent
18 | selection=multipleValue
19 | multiple=true
20 |
21 | titleTranslation="tranlation.key.title"
22 | selectAllTranslation="tranlation.key.select_all"
23 | selectNoneTranslation="tranlation.key.select_none"
24 | nothingSelectedMessageTranslation="tranlation.key.prompt"
25 |
26 | summaryMessageKey="i18n.tranlation.key.summary"
27 |
28 | optionLabelPath="content.label"
29 | optionValuePath="content.value"}}
30 | {{/highlight-code}}
31 |
32 |
33 |
34 |
35 |
36 |
JSON Strings Example
37 |
38 |
39 | {{#highlight-code lang="json"}}
40 | {
41 | "translation": {
42 | "key: {
43 | "title": "translated title used for accessibility",
44 | "prompt": "translated prompt",
45 | "select_all": "Select All",
46 | "select_none": "Select None",
47 | "summary": {
48 | "one": "You selected \{{item}}",
49 | "other": "You selected \{{count}} items: \{{list}}"
50 | }
51 | }
52 | }
53 | }
54 | {{/highlight-code}}
55 |
56 |
57 |
58 |
59 |
60 |
Caveats
61 |
62 |
63 |
Because translated properties can not have interpolated values the summaryMessage
uses summaryMessageKey
instead of summaryMessageTranslation
. It has the following values:
64 |
65 | count
66 | Number of selected items. If zero then nothingSelectedMessage
is used instead.
67 | item
68 | The item selected (first item in the list if multiple items are selected).
69 | list
70 | The list of selected items comma separated.
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/index.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Single Selection
4 |
5 |
6 |
7 |
8 |
Results: {{singleChoiceStr}}
9 |
10 |
11 |
12 |
13 |
{{select-picker content=singleContent
14 | selection=singleChoice
15 | action=(action "setSingleChoice")
16 | optionLabelPath="content.label"
17 | optionValuePath="content.value"}}
18 |
19 |
20 | {{#highlight-code lang="handlebars"}}
21 | \{{select-picker content=singleContent
22 | selection=singleChoice
23 | action=(action "setSingleChoice")
24 | optionLabelPath="content.label"
25 | optionValuePath="content.value"}}
26 | {{/highlight-code}}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
Multiple Selections
35 |
36 |
37 |
38 |
39 |
Results: {{multipleChoicesStr}}
40 |
41 |
42 |
43 |
44 |
{{select-picker content=multipleContent
45 | selection=multipleChoices
46 | action=(action "setMultipleChoices")
47 | multiple=true
48 | optionGroupPath="group"
49 | optionLabelPath="content.label"
50 | optionValuePath="content.value"}}
51 |
52 |
53 | {{#highlight-code lang="handlebars"}}
54 | \{{select-picker content=multipleContent
55 | selection=multipleChoices
56 | action=(action "setMultipleChoices")
57 | multiple=true
58 | optionGroupPath="group"
59 | optionLabelPath="content.label"
60 | optionValuePath="content.value"}}
61 | {{/highlight-code}}
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
List Picker
71 |
72 |
73 |
74 |
75 |
Results: {{listChoicesStr}}
76 |
77 |
78 |
79 |
80 |
{{list-picker content=listContent
81 | selection=listChoices
82 | action=(action "setListChoices")
83 | multiple="true"
84 | optionGroupPath="group"
85 | optionLabelPath="content.label"
86 | optionValuePath="content.value"}}
87 |
88 |
89 | {{#highlight-code lang="handlebars"}}
90 | \{{list-picker content=listContent
91 | selection=listChoices
92 | action=(action "setListChoices")
93 | multiple="true"
94 | optionGroupPath="group"
95 | optionLabelPath="content.label"
96 | optionValuePath="content.value"}}
97 | {{/highlight-code}}
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/install.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Installing
4 |
5 |
6 |
This is an Ember CLI Addon and can be installed through the ember
command.
7 |
$ ember install ember-cli-select-picker
8 |
9 |
10 |
11 |
24 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/keyboard.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Keyboard Support
4 |
5 |
6 |
To use keyboard support you first need to install the ember-keyboard-shortcuts
addon to your applicaiton project.
7 |
$ ember install ember-keyboard-shortcuts
8 |
9 |
10 |
11 |
12 | Key
13 | Action
14 |
15 |
16 |
17 |
18 | Esc
19 | Close the dropdown list
20 |
21 |
22 | Enter
23 | Open the dropdown list or Select/Unselect item the cursor is on
24 |
25 |
26 | Down
27 | Move cursor to next item
28 |
29 |
30 | Up
31 | Move cursor to previous item
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
Single Selection
40 |
41 |
42 |
43 |
44 |
Results: {{singleChoiceStr}}
45 |
46 |
47 |
48 |
49 | {{keyboard-select-picker content=singleContent
50 | selection=singleChoice
51 | action=(action "setSingleChoice")
52 | optionLabelPath="content.label"
53 | optionValuePath="content.value"}}
54 |
55 |
56 | {{#highlight-code lang="handlebars"}}
57 | \{{keyboard-select-picker content=singleContent
58 | selection=singleChoice
59 | action=(action "setSingleChoice")
60 | optionLabelPath="content.label"
61 | optionValuePath="content.value"}}
62 | {{/highlight-code}}
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
Multiple Selections
71 |
72 |
73 |
74 |
75 |
Results: {{multipleChoicesStr}}
76 |
77 |
78 |
79 |
80 | {{keyboard-select-picker content=multipleContent
81 | selection=multipleChoices
82 | action=(action "setMultipleChoices")
83 | multiple=true
84 | optionGroupPath="group"
85 | optionLabelPath="content.label"
86 | optionValuePath="content.value"}}
87 |
88 |
89 | {{#highlight-code lang="handlebars"}}
90 | \{{keyboard-select-picker content=multipleContent
91 | selection=multipleChoices
92 | action=(action "setMultipleChoices")
93 | multiple=true
94 | optionGroupPath="group"
95 | optionLabelPath="content.label"
96 | optionValuePath="content.value"}}
97 | {{/highlight-code}}
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/options.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Badges
4 |
5 |
6 |
7 |
8 |
Results: {{multipleChoicesStr}}
9 |
10 |
11 |
12 |
13 |
{{select-picker content=multipleContent
14 | selection=multipleChoices
15 | action=(action "setMultipleChoices")
16 | multiple=true
17 | showBadge=true
18 | summaryMessage="Multiple items selected"
19 | optionLabelPath="content.label"
20 | optionValuePath="content.value"}}
21 |
22 |
23 | {{#highlight-code lang="handlebars"}}
24 | \{{select-picker content=multipleContent
25 | selection=multipleChoices
26 | action=(action "setMultipleChoices")
27 | multiple=true
28 | showBadge=true
29 | summaryMessage="Multiple items selected"
30 | optionLabelPath="content.label"
31 | optionValuePath="content.value"}}
32 | {{/highlight-code}}
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
Mobile Support
41 |
42 |
43 |
44 |
45 |
By default the native select widget is used for small screens (mobile) If you want the Bootstrap styled picker on mobile devices then you need to set nativeMobile
to false .
46 |
Results: {{multipleChoicesStr}}
47 |
48 |
49 |
50 |
51 |
{{select-picker content=multipleContent
52 | selection=multipleChoices
53 | action=(action "setMultipleChoices")
54 | multiple=true
55 | nativeMobile=false
56 | optionLabelPath="content.label"
57 | optionValuePath="content.value"}}
58 |
59 |
60 | {{#highlight-code lang="handlebars"}}
61 | \{{select-picker content=multipleContent
62 | selection=multipleChoices
63 | action=(action "setMultipleChoices")
64 | multiple=true
65 | nativeMobile=false
66 | optionLabelPath="content.label"
67 | optionValuePath="content.value"}}
68 | {{/highlight-code}}
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
Custom content
77 |
78 |
79 |
80 |
81 |
You can add your own custom content to the top of the drop down by using Ember's block syntax.
82 |
Results: {{multipleChoicesStr}}
83 |
84 |
85 |
86 |
87 |
{{#select-picker content=multipleContent
88 | selection=multipleChoices
89 | action=(action "setMultipleChoices")
90 | multiple=true
91 | summaryMessage="Example content inside"
92 | optionLabelPath="content.label"
93 | optionValuePath="content.value"}}
94 |
95 | Hello! Try this: button
96 |
97 | {{/select-picker}}
98 |
99 |
100 | {{#highlight-code lang="handlebars"}}
101 | \{{#select-picker content=multipleContent
102 | selection=multipleChoices
103 | action=(action "setMultipleChoices")
104 | multiple=true
105 | summaryMessage="Example content inside"
106 | optionLabelPath="content.label"
107 | optionValuePath="content.value"}}
108 | <div class="alert alert-info">
109 | Hello! Try this <button \{{action "showHelloDialog"}}>button</button>
110 | </div>
111 | \{{/select-picker}}
112 | {{/highlight-code}}
113 |
114 |
115 |
116 |
117 |
118 | {{#modal-dialog title="Hello User" isVisible=showHelloDialog onDismiss=(action "hideHelloDialog")}}
119 | Hello random user!
120 | {{/modal-dialog}}
121 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/searching.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Single Search
4 |
5 |
6 |
7 |
8 |
Simple search attempts to match terms as full strings.
9 |
For example the term def
would match the words abcdef
, def ghi
, or indef initely
.
10 |
Result: {{simpleSearchChoicesStr}}
11 |
12 |
13 |
14 |
15 | {{select-picker content=simpleSearchContent
16 | selection=simpleSearchChoices
17 | action=(action "setSimpleSearchChoices")
18 | multiple=true
19 | liveSearch=true
20 | optionGroupPath="group"
21 | optionLabelPath="content.label"
22 | optionValuePath="content.value"}}
23 |
24 |
25 | {{#highlight-code lang="handlebars"}}
26 | \{{select-picker content=simpleSearchContent
27 | selection=simpleSearchChoices
28 | action=(action "setSimpleSearchChoices")
29 | multiple=true
30 | liveSearch=true
31 | optionGroupPath="group"
32 | optionLabelPath="content.label"
33 | optionValuePath="content.value"}}
34 | {{/highlight-code}}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
Advanced Search
43 |
44 |
45 |
46 |
47 |
Advanced search finds terms through a fuzzy match. It looks for characters in the string that appear in order from left to right with any number of other character in between.
48 |
For example the search term def
would match abcdef
or d ive rsif y
.
49 |
Result: {{advancedSearchChoicesStr}}
50 |
51 |
52 |
53 |
54 | {{select-picker content=advancedSearchContent
55 | selection=advancedSearchChoices
56 | action=(action "setAdvancedSearchChoices")
57 | multiple=true
58 | liveSearch="advanced"
59 | optionGroupPath="group"
60 | optionLabelPath="content.label"
61 | optionValuePath="content.value"}}
62 |
63 |
64 | {{#highlight-code lang="handlebars"}}
65 | \{{select-picker content=advancedSearchContent
66 | selection=advancedSearchChoices
67 | action=(action "setAdvancedSearchChoices")
68 | multiple=true
69 | liveSearch="advanced"
70 | optionGroupPath="group"
71 | optionLabelPath="content.label"
72 | optionValuePath="content.value"}}
73 | {{/highlight-code}}
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
List Picker
82 |
83 |
84 |
85 |
86 |
Result: {{listSearchChoicesStr}}
87 |
88 |
89 |
90 |
91 | {{list-picker content=listSearchContent
92 | selection=listSearchChoices
93 | action=(action "setListSearchChoices")
94 | multiple=true
95 | liveSearch="advanced"
96 | optionGroupPath="group"
97 | optionLabelPath="content.label"
98 | optionValuePath="content.value"}}
99 |
100 |
101 | {{#highlight-code lang="handlebars"}}
102 | \{{list-picker content=advancedSearchContent
103 | selection=listSearchChoices
104 | action=(action "setListSearchChoices")
105 | multiple=true
106 | liveSearch="advanced"
107 | optionGroupPath="group"
108 | optionLabelPath="content.label"
109 | optionValuePath="content.value"}}
110 | {{/highlight-code}}
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/test-list-picker.hbs:
--------------------------------------------------------------------------------
1 | list-picker
2 |
3 |
4 | {{list-picker content=theContent
5 | value=singleValue
6 | nativeMobile=false
7 | optionLabelPath="content.name"
8 | optionValuePath="content.id"}}
9 |
10 |
11 |
12 | {{list-picker content=theContent
13 | selection=multipleValue
14 | multiple=true
15 | nativeMobile=false
16 | optionLabelPath="content.name"
17 | optionValuePath="content.id"}}
18 |
19 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/test-select-picker.hbs:
--------------------------------------------------------------------------------
1 | select-picker
2 |
3 |
4 | {{select-picker content=theContent
5 | value=singleValue
6 | nativeMobile=false
7 | optionLabelPath="content.name"
8 | optionValuePath="content.id"}}
9 |
10 |
11 |
12 | {{select-picker content=theContent
13 | selection=multipleValue
14 | multiple=true
15 | showBadge=true
16 | nativeMobile=false
17 | optionLabelPath="content.name"
18 | optionValuePath="content.id"}}
19 |
20 |
21 |
22 | {{!select-picker content=theContent
23 | multiple=true
24 | selection=multipleValue
25 | liveSearch=true
26 | nativeMobile=false
27 | optionLabelPath="content.name"
28 | optionValuePath="content.id"}}
29 |
30 |
31 |
32 | {{!select-picker content=theContent
33 | multiple=true
34 | selection=multipleValue
35 | liveSearch="advanced"
36 | nativeMobile=false
37 | optionLabelPath="content.name"
38 | optionValuePath="content.id"}}
39 |
40 |
--------------------------------------------------------------------------------
/tests/dummy/app/views/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sukima/ember-cli-select-picker/ea014668ae740d2f72375e91aa3de3e9e2e48b80/tests/dummy/app/views/.gitkeep
--------------------------------------------------------------------------------
/tests/dummy/config/environment.js:
--------------------------------------------------------------------------------
1 | /* jshint node: true */
2 | var pkg = require('../../../package.json');
3 |
4 | module.exports = function(environment) {
5 | var ENV = {
6 | modulePrefix: 'dummy',
7 | environment: environment,
8 | baseURL: '/',
9 | locationType: 'auto',
10 | EmberENV: {
11 | FEATURES: {
12 | // Here you can enable experimental features on an ember canary build
13 | // e.g. 'with-controller': true
14 | }
15 | },
16 |
17 | APP: {
18 | // Here you can pass flags/options to your application instance
19 | // when it is created
20 | addonVersion: pkg.version
21 | }
22 | };
23 |
24 | if (environment === 'development') {
25 | // ENV.APP.LOG_RESOLVER = true;
26 | // ENV.APP.LOG_ACTIVE_GENERATION = true;
27 | // ENV.APP.LOG_TRANSITIONS = true;
28 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
29 | // ENV.APP.LOG_VIEW_LOOKUPS = true;
30 | }
31 |
32 | if (environment === 'test') {
33 | // Testem prefers this...
34 | ENV.baseURL = '/';
35 | ENV.locationType = 'none';
36 |
37 | // keep test console output quieter
38 | ENV.APP.LOG_ACTIVE_GENERATION = false;
39 | ENV.APP.LOG_VIEW_LOOKUPS = false;
40 |
41 | ENV.APP.rootElement = '#ember-testing';
42 | }
43 |
44 | if (environment === 'production') {
45 | ENV.baseURL = '/ember-cli-select-picker';
46 | }
47 |
48 | return ENV;
49 | };
50 |
--------------------------------------------------------------------------------
/tests/dummy/public/crossdomain.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
15 |
16 |
--------------------------------------------------------------------------------
/tests/dummy/public/robots.txt:
--------------------------------------------------------------------------------
1 | # http://www.robotstxt.org
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/tests/dummy/public/screen-shot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sukima/ember-cli-select-picker/ea014668ae740d2f72375e91aa3de3e9e2e48b80/tests/dummy/public/screen-shot.png
--------------------------------------------------------------------------------
/tests/helpers/resolver.js:
--------------------------------------------------------------------------------
1 | import Resolver from 'ember/resolver';
2 | import config from '../../config/environment';
3 |
4 | var resolver = Resolver.create();
5 |
6 | resolver.namespace = {
7 | modulePrefix: config.modulePrefix,
8 | podModulePrefix: config.podModulePrefix
9 | };
10 |
11 | export default resolver;
12 |
--------------------------------------------------------------------------------
/tests/helpers/start-app.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import Application from '../../app';
3 | import config from '../../config/environment';
4 |
5 | export default function startApp(attrs) {
6 | var application;
7 |
8 | var attributes = Ember.merge({}, config.APP);
9 | attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
10 |
11 | Ember.run(function() {
12 | application = Application.create(attributes);
13 | application.setupForTesting();
14 | application.injectTestHelpers();
15 | });
16 |
17 | return application;
18 | }
19 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dummy Tests
7 |
8 |
9 |
10 | {{content-for 'head'}}
11 | {{content-for 'test-head'}}
12 |
13 |
14 |
15 |
16 |
17 | {{content-for 'head-footer'}}
18 | {{content-for 'test-head-footer'}}
19 |
20 |
21 |
22 | {{content-for 'body'}}
23 | {{content-for 'test-body'}}
24 |
25 |
26 |
27 |
28 |
29 |
30 | {{content-for 'body-footer'}}
31 | {{content-for 'test-body-footer'}}
32 |
33 |
34 |
--------------------------------------------------------------------------------
/tests/integration/list-picker-test.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import { test, module } from 'qunit';
3 | import startApp from '../helpers/start-app';
4 |
5 | var App;
6 |
7 | function checkMarkVisibilityTest(selectPickerID) {
8 | var selector =
9 | `#${selectPickerID} .list-picker-items-container button:eq(%@) span.check-mark`;
10 | return function(itemIndex) {
11 | return !find(Ember.String.fmt(selector, itemIndex)).hasClass('invisible');
12 | };
13 | }
14 |
15 | module('List Picker Integration', {
16 | beforeEach: function() {
17 | App = startApp();
18 | },
19 | afterEach: function() {
20 | Ember.run(App, App.destroy);
21 | }
22 | });
23 |
24 | test('Select multiple', function(assert) {
25 | var isChecked = checkMarkVisibilityTest('multiple-picker');
26 |
27 | assert.expect(3);
28 |
29 | visit('/test-list-picker')
30 |
31 | .click('#multiple-picker .list-picker-items-container button:eq(0)')
32 | .click('#multiple-picker .list-picker-items-container button:eq(1)')
33 |
34 | .then(function() {
35 | // Dropdowns might not be visible causing the check mark to not be visible.
36 | // .is(':visible') and .is(':hidden') are not reliable tests for check
37 | // marks. use hasClass('hidden') instead.
38 | assert.ok(isChecked(0), 'first item should show a check mark');
39 | assert.ok(isChecked(1), 'second item should show a check mark');
40 | assert.ok(!isChecked(2), 'third item should not show a check mark');
41 | });
42 | });
43 |
44 | test('Select single', function(assert) {
45 | var isChecked = checkMarkVisibilityTest('single-picker');
46 |
47 | assert.expect(4);
48 |
49 | visit('/test-list-picker')
50 |
51 | .click('#single-picker .list-picker-items-container button:eq(0)')
52 |
53 | .then(function() {
54 | assert.ok(isChecked(0), 'first item should show a check mark');
55 | assert.ok(!isChecked(1), 'second item should not show a check mark');
56 | })
57 |
58 | .click('#single-picker .list-picker-items-container button:eq(1)')
59 |
60 | .then(function() {
61 | assert.ok(!isChecked(0), 'first item should not show a check mark');
62 | assert.ok(isChecked(1), 'second item should show a check mark');
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/tests/integration/select-picker-test.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import { test, module } from 'qunit';
3 | import startApp from '../helpers/start-app';
4 |
5 | var App;
6 |
7 | function checkMarkVisibilityTest(selectPickerID) {
8 | var selector =
9 | `#${selectPickerID} .dropdown-menu li>a:eq(%@) span.check-mark`;
10 | return function(itemIndex) {
11 | return !find(Ember.String.fmt(selector, itemIndex)).hasClass('hidden');
12 | };
13 | }
14 |
15 | module('Select Picker Integration', {
16 | beforeEach: function() {
17 | App = startApp();
18 | },
19 | afterEach: function() {
20 | Ember.run(App, App.destroy);
21 | }
22 | });
23 |
24 | test('Show and Hide', function(assert) {
25 | assert.expect(3);
26 |
27 | visit('/test-select-picker')
28 |
29 | .then(function() {
30 | assert.ok(
31 | find('#single-picker .dropdown-menu').is(':hidden'),
32 | 'dropdown menu should be hidden'
33 | );
34 | })
35 |
36 | .click('#single-picker button.dropdown-toggle')
37 |
38 | .then(function() {
39 | assert.ok(
40 | find('#single-picker .dropdown-menu').is(':visible'),
41 | 'dropdown menu should be visible'
42 | );
43 | })
44 |
45 | .click('#single-picker')
46 |
47 | .then(function() {
48 | assert.ok(
49 | find('#single-picker .dropdown-menu').is(':hidden'),
50 | 'dropdown menu should be hidden'
51 | );
52 | });
53 | });
54 |
55 | test('Select multiple', function(assert) {
56 | var isChecked = checkMarkVisibilityTest('multiple-picker');
57 |
58 | assert.expect(4);
59 |
60 | visit('/test-select-picker')
61 |
62 | .click('#multiple-picker button.dropdown-toggle')
63 | .click('#multiple-picker .dropdown-menu li>a:eq(0)')
64 | .click('#multiple-picker .dropdown-menu li>a:eq(1)')
65 |
66 | .then(function() {
67 | // Dropdowns might not be visible causing the check mark to not be visible.
68 | // .is(':visible') and .is(':hidden') are not reliable tests for check
69 | // marks. use hasClass('hidden') instead.
70 | assert.ok(isChecked(0), 'first item should show a check mark');
71 | assert.ok(isChecked(1), 'second item should show a check mark');
72 | assert.ok(!isChecked(2), 'third item should not show a check mark');
73 |
74 | assert.equal(
75 | find('#multiple-picker .badge').text(), 2,
76 | 'badge should show number of selected items'
77 | );
78 | });
79 | });
80 |
81 | test('Select single', function(assert) {
82 | var isChecked = checkMarkVisibilityTest('single-picker');
83 |
84 | assert.expect(4);
85 |
86 | visit('/test-select-picker')
87 |
88 | .click('#single-picker button.dropdown-toggle')
89 | .click('#single-picker .dropdown-menu li>a:eq(0)')
90 |
91 | .then(function() {
92 | assert.ok(isChecked(0), 'first item should show a check mark');
93 | assert.ok(!isChecked(1), 'second item should not show a check mark');
94 | })
95 |
96 | .click('#single-picker .dropdown-menu li>a:eq(1)')
97 |
98 | .then(function() {
99 | assert.ok(!isChecked(0), 'first item should not show a check mark');
100 | assert.ok(isChecked(1), 'second item should show a check mark');
101 | });
102 | });
103 |
--------------------------------------------------------------------------------
/tests/test-helper.js:
--------------------------------------------------------------------------------
1 | import resolver from './helpers/resolver';
2 | import {
3 | setResolver
4 | } from 'ember-qunit';
5 |
6 | setResolver(resolver);
7 |
--------------------------------------------------------------------------------
/tests/unit/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sukima/ember-cli-select-picker/ea014668ae740d2f72375e91aa3de3e9e2e48b80/tests/unit/.gitkeep
--------------------------------------------------------------------------------
/tests/unit/components/list-picker-test.js:
--------------------------------------------------------------------------------
1 | import { moduleForComponent } from 'ember-qunit';
2 | import commonPickerTests from '../../common/select-picker';
3 |
4 | moduleForComponent('list-picker', {
5 | // specify the other units that are required for this test
6 | // needs: ['component:foo', 'helper:bar']
7 | });
8 |
9 | commonPickerTests();
10 |
--------------------------------------------------------------------------------
/tests/unit/components/select-picker-test.js:
--------------------------------------------------------------------------------
1 | import { moduleForComponent } from 'ember-qunit';
2 | import commonPickerTests from '../../common/select-picker';
3 |
4 | moduleForComponent('select-picker', {
5 | // specify the other units that are required for this test
6 | // needs: ['component:foo', 'helper:bar']
7 | });
8 |
9 | commonPickerTests();
10 |
--------------------------------------------------------------------------------
/vendor/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sukima/ember-cli-select-picker/ea014668ae740d2f72375e91aa3de3e9e2e48b80/vendor/.gitkeep
--------------------------------------------------------------------------------
/vendor/select-picker.css:
--------------------------------------------------------------------------------
1 | /* Fix to enable Ember actions
2 | * http://stackoverflow.com/a/17490775/227176
3 | */
4 | .select-picker .dropdown-menu li a {
5 | cursor: pointer;
6 | }
7 |
8 | .select-picker .bs-select span.check-mark {
9 | position: absolute;
10 | display: inline-block;
11 | top: inherit;
12 | right: 10px;
13 | margin-top: 5px;
14 | }
15 |
16 | .select-picker .dropdown-menu {
17 | padding-right: 5px;
18 | padding-left: 5px;
19 | overflow-y: auto;
20 | max-height: 400px;
21 | }
22 |
23 | .select-picker .dropdown-menu .dropdown-header {
24 | padding-left: 10px;
25 | font-size: 14px;
26 | }
27 |
28 | .select-picker .select-all-none {
29 | margin-top: 5px;
30 | }
31 |
32 | .select-picker .select-all-none button {
33 | width: 50%;
34 | text-align: center;
35 | }
36 |
--------------------------------------------------------------------------------