├── .gitignore
├── LICENSE.md
├── README.md
├── bower.json
├── demo
├── app
│ └── app.js
└── index.html
├── dist
├── proton.multi-list-picker.css
└── proton.multi-list-picker.min.js
├── gulpfile.js
├── package.json
└── src
├── proton.multi-list-picker.js
├── proton.multi-list-picker.scss
└── proton.multi-list-picker.xsd
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
3 | *.ipr
4 | *.iws
5 | node_modules/
6 | bower_components/
7 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016 Milad Naseri
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
4 | software and associated documentation files (the "Software"), to deal in the Software
5 | without restriction, including without limitation the rights to use, copy, modify, merge,
6 | publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7 | to whom the Software is furnished to do so, subject to the following conditions:
8 |
9 | The above copyright notice and this permission notice shall be included in all copies or
10 | substantial portions of the Software.
11 |
12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
16 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Multi-list Picker
2 | =================
3 |
4 | ### This project is not being maintained
5 |
6 | Most of the code should work, and it has been tested well enough. However, without any need from my own projects for such component at the time, I simply do not have the time to properly maintain it. If anyone is interested in owning this project, I am more than happy to transfer ownership.
7 |
8 | Meanwhile, you can check out https://github.com/jasonmamy/cordova-wheel-selector-plugin which seems to be a maintained project with some of the same capabilities as this one.
9 |
10 | ## Introduction
11 |
12 | The multi-list picker is a component that lets you pick a value
13 | from a list. Imagine a combobox, but only better.
14 |
15 | The picker helps you define lists that are bound to various properties
16 | of a model and specify the available values.
17 |
18 | Here is a simple example with static values and a single list:
19 |
20 |
21 |
22 | iPhone 5
23 | iPhone 6
24 | iPhone 6 Plus
25 | iPhone 6s
26 | iPhone 6s Plus
27 | iPhone 5se
28 |
29 |
30 |
31 | which results in this list-picker:
32 |
33 | 
34 |
35 | This comes with support for scrolling the list to select an item with
36 | momentum and every other thing you would expect to see in a native iOS
37 | list-picker. You can easily change the `attachment` type and other
38 | options on the list-picker to achieve a more sophisticated setting.
39 |
40 | Usage
41 | -----
42 |
43 | You have to import the following two files into your `index.html`:
44 |
45 | * `dist/proton.multi-list-picker.min.js`
46 | * `dist/proton.multi-list-picker.css`
47 |
48 | and add a dependency to the parent module to your application:
49 |
50 | angular.module('myApplication', [..., 'proton.multi-list-picker']);
51 |
52 | Dependencies
53 | ------------
54 |
55 | This directive does not have any dependencies whatsoever. The only dependency is
56 | AngularJS itself, and there is a soft dependency on `ngSanitize` if you need HTML
57 | captions.
58 |
59 | Directives
60 | ----------
61 |
62 | This module comes with multiple, nested directives.
63 |
64 | > ##### Development Tip
65 | > To help you with your development and benefit from IDE completion, and
66 | XSD file is included with detailed instructions on the
67 | directives. To use the XSD, add the following to your `index.html` file:
68 | >
69 | > xmlns:proton="http://github.com/mmnaseri/proton.multi-list-picker"
70 | >
71 | > and if necessary, add a local path, as well. Now, you can use all the directives
72 | > with the benefit of code completion. Just remember to use the *namespace* form
73 | > when using the directives. This means using `` instead
74 | > of ``
75 |
76 | ## proton-multi-list-picker
77 |
78 | This is the root element for defining a multi-list picker component.
79 |
80 | ##### Format
81 |
82 |
87 | :
88 |
89 |
90 | ##### Options
91 |
92 | * `ngModel`: this is to let you bind the top-level object for the list picker
93 | to the directive.
94 | * `attachment` (`enum`, default: `inline`): This attribute will let you define
95 | the attachment type of the component to the screen. Possible values are:
96 | * `inline`: the component will be displayed as an inline block
97 | * `modal`: the component will be displayed as a modal dialog with appropriate
98 | close buttons
99 | * `bottom`: the component will be affixed to the bottom of the screen
100 | * `top`: the component will be affixed to the top of the screen
101 | * `bindHtml` (`boolean`, default: `false`): Whether or not list item labels should
102 | be interpreted as HTML values or normal text. If you set this to `true` you will need
103 | to add a dependency to [`ngSanitize`](https://docs.angularjs.org/api/ngSanitize) to
104 | your application, as the template will internally use [`ngBindHtml`](https://docs.angularjs.org/api/ng/directive/ngBindHtml)
105 | to accomplish proper rendering.
106 | * `done` (`AngularJS Expression`, default: `""`): this expression will be executed with
107 | the value `$model` interpolated with the bound model object (or the resolved model, if no
108 | model is bound) once the `Done` button in the toolbar is clicked. There will be no
109 | toolbar displayed if the attachment is set to `inline`.
110 |
111 | ##### Nested Children
112 |
113 | * ``: to specify a child list
114 | * ``: to specify a divider
115 |
116 | ## proton-multi-list-picker-list
117 |
118 | Let's you define a list for the multi-list picker component.
119 |
120 | ##### Format
121 |
122 |
128 |
129 |
130 | ##### Options
131 |
132 | * `alias` (`string`, default: numerical index of the list): the name of the model
133 | property to which the value of this list is bound. If none is specified, then the
134 | numerical value of the list will be used as the property. In the example above, it
135 | would create a property named `0` in any model bound (or if an array was bound, the
136 | first item would have been set to the value picked via the list).
137 | * `source` (`AngularJS expression`, default: `undefined`): the source for the items
138 | within this list. If nothing is provided, the contents will be used instead. If you
139 | are providing a method, beware that it is going to be called a lot, since we are
140 | watching for changes in the source to update the list picker accordingly. If you
141 | want to disable this feature use `static` binding.
142 | * `static` (`boolean`, default: `false`): Sets the binding type to static so that
143 | once read first, the value of the `source` property is not read again. If you do
144 | not specify a `source` this will be automatically set to `true`. Set this to `true`
145 | if you are providing the items in this list via an AngularJS expression and you know
146 | the contents of the list are not going to change.
147 | * `cycle` (`boolean`, default: `false`): whether the list should *cycle*. If you set
148 | this to `true`, the list will have no end. It will be like working with a circular
149 | dial (thus the name). This means that you will get infinite scrolling on this list.
150 | * `strictMatching` (`boolean`, default: `false`): this property directs the directive
151 | to perform strict label matching when determining the absolute width of a list. Since
152 | setting this to `true` means every item in the list will be tried as a separate HTML
153 | object to account for the width, only set it to `true` if you are experiencing
154 | problems with the width of the list and if the width is changing when you are scrolling
155 | through the items.
156 |
157 | ##### Nested Children
158 |
159 | * ``: use this to specify static items for the
160 | list. If the `` directive specifies a `source` these
161 | items will be completely ignored. Also note that since this is a *static* definition,
162 | you will not benefit from any bindings or other DOM manipulations after the items are
163 | initially scanned.
164 |
165 | ## proton-multi-list-picker-divider
166 |
167 | A divider element between lists. The content of the element will be picked up and
168 | placed in the component. If `bindHtml` is set to `true` on the parent
169 | `proton-multi-list-picker`, it will be treated as HTML content.
170 |
171 | You can leave the content as blank if you want a breaking divider that visually
172 | divides the multi-list picker into two sections. Since the value of the divider
173 | is treated statically, it will not be updated once the picker is rendered. This
174 | means no further DOM manipulations or AngularJS bindings.
175 |
176 | ##### Format
177 |
178 | ...
179 |
180 | ##### Options
181 |
182 | This directive has no options
183 |
184 | ##### Nested Children
185 |
186 | Anything nested within this directive will be treated as the caption for the divider.
187 |
188 | ## proton-multi-list-picker-list-item
189 |
190 | This element let's you define a single list item. The contents of this tag
191 | will be treated as the caption for the item, and if `bindHtml` is set to `true`
192 | on the parent `` it will be treated as HTML.
193 | You will need to specify a `value` attribute if you want to have values
194 | that are different from the inline HTML value of the tag.
195 |
196 | ##### Format
197 |
198 |
200 | ...
201 |
202 |
203 | ##### Options
204 |
205 | * `value` (`string`, default: same as caption): the value bound to this item. This
206 | is the same as specifying a `value` on an `` HTML tag in a ``
207 | component.
208 |
209 | ##### Nested Children
210 |
211 | This directive has no nested children and anything written within the tag as children
212 | will be read as the caption for the item.
213 |
214 | Example
215 | -------
216 |
217 | An example is provided under the `/demo` folder. You can clone this repository and
218 | look at the examples to get a better understanding of how this works.
219 |
220 | All the options are explored in the example, and you will see a more sophisticated
221 | list picker.
222 |
223 | Below is an image of the example application in action:
224 |
225 | 
226 |
227 | Sizing
228 | ------
229 |
230 | The size of all the elements here is determined relative to the font-size of the
231 | list picker component. Therefore, if you want to change the size of a component
232 | specifically, you can select it by ID or via the `proton-multi-list-picker` class
233 | name:
234 |
235 | .proton-multi-list-picker {
236 | font-size: 20px;
237 | }
238 |
239 | Styling
240 | -------
241 |
242 | The list is configured to show five items at a time and simulate a bevel effect as
243 | though the underlying list is a circular dial.
244 |
245 | This is all accomplished via the CSS file. You can play with these settings by modifying
246 | the SASS file `proton.multi-list-picker.scss` under `src`, or use your own style.
247 |
248 | The template for the directive can be loaded via `$templateCache` like this:
249 |
250 | var template = $templateCache.get("$/proton/multi-list-picker/picker.html");
251 |
252 | License
253 | -------
254 |
255 | This work is distributed under the MIT license, which can be found in `LICENSE.md`
256 | next to this readme.
257 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "proton-multi-list-picker",
3 | "description": "Proton: Multi-List Picker",
4 | "main": "src/proton.multi-list-picker.js",
5 | "authors": [
6 | "Milad Naseri "
7 | ],
8 | "license": "MIT",
9 | "keywords": [
10 | "touch",
11 | "datepicker",
12 | "time",
13 | "date",
14 | "list",
15 | "choice",
16 | "select"
17 | ],
18 | "homepage": "",
19 | "private": true,
20 | "ignore": [
21 | "**/.*",
22 | "node_modules",
23 | "bower_components",
24 | "test",
25 | "tests"
26 | ],
27 | "dependencies": {
28 | "angular": "angularjs#^1.5.3"
29 | },
30 | "devDependencies": {
31 | "angular-sanitize": "^1.5.5"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/demo/app/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Milad Naseri (mmnaseri@programmer.net)
3 | * @since 1.0 (4/8/16)
4 | */
5 | /**
6 | * AngularJS module for demoing the component
7 | * @type {angular.Module}
8 | */
9 | (function () {
10 | var module = angular.module('protonMultiListSelector', ['proton.multi-list-picker', 'ngSanitize']);
11 |
12 | module.run(function () {
13 | self && self.webView && self.webView.scrollView && (self.webView.scrollView.bounces = NO);
14 | angular.element(document.body).on("touchmove", function (event) {
15 | event.preventDefault();
16 | })
17 | });
18 |
19 | module.controller("MainController", function ($scope) {
20 | $scope.pivotYear = 2000;
21 | $scope.model = {
22 | hour: 11,
23 | minute: 59
24 | };
25 | $scope.attachment = "inline";
26 | $scope.bindHtml = "true";
27 | var years = [];
28 | $scope.getYears = function () {
29 | return years;
30 | };
31 | $scope.$watch('pivotYear', function (year) {
32 | year -= 50;
33 | years = [];
34 | for (var i = 0; i < 100; i ++) {
35 | years.push(year + i);
36 | }
37 | });
38 | $scope.getDays = function (year, month) {
39 | var result = [];
40 | for (var i = 1; i < 30; i ++) {
41 | result.push(i);
42 | }
43 | return result;
44 | };
45 | $scope.getMinutes = function () {
46 | var result = {};
47 | for (var i = 0; i < 60; i ++) {
48 | result[i] = i < 10 ? ("0" + i) : i;
49 | }
50 | return result;
51 | };
52 | $scope.$watch('model.hour', function (newValue, oldValue) {
53 | var now = parseInt(newValue);
54 | var then = parseInt(oldValue);
55 | if (now > 9 && then < 9 || now < 3 && then > 9) {
56 | $scope.model.time = $scope.model.time == "AM" ? "PM" : "AM";
57 | }
58 | });
59 | });
60 | })();
61 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
21 |
22 |
23 |
25 |
26 |
27 |
28 |
37 |
40 |
41 |
46 |
47 |
49 |
50 |
51 |
52 | January
53 |
54 | February
55 | March
56 | April
57 | May
58 | June
59 | July
60 | August
61 | September
62 | October
63 | November
64 | December
65 |
66 |
68 |
69 |
70 |
71 |
72 | 00
73 | 01
74 | 02
75 | 03
76 | 04
77 | 05
78 | 06
79 | 07
80 | 08
81 | 09
82 | 10
83 | 11
84 |
85 | :
86 |
87 |
88 |
89 | AM
90 | PM
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/dist/proton.multi-list-picker.css:
--------------------------------------------------------------------------------
1 | .proton-multi-list-picker .toolbar{display:none}.proton-multi-list-picker{display:block;font-size:1em;font-family:"Helvetica Neue", Helvetica, "Open Sans", sans-serif;font-weight:200;text-outline:none;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;box-sizing:border-box}.proton-multi-list-picker .sandbox-item{position:relative;top:-1.8em;display:inline-block;margin:0 5px;height:1em;padding:0.2em 0.5em;text-align:center;visibility:hidden}.proton-multi-list-picker .proton-multi-list-picker-contents{display:none}.proton-multi-list-picker .container .lists{display:inline-block;margin:0 auto;padding:0;border:none}.proton-multi-list-picker .container .lists .list-container{display:inline-block;vertical-align:top;margin:0;padding:0}.proton-multi-list-picker .container .lists .list-container.transitioning.one-up .item{-webkit-animation:moveUp 0.1s;-o-animation:moveUp 0.1s;animation:moveUp 0.1s}.proton-multi-list-picker .container .lists .list-container.transitioning.one-down .item{-webkit-animation:moveDown 0.1s;-o-animation:moveDown 0.1s;animation:moveDown 0.1s}.proton-multi-list-picker .container .lists .list-container.first{border-left:1px solid #e9e9e9}.proton-multi-list-picker .container .lists .list-container.last{border-right:1px solid #e9e9e9}.proton-multi-list-picker .container .lists .list-container .list{border-top:1px solid #e9e9e9;border-bottom:1px solid #e9e9e9;height:7.4em;overflow:hidden;display:block;padding:0;margin:0;min-width:1.5em}.proton-multi-list-picker .container .lists .list-container .list .item{position:relative;top:-1.8em;display:block;margin:0 5px;height:1em;padding:0.2em 0.5em;text-align:center}.proton-multi-list-picker .container .lists .list-container .list .item.selected{margin-top:0.4em;margin-bottom:0.4em;font-weight:300}.proton-multi-list-picker .container .lists .list-container .list .item.distance-1{opacity:0.6;transform:scale(1, 0.7);-webkit-transform:scale(1, 0.7);-moz-transform:scale(1, 0.7);-ms-transform:scale(1, 0.7);-o-transform:scale(1, 0.7)}.proton-multi-list-picker .container .lists .list-container .list .item.distance-2{opacity:0.3;transform:scale(0.95, 0.4);-webkit-transform:scale(0.95, 0.4);-moz-transform:scale(0.95, 0.4);-ms-transform:scale(0.95, 0.4);-o-transform:scale(0.95, 0.4)}.proton-multi-list-picker .container .lists .list-container .list .item.distance-3{opacity:0.1;transform:scale(1, 0.1);-webkit-transform:scale(1, 0.1);-moz-transform:scale(1, 0.1);-ms-transform:scale(1, 0.1);-o-transform:scale(1, 0.1)}.proton-multi-list-picker .container .lists .list-container .list .item.offset-down-3{top:2.4em}.proton-multi-list-picker .container .lists .list-container .list .item.offset-down-2{top:1em}.proton-multi-list-picker .container .lists .list-container .list .item.offset-down-1{top:-0.4em}.proton-multi-list-picker .container .lists .list-container .divider{display:block;padding:0 1em 0 0.3em;margin:0;font-weight:600;border-top:1px solid #e9e9e9;border-bottom:1px solid #e9e9e9;height:7.4em;overflow:hidden;text-align:center}.proton-multi-list-picker .container .lists .list-container .divider.blank{border-top:none;border-bottom:none;border-left:1px solid #e9e9e9;border-right:1px solid #e9e9e9}.proton-multi-list-picker .container .lists .list-container .divider span{top:2.8em;position:relative}.proton-multi-list-picker .container .before-lists{display:block;height:1.4em;border-top:1px solid #d6d6d6;border-bottom:1px solid #d6d6d6;background-color:#eaeaea;position:relative;top:3em;margin-bottom:-1.4em;opacity:0.4}.proton-multi-list-picker .container .after-lists{display:none}.proton-multi-list-picker.inline{margin-top:5px;margin-bottom:5px}.proton-multi-list-picker.modal .container,.proton-multi-list-picker.top .container,.proton-multi-list-picker.bottom .container{position:absolute;left:50%;top:50%}.proton-multi-list-picker.modal .container .lists,.proton-multi-list-picker.top .container .lists,.proton-multi-list-picker.bottom .container .lists{position:relative;left:-50%;top:-3.8em;white-space:nowrap;height:9.1em;background:#ffffff}.proton-multi-list-picker.modal .container .lists .toolbar,.proton-multi-list-picker.top .container .lists .toolbar,.proton-multi-list-picker.bottom .container .lists .toolbar{display:block;height:1em;padding:.25em;text-align:right;background:#ededed}.proton-multi-list-picker.modal .container .lists .toolbar .toolbar-button,.proton-multi-list-picker.top .container .lists .toolbar .toolbar-button,.proton-multi-list-picker.bottom .container .lists .toolbar .toolbar-button{color:#6495ed;font-weight:400;font-size:1em;line-height:1em;cursor:pointer}.proton-multi-list-picker.modal .before,.proton-multi-list-picker.top .before,.proton-multi-list-picker.bottom .before{position:absolute;background:#000;left:0;right:0;top:0;bottom:0;opacity:0.4}.proton-multi-list-picker.top .container{top:-2px}.proton-multi-list-picker.top .container .lists{top:0}.proton-multi-list-picker.top .container .lists .list{position:relative}.proton-multi-list-picker.top .toolbar{display:block;position:relative;margin-bottom:-1.7em;top:7.6em}.proton-multi-list-picker.bottom .container{top:100%}.proton-multi-list-picker.bottom .container .lists{top:-9em}.proton-multi-list-picker.bottom .container .lists .list{position:relative}.proton-multi-list-picker.modal .toolbar{top:-1.4em}
2 |
3 | /*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdG9uLm11bHRpLWxpc3QtcGlja2VyLmNzcyIsInNvdXJjZXMiOlsicHJvdG9uLm11bHRpLWxpc3QtcGlja2VyLnNjc3MiXSwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoYykgMjAxNiBNaWxhZCBOYXNlcmlcbiAqXG4gKiBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5IG9mIHRoaXNcbiAqIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZSBcIlNvZnR3YXJlXCIpLCB0byBkZWFsIGluIHRoZSBTb2Z0d2FyZVxuICogd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSxcbiAqIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsIGNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXQgcGVyc29uc1xuICogdG8gd2hvbSB0aGUgU29mdHdhcmUgaXMgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczpcbiAqXG4gKiBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpbiBhbGwgY29waWVzIG9yXG4gKiBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuXG4gKlxuICogVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEIFwiQVMgSVNcIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUiBJTVBMSUVELFxuICogSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFksIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUlxuICogUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQVVUSE9SUyBPUiBDT1BZUklHSFQgSE9MREVSUyBCRSBMSUFCTEVcbiAqIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVIgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsXG4gKiBBUklTSU5HIEZST00sIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFNPRlRXQVJFIE9SIFRIRSBVU0UgT1IgT1RIRVIgREVBTElOR1MgSU4gVEhFXG4gKiBTT0ZUV0FSRS5cbiAqL1xuXG4ucHJvdG9uLW11bHRpLWxpc3QtcGlja2VyIHtcblxuICAudG9vbGJhciB7XG4gICAgZGlzcGxheTogbm9uZTtcbiAgfVxuXG4gICYge1xuICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgIGZvbnQtc2l6ZTogMWVtO1xuICAgIGZvbnQtZmFtaWx5OiBcIkhlbHZldGljYSBOZXVlXCIsIEhlbHZldGljYSwgXCJPcGVuIFNhbnNcIiwgc2Fucy1zZXJpZjtcbiAgICBmb250LXdlaWdodDogMjAwO1xuICAgIHRleHQtb3V0bGluZTogbm9uZTtcbiAgICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7IC8qIGlPUyBTYWZhcmkgKi9cbiAgICAtd2Via2l0LXVzZXItc2VsZWN0OiBub25lOyAgIC8qIENocm9tZS9TYWZhcmkvT3BlcmEgKi9cbiAgICAta2h0bWwtdXNlci1zZWxlY3Q6IG5vbmU7ICAgIC8qIEtvbnF1ZXJvciAqL1xuICAgIC1tb3otdXNlci1zZWxlY3Q6IG5vbmU7ICAgICAgLyogRmlyZWZveCAqL1xuICAgIC1tcy11c2VyLXNlbGVjdDogbm9uZTsgICAgICAgLyogSW50ZXJuZXQgRXhwbG9yZXIvRWRnZSAqL1xuICAgIHVzZXItc2VsZWN0OiBub25lOyAgICAgICAgICAgLyogTm9uLXByZWZpeGVkIHZlcnNpb24sIGN1cnJlbnRseSBub3Qgc3VwcG9ydGVkIGJ5IGFueSBicm93c2VyICovXG4gICAgYm94LXNpemluZzogYm9yZGVyLWJveDtcbiAgfVxuXG4gIC5zYW5kYm94LWl0ZW0ge1xuICAgIHBvc2l0aW9uOiByZWxhdGl2ZTtcbiAgICB0b3A6IC0xLjhlbTtcbiAgICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG4gICAgbWFyZ2luOiAwIDVweDtcbiAgICBoZWlnaHQ6IDFlbTtcbiAgICBwYWRkaW5nOiAwLjJlbSAwLjVlbTtcbiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7XG4gICAgdmlzaWJpbGl0eTogaGlkZGVuO1xuICB9XG4gIFxuICAucHJvdG9uLW11bHRpLWxpc3QtcGlja2VyLWNvbnRlbnRzIHtcbiAgICBkaXNwbGF5OiBub25lO1xuICB9XG5cbiAgLmNvbnRhaW5lciB7XG5cbiAgICAubGlzdHMge1xuXG4gICAgICAmIHtcbiAgICAgICAgZGlzcGxheTogaW5saW5lLWJsb2NrO1xuICAgICAgICBtYXJnaW46IDAgYXV0bztcbiAgICAgICAgcGFkZGluZzogMDtcbiAgICAgICAgYm9yZGVyOiBub25lO1xuICAgICAgfVxuXG4gICAgICAubGlzdC1jb250YWluZXIge1xuXG4gICAgICAgICYge1xuICAgICAgICAgIGRpc3BsYXk6IGlubGluZS1ibG9jaztcbiAgICAgICAgICB2ZXJ0aWNhbC1hbGlnbjogdG9wO1xuICAgICAgICAgIG1hcmdpbjogMDtcbiAgICAgICAgICBwYWRkaW5nOiAwO1xuICAgICAgICB9XG5cbiAgICAgICAgJi50cmFuc2l0aW9uaW5nIHtcblxuICAgICAgICAgICYub25lLXVwIHtcbiAgICAgICAgICAgIC5pdGVtIHtcbiAgICAgICAgICAgICAgLXdlYmtpdC1hbmltYXRpb246IG1vdmVVcCAwLjFzO1xuICAgICAgICAgICAgICAtby1hbmltYXRpb246IG1vdmVVcCAwLjFzO1xuICAgICAgICAgICAgICBhbmltYXRpb246IG1vdmVVcCAwLjFzO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgICYub25lLWRvd24ge1xuICAgICAgICAgICAgLml0ZW0ge1xuICAgICAgICAgICAgICAtd2Via2l0LWFuaW1hdGlvbjogbW92ZURvd24gMC4xcztcbiAgICAgICAgICAgICAgLW8tYW5pbWF0aW9uOiBtb3ZlRG93biAwLjFzO1xuICAgICAgICAgICAgICBhbmltYXRpb246IG1vdmVEb3duIDAuMXM7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuXG4gICAgICAgIH1cblxuICAgICAgICAmLmZpcnN0IHtcbiAgICAgICAgICBib3JkZXItbGVmdDogMXB4IHNvbGlkICNlOWU5ZTk7XG4gICAgICAgIH1cblxuICAgICAgICAmLmxhc3Qge1xuICAgICAgICAgIGJvcmRlci1yaWdodDogMXB4IHNvbGlkICNlOWU5ZTk7XG4gICAgICAgIH1cblxuICAgICAgICAubGlzdCB7XG5cbiAgICAgICAgICAmIHtcbiAgICAgICAgICAgIGJvcmRlci10b3A6IDFweCBzb2xpZCAjZTllOWU5O1xuICAgICAgICAgICAgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkICNlOWU5ZTk7XG4gICAgICAgICAgICBoZWlnaHQ6IDcuNGVtO1xuICAgICAgICAgICAgb3ZlcmZsb3c6IGhpZGRlbjtcbiAgICAgICAgICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgICAgICAgICAgcGFkZGluZzogMDtcbiAgICAgICAgICAgIG1hcmdpbjogMDtcbiAgICAgICAgICAgIG1pbi13aWR0aDogMS41ZW07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLml0ZW0ge1xuXG4gICAgICAgICAgICAmIHtcbiAgICAgICAgICAgICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgICAgICAgICAgICB0b3A6IC0xLjhlbTtcbiAgICAgICAgICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICAgICAgICAgIG1hcmdpbjogMCA1cHg7XG4gICAgICAgICAgICAgIGhlaWdodDogMWVtO1xuICAgICAgICAgICAgICBwYWRkaW5nOiAwLjJlbSAwLjVlbTtcbiAgICAgICAgICAgICAgdGV4dC1hbGlnbjogY2VudGVyO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAmLnNlbGVjdGVkIHtcbiAgICAgICAgICAgICAgbWFyZ2luLXRvcDogMC40ZW07XG4gICAgICAgICAgICAgIG1hcmdpbi1ib3R0b206IDAuNGVtO1xuICAgICAgICAgICAgICBmb250LXdlaWdodDogMzAwO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAmLmRpc3RhbmNlLTEge1xuICAgICAgICAgICAgICBvcGFjaXR5OiAwLjY7XG4gICAgICAgICAgICAgIHRyYW5zZm9ybSA6IHNjYWxlKDEsMC43KTtcbiAgICAgICAgICAgICAgLXdlYmtpdC10cmFuc2Zvcm06c2NhbGUoMSwwLjcpOyAvKiBTYWZhcmkgYW5kIENocm9tZSAqL1xuICAgICAgICAgICAgICAtbW96LXRyYW5zZm9ybTpzY2FsZSgxLDAuNyk7IC8qIEZpcmVmb3ggKi9cbiAgICAgICAgICAgICAgLW1zLXRyYW5zZm9ybTpzY2FsZSgxLDAuNyk7IC8qIElFIDkrICovXG4gICAgICAgICAgICAgIC1vLXRyYW5zZm9ybTpzY2FsZSgxLDAuNyk7IC8qIE9wZXJhICovXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICYuZGlzdGFuY2UtMiB7XG4gICAgICAgICAgICAgIG9wYWNpdHk6IDAuMztcbiAgICAgICAgICAgICAgdHJhbnNmb3JtIDogc2NhbGUoMC45NSwwLjQpO1xuICAgICAgICAgICAgICAtd2Via2l0LXRyYW5zZm9ybTpzY2FsZSgwLjk1LDAuNCk7IC8qIFNhZmFyaSBhbmQgQ2hyb21lICovXG4gICAgICAgICAgICAgIC1tb3otdHJhbnNmb3JtOnNjYWxlKDAuOTUsMC40KTsgLyogRmlyZWZveCAqL1xuICAgICAgICAgICAgICAtbXMtdHJhbnNmb3JtOnNjYWxlKDAuOTUsMC40KTsgLyogSUUgOSsgKi9cbiAgICAgICAgICAgICAgLW8tdHJhbnNmb3JtOnNjYWxlKDAuOTUsMC40KTsgLyogT3BlcmEgKi9cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgJi5kaXN0YW5jZS0zIHtcbiAgICAgICAgICAgICAgb3BhY2l0eTogMC4xO1xuICAgICAgICAgICAgICB0cmFuc2Zvcm0gOiBzY2FsZSgxLDAuMSk7XG4gICAgICAgICAgICAgIC13ZWJraXQtdHJhbnNmb3JtOnNjYWxlKDEsMC4xKTsgLyogU2FmYXJpIGFuZCBDaHJvbWUgKi9cbiAgICAgICAgICAgICAgLW1vei10cmFuc2Zvcm06c2NhbGUoMSwwLjEpOyAvKiBGaXJlZm94ICovXG4gICAgICAgICAgICAgIC1tcy10cmFuc2Zvcm06c2NhbGUoMSwwLjEpOyAvKiBJRSA5KyAqL1xuICAgICAgICAgICAgICAtby10cmFuc2Zvcm06c2NhbGUoMSwwLjEpOyAvKiBPcGVyYSAqL1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAmLm9mZnNldC1kb3duLTMge1xuICAgICAgICAgICAgICB0b3A6IDIuNGVtO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAmLm9mZnNldC1kb3duLTIge1xuICAgICAgICAgICAgICB0b3A6IDFlbTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgJi5vZmZzZXQtZG93bi0xIHtcbiAgICAgICAgICAgICAgdG9wOiAtMC40ZW07XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICB9XG5cbiAgICAgICAgfVxuXG4gICAgICAgIC5kaXZpZGVyIHtcblxuICAgICAgICAgICYge1xuICAgICAgICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICAgICAgICBwYWRkaW5nOiAwIDFlbSAwIDAuM2VtO1xuICAgICAgICAgICAgbWFyZ2luOiAwO1xuICAgICAgICAgICAgZm9udC13ZWlnaHQ6IDYwMDtcbiAgICAgICAgICAgIGJvcmRlci10b3A6IDFweCBzb2xpZCAjZTllOWU5O1xuICAgICAgICAgICAgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkICNlOWU5ZTk7XG4gICAgICAgICAgICBoZWlnaHQ6IDcuNGVtO1xuICAgICAgICAgICAgb3ZlcmZsb3c6IGhpZGRlbjtcbiAgICAgICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAmLmJsYW5rIHtcbiAgICAgICAgICAgIGJvcmRlci10b3A6IG5vbmU7XG4gICAgICAgICAgICBib3JkZXItYm90dG9tOiBub25lO1xuICAgICAgICAgICAgYm9yZGVyLWxlZnQ6IDFweCBzb2xpZCAjZTllOWU5O1xuICAgICAgICAgICAgYm9yZGVyLXJpZ2h0OiAxcHggc29saWQgI2U5ZTllOTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBzcGFuIHtcbiAgICAgICAgICAgIHRvcDogMi44ZW07XG4gICAgICAgICAgICBwb3NpdGlvbjogcmVsYXRpdmU7XG4gICAgICAgICAgfVxuXG4gICAgICAgIH1cblxuICAgICAgfVxuXG4gICAgfVxuXG4gICAgLmJlZm9yZS1saXN0cyB7XG4gICAgICBkaXNwbGF5OiBibG9jaztcbiAgICAgIGhlaWdodDogMS40ZW07XG4gICAgICBib3JkZXItdG9wOiAxcHggc29saWQgI2Q2ZDZkNjtcbiAgICAgIGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZDZkNmQ2O1xuICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2VhZWFlYTtcbiAgICAgIHBvc2l0aW9uOiByZWxhdGl2ZTtcbiAgICAgIHRvcDogM2VtO1xuICAgICAgbWFyZ2luLWJvdHRvbTogLTEuNGVtO1xuICAgICAgb3BhY2l0eTogMC40O1xuICAgIH1cblxuICAgIC5hZnRlci1saXN0cyB7XG4gICAgICBkaXNwbGF5OiBub25lO1xuXG4gICAgfVxuXG4gIH1cblxuICAmLmlubGluZSB7XG4gICAgbWFyZ2luLXRvcDogNXB4O1xuICAgIG1hcmdpbi1ib3R0b206IDVweDtcbiAgfVxuXG4gICYubW9kYWwsICYudG9wLCAmLmJvdHRvbSB7XG5cbiAgICAuY29udGFpbmVyIHtcblxuICAgICAgJiB7XG4gICAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICAgICAgbGVmdDogNTAlO1xuICAgICAgICB0b3A6IDUwJTtcbiAgICAgIH1cblxuICAgICAgLmxpc3RzIHtcblxuICAgICAgICAmIHtcbiAgICAgICAgICBwb3NpdGlvbjogcmVsYXRpdmU7XG4gICAgICAgICAgbGVmdDogLTUwJTtcbiAgICAgICAgICB0b3A6IC0zLjhlbTtcbiAgICAgICAgICB3aGl0ZS1zcGFjZTogbm93cmFwO1xuICAgICAgICAgIGhlaWdodDogOS4xZW07XG4gICAgICAgICAgYmFja2dyb3VuZDogI2ZmZmZmZjtcbiAgICAgICAgfVxuXG4gICAgICAgIC50b29sYmFyIHtcblxuICAgICAgICAgICYge1xuICAgICAgICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICAgICAgICBoZWlnaHQ6IDFlbTtcbiAgICAgICAgICAgIHBhZGRpbmc6IC4yNWVtO1xuICAgICAgICAgICAgdGV4dC1hbGlnbjogcmlnaHQ7XG4gICAgICAgICAgICBiYWNrZ3JvdW5kOiAjZWRlZGVkO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC50b29sYmFyLWJ1dHRvbiB7XG4gICAgICAgICAgICBjb2xvcjogIzY0OTVlZDtcbiAgICAgICAgICAgIGZvbnQtd2VpZ2h0OiA0MDA7XG4gICAgICAgICAgICBmb250LXNpemU6IDFlbTtcbiAgICAgICAgICAgIGxpbmUtaGVpZ2h0OiAxZW07XG4gICAgICAgICAgICBjdXJzb3I6IHBvaW50ZXI7XG4gICAgICAgICAgfVxuXG4gICAgICAgIH1cblxuICAgICAgfVxuICAgIH1cblxuICAgIC5iZWZvcmUge1xuICAgICAgcG9zaXRpb246IGFic29sdXRlO1xuICAgICAgYmFja2dyb3VuZDogIzAwMDtcbiAgICAgIGxlZnQ6IDA7XG4gICAgICByaWdodDogMDtcbiAgICAgIHRvcDogMDtcbiAgICAgIGJvdHRvbTogMDtcbiAgICAgIG9wYWNpdHk6IDAuNDtcbiAgICB9XG5cbiAgfVxuXG4gICYudG9wIHtcblxuICAgIC5jb250YWluZXIge1xuICAgICAgdG9wOiAtMnB4O1xuXG4gICAgICAubGlzdHMge1xuICAgICAgICB0b3A6IDA7XG5cbiAgICAgICAgLmxpc3Qge1xuICAgICAgICAgIHBvc2l0aW9uOiByZWxhdGl2ZTtcbiAgICAgICAgfVxuXG4gICAgICB9XG5cbiAgICB9XG5cbiAgICAudG9vbGJhciB7XG4gICAgICBkaXNwbGF5OiBibG9jaztcbiAgICAgIHBvc2l0aW9uOiByZWxhdGl2ZTtcbiAgICAgIG1hcmdpbi1ib3R0b206IC0xLjdlbTtcbiAgICAgIHRvcDogNy42ZW07XG4gICAgfVxuXG4gIH1cblxuICAmLmJvdHRvbSB7XG5cbiAgICAuY29udGFpbmVyIHtcblxuICAgICAgdG9wOiAxMDAlO1xuXG4gICAgICAubGlzdHMge1xuXG4gICAgICAgIHRvcDogLTllbTtcblxuICAgICAgICAubGlzdCB7XG4gICAgICAgICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgICAgICB9XG5cbiAgICAgIH1cblxuICAgIH1cblxuICB9XG5cbiAgJi5tb2RhbCB7XG5cbiAgICAudG9vbGJhciB7XG4gICAgICB0b3A6IC0xLjRlbTtcbiAgICB9XG5cbiAgfVxuXG59Il0sIm1hcHBpbmdzIjoiQUFvQkEsQUFFRSx5QkFGdUIsQ0FFdkIsUUFBUSxBQUFDLENBQ1AsT0FBTyxDQUFFLElBQUssQ0FDZixBQUpILEFBQUEseUJBQXlCLEFBTXJCLENBQ0EsT0FBTyxDQUFFLEtBQU0sQ0FDZixTQUFTLENBQUUsR0FBSSxDQUNmLFdBQVcsQ0FBRSxvREFBcUQsQ0FDbEUsV0FBVyxDQUFFLEdBQUksQ0FDakIsWUFBWSxDQUFFLElBQUssQ0FDbkIscUJBQXFCLENBQUUsSUFBSyxDQUM1QixtQkFBbUIsQ0FBRSxJQUFLLENBQzFCLGtCQUFrQixDQUFFLElBQUssQ0FDekIsZ0JBQWdCLENBQUUsSUFBSyxDQUN2QixlQUFlLENBQUUsSUFBSyxDQUN0QixXQUFXLENBQUUsSUFBSyxDQUNsQixVQUFVLENB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 */
4 |
--------------------------------------------------------------------------------
/dist/proton.multi-list-picker.min.js:
--------------------------------------------------------------------------------
1 | !function(){var e={picker:"$/proton/multi-list-picker/picker.html"},t=angular.module("proton.multi-list-picker",[]);t.run(["$templateCache",function(t){t.put(e.picker,"
");
30 | }]);
31 | module.directive('protonMultiListPicker', ["$parse", function ($parse) {
32 | var controller = function ProtonMultiListPickerController($scope) {
33 | $scope.items = [];
34 | this.addDivider = function (divider) {
35 | $scope.items.push({
36 | $divider: true,
37 | value: divider
38 | });
39 | };
40 | this.add = function (item) {
41 | if (!item.alias) {
42 | item.alias = $scope.items.length;
43 | }
44 | item.index = $scope.items.length;
45 | $scope.items.push(item);
46 | var sourceReader = function (value) {
47 | item.cached = value || [];
48 | $scope.$broadcast('protonMultiListPicker:changed', item);
49 | };
50 | if (item.static) {
51 | sourceReader(item.source());
52 | } else {
53 | //we need to set up this watch so that any external changes to this list are reflected immediately
54 | $scope.$watch(function () {
55 | return item.source();
56 | }, sourceReader, true);
57 | }
58 | };
59 | };
60 | return {
61 | restrict: "E",
62 | replace: true,
63 | templateUrl: templates.picker,
64 | require: "?ngModel",
65 | scope: {
66 | done: "&?"
67 | },
68 | transclude: true,
69 | controller: ["$scope", controller],
70 | link: function ($scope, $element, $attrs) {
71 | var parentScope = $element.parent().scope();
72 | var ngModel = $parse($attrs.ngModel);
73 | $scope.model = null;
74 | $scope.$watch(function () {
75 | return ngModel(parentScope);
76 | }, function (model) {
77 | if (model) {
78 | $scope.model = model;
79 | } else {
80 | $scope.model = {};
81 | }
82 | $scope.$broadcast('protonMultiListPicker:changed', model);
83 | }, true);
84 | $scope.$watch('model', function (model) {
85 | $scope.$broadcast('protonMultiListPicker:changed', model);
86 | }, true);
87 | $attrs.$observe("bindHtml", function (value) {
88 | $scope.bindHtml = value == "true";
89 | });
90 | $attrs.$observe("attachment", function (value) {
91 | $scope.attachment = value || "inline";
92 | });
93 | var cachedLabels = {};
94 | $scope.$on('protonMultiListPicker:changed', function () {
95 | //let's see if we can clean up the cache first
96 | angular.forEach(cachedLabels, function (cache, alias) {
97 | var found = false;
98 | angular.forEach($scope.items, function (list) {
99 | found = found || list.alias == alias;
100 | });
101 | if (!found) {
102 | delete cachedLabels[alias];
103 | }
104 | });
105 | //we need to convert the values, so that the given items are all lists of {label,value}s.
106 | angular.forEach($scope.items, function (list) {
107 | if (angular.isDefined(list.$divider)) {
108 | return;
109 | }
110 | if (!angular.isObject(cachedLabels[list.alias])) {
111 | cachedLabels[list.alias] = {};
112 | }
113 | var source = angular.copy(list.cached);
114 | var result;
115 | if (!angular.isArray(source)) {
116 | result = [];
117 | if (angular.isObject(source)) {
118 | angular.forEach(source, function (value, property) {
119 | result.push({
120 | label: value,
121 | value: property
122 | });
123 | });
124 | result.sort(function (first, second) {
125 | return first.label < second.label ? -1 : 1;
126 | });
127 | } else {
128 | result.push({
129 | label: source,
130 | value: source
131 | });
132 | }
133 | } else {
134 | result = source;
135 | }
136 | source = result;
137 | result = [];
138 | angular.forEach(source, function (item, index) {
139 | item.index = index;
140 | if (angular.isObject(item) && angular.isDefined(item.label) && angular.isDefined(item.value)) {
141 | result.push(item);
142 | } else {
143 | result.push({
144 | label: item,
145 | value: item
146 | });
147 | }
148 | result[result.length - 1].label = String(result[result.length - 1].label);
149 | });
150 | var needsRefresh = false;
151 | var longest = 0;
152 | var longestText = "";
153 | if (list.strictMatching) {
154 | angular.forEach(result, function (item) {
155 | if (needsRefresh || !angular.isDefined(cachedLabels[list.alias][item.label])) {
156 | needsRefresh = true;
157 | }
158 | });
159 | } else {
160 | angular.forEach(result, function (item) {
161 | if (needsRefresh) {
162 | return;
163 | }
164 | var strippedLabel = item.label.replace(/<[^>]+>/g, "");
165 | if (!angular.isDefined(cachedLabels[list.alias][item.label]) && cachedLabels[list.alias].$$longestText && cachedLabels[list.alias].$$longestText.length < strippedLabel.length) {
166 | needsRefresh = true;
167 | }
168 | });
169 | if (!needsRefresh) {
170 | cachedLabels[list.alias] = {};
171 | angular.forEach(result, function (item) {
172 | cachedLabels[list.alias][item.label] = true;
173 | });
174 | }
175 | }
176 | if (needsRefresh) {
177 | cachedLabels[list.alias] = {};
178 | //let's figure out the length of the texts
179 | angular.forEach(result, function (item) {
180 | var label = item.label;
181 | cachedLabels[list.alias][label] = true;
182 | var strippedLabel = label.replace(/<[^>]+>/g, "");
183 | if (strippedLabel.length > longestText.length) {
184 | longestText = strippedLabel;
185 | var element = document.createElement("div");
186 | element.innerHTML = label;
187 | element.className = "sandbox-item";
188 | element.style.backgroundColor = "red";
189 | $element.append(element);
190 | longest = Math.max(element.offsetWidth, longest);
191 | element.parentNode.removeChild(element);
192 | }
193 | });
194 | } else {
195 | longest = list.width;
196 | }
197 | cachedLabels[list.alias].$$longestText = longestText;
198 | list.width = longest;
199 | list.array = result;
200 | list.selected = 0;
201 | });
202 | if (angular.isObject($scope.model)) {
203 | //first, let's see if the model has all the required properties
204 | angular.forEach($scope.items, function (list) {
205 | if (list.$divider) {
206 | return;
207 | }
208 | if (angular.isUndefined($scope.model[list.alias])) {
209 | $scope.model[list.alias] = list.array.length ? list.array[0].value : null;
210 | }
211 | });
212 | //now, let's figure out which one is selected
213 | angular.forEach($scope.items, function (list) {
214 | if (list.$divider) {
215 | return;
216 | }
217 | list.selected = -1;
218 | angular.forEach(list.array, function (item, index) {
219 | if (list.selected > -1) {
220 | return;
221 | }
222 | if (item.value == $scope.model[list.alias]) {
223 | list.selected = index;
224 | }
225 | });
226 | if (list.selected == -1) {
227 | //if the current selection is invalid, we will choose the first
228 | $scope.model[list.alias] = list.array.length ? list.array[0].value : null;
229 | list.selected = 0;
230 | }
231 | $scope.$broadcast('protonMultiListPicker:selected', list);
232 | });
233 | }
234 | });
235 | $scope.$on('protonMultiListPicker:selected', function (event, list) {
236 | if (!angular.isArray(list.array)) {
237 | return;
238 | }
239 | list.view = [];
240 | var from = Math.max(list.selected - 3, 0);
241 | var to = Math.min(list.array.length - 1, list.selected + 3);
242 | var i;
243 | var item;
244 | for (i = from; i <= to; i++) {
245 | item = angular.copy(list.array[i]);
246 | item.index = i;
247 | list.view.push(item);
248 | }
249 | if (list.cycle) {
250 | if (list.view.length < 7 && list.array.length > 0) {
251 | var pivot = list.selected - from;
252 | var cursor;
253 | //we need to add stuff to the beginning of the list, until the selected index is in the middle
254 | cursor = list.array.length - 1;
255 | while (pivot < 3) {
256 | var copy = angular.copy(list.array[cursor]);
257 | if (!copy) {
258 | console.log(cursor);
259 | }
260 | copy.index = cursor;
261 | list.view.splice(0, 0, copy);
262 | cursor--;
263 | pivot++;
264 | if (cursor < 0) {
265 | cursor = list.array.length - 1;
266 | }
267 | }
268 | //if after padding from the left, the view is still short of 7 items, we need to pad from the right
269 | cursor = 0;
270 | while (list.view.length < 7) {
271 | item = angular.copy(list.array[cursor]);
272 | item.index = cursor;
273 | list.view.push(item);
274 | cursor++;
275 | if (cursor == list.array.length) {
276 | cursor = 0;
277 | }
278 | }
279 | }
280 | if (list.view.length > 0) {
281 | var cycleIndex = list.view[3].index - 3;
282 | angular.forEach(list.view, function (item) {
283 | item.cycleIndex = cycleIndex ++;
284 | });
285 | }
286 | }
287 | });
288 | (function () {
289 | var list;
290 | var $list;
291 | var next = function () {
292 | list.selected++;
293 | if (list.selected == list.array.length) {
294 | if (list.cycle) {
295 | list.selected = 0;
296 | } else {
297 | list.selected = list.array.length - 1;
298 | }
299 | }
300 | };
301 | var previous = function () {
302 | list.selected--;
303 | if (list.selected < 0) {
304 | if (list.cycle) {
305 | list.selected = list.array.length - 1;
306 | } else {
307 | list.selected = 0;
308 | }
309 | }
310 | };
311 | function setSelection(event) {
312 | var direction = -1 * Math.sign(event.speedY);
313 | if (direction < 0) {
314 | previous();
315 | } else {
316 | next();
317 | }
318 | $scope.select(list, list.selected);
319 | }
320 | var listElement = function (e) {
321 | var element = angular.element(e);
322 | while (element.length && !element.hasClass('list-container')) {
323 | element = element.parent();
324 | }
325 | return element;
326 | };
327 | $scope.motionStart = function (event) {
328 | $list = listElement(event.target);
329 | var listIndex = $list.attr('data-index');
330 | list = $scope.items[listIndex];
331 | event.checkpoint();
332 | };
333 |
334 | $scope.motionChange = function (event) {
335 | if (!$list) {
336 | return;
337 | }
338 | var element = $list[0];
339 | if (!element) {
340 | return;
341 | }
342 | var last = event.last();
343 | element = element.getElementsByTagName('li')[0];
344 | if (!element) {
345 | return;
346 | }
347 | var threshold = element.offsetHeight;
348 | if (Math.abs(last.y - event.y) >= threshold) {
349 | event.checkpoint();
350 | } else {
351 | return;
352 | }
353 | setSelection(event);
354 | };
355 | $scope.motionEnd = function (event) {
356 | if (!$list || !$list.length) {
357 | return;
358 | }
359 | event.slowDown(0.0001, Math.min(Math.floor(Math.abs(event.speedY) / 0.3), 8), function () {
360 | setSelection(event);
361 | });
362 | };
363 | })();
364 | $scope.select = function (list, index) {
365 | if (angular.isObject($scope.model)) {
366 | $scope.model[list.alias] = list.array.length ? list.array[index].value : null;
367 | }
368 | var previous = list.selected;
369 | list.selected = index;
370 | $scope.$broadcast('protonMultiListPicker:selected', list);
371 | $scope.$emit('protonMultiListPicker:selected', list, index, previous);
372 | };
373 | }
374 | };
375 | }]);
376 | module.directive('protonMultiListPickerList', [function () {
377 | var controller = function ProtonMultiListPickerListController($scope) {
378 | $scope.items = [];
379 | this.add = function (item) {
380 | $scope.items.push(item);
381 | };
382 | };
383 | return {
384 | scope: {
385 | source: "&?",
386 | static: '@?',
387 | alias: "@?",
388 | cycle: "@?",
389 | strictMatching: "@?"
390 | },
391 | restrict: "E",
392 | require: "^protonMultiListPicker",
393 | controller: ["$scope", controller],
394 | link: function ($scope, $element, $attrs, parent) {
395 | parent.add({
396 | source: function () {
397 | return $scope.source ? $scope.source() : $scope.items;
398 | },
399 | alias: $scope.alias,
400 | cycle: $scope.cycle == "true",
401 | static: !$scope.source || $scope.static != "false",
402 | strictMatching: $scope.strictMatching == "true"
403 | });
404 | }
405 | };
406 | }]);
407 | module.directive('protonMultiListPickerDivider', [function () {
408 | return {
409 | restrict: "E",
410 | require: "^protonMultiListPicker",
411 | link: function ($scope, $element, $attrs, parentController) {
412 | parentController.addDivider(($element.html() || "").trim());
413 | }
414 | };
415 | }]);
416 | module.directive('protonMultiListPickerListItem', [function () {
417 | return {
418 | restrict: "E",
419 | require: "^protonMultiListPickerList",
420 | link: function ($scope, $element, $attrs, parent) {
421 | parent.add({
422 | value: $attrs.value || $element.html(),
423 | label: $element.html()
424 | });
425 | }
426 | };
427 | }]);
428 | module.service('protonMultiListMomentum', ["$timeout", function ($timeout) {
429 | var details = function (event) {
430 | var description = {
431 | event: event,
432 | type: event.touches ? "Touch" : "Mouse",
433 | x: event.touches ? event.touches[0].screenX : event.screenX,
434 | y: event.touches ? event.touches[0].screenY : event.screenY,
435 | target: event.touches ? event.touches[0].target : event.target,
436 | timestamp: Date.now(),
437 | xDisplacement: 0,
438 | yDisplacement: 0,
439 | speedX: 0,
440 | speedY: 0,
441 | speedXState: 'stopped',
442 | speedYState: 'stopped',
443 | accelerationX: 0,
444 | accelerationY: 0,
445 | speed: 0,
446 | acceleration: 0,
447 | angle: NaN,
448 | origin: coordinates ? (coordinates.origin ? coordinates.origin : coordinates) : null,
449 | remembered: coordinates ? (coordinates.remembered ? coordinates.remembered : null) : null
450 | };
451 | description.checkpoint = function () {
452 | description.remembered = description;
453 | };
454 | description.last = function () {
455 | return description.remembered;
456 | };
457 | description.slowDown = function (acceleration, steps, callback) {
458 | var accelerationX = -1 * Math.sign(description.speedX) * Math.abs(acceleration);
459 | var accelerationY = -1 * Math.sign(description.speedY) * Math.abs(acceleration);
460 | var xIntervals = Math.abs(description.speedX * 10 / accelerationX / steps);
461 | var yIntervals = Math.abs(description.speedY * 10 / accelerationY / steps);
462 | var intervals = Math.max(xIntervals, yIntervals) / 1000;
463 | var step = 0;
464 | (function proceed(delay) {
465 | if (isNaN(delay)) {
466 | return;
467 | }
468 | $timeout(function () {
469 | if (angular.isFunction(callback)) {
470 | callback(step);
471 | }
472 | step++;
473 | if (step < steps) {
474 | proceed(delay * 1.1);
475 | }
476 | }, delay);
477 | })(intervals);
478 | };
479 | return description;
480 | };
481 | var coordinates = null;
482 | this.start = function (event, callback) {
483 | if (event.touches && event.touches.length > 1) {
484 | return;
485 | }
486 | coordinates = details(event);
487 | if (angular.isFunction(callback)) {
488 | callback(coordinates);
489 | }
490 | };
491 | this.move = function (event, callback) {
492 | if (coordinates === null) {
493 | return;
494 | }
495 | var newCoordinates = details(event);
496 | var timeDifference = newCoordinates.timestamp - coordinates.timestamp;
497 | var xDifference = newCoordinates.x - coordinates.x;
498 | var yDifference = newCoordinates.y - coordinates.y;
499 | newCoordinates.xDisplacement = xDifference;
500 | newCoordinates.yDisplacement = yDifference;
501 | newCoordinates.angle = Math.atan2(newCoordinates.x, newCoordinates.y);
502 | if (timeDifference == 0) {
503 | if (xDifference == 0) {
504 | newCoordinates.speedX = 0;
505 | newCoordinates.speedXState = 'stopped';
506 | } else {
507 | newCoordinates.speedX = Math.sign(xDifference) * Infinity;
508 | newCoordinates.speedXState = 'fast';
509 | }
510 | if (yDifference == 0) {
511 | newCoordinates.speedY = 0;
512 | newCoordinates.speedYState = 'stopped';
513 | } else {
514 | newCoordinates.speedY = Math.sign(yDifference) * Infinity;
515 | newCoordinates.speedYState = 'fast';
516 | }
517 | } else {
518 | newCoordinates.speedX = xDifference / timeDifference;
519 | newCoordinates.speedY = yDifference / timeDifference;
520 | if (Math.abs(newCoordinates.speedX) < 0.2) {
521 | newCoordinates.speedXState = 'slow';
522 | } else if (Math.abs(newCoordinates.speedX) < 0.6) {
523 | newCoordinates.speedXState = 'moderate';
524 | } else {
525 | newCoordinates.speedXState = 'fast';
526 | }
527 | if (Math.abs(newCoordinates.speedY) < 0.2) {
528 | newCoordinates.speedYState = 'slow';
529 | } else if (Math.abs(newCoordinates.speedY) < 0.6) {
530 | newCoordinates.speedYState = 'moderate';
531 | } else {
532 | newCoordinates.speedYState = 'fast';
533 | }
534 | }
535 | var speedXDifference = newCoordinates.speedX - coordinates.speedX;
536 | var speedYDifference = newCoordinates.speedY - coordinates.speedY;
537 | if (timeDifference == 0) {
538 | if (speedXDifference == 0) {
539 | newCoordinates.accelerationX = 0;
540 | } else {
541 | newCoordinates.accelerationX = Math.sign(speedXDifference) * Infinity;
542 | }
543 | if (speedYDifference == 0) {
544 | newCoordinates.accelerationY = 0;
545 | } else {
546 | newCoordinates.accelerationY = Math.sign(speedYDifference) * Infinity;
547 | }
548 | } else {
549 | newCoordinates.accelerationX = speedXDifference / timeDifference;
550 | newCoordinates.accelerationY = speedYDifference / timeDifference;
551 | }
552 | newCoordinates.speed = Math.sqrt(Math.pow(newCoordinates.speedX, 2) + Math.pow(newCoordinates.speedY, 2));
553 | newCoordinates.acceleration = Math.sqrt(Math.pow(newCoordinates.accelerationX, 2) + Math.pow(newCoordinates.accelerationY, 2));
554 | coordinates = newCoordinates;
555 | if (angular.isFunction(callback)) {
556 | callback(coordinates);
557 | }
558 | };
559 | this.end = function (event, callback) {
560 | if (coordinates === null) {
561 | return;
562 | }
563 | if (angular.isFunction(callback)) {
564 | callback(coordinates);
565 | }
566 | coordinates = null;
567 | };
568 | }]);
569 | module.directive('protonMultiListMotion', ["protonMultiListMomentum", "$parse", function (protonMultiListMomentum, $parse) {
570 | return {
571 | restrict: "A",
572 | link: function ($scope, $element, $attrs) {
573 | var move = $attrs.motionChange && $parse($attrs.motionChange);
574 | var end = $attrs.motionEnd && $parse($attrs.motionEnd);
575 | var start = $attrs.motionStart && $parse($attrs.motionStart);
576 | $element.on("mousedown touchstart", function (event) {
577 | protonMultiListMomentum.start(event, function (event) {
578 | $scope.$apply(function () {
579 | start && start($scope, {
580 | $event: event
581 | });
582 | });
583 | });
584 | });
585 | $element.on("mousemove touchmove", function (event) {
586 | protonMultiListMomentum.move(event, function (event) {
587 | $scope.$apply(function () {
588 | move && move($scope, {
589 | $event: event
590 | });
591 | });
592 | });
593 | });
594 | $element.on("mouseup touchend touchcancel", function (event) {
595 | protonMultiListMomentum.end(event, function (event) {
596 | $scope.$apply(function () {
597 | end && end($scope, {
598 | $event: event
599 | });
600 | });
601 | });
602 | });
603 | }
604 | };
605 | }]);
606 | })();
--------------------------------------------------------------------------------
/src/proton.multi-list-picker.scss:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Milad Naseri
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | * software and associated documentation files (the "Software"), to deal in the Software
6 | * without restriction, including without limitation the rights to use, copy, modify, merge,
7 | * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
8 | * to whom the Software is furnished to do so, subject to the following conditions:
9 | *
10 | * The above copyright notice and this permission notice shall be included in all copies or
11 | * substantial portions of the Software.
12 | *
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
14 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
15 | * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
16 | * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
17 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18 | * SOFTWARE.
19 | */
20 |
21 | .proton-multi-list-picker {
22 |
23 | .toolbar {
24 | display: none;
25 | }
26 |
27 | & {
28 | display: block;
29 | font-size: 1em;
30 | font-family: "Helvetica Neue", Helvetica, "Open Sans", sans-serif;
31 | font-weight: 200;
32 | text-outline: none;
33 | -webkit-touch-callout: none; /* iOS Safari */
34 | -webkit-user-select: none; /* Chrome/Safari/Opera */
35 | -khtml-user-select: none; /* Konqueror */
36 | -moz-user-select: none; /* Firefox */
37 | -ms-user-select: none; /* Internet Explorer/Edge */
38 | user-select: none; /* Non-prefixed version, currently not supported by any browser */
39 | box-sizing: border-box;
40 | }
41 |
42 | .sandbox-item {
43 | position: relative;
44 | top: -1.8em;
45 | display: inline-block;
46 | margin: 0 5px;
47 | height: 1em;
48 | padding: 0.2em 0.5em;
49 | text-align: center;
50 | visibility: hidden;
51 | }
52 |
53 | .proton-multi-list-picker-contents {
54 | display: none;
55 | }
56 |
57 | .container {
58 |
59 | .lists {
60 |
61 | & {
62 | display: inline-block;
63 | margin: 0 auto;
64 | padding: 0;
65 | border: none;
66 | }
67 |
68 | .list-container {
69 |
70 | & {
71 | display: inline-block;
72 | vertical-align: top;
73 | margin: 0;
74 | padding: 0;
75 | }
76 |
77 | &.transitioning {
78 |
79 | &.one-up {
80 | .item {
81 | -webkit-animation: moveUp 0.1s;
82 | -o-animation: moveUp 0.1s;
83 | animation: moveUp 0.1s;
84 | }
85 | }
86 |
87 | &.one-down {
88 | .item {
89 | -webkit-animation: moveDown 0.1s;
90 | -o-animation: moveDown 0.1s;
91 | animation: moveDown 0.1s;
92 | }
93 | }
94 |
95 | }
96 |
97 | &.first {
98 | border-left: 1px solid #e9e9e9;
99 | }
100 |
101 | &.last {
102 | border-right: 1px solid #e9e9e9;
103 | }
104 |
105 | .list {
106 |
107 | & {
108 | border-top: 1px solid #e9e9e9;
109 | border-bottom: 1px solid #e9e9e9;
110 | height: 7.4em;
111 | overflow: hidden;
112 | display: block;
113 | padding: 0;
114 | margin: 0;
115 | min-width: 1.5em;
116 | }
117 |
118 | .item {
119 |
120 | & {
121 | position: relative;
122 | top: -1.8em;
123 | display: block;
124 | margin: 0 5px;
125 | height: 1em;
126 | padding: 0.2em 0.5em;
127 | text-align: center;
128 | }
129 |
130 | &.selected {
131 | margin-top: 0.4em;
132 | margin-bottom: 0.4em;
133 | font-weight: 300;
134 | }
135 |
136 | &.distance-1 {
137 | opacity: 0.6;
138 | transform : scale(1,0.7);
139 | -webkit-transform:scale(1,0.7); /* Safari and Chrome */
140 | -moz-transform:scale(1,0.7); /* Firefox */
141 | -ms-transform:scale(1,0.7); /* IE 9+ */
142 | -o-transform:scale(1,0.7); /* Opera */
143 | }
144 |
145 | &.distance-2 {
146 | opacity: 0.3;
147 | transform : scale(0.95,0.4);
148 | -webkit-transform:scale(0.95,0.4); /* Safari and Chrome */
149 | -moz-transform:scale(0.95,0.4); /* Firefox */
150 | -ms-transform:scale(0.95,0.4); /* IE 9+ */
151 | -o-transform:scale(0.95,0.4); /* Opera */
152 | }
153 |
154 | &.distance-3 {
155 | opacity: 0.1;
156 | transform : scale(1,0.1);
157 | -webkit-transform:scale(1,0.1); /* Safari and Chrome */
158 | -moz-transform:scale(1,0.1); /* Firefox */
159 | -ms-transform:scale(1,0.1); /* IE 9+ */
160 | -o-transform:scale(1,0.1); /* Opera */
161 | }
162 |
163 | &.offset-down-3 {
164 | top: 2.4em;
165 | }
166 |
167 | &.offset-down-2 {
168 | top: 1em;
169 | }
170 |
171 | &.offset-down-1 {
172 | top: -0.4em;
173 | }
174 |
175 | }
176 |
177 | }
178 |
179 | .divider {
180 |
181 | & {
182 | display: block;
183 | padding: 0 1em 0 0.3em;
184 | margin: 0;
185 | font-weight: 600;
186 | border-top: 1px solid #e9e9e9;
187 | border-bottom: 1px solid #e9e9e9;
188 | height: 7.4em;
189 | overflow: hidden;
190 | text-align: center;
191 | }
192 |
193 | &.blank {
194 | border-top: none;
195 | border-bottom: none;
196 | border-left: 1px solid #e9e9e9;
197 | border-right: 1px solid #e9e9e9;
198 | }
199 |
200 | span {
201 | top: 2.8em;
202 | position: relative;
203 | }
204 |
205 | }
206 |
207 | }
208 |
209 | }
210 |
211 | .before-lists {
212 | display: block;
213 | height: 1.4em;
214 | border-top: 1px solid #d6d6d6;
215 | border-bottom: 1px solid #d6d6d6;
216 | background-color: #eaeaea;
217 | position: relative;
218 | top: 3em;
219 | margin-bottom: -1.4em;
220 | opacity: 0.4;
221 | }
222 |
223 | .after-lists {
224 | display: none;
225 |
226 | }
227 |
228 | }
229 |
230 | &.inline {
231 | margin-top: 5px;
232 | margin-bottom: 5px;
233 | }
234 |
235 | &.modal, &.top, &.bottom {
236 |
237 | .container {
238 |
239 | & {
240 | position: absolute;
241 | left: 50%;
242 | top: 50%;
243 | }
244 |
245 | .lists {
246 |
247 | & {
248 | position: relative;
249 | left: -50%;
250 | top: -3.8em;
251 | white-space: nowrap;
252 | height: 9.1em;
253 | background: #ffffff;
254 | }
255 |
256 | .toolbar {
257 |
258 | & {
259 | display: block;
260 | height: 1em;
261 | padding: .25em;
262 | text-align: right;
263 | background: #ededed;
264 | }
265 |
266 | .toolbar-button {
267 | color: #6495ed;
268 | font-weight: 400;
269 | font-size: 1em;
270 | line-height: 1em;
271 | cursor: pointer;
272 | }
273 |
274 | }
275 |
276 | }
277 | }
278 |
279 | .before {
280 | position: absolute;
281 | background: #000;
282 | left: 0;
283 | right: 0;
284 | top: 0;
285 | bottom: 0;
286 | opacity: 0.4;
287 | }
288 |
289 | }
290 |
291 | &.top {
292 |
293 | .container {
294 | top: -2px;
295 |
296 | .lists {
297 | top: 0;
298 |
299 | .list {
300 | position: relative;
301 | }
302 |
303 | }
304 |
305 | }
306 |
307 | .toolbar {
308 | display: block;
309 | position: relative;
310 | margin-bottom: -1.7em;
311 | top: 7.6em;
312 | }
313 |
314 | }
315 |
316 | &.bottom {
317 |
318 | .container {
319 |
320 | top: 100%;
321 |
322 | .lists {
323 |
324 | top: -9em;
325 |
326 | .list {
327 | position: relative;
328 | }
329 |
330 | }
331 |
332 | }
333 |
334 | }
335 |
336 | &.modal {
337 |
338 | .toolbar {
339 | top: -1.4em;
340 | }
341 |
342 | }
343 |
344 | }
--------------------------------------------------------------------------------
/src/proton.multi-list-picker.xsd:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 | This is the root element for defining a multi-list picker
12 | component.
13 |
14 |
15 |
16 |
17 |
18 |
19 | A divider element between lists. The content of the
20 | element will be picked up and placed in the component. If bindHtml is set to
21 | true, it will be treated as HTML content.
22 |
23 |
24 |
25 |
26 |
27 |
28 | Let's you bind the value of this selector to an arbitrary
29 | model in your scope. See documentation for ngModel in the AngularJS documentation for
30 | more information.
31 |
32 |
33 |
34 |
35 |
36 | To use HTML binding, you will need to add a dependency
37 | to ngSanitize to your module. If you do not have that dependency set up properly,
38 | you will face exceptions when list items are being rendered
39 |
40 |
41 |
42 |
43 | This is used to carry out an action when the
44 | Done link is clicked. This link is not available in the inline
45 | attachment mode.
46 |
47 |
48 |
49 |
50 |
51 |
52 | Let's you define a list for the multi-list picker component.
53 |
54 |
55 |
56 |
57 |
58 |
59 | The property to which the value of this particular
60 | list should be bound. This effectively refers to a specific property in the model object
61 | as specified by ngModel on the list picker. If no alias is provided the numerical
62 | index of this element is chosen.
63 |
64 |
65 |
66 |
67 | Let's you choose a data provider for the list. If none
68 | is provided, we will expect to see a list of protonMultiListPickerListItems.
69 |
70 |
71 |
72 |
73 | If set to true the source
74 | provider will be only consulted once, This will improve the performance of the
75 | list picker.
76 |
77 |
78 |
79 |
80 | Whether or not the values of this list should form a cycle
81 | and create a continuous scroll effect
82 |
83 |
84 |
85 |
86 | You should set this to true if you are experiencing
87 | issues with the width of the list picker changing during scrolling.
88 |
89 |
90 |
91 |
92 |
93 |
94 | This attribute will let you define the attachment type of
95 | the component to the screen. Possible values are:
96 |
97 |
modal: the component will be displayed as a modal dialog with appropriate close buttons
98 |
inline: the component will be displayed as an inline block
99 |
bottom: the component will be affixed to the bottom of the screen
100 |
top: the component will be affixed to the top of the screen
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | This element let's you define a single list item. The
115 | contents of this tag will be treated as the caption for the item, and if bindHtml
116 | is set to true it will be treated as HTML. You will need to specify a value
117 | attribute if you want to have values that are different from the inline HTML value of the tag.
118 |
119 |
120 |
121 |
122 |
123 |
124 | Let's you define a value for this item that is different
125 | from the inline tag content. When this item is picked, this is the value that will be set on the
126 | model property.
127 |
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------